--- nono/host/hostnet.cpp 2026/04/29 17:05:39 1.1.1.7 +++ nono/host/hostnet.cpp 2026/04/29 17:06:02 1.1.1.12 @@ -77,10 +77,12 @@ #endif #if defined(HAVE_HOSTNET_SLIRP) #include "netdriver_slirp.h" +#include #endif #if defined(HAVE_HOSTNET_TAP) #include "netdriver_tap.h" #endif +#include "uimessage.h" #include #include #include @@ -108,17 +110,41 @@ HostNetDevice::GetDrivers() return list; } +// libslirp のバージョン文字列を返す。 +// libslirp が有効でなければ NULL を返す。 +/*static*/ const char * +HostNetDevice::GetSlirpVersion() +{ +#if defined(SLIRP_VERSION_STRING) + return SLIRP_VERSION_STRING; +#else + return NULL; +#endif +} + // コンストラクタ -HostNetDevice::HostNetDevice(Device *parent_, uint n) - : inherited(parent_, OBJ_HOSTNET(n)) +HostNetDevice::HostNetDevice(Device *parent_, uint n, + const std::string& portname_) + : inherited(parent_, OBJ_HOSTNET(n), portname_) { ihwaddrfilter = dynamic_cast(parent); assert(ihwaddrfilter); monitor = gMonitorManager->Regist(ID_MONITOR_HOSTNET(n), this); - monitor->func = ToMonitorCallback(&HostNetDevice::MonitorUpdate); + monitor->SetCallback(&HostNetDevice::MonitorScreen); monitor->SetSize(48, 24); + +#if defined(HAVE_HOSTNET_SLIRP) + // SLIRP モニタは usermode を使う時のみ必要だが、現状モニタは + // 起動時に1回登録しなければならない (以後増減できない) という制約が + // あるのでここで登録だけしておく。うーんこの…。 + // usermode は現状高々1つなので、登録されてない時だけ登録する。 + Monitor *mon = gMonitorManager->Find(ID_MONITOR_SLIRP); + if (mon == NULL) { + gMonitorManager->Regist(ID_MONITOR_SLIRP, NULL); + } +#endif } // デストラクタ @@ -132,6 +158,7 @@ HostNetDevice::SetLogLevel(int loglevel_ { inherited::SetLogLevel(loglevel_); + RWLockGuard_Read guard(driverlock); if ((bool)driver) { driver->SetLogLevel(loglevel_); } @@ -145,25 +172,29 @@ HostNetDevice::Create2() return false; } - if (SelectDriver() == false) { + if (SelectDriver(true) == false) { return false; } return true; } -// ドライバ(再)選択 +// ドライバ(再)選択。 +// エラーが起きた場合、 +// 起動時なら、warn() 等で表示して false を返す(終了する)。 +// 実行中なら、warn() 等で表示してもいいが、必ず None にフォールバックして +// true を返すこと。 bool -HostNetDevice::SelectDriver() +HostNetDevice::SelectDriver(bool startup) { - int n = GetId() - OBJ_HOSTNET0; - const std::string key_driver = string_format("hostnet%d-driver", n); - const std::string key_fallback = string_format("hostnet%d-fallback", n); - const ConfigItem& item = gConfig->Find(key_driver); - std::string type = item.AsString(); - bool fallback = gConfig->Find(key_fallback).AsInt(); + RWLockGuard_Write guard(driverlock); driver.reset(); + errmsg.clear(); + + std::string key = GetConfigKey(); + const ConfigItem& item = gConfig->Find(key + "-driver"); + std::string type = item.AsString(); // auto は usermode か none と同義にする。 if (type == "auto") { @@ -174,57 +205,75 @@ HostNetDevice::SelectDriver() #endif } - if (type != "none") { - if (type == "usermode") { + enum { + ERR_CONFIG, // 設定でのエラー + ERR_INVALID, // 知らないドライバ + ERR_NOTSUPP, // コンパイルされてない + } reason = ERR_CONFIG; + + if (type == "none") { + CreateNone(); + + } else if (type == "usermode") { #if defined(HAVE_HOSTNET_SLIRP) - CreateSlirp(); + CreateSlirp(startup); #else - item.Err("Hostnet driver %s not compiled", type.c_str()); + reason = ERR_NOTSUPP; #endif - } else if (type == "tap") { + } else if (type == "tap") { #if defined(HAVE_HOSTNET_TAP) - CreateTap(); + CreateTap(); #else - item.Err("Hostnet driver %s not compiled", type.c_str()); + reason = ERR_NOTSUPP; #endif - } else if (type == "bpf") { + } else if (type == "bpf") { #if defined(HAVE_HOSTNET_BPF) - CreateBPF(); + CreateBPF(); #else - item.Err("Hostnet driver %s not compiled", type.c_str()); + reason = ERR_NOTSUPP; #endif - } else if (type == "afpacket") { + } else if (type == "afpacket") { #if defined(HAVE_HOSTNET_AFPACKET) - CreateAFPacket(); + CreateAFPacket(); #else - item.Err("Hostnet driver %s not compiled", type.c_str()); + reason = ERR_NOTSUPP; #endif - } else { - // 知らないドライバ種別 - item.Err(); - } - - if ((bool)driver == false) { - putmsg(1, "hostnet-fallback=%d", fallback ? 1 : 0); - - if (fallback == false) { - // フォールバックしないならエラー終了 - errmsg = "No host network driver found"; - putmsg(1, "%s", errmsg.c_str()); - warnx("%s (See details with options -C -Lhostnet=1)", - errmsg.c_str()); - - return false; - } - } + } else { + // 知らないドライバ種別 + reason = ERR_INVALID; } if ((bool)driver == false) { - CreateNone(); + // 指定のドライバが使えなかった場合、 + // o 起動時ならエラー終了する。 + // o 実行中なら none にフォールバックする。 + if (startup) { + switch (reason) { + case ERR_INVALID: + item.Err("Invalid driver name"); + break; + case ERR_CONFIG: + item.Err("Could not configure the driver"); + warnx("(See details with option -C -L%s=1)", key.c_str()); + break; + case ERR_NOTSUPP: + item.Err("Hostnet driver %s not compiled", type.c_str()); + break; + default: + assert(false); + } + return false; + } else { + // UI に通知してフォールバック。 + int n = GetId() - OBJ_HOSTNET0; + gMainApp.GetUIMessage()->Post(UIMessage::HOSTNET_FAILED, n); + + CreateNone(); + } } assert((bool)driver); @@ -235,42 +284,47 @@ HostNetDevice::SelectDriver() return true; } -// None ドライバを生成する -bool +// None ドライバを生成する。 +// 戻り値はなく、成否は (bool)driver で判断する。 +void HostNetDevice::CreateNone() { - driver.reset(new NetDriverNone(this)); + try { + driver.reset(new NetDriverNone(this)); + } catch (...) { } if ((bool)driver) { if (driver->InitDriver()) { // 成功 - return true; + return; } errmsg = driver->errmsg; driver.reset(); } - return false; } -// Slirp ドライバを生成する -bool -HostNetDevice::CreateSlirp() +// Slirp ドライバを生成する。 +// 戻り値はなく、成否は (bool)driver で判断する。 +void +HostNetDevice::CreateSlirp(bool startup) { #if defined(HAVE_HOSTNET_SLIRP) - driver.reset(new NetDriverSlirp(this)); + try { + driver.reset(new NetDriverSlirp(this)); + } catch (...) { } if ((bool)driver) { - if (driver->InitDriver()) { + if (driver->InitDriver(startup)) { // 成功 - return true; + return; } errmsg = driver->errmsg; driver.reset(); } #endif - return false; } -// Tap ドライバを生成する -bool +// Tap ドライバを生成する。 +// 戻り値はなく、成否は (bool)driver で判断する。 +void HostNetDevice::CreateTap() { #if defined(HAVE_HOSTNET_TAP) @@ -279,21 +333,23 @@ HostNetDevice::CreateTap() const ConfigItem& item = gConfig->Find(keyname); const std::string& devpath = item.AsString(); - driver.reset(new NetDriverTap(this, devpath)); + try { + driver.reset(new NetDriverTap(this, devpath)); + } catch (...) { } if ((bool)driver) { if (driver->InitDriver()) { // 成功 - return true; + return; } errmsg = driver->errmsg; driver.reset(); } #endif - return false; } -// bpf ドライバを生成する -bool +// bpf ドライバを生成する。 +// 戻り値はなく、成否は (bool)driver で判断する。 +void HostNetDevice::CreateBPF() { #if defined(HAVE_HOSTNET_BPF) @@ -302,21 +358,23 @@ HostNetDevice::CreateBPF() const ConfigItem& item = gConfig->Find(keyname); const std::string& ifname = item.AsString(); - driver.reset(new NetDriverBPF(this, ifname)); + try { + driver.reset(new NetDriverBPF(this, ifname)); + } catch (...) { } if ((bool)driver) { if (driver->InitDriver()) { // 成功 - return true; + return; } errmsg = driver->errmsg; driver.reset(); } #endif - return false; } -// AFPacket ドライバを生成する -bool +// AFPacket ドライバを生成する。 +// 戻り値はなく、成否は (bool)driver で判断する。 +void HostNetDevice::CreateAFPacket() { #if defined(HAVE_HOSTNET_AFPACKET) @@ -325,17 +383,18 @@ HostNetDevice::CreateAFPacket() const ConfigItem& item = gConfig->Find(keyname); const std::string& ifname = item.AsString(); - driver.reset(new NetDriverAFPacket(this, ifname)); + try { + driver.reset(new NetDriverAFPacket(this, ifname)); + } catch (...) { } if ((bool)driver) { if (driver->InitDriver()) { // 成功 - return true; + return; } errmsg = driver->errmsg; driver.reset(); } #endif - return false; } // VM からの送信 (VM スレッドで呼ばれる) @@ -344,7 +403,7 @@ HostNetDevice::Tx(const NetPacket& packe { // 長さ 0 のパケットは破棄する。バグでもないと通常は起きない。 // runt frame はどうするか? - if (packet.length < 1) { + if (packet.Length() < 1) { stat.txzero_pkts++; putlog(1, "Tx: drop empty packet"); return true; @@ -353,19 +412,19 @@ HostNetDevice::Tx(const NetPacket& packe // 送信キューに入れて.. if (txq.Enqueue(packet) == false) { stat.txqfull_pkts++; - stat.txqfull_bytes += packet.length; + stat.txqfull_bytes += packet.Length(); putlog(2, "txq exhausted"); return false; } stat.tx_pkts++; - stat.tx_bytes += packet.length; + stat.tx_bytes += packet.Length(); // ざっくりピーク値 stat.txq_peak = std::max((uint)txq.Length(), stat.txq_peak); - // パイプに通知 (値はダミー) - return WritePipe(0); + // パイプで通知。 + return WritePipe(PIPE_TX); } // rxq への投入を行う場合は true。 @@ -382,7 +441,7 @@ HostNetDevice::EnableRx(bool enable) NetPacket drop; while (rxq.Dequeue(&drop)) { stat.rxdisable_pkts++; - stat.rxdisable_bytes += drop.length; + stat.rxdisable_bytes += drop.Length(); } } } @@ -396,17 +455,18 @@ HostNetDevice::Rx(NetPacket *dst) // キューが空 return false; } + uint length = dst->Length(); stat.rx_pkts++; - stat.rx_bytes += dst->length; + stat.rx_bytes += length; if (__predict_false(loglevel >= 2)) { - putlogn("Recv %u bytes", dst->length); + putlogn("Recv %u bytes", length); if (loglevel >= 4) { std::string buf; - for (uint i = 0; i < dst->length; i++) { + for (uint i = 0; i < length; i++) { if (i % 16 == 0) { buf += string_format("%04x:", i); } - buf += string_format(" %02x", (*dst)[i]); + buf += string_format(" %02x", dst->Peek(i)); if (i % 16 == 7) { buf += " "; } else if (i % 16 == 15) { @@ -431,6 +491,7 @@ HostNetDevice::Read() int queued = 0; int left = 0; + RWLockGuard_Read guard(driverlock); assert((bool)driver); do { @@ -444,7 +505,7 @@ HostNetDevice::Read() break; } stat.rxqfull_pkts++; - stat.rxqfull_bytes += discard.length; + stat.rxqfull_bytes += discard.Length(); continue; } @@ -460,45 +521,45 @@ HostNetDevice::Read() NetPacket& packet = *p; stat.read_pkts++; - stat.read_bytes += packet.length; + stat.read_bytes += packet.Length(); if (rx_enable == false) { rxq.CancelWrite(); stat.rxdisable_pkts++; - stat.rxdisable_bytes += packet.length; + stat.rxdisable_bytes += packet.Length(); continue; } // イーサネットフレームより長いパケットはゲスト OS が期待していない。 // ここでドロップしてみる。 // これによりゲストでの dropping chained buffer が出なくなる。 - if (packet.length > 1518) { + if (packet.Length() > 1518) { rxq.CancelWrite(); stat.rxjumbo_pkts++; - stat.rxjumbo_bytes += packet.length; + stat.rxjumbo_bytes += packet.Length(); continue; } // ホストカーネルから直接着信したパケット(というかフレームか)は // 60バイトパディングされないまま読み出せるので、ここでパディングする。 - while (packet.length < 60) { - packet.Append(0x00); + while (packet.Length() < 60) { + packet.Resize(60); } // デバイスがこのアドレス宛のフレームを受け取るか。 // 受け取らないと分かっているフレームは VM へのキューに入れる前に // 落としておきたい。 - MacAddr dstaddr(&packet[0]); + MacAddr dstaddr(packet.Data()); auto res = ihwaddrfilter->HWAddrFilter(dstaddr); if (res != 0) { switch (res) { case IHWAddrFilter::HPF_DROP_UNICAST: stat.rxnotme_pkts++; - stat.rxnotme_bytes += packet.length; + stat.rxnotme_bytes += packet.Length(); break; case IHWAddrFilter::HPF_DROP_MULTICAST: stat.rxmcast_pkts++; - stat.rxmcast_bytes += packet.length; + stat.rxmcast_bytes += packet.Length(); break; default: assertmsg(false, "corrupted res=%u", res); @@ -508,9 +569,7 @@ HostNetDevice::Read() } // CRC XXX 計算すること - for (int i = 0; i < 4; i++) { - packet.Append(0x00); - } + packet.Resize(packet.Length() + 4); rxq.EndWrite(); queued++; @@ -524,25 +583,27 @@ HostNetDevice::Read() // 外部への書き出し(パケットを送信) void -HostNetDevice::Write(uint32 data) +HostNetDevice::Write() { const NetPacket *p; + RWLockGuard_Read guard(driverlock); assert((bool)driver); // 送信キューから取り出す while ((p = txq.BeginRead()) != NULL) { const NetPacket& packet = *p; + uint length = packet.Length(); if (__predict_false(loglevel >= 2)) { - putlogn("Send %u bytes", packet.length); + putlogn("Send %u bytes", length); if (loglevel >= 4) { std::string buf; - for (uint i = 0; i < packet.length; i++) { + for (uint i = 0; i < length; i++) { if (i % 16 == 0) { buf += string_format("%04x:", i); } - buf += string_format(" %02x", packet[i]); + buf += string_format(" %02x", packet.Peek(i)); if (i % 16 == 7) { buf += " "; } else if (i % 16 == 15) { @@ -556,17 +617,18 @@ HostNetDevice::Write(uint32 data) } } stat.write_pkts++; - stat.write_bytes += packet.length; + stat.write_bytes += length; - driver->Write(packet.data(), packet.length); + driver->Write(packet.Data(), length); txq.EndRead(); } } // ドライバ名を返す -const std::string -HostNetDevice::GetDriverName() const +const std::string& +HostNetDevice::GetDriverName() { + RWLockGuard_Read guard(driverlock); assert((bool)driver); return driver->GetDriverName(); } @@ -583,14 +645,18 @@ HostNetDevice::CountTXUnsupp(size_t byte // モニタ void -HostNetDevice::MonitorUpdate(Monitor *, TextScreen& screen) +HostNetDevice::MonitorScreen(Monitor *, TextScreen& screen) { screen.Clear(); - screen.Print(0, 0, "Parent Device : %s", parent->GetName().c_str()); - screen.Print(0, 1, "HostNet Driver: %s", driver->GetDriverName()); - // 次の2行はドライバ依存情報 - driver->MonitorUpdateMD(screen, 2); + screen.Print(0, 0, "Device(Port) : %s", GetPortName().c_str()); + { + RWLockGuard_Read guard(driverlock); + screen.Print(0, 1, "HostNet Driver: %s", + driver->GetDriverName().c_str()); + // 次の2行はドライバ依存情報 + driver->MonitorScreenMD(screen, 2); + } // 01234567890123456 // Write to host @@ -1427,6 +1493,53 @@ HostNetDevice::GetServByPort(uint16 port } +// +// NetPacket +// + +// このパケットを src から srclen バイトの内容に置き換える。 +void +NetPacket::Assign(const void *src, uint srclen) +{ + Resize(srclen); + uint copylen = std::min(srclen, (uint)buf.size()); + memcpy(buf.data(), src, copylen); +} + +// パケット長を変更する。 +void +NetPacket::Resize(uint newlength) +{ + uint cliplength = newlength; + + if (__predict_false(cliplength > buf.size())) { + cliplength = buf.size(); + } + + // 新たに増えたところは 0 で埋める。 + if (cliplength > length) { + memset(Data() + length, 0, cliplength - length); + } + + // 長さは buf.size() を超えることを許容する。 + length = newlength; +} + +// パケットに1バイト追加する。 +void +NetPacket::Append(uint8 data) +{ + if (__predict_true(length < buf.size())) { + buf[length] = data; + } + length++; +} + + +// +// IHWAddrFilter +// + // IHWAddrFilter の デストラクタ IHWAddrFilter::~IHWAddrFilter() {