|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // ネットワークのホストデバイス
9: //
10:
11: // ログ名
12: // Ethernet HostDevice NetDriver
13: // | | |
14: // v v v
15: // Lance -> HostNetDevice -> NetDriver***
16: // 表示名 (Lance) (HostNet) (HostNet.***)
17: // 識別名 "lance" "hostnet" "hostnet"
18: //
19: // HostNet は 1VM あたり高々1つなので COMDriver のように同じインスタンス間で
20: // 互いを区別する必要がないのと、NetDriver 派生クラス選択時はフォールバックも
21: // 含めて、どれが選択されるか複雑なこともあるのでログ表示名に自分のドライバ名
22: // も表示したい。
23:
24: #include "hostnet.h"
25: #include "config.h"
26: #include "netdriver_none.h"
27: #if defined(HAVE_HOSTNET_AFPACKET)
28: #include "netdriver_afpacket.h"
29: #endif
30: #if defined(HAVE_HOSTNET_BPF)
31: #include "netdriver_bpf.h"
32: #endif
33: #if defined(HAVE_HOSTNET_TAP)
34: #include "netdriver_tap.h"
35: #endif
36:
37: // コンパイル済みのドライバ名一覧を返す (MainApp から呼ばれる)
38: /*static*/ std::vector<std::string>
39: HostNetDevice::GetDrivers()
40: {
41: std::vector<std::string> list;
42:
43: #if defined(HAVE_HOSTNET_AFPACKET)
44: list.emplace_back("afpacket");
45: #endif
46: #if defined(HAVE_HOSTNET_BPF)
47: list.emplace_back("bpf");
48: #endif
49: #if defined(HAVE_HOSTNET_TAP)
50: list.emplace_back("tap");
51: #endif
52:
53: return list;
54: }
55:
56:
57: // コンストラクタ
58: HostNetDevice::HostNetDevice()
59: : inherited("HostNet")
60: {
61: monitor.func = ToMonitorCallback(&HostNetDevice::MonitorUpdate);
62: monitor.SetSize(48, 20);
63: monitor.Regist(ID_MONITOR_HOSTNET);
64: }
65:
66: // デストラクタ
67: HostNetDevice::~HostNetDevice()
68: {
69: }
70:
71: // ログレベル設定
72: void
73: HostNetDevice::SetLogLevel(int loglevel_)
74: {
75: inherited::SetLogLevel(loglevel_);
76:
77: if ((bool)driver) {
78: driver->SetLogLevel(loglevel_);
79: }
80: }
81:
82: // 初期化
83: bool
84: HostNetDevice::Init()
85: {
86: if (inherited::Init() == false) {
87: return false;
88: }
89:
90: if (SelectDriver() == false) {
91: return false;
92: }
93:
94: return true;
95: }
96:
97: // ドライバ(再)選択
98: bool
99: HostNetDevice::SelectDriver()
100: {
101: const ConfigItem& item = gConfig->Find("hostnet-driver");
102: const std::string& type = item.AsString();
103: bool fallback = gConfig->Find("hostnet-fallback").AsInt();
104:
105: driver.reset();
106:
107: if (type != "none") {
108: if (type == "tap") {
109: #if defined(HAVE_HOSTNET_TAP)
110: CreateTap();
111: #else
112: item.Err("hostnet driver %s not compiled", type.c_str());
113: #endif
114:
115: } else if (type == "bpf") {
116: #if defined(HAVE_HOSTNET_BPF)
117: CreateBPF();
118: #else
119: item.Err("hostnet driver %s not compiled", type.c_str());
120: #endif
121:
122: } else if (type == "afpacket") {
123: #if defined(HAVE_HOSTNET_AFPACKET)
124: CreateAFPacket();
125: #else
126: item.Err("hostnet driver %s not compiled", type.c_str());
127: #endif
128:
129: } else if (type == "auto") {
130: // auto なら Tap -> { BPF, AFPacket } の順で試す。
131: //
132: // Tap は事前の準備が面倒だが制約がない。
133: // BPF, AFPacket (この2つは OS の違いだけでほぼ同じもの) は
134: // 準備に手間がかからないが自ホストと通信できない制約がある。
135: // そのためどの環境でも Tap が使えれば Tap を、そうでなければ
136: // BPF, AFPacket を使う、という順にしておく。
137: bool r;
138:
139: r = CreateTap();
140: if (r == false) {
141: r = CreateAFPacket();
142: }
143: if (r == false) {
144: r = CreateBPF();
145: }
146:
147: } else {
148: // 知らないドライバ種別
149: item.Err();
150: }
151:
152: if ((bool)driver == false) {
153: putmsg(1, "hostnet-fallback=%d", fallback ? 1 : 0);
154:
155: if (fallback == false) {
156: // フォールバックしないならエラー終了
157: errmsg = "No host network driver found";
158: putmsg(1, "%s", errmsg.c_str());
159: warnx("%s (See details with options -C -Lhostnet=1)",
160: errmsg.c_str());
161:
162: return false;
163: }
164: }
165: }
166:
167: if ((bool)driver == false) {
168: CreateNone();
169: }
170: assert((bool)driver);
171:
172: // ドライバ名は Capitalize だがログはほぼ小文字なので雰囲気を揃える…
173: putmsg(1, "selected host driver: %s",
174: string_tolower(driver->GetDriverName()).c_str());
175:
176: return true;
177: }
178:
179: // None ドライバを生成する
180: bool
181: HostNetDevice::CreateNone()
182: {
183: driver.reset(new NetDriverNone(this));
184: if ((bool)driver) {
185: if (driver->InitDriver()) {
186: // 成功
187: return true;
188: }
189: errmsg = driver->errmsg;
190: driver.reset();
191: }
192: return false;
193: }
194:
195: // Tap ドライバを生成する
196: bool
197: HostNetDevice::CreateTap()
198: {
199: #if defined(HAVE_HOSTNET_TAP)
200: const ConfigItem& item = gConfig->Find("hostnet-tap-devpath");
201: const std::string& devpath = item.AsString();
202:
203: driver.reset(new NetDriverTap(this, devpath));
204: if ((bool)driver) {
205: if (driver->InitDriver()) {
206: // 成功
207: return true;
208: }
209: errmsg = driver->errmsg;
210: driver.reset();
211: }
212: #endif
213: return false;
214: }
215:
216: // bpf ドライバを生成する
217: bool
218: HostNetDevice::CreateBPF()
219: {
220: #if defined(HAVE_HOSTNET_BPF)
221: const ConfigItem& item = gConfig->Find("hostnet-bpf-ifname");
222: const std::string& ifname = item.AsString();
223:
224: driver.reset(new NetDriverBPF(this, ifname));
225: if ((bool)driver) {
226: if (driver->InitDriver()) {
227: // 成功
228: return true;
229: }
230: errmsg = driver->errmsg;
231: driver.reset();
232: }
233: #endif
234: return false;
235: }
236:
237: // AFPacket ドライバを生成する
238: bool
239: HostNetDevice::CreateAFPacket()
240: {
241: #if defined(HAVE_HOSTNET_AFPACKET)
242: const ConfigItem& item = gConfig->Find("hostnet-afpacket-ifname");
243: const std::string& ifname = item.AsString();
244:
245: driver.reset(new NetDriverAFPacket(this, ifname));
246: if ((bool)driver) {
247: if (driver->InitDriver()) {
248: // 成功
249: return true;
250: }
251: errmsg = driver->errmsg;
252: driver.reset();
253: }
254: #endif
255: return false;
256: }
257:
258: // VM からの送信 (VM スレッドで呼ばれる)
259: bool
260: HostNetDevice::Tx(const NetPacket& packet)
261: {
262: // 送信キューに入れて..
263: if (txq.Enqueue(packet) == false) {
264: stat.txqfull_pkts++;
265: stat.txqfull_bytes += packet.length;
266: putlog(2, "txq exhausted");
267: return false;
268: }
269:
270: stat.tx_pkts++;
271: stat.tx_bytes += packet.length;
272:
273: // ざっくりピーク値
274: stat.txq_peak = std::max((int)txq.Length(), stat.txq_peak);
275:
276: // パイプに通知 (値はダミー)
277: return WritePipe(0);
278: }
279:
280: // rxq への投入を行う場合は true
281: void
282: HostNetDevice::EnableRx(bool enable)
283: {
284: rx_enable = enable;
285: }
286:
287: // 自身の MAC アドレスを設定する。
288: void
289: HostNetDevice::SetMyAddr(const macaddr_t& myaddr_)
290: {
291: myaddr = myaddr_;
292: }
293:
294: // プロミスキャスモードを設定する。
295: void
296: HostNetDevice::SetPromisc(bool promisc_)
297: {
298: promisc = promisc_;
299: }
300:
301: // パケットをキューから取り出す。
302: // VM 側から呼ばれる。
303: bool
304: HostNetDevice::Rx(NetPacket *dst)
305: {
306: if (rxq.Dequeue(dst) == false) {
307: // キューが空
308: return false;
309: }
310: stat.rx_pkts++;
311: stat.rx_bytes += dst->length;
312: putlog(2, "Recv %d bytes", dst->length);
313:
314: return true;
315: }
316:
317: // 外部からの読み込み (パケットを受信)。
318: // 戻り値はキューに投入したパケット数。
319: int
320: HostNetDevice::Read()
321: {
322: int queued = 0;
323: int left = 0;
324:
325: assert((bool)driver);
326:
327: do {
328: NetPacket *p;
329:
330: p = rxq.BeginWrite();
331: if (p == NULL) {
332: NetPacket discard;
333: left = driver->Read(&discard);
334: if (left < 0) {
335: break;
336: }
337: stat.rxqfull_pkts++;
338: stat.rxqfull_bytes += discard.length;
339: continue;
340: }
341:
342: // driver->Read() は VM に投入すべきパケットがなければ負数を返す。
343: // パケットがあれば p に書き戻して、driver 側のバッファに残っている
344: // パケット数 (といっても 0 か 1以上) を返してくる。
345: left = driver->Read(p);
346: if (left < 0) {
347: rxq.CancelWrite();
348: break;
349: }
350:
351: NetPacket& packet = *p;
352:
353: stat.read_pkts++;
354: stat.read_bytes += packet.length;
355:
356: if (rx_enable == false) {
357: rxq.CancelWrite();
358: stat.rxdisable_pkts++;
359: stat.rxdisable_bytes += packet.length;
360: continue;
361: }
362:
363: // イーサネットフレームより長いパケットはゲスト OS が期待していない。
364: // ここでドロップしてみる。
365: // これによりゲストでの dropping chained buffer が出なくなる。
366: if (packet.length > 1518) {
367: rxq.CancelWrite();
368: stat.rxjumbo_pkts++;
369: stat.rxjumbo_bytes += packet.length;
370: continue;
371: }
372:
373: // ホストカーネルから直接着信したパケット(というかフレームか)は
374: // 60バイトパディングされないまま読み出せるので、ここでパディングする。
375: while (packet.length < 60) {
376: packet.Append(0x00);
377: }
378:
379: // パケットフィルタリング
380: if (__predict_true(promisc == false)) {
381: bool filter = false;
382: if (__predict_false(packet[0] & 1)) {
383: // マルチキャスト
384: // XXX いまのところ全部通す
385: #if 0
386: // LANCE では LADRF レジスタ、NE2000 では MAR0-7 レジスタで
387: // データシートを見る限りはロジックは同じものに見える...
388:
389: // 宛先アドレスの CRC32 を計算する
390: uint32 crc = ~crc32(~0U, &packet[0], 6);
391: // crc 上位 6bit をとりだして検査ビット位置とする
392: uint bit = 1 << (crc >> 26);
393: // LADRF,MAR の検査ビット位置が 1 ならば受信、0 ならば破棄
394: if ((multicast_filter & bit) == 0) {
395: filter = true;
396: }
397: #endif
398: } else {
399: // ユニキャスト
400: if (memcmp(&packet[0], &myaddr[0], 6) != 0) {
401: filter = true;
402: }
403: }
404: if (filter) {
405: stat.rxfilter_pkts++;
406: stat.rxfilter_bytes += packet.length;
407: rxq.CancelWrite();
408: continue;
409: }
410: }
411:
412: // CRC XXX 計算すること
413: for (int i = 0; i < 4; i++) {
414: packet.Append(0x00);
415: }
416:
417: rxq.EndWrite();
418: queued++;
419: } while (left > 0);
420:
421: // ざっくりピーク値
422: stat.rxq_peak = std::max((int)rxq.Length(), stat.rxq_peak);
423:
424: return queued;
425: }
426:
427: // 外部への書き出し(パケットを送信)
428: void
429: HostNetDevice::Write(uint32 data)
430: {
431: const NetPacket *p;
432:
433: assert((bool)driver);
434:
435: // 送信キューから取り出す
436: while ((p = txq.BeginRead()) != NULL) {
437: const NetPacket& packet = *p;
438:
439: putlog(2, "Send %d bytes", packet.length);
440: stat.write_pkts++;
441: stat.write_bytes += packet.length;
442:
443: driver->Write(packet.data(), packet.length);
444: txq.EndRead();
445: }
446: }
447:
448: // ドライバ名を返す
449: const std::string
450: HostNetDevice::GetDriverName() const
451: {
452: assert((bool)driver);
453: return driver->GetDriverName();
454: }
455:
456: // モニタ
457: void
458: HostNetDevice::MonitorUpdate(Monitor *, TextScreen& screen)
459: {
460: screen.Clear();
461:
462: screen.Print(0, 0, "HostNet Driver: %s", driver->GetDriverName());
463: // 次の2行はドライバ依存情報
464: driver->MonitorUpdateMD(screen);
465:
466: // 01234567890123456
467: // Write to host
468: // Drop:TxQ Full
469: // Drop:Rx Disabled
470: // Drop:Jumbo Frame
471: // Drop:Addr Filter
472:
473: int y = 4;
474: screen.Print(0, y++, "%-17s%13s %17s", "<Tx>", "Packets", "Bytes");
475: screen.Print(0, y++, "%-17s%13s %17s", "VM sends",
476: format_number(stat.tx_pkts).c_str(),
477: format_number(stat.tx_bytes).c_str());
478: screen.Print(0, y++, "%-17s%13s %17s", "Write to host",
479: format_number(stat.write_pkts).c_str(),
480: format_number(stat.write_bytes).c_str());
481: screen.Print(0, y++, "%-17s%13s %17s", "Drop:TxQ Full",
482: format_number(stat.txqfull_pkts).c_str(),
483: format_number(stat.txqfull_bytes).c_str());
484: y++;
485:
486: screen.Print(0, y++, "%-17s%13s %17s", "<Rx>", "Packets", "Bytes");
487: screen.Print(0, y++, "%-17s%13s %17s", "Read from host",
488: format_number(stat.read_pkts).c_str(),
489: format_number(stat.read_bytes).c_str());
490: screen.Print(0, y++, "%-17s%13s %17s", "VM receives",
491: format_number(stat.rx_pkts).c_str(),
492: format_number(stat.rx_bytes).c_str());
493: screen.Print(0, y++, "%-17s%13s %17s", "Drop:Rx Disabled",
494: format_number(stat.rxdisable_pkts).c_str(),
495: format_number(stat.rxdisable_bytes).c_str());
496: screen.Print(0, y++, "%-17s%13s %17s", "Drop:Jumbo Frame",
497: format_number(stat.rxjumbo_pkts).c_str(),
498: format_number(stat.rxjumbo_bytes).c_str());
499: screen.Print(0, y++, "%-17s%13s %17s", "Drop:RxQ Full",
500: format_number(stat.rxqfull_pkts).c_str(),
501: format_number(stat.rxqfull_bytes).c_str());
502: screen.Print(0, y++, "%-17s%13s %17s", "Drop:Addr Filter",
503: format_number(stat.rxfilter_pkts).c_str(),
504: format_number(stat.rxfilter_bytes).c_str());
505: y++;
506:
507: screen.Print(5, y++, "Capacity Peak");
508: screen.Print(0, y++, "TxQ %4d/%4d %4d",
509: (int)txq.Length(), (int)txq.Capacity(), stat.txq_peak);
510: screen.Print(0, y++, "RxQ %4d/%4d %4d",
511: (int)rxq.Length(), (int)rxq.Capacity(), stat.rxq_peak);
512: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.