--- nono/host/netdriver_afpacket.cpp 2026/04/29 17:04:54 1.1.1.1 +++ nono/host/netdriver_afpacket.cpp 2026/04/29 17:05:11 1.1.1.2 @@ -4,21 +4,26 @@ // Licensed under nono-license.txt // -// AF_PACKET を用いたドライバ -// Linux 2.2 以降専用 +// +// ホストネットワークの AF_PACKET ドライバ +// (Linux 専用) +// #include "netdriver_afpacket.h" #include "autofd.h" +#include "hostnet.h" #include #include #include #include #include +#include +#include // コンストラクタ -NetDriverAFPacket::NetDriverAFPacket(EthernetDevice *parent_, int loglevel_, +NetDriverAFPacket::NetDriverAFPacket(HostDevice *hostdev_, const std::string& ifname_) - : inherited("AFPacket", parent_, loglevel_) + : inherited(hostdev_, "AFPacket") { ifname = ifname_; if (ifname.empty()) { @@ -32,7 +37,7 @@ NetDriverAFPacket::~NetDriverAFPacket() Close(); } -// ドライバ初期化 & オープン +// ドライバ初期化 bool NetDriverAFPacket::InitDriver() { @@ -47,6 +52,7 @@ NetDriverAFPacket::InitDriver() sll.sll_protocol = htons(ETH_P_ALL); sll.sll_ifindex = SelectInterface(); if (sll.sll_ifindex == 0) { + // errmsg はセットしてある goto abort; } @@ -75,6 +81,46 @@ NetDriverAFPacket::InitDriver() goto abort; } +#if defined(SIOCETHTOOL) && defined(ETHTOOL_GGRO) + // GRO が設定されていると再構成されたジャンボパケットが届いてしまうが、 + // VM (というか当時の NIC) にそんなものが届いても困るので、 + // GRO が設定されていたらエラー終了する。 + // + // 確認 /sbin/ethtool -k | generic-receive-offload + // 設定 sudo ethtool -K generic-receive-offload + struct ifreq ifr; + struct ethtool_value ethval; + + memset(&ifr, 0, sizeof(ifr)); + memset(ðval, 0, sizeof(ethval)); + strlcpy(ifr.ifr_name, ifname.c_str(), sizeof(ifr.ifr_name)); + ethval.cmd = ETHTOOL_GGRO; + ifr.ifr_data = (char *)ðval; + if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) { + if (errno == EOPNOTSUPP) { + // この ioctl 自体をサポートしていなければ + // たぶんオフローディングは設定されていないはず。 + ethval.data = 1; + } else { + errmsg = string_format("SIOCETHTOOL(%s): %s", + ifname.c_str(), strerror(errno)); + goto abort; + } + } + + if (ethval.data != 0) { + errmsg = "GRO is enabled"; + putmsg(1, "%s", errmsg.c_str()); + warnx("%s", errmsg.c_str()); + errmsg.clear(); + fprintf(stderr, + " ** Disable GRO(generic-receive-offload)" + " to avoid bother network problem.\n" + " ** ex) sudo ethtool -K %s gro off\n", ifname.c_str()); + return false; + } +#endif + // PROMISC セット mreq.mr_ifindex = sll.sll_ifindex; mreq.mr_type = PACKET_MR_PROMISC; @@ -91,15 +137,21 @@ NetDriverAFPacket::InitDriver() goto abort; } + if (hostdev->AddOuter(fd) < 0) { + errmsg = string_format("AddOuter(fd=%d): %s", (int)fd, strerror(errno)); + goto abort; + } + putmsg(1, "opened for %s", ifname.c_str()); - putmsg(1, "warning: communicating with the localhost is not possible"); // AFPacket では ifup スクリプトは実行しない return true; abort: - putmsg(1, "%s", errmsg.c_str()); + if (errmsg.empty() == false) { + putmsg(1, "%s", errmsg.c_str()); + } Close(); return false; } @@ -109,9 +161,9 @@ NetDriverAFPacket::InitDriver() void NetDriverAFPacket::Close() { - if (IsOpen()) { - close(fd); - fd = -1; + if (fd.Valid()) { + hostdev->DelOuter(fd); + fd.Close(); } } @@ -123,43 +175,35 @@ NetDriverAFPacket::MonitorUpdateMD(TextS } // パケットを送信する -bool -NetDriverAFPacket::SendPacket(const std::vector& data) +void +NetDriverAFPacket::Write(const void *buf, int buflen) { - int n; - - assert(IsOpen()); + ssize_t n; - putlog(2, "SendPacket %zu bytes", data.size()); - tx_pkts++; - tx_bytes += data.size(); - - n = sendto(fd, data.data(), data.size(), 0, - (const sockaddr *)&sll, sizeof(sll)); - if (n == -1) { - warn("NetDriverAFPacket.SendPacket: sendto failed: fd=%d datalen=%zu", - fd, data.size()); - return false; - } - - if (n < (int)data.size()) { - return false; + n = sendto(fd, buf, buflen, 0, (const sockaddr *)&sll, sizeof(sll)); + if (n < 0) { + putmsg(0, "write: %s", strerror(errno)); + return; + } + if (n < buflen) { + putmsg(0, "write: short"); + return; } - return true; } // パケットを受信する -ssize_t -NetDriverAFPacket::RecvPacket() +int +NetDriverAFPacket::Read(NetPacket *p) { - uint8 buf[10240]; - ssize_t rv; + int n; - rv = recv(fd, buf, sizeof(buf), 0); - if (rv > 0) { - RecvEnqueue(buf, rv); + n = recv(fd, p->data(), p->size(), 0); + if (n < 0) { + putmsg(0, "recv: %s", strerror(errno)); + return NODATA; } - return rv; + p->length = n; + return 0; } // インタフェース番号を返す。