Annotation of nono/host/hostnet.cpp, revision 1.1.1.5

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***
1.1.1.3   root       16: // 表示名 (Lance)    (HostNet0)      (HostNet0.***)
                     17: // 識別名 "lance"    "hostnet0"      "hostnet0"
1.1       root       18: //
1.1.1.3   root       19: // HostNet は、特に NetDriver 派生クラス選択時はフォールバックも含めて
                     20: // どれが選択されるか複雑なこともあるので、ログ表示名に自分のドライバ名
1.1       root       21: // も表示したい。
                     22: 
                     23: #include "hostnet.h"
                     24: #include "config.h"
                     25: #include "netdriver_none.h"
                     26: #if defined(HAVE_HOSTNET_AFPACKET)
                     27: #include "netdriver_afpacket.h"
                     28: #endif
                     29: #if defined(HAVE_HOSTNET_BPF)
                     30: #include "netdriver_bpf.h"
                     31: #endif
                     32: #if defined(HAVE_HOSTNET_TAP)
                     33: #include "netdriver_tap.h"
                     34: #endif
1.1.1.4   root       35: #include <zlib.h>
1.1       root       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: // コンストラクタ
1.1.1.5 ! root       58: HostNetDevice::HostNetDevice(Device *parent_, uint n)
1.1.1.3   root       59:        : inherited(parent_, OBJ_HOSTNET(n))
1.1       root       60: {
                     61:        monitor.func = ToMonitorCallback(&HostNetDevice::MonitorUpdate);
1.1.1.5 ! root       62:        monitor.SetSize(48, 23);
1.1.1.3   root       63:        monitor.Regist(ID_MONITOR_HOSTNET(n));
1.1       root       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: {
1.1.1.3   root      101:        int n = GetId() - OBJ_HOSTNET0;
                    102:        const std::string key_driver = string_format("hostnet%d-driver", n);
                    103:        const std::string key_fallback = string_format("hostnet%d-fallback", n);
                    104:        const ConfigItem& item = gConfig->Find(key_driver);
1.1       root      105:        const std::string& type = item.AsString();
1.1.1.3   root      106:        bool fallback = gConfig->Find(key_fallback).AsInt();
1.1       root      107: 
                    108:        driver.reset();
                    109: 
                    110:        if (type != "none") {
                    111:                if (type == "tap") {
                    112: #if defined(HAVE_HOSTNET_TAP)
                    113:                        CreateTap();
                    114: #else
1.1.1.2   root      115:                        item.Err("Hostnet driver %s not compiled", type.c_str());
1.1       root      116: #endif
                    117: 
                    118:                } else if (type == "bpf") {
                    119: #if defined(HAVE_HOSTNET_BPF)
                    120:                        CreateBPF();
                    121: #else
1.1.1.2   root      122:                        item.Err("Hostnet driver %s not compiled", type.c_str());
1.1       root      123: #endif
                    124: 
                    125:                } else if (type == "afpacket") {
                    126: #if defined(HAVE_HOSTNET_AFPACKET)
                    127:                        CreateAFPacket();
                    128: #else
1.1.1.2   root      129:                        item.Err("Hostnet driver %s not compiled", type.c_str());
1.1       root      130: #endif
                    131: 
                    132:                } else if (type == "auto") {
                    133:                        // auto なら Tap -> { BPF, AFPacket } の順で試す。
                    134:                        //
                    135:                        // Tap は事前の準備が面倒だが制約がない。
                    136:                        // BPF, AFPacket (この2つは OS の違いだけでほぼ同じもの) は
                    137:                        // 準備に手間がかからないが自ホストと通信できない制約がある。
                    138:                        // そのためどの環境でも Tap が使えれば Tap を、そうでなければ
                    139:                        // BPF, AFPacket を使う、という順にしておく。
                    140:                        bool r;
                    141: 
                    142:                        r = CreateTap();
                    143:                        if (r == false) {
                    144:                                r = CreateAFPacket();
                    145:                        }
                    146:                        if (r == false) {
                    147:                                r = CreateBPF();
                    148:                        }
                    149: 
                    150:                } else {
                    151:                        // 知らないドライバ種別
                    152:                        item.Err();
                    153:                }
                    154: 
                    155:                if ((bool)driver == false) {
                    156:                        putmsg(1, "hostnet-fallback=%d", fallback ? 1 : 0);
                    157: 
                    158:                        if (fallback == false) {
                    159:                                // フォールバックしないならエラー終了
                    160:                                errmsg = "No host network driver found";
                    161:                                putmsg(1, "%s", errmsg.c_str());
                    162:                                warnx("%s (See details with options -C -Lhostnet=1)",
                    163:                                        errmsg.c_str());
                    164: 
                    165:                                return false;
                    166:                        }
                    167:                }
                    168:        }
                    169: 
                    170:        if ((bool)driver == false) {
                    171:                CreateNone();
                    172:        }
                    173:        assert((bool)driver);
                    174: 
                    175:        // ドライバ名は Capitalize だがログはほぼ小文字なので雰囲気を揃える…
                    176:        putmsg(1, "selected host driver: %s",
                    177:                string_tolower(driver->GetDriverName()).c_str());
                    178: 
                    179:        return true;
                    180: }
                    181: 
                    182: // None ドライバを生成する
                    183: bool
                    184: HostNetDevice::CreateNone()
                    185: {
                    186:        driver.reset(new NetDriverNone(this));
                    187:        if ((bool)driver) {
                    188:                if (driver->InitDriver()) {
                    189:                        // 成功
                    190:                        return true;
                    191:                }
                    192:                errmsg = driver->errmsg;
                    193:                driver.reset();
                    194:        }
                    195:        return false;
                    196: }
                    197: 
                    198: // Tap ドライバを生成する
                    199: bool
                    200: HostNetDevice::CreateTap()
                    201: {
                    202: #if defined(HAVE_HOSTNET_TAP)
1.1.1.3   root      203:        int n = GetId() - OBJ_HOSTNET0;
                    204:        const std::string keyname = string_format("hostnet%d-tap-devpath", n);
                    205:        const ConfigItem& item = gConfig->Find(keyname);
1.1       root      206:        const std::string& devpath = item.AsString();
                    207: 
                    208:        driver.reset(new NetDriverTap(this, devpath));
                    209:        if ((bool)driver) {
                    210:                if (driver->InitDriver()) {
                    211:                        // 成功
                    212:                        return true;
                    213:                }
                    214:                errmsg = driver->errmsg;
                    215:                driver.reset();
                    216:        }
                    217: #endif
                    218:        return false;
                    219: }
                    220: 
                    221: // bpf ドライバを生成する
                    222: bool
                    223: HostNetDevice::CreateBPF()
                    224: {
                    225: #if defined(HAVE_HOSTNET_BPF)
1.1.1.3   root      226:        int n = GetId() - OBJ_HOSTNET0;
                    227:        const std::string keyname = string_format("hostnet%d-bpf-ifname", n);
                    228:        const ConfigItem& item = gConfig->Find(keyname);
1.1       root      229:        const std::string& ifname = item.AsString();
                    230: 
                    231:        driver.reset(new NetDriverBPF(this, ifname));
                    232:        if ((bool)driver) {
                    233:                if (driver->InitDriver()) {
                    234:                        // 成功
                    235:                        return true;
                    236:                }
                    237:                errmsg = driver->errmsg;
                    238:                driver.reset();
                    239:        }
                    240: #endif
                    241:        return false;
                    242: }
                    243: 
                    244: // AFPacket ドライバを生成する
                    245: bool
                    246: HostNetDevice::CreateAFPacket()
                    247: {
                    248: #if defined(HAVE_HOSTNET_AFPACKET)
1.1.1.3   root      249:        int n = GetId() - OBJ_HOSTNET0;
                    250:        const std::string keyname = string_format("hostnet%d-afpacket-ifname", n);
                    251:        const ConfigItem& item = gConfig->Find(keyname);
1.1       root      252:        const std::string& ifname = item.AsString();
                    253: 
                    254:        driver.reset(new NetDriverAFPacket(this, ifname));
                    255:        if ((bool)driver) {
                    256:                if (driver->InitDriver()) {
                    257:                        // 成功
                    258:                        return true;
                    259:                }
                    260:                errmsg = driver->errmsg;
                    261:                driver.reset();
                    262:        }
                    263: #endif
                    264:        return false;
                    265: }
                    266: 
                    267: // VM からの送信 (VM スレッドで呼ばれる)
                    268: bool
                    269: HostNetDevice::Tx(const NetPacket& packet)
                    270: {
1.1.1.5 ! root      271:        // 長さ 0 のパケットは破棄する。バグでもないと通常は起きない。
        !           272:        // runt frame はどうするか?
        !           273:        if (packet.length < 1) {
        !           274:                stat.txzero_pkts++;
        !           275:                putlog(1, "Tx: drop empty packet");
        !           276:                return true;
        !           277:        }
        !           278: 
1.1       root      279:        // 送信キューに入れて..
                    280:        if (txq.Enqueue(packet) == false) {
                    281:                stat.txqfull_pkts++;
                    282:                stat.txqfull_bytes += packet.length;
                    283:                putlog(2, "txq exhausted");
                    284:                return false;
                    285:        }
                    286: 
                    287:        stat.tx_pkts++;
                    288:        stat.tx_bytes += packet.length;
                    289: 
                    290:        // ざっくりピーク値
1.1.1.5 ! root      291:        stat.txq_peak = std::max((uint)txq.Length(), stat.txq_peak);
1.1       root      292: 
                    293:        // パイプに通知 (値はダミー)
                    294:        return WritePipe(0);
                    295: }
                    296: 
                    297: // rxq への投入を行う場合は true
                    298: void
                    299: HostNetDevice::EnableRx(bool enable)
                    300: {
                    301:        rx_enable = enable;
                    302: }
                    303: 
1.1.1.3   root      304: // 受信用の自身の MAC アドレスを設定する。
1.1       root      305: void
                    306: HostNetDevice::SetMyAddr(const macaddr_t& myaddr_)
                    307: {
                    308:        myaddr = myaddr_;
                    309: }
                    310: 
1.1.1.4   root      311: // マルチキャストフィルタを設定する。
                    312: void
                    313: HostNetDevice::SetMCastFilter(uint64 filter_)
                    314: {
                    315:        multicast_filter = filter_;
                    316: }
                    317: 
1.1       root      318: // プロミスキャスモードを設定する。
                    319: void
                    320: HostNetDevice::SetPromisc(bool promisc_)
                    321: {
                    322:        promisc = promisc_;
                    323: }
                    324: 
                    325: // パケットをキューから取り出す。
                    326: // VM 側から呼ばれる。
                    327: bool
                    328: HostNetDevice::Rx(NetPacket *dst)
                    329: {
                    330:        if (rxq.Dequeue(dst) == false) {
                    331:                // キューが空
                    332:                return false;
                    333:        }
                    334:        stat.rx_pkts++;
                    335:        stat.rx_bytes += dst->length;
1.1.1.3   root      336:        if (__predict_false(loglevel >= 2)) {
1.1.1.5 ! root      337:                putlogn("Recv %u bytes", dst->length);
1.1.1.3   root      338:                if (loglevel >= 3) {
                    339:                        std::string buf;
1.1.1.5 ! root      340:                        for (uint i = 0; i < dst->length; i++) {
1.1.1.3   root      341:                                if (i % 16 == 0) {
                    342:                                        buf += string_format("%04x:", i);
                    343:                                }
                    344:                                buf += string_format(" %02x", (*dst)[i]);
                    345:                                if (i % 16 == 7) {
                    346:                                        buf += " ";
                    347:                                } else if (i % 16 == 15) {
                    348:                                        putlogn("%s", buf.c_str());
                    349:                                        buf = "";
                    350:                                }
                    351:                        }
                    352:                        if (buf.empty() == false) {
                    353:                                putlogn("%s", buf.c_str());
                    354:                        }
                    355:                }
                    356:        }
1.1       root      357: 
                    358:        return true;
                    359: }
                    360: 
                    361: // 外部からの読み込み (パケットを受信)。
                    362: // 戻り値はキューに投入したパケット数。
                    363: int
                    364: HostNetDevice::Read()
                    365: {
                    366:        int queued = 0;
                    367:        int left = 0;
                    368: 
                    369:        assert((bool)driver);
                    370: 
                    371:        do {
                    372:                NetPacket *p;
                    373: 
                    374:                p = rxq.BeginWrite();
                    375:                if (p == NULL) {
                    376:                        NetPacket discard;
                    377:                        left = driver->Read(&discard);
                    378:                        if (left < 0) {
                    379:                                break;
                    380:                        }
                    381:                        stat.rxqfull_pkts++;
                    382:                        stat.rxqfull_bytes += discard.length;
                    383:                        continue;
                    384:                }
                    385: 
                    386:                // driver->Read() は VM に投入すべきパケットがなければ負数を返す。
                    387:                // パケットがあれば p に書き戻して、driver 側のバッファに残っている
                    388:                // パケット数 (といっても 0 か 1以上) を返してくる。
                    389:                left = driver->Read(p);
                    390:                if (left < 0) {
                    391:                        rxq.CancelWrite();
                    392:                        break;
                    393:                }
                    394: 
                    395:                NetPacket& packet = *p;
                    396: 
                    397:                stat.read_pkts++;
                    398:                stat.read_bytes += packet.length;
                    399: 
                    400:                if (rx_enable == false) {
                    401:                        rxq.CancelWrite();
                    402:                        stat.rxdisable_pkts++;
                    403:                        stat.rxdisable_bytes += packet.length;
                    404:                        continue;
                    405:                }
                    406: 
                    407:                // イーサネットフレームより長いパケットはゲスト OS が期待していない。
                    408:                // ここでドロップしてみる。
                    409:                // これによりゲストでの dropping chained buffer が出なくなる。
                    410:                if (packet.length > 1518) {
                    411:                        rxq.CancelWrite();
                    412:                        stat.rxjumbo_pkts++;
                    413:                        stat.rxjumbo_bytes += packet.length;
                    414:                        continue;
                    415:                }
                    416: 
                    417:                // ホストカーネルから直接着信したパケット(というかフレームか)は
                    418:                // 60バイトパディングされないまま読み出せるので、ここでパディングする。
                    419:                while (packet.length < 60) {
                    420:                        packet.Append(0x00);
                    421:                }
                    422: 
                    423:                // パケットフィルタリング
                    424:                if (__predict_true(promisc == false)) {
1.1.1.4   root      425:                        static std::array<uint8, 6> bcast {
                    426:                                0xff, 0xff, 0xff, 0xff, 0xff, 0xff
                    427:                        };
                    428:                        if (memcmp(&packet[0], &bcast[0], bcast.size()) == 0) {
                    429:                                // ブロードキャストは素通し。
                    430:                        } else if (__predict_false(packet[0] & 1)) {
                    431:                                // マルチキャスト。
1.1       root      432:                                // LANCE では LADRF レジスタ、NE2000 では MAR0-7 レジスタで
1.1.1.4   root      433:                                // データシートを見る限りはロジックは同じもののようだ。
1.1       root      434: 
1.1.1.4   root      435:                                // 宛先アドレスの CRC32 を計算する。
                    436:                                // イーサネットの CRC は zlib の crc32() と同じもの。
1.1.1.5 ! root      437:                                uint32 crc = crc32(0, Z_NULL, 0);
1.1.1.4   root      438:                                crc = crc32(crc, &packet[0], 6);
                    439: 
                    440:                                // crc の上位6ビットをとりだして検査ビット位置とする(=pos)。
                    441:                                // multicast_filter の pos の位置のビットが 1 なら受信、
                    442:                                // 0 なら破棄。
                    443:                                // multicast_filter は64ビットで LSB が0番目、MSB が63番目
                    444:                                // としたもの (NIC 側でこの通りに並べ替えて渡してある)。
                    445:                                uint pos = (crc >> 26) & 0x3f;
                    446:                                uint64 bit = 1U << pos;
1.1       root      447:                                if ((multicast_filter & bit) == 0) {
1.1.1.4   root      448:                                        putlog(2, "mcast %02x:%02x:%02x:%02x:%02x:%02x pos=%d drop",
                    449:                                                packet[0], packet[1], packet[2],
                    450:                                                packet[3], packet[4], packet[5], pos);
                    451:                                        stat.rxmcast_pkts++;
                    452:                                        stat.rxmcast_bytes += packet.length;
                    453:                                        rxq.CancelWrite();
                    454:                                        continue;
1.1       root      455:                                }
1.1.1.4   root      456:                                putlog(2, "mcast %02x:%02x:%02x:%02x:%02x:%02x pos=%d pass",
                    457:                                        packet[0], packet[1], packet[2],
                    458:                                        packet[3], packet[4], packet[5], pos);
1.1       root      459:                        } else {
                    460:                                // ユニキャスト
                    461:                                if (memcmp(&packet[0], &myaddr[0], 6) != 0) {
1.1.1.4   root      462:                                        stat.rxnotme_pkts++;
                    463:                                        stat.rxnotme_bytes += packet.length;
                    464:                                        rxq.CancelWrite();
                    465:                                        continue;
1.1       root      466:                                }
                    467:                        }
                    468:                }
                    469: 
                    470:                // CRC XXX 計算すること
                    471:                for (int i = 0; i < 4; i++) {
                    472:                        packet.Append(0x00);
                    473:                }
                    474: 
                    475:                rxq.EndWrite();
                    476:                queued++;
                    477:        } while (left > 0);
                    478: 
                    479:        // ざっくりピーク値
1.1.1.5 ! root      480:        stat.rxq_peak = std::max((uint)rxq.Length(), stat.rxq_peak);
1.1       root      481: 
                    482:        return queued;
                    483: }
                    484: 
                    485: // 外部への書き出し(パケットを送信)
                    486: void
                    487: HostNetDevice::Write(uint32 data)
                    488: {
                    489:        const NetPacket *p;
                    490: 
                    491:        assert((bool)driver);
                    492: 
                    493:        // 送信キューから取り出す
                    494:        while ((p = txq.BeginRead()) != NULL) {
                    495:                const NetPacket& packet = *p;
                    496: 
1.1.1.3   root      497:                if (__predict_false(loglevel >= 2)) {
1.1.1.5 ! root      498:                        putlogn("Send %u bytes", packet.length);
1.1.1.3   root      499:                        if (loglevel >= 3) {
                    500:                                std::string buf;
1.1.1.5 ! root      501:                                for (uint i = 0; i < packet.length; i++) {
1.1.1.3   root      502:                                        if (i % 16 == 0) {
                    503:                                                buf += string_format("%04x:", i);
                    504:                                        }
                    505:                                        buf += string_format(" %02x", packet[i]);
                    506:                                        if (i % 16 == 7) {
                    507:                                                buf += " ";
                    508:                                        } else if (i % 16 == 15) {
                    509:                                                putlogn("%s", buf.c_str());
                    510:                                                buf = "";
                    511:                                        }
                    512:                                }
                    513:                                if (buf.empty() == false) {
                    514:                                        putlogn("%s", buf.c_str());
                    515:                                }
                    516:                        }
                    517:                }
1.1       root      518:                stat.write_pkts++;
                    519:                stat.write_bytes += packet.length;
                    520: 
                    521:                driver->Write(packet.data(), packet.length);
                    522:                txq.EndRead();
                    523:        }
                    524: }
                    525: 
                    526: // ドライバ名を返す
                    527: const std::string
                    528: HostNetDevice::GetDriverName() const
                    529: {
                    530:        assert((bool)driver);
                    531:        return driver->GetDriverName();
                    532: }
                    533: 
                    534: // モニタ
                    535: void
                    536: HostNetDevice::MonitorUpdate(Monitor *, TextScreen& screen)
                    537: {
                    538:        screen.Clear();
                    539: 
1.1.1.3   root      540:        screen.Print(0, 0, "Parent Device : %s", parent->GetName().c_str());
                    541:        screen.Print(0, 1, "HostNet Driver: %s", driver->GetDriverName());
1.1       root      542:        // 次の2行はドライバ依存情報
1.1.1.3   root      543:        driver->MonitorUpdateMD(screen, 2);
1.1       root      544: 
                    545:        // 01234567890123456
                    546:        // Write to host
                    547:        // Drop:TxQ Full
                    548:        // Drop:Rx Disabled
                    549:        // Drop:Jumbo Frame
                    550:        // Drop:Addr Filter
                    551: 
1.1.1.3   root      552:        int y = 5;
1.1       root      553:        screen.Print(0, y++, "%-17s%13s %17s", "<Tx>", "Packets", "Bytes");
                    554:        screen.Print(0, y++, "%-17s%13s %17s", "VM sends",
                    555:                format_number(stat.tx_pkts).c_str(),
                    556:                format_number(stat.tx_bytes).c_str());
                    557:        screen.Print(0, y++, "%-17s%13s %17s", "Write to host",
                    558:                format_number(stat.write_pkts).c_str(),
                    559:                format_number(stat.write_bytes).c_str());
1.1.1.5 ! root      560:        screen.Print(0, y++, "%-17s%13s %17s", "Drop:Empty Frame",
        !           561:                format_number(stat.txzero_pkts).c_str(),
        !           562:                "0");
1.1       root      563:        screen.Print(0, y++, "%-17s%13s %17s", "Drop:TxQ Full",
                    564:                format_number(stat.txqfull_pkts).c_str(),
                    565:                format_number(stat.txqfull_bytes).c_str());
                    566:        y++;
                    567: 
                    568:        screen.Print(0, y++, "%-17s%13s %17s", "<Rx>", "Packets", "Bytes");
                    569:        screen.Print(0, y++, "%-17s%13s %17s", "Read from host",
                    570:                format_number(stat.read_pkts).c_str(),
                    571:                format_number(stat.read_bytes).c_str());
                    572:        screen.Print(0, y++, "%-17s%13s %17s", "VM receives",
                    573:                format_number(stat.rx_pkts).c_str(),
                    574:                format_number(stat.rx_bytes).c_str());
                    575:        screen.Print(0, y++, "%-17s%13s %17s", "Drop:Rx Disabled",
                    576:                format_number(stat.rxdisable_pkts).c_str(),
                    577:                format_number(stat.rxdisable_bytes).c_str());
                    578:        screen.Print(0, y++, "%-17s%13s %17s", "Drop:Jumbo Frame",
                    579:                format_number(stat.rxjumbo_pkts).c_str(),
                    580:                format_number(stat.rxjumbo_bytes).c_str());
                    581:        screen.Print(0, y++, "%-17s%13s %17s", "Drop:RxQ Full",
                    582:                format_number(stat.rxqfull_pkts).c_str(),
                    583:                format_number(stat.rxqfull_bytes).c_str());
1.1.1.4   root      584:        screen.Print(0, y++, "%-17s%13s %17s", "Drop:Unicast",
                    585:                format_number(stat.rxnotme_pkts).c_str(),
                    586:                format_number(stat.rxnotme_bytes).c_str());
                    587:        screen.Print(0, y++, "%-17s%13s %17s", "Drop:Multicast",
                    588:                format_number(stat.rxmcast_pkts).c_str(),
                    589:                format_number(stat.rxmcast_bytes).c_str());
1.1       root      590:        y++;
                    591: 
                    592:        screen.Print(5, y++, "Capacity Peak");
1.1.1.5 ! root      593:        screen.Print(0, y++, "TxQ %4u/%4u %4u",
        !           594:                (uint)txq.Length(), (uint)txq.Capacity(), stat.txq_peak);
        !           595:        screen.Print(0, y++, "RxQ %4u/%4u %4u",
        !           596:                (uint)rxq.Length(), (uint)rxq.Capacity(), stat.rxq_peak);
1.1       root      597: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.