--- nono/host/netdriver_tap.cpp 2026/04/29 17:04:51 1.1.1.4 +++ nono/host/netdriver_tap.cpp 2026/04/29 17:04:54 1.1.1.5 @@ -6,17 +6,10 @@ #include "netdriver_tap.h" #include "autofd.h" -#include "cvprompt.h" #include #include #include #include -#if defined(HAVE_SYS_EVENT_H) -#include -#endif -#if defined(HAVE_KQUEUE_SYS_EVENT_H) -#include -#endif #if defined(__linux__) #include #include @@ -42,12 +35,14 @@ // /dev/tapN をオープンする方式も使える。 // コンストラクタ -NetDriverTap::NetDriverTap(EthernetDevice *parent_, - std::mutex *mtx_, std::condition_variable *cv_, +NetDriverTap::NetDriverTap(EthernetDevice *parent_, int loglevel_, const std::string& devpath_) - : inherited("BSD tap", parent_, mtx_, cv_) + : inherited("Tap", parent_, loglevel_) { devpath = devpath_; + if (devpath.empty()) { + devpath = "auto"; + } } // デストラクタ @@ -56,17 +51,14 @@ NetDriverTap::~NetDriverTap() Close(); } -// 初期化 & オープン +// ドライバ初期化 & オープン bool -NetDriverTap::Init() +NetDriverTap::InitDriver() { bool r; - // 空文字列もデフォルトと同じ "auto" にする。 - if (devpath.empty()) { - devpath = "auto"; - } - putmsg(1, "NetDriverTap: device \"%s\"", devpath.c_str()); + putmsg(1, "trying tap..."); + putmsg(1, "argument: devpath=\"%s\"", devpath.c_str()); // デバイス名や tap の作り方は OS ごとに異なる。 // もっと言えば、なんか適当にオープンしてほしいデフォルト挙動も全部違う。 @@ -74,23 +66,29 @@ NetDriverTap::Init() // いずれの Open*() 関数も (必要なら) devpath に応じてデバイスをオープンし // 成功すれば fd にデバイスのディスクリプタ、ifname に作成した // インタフェース名をセットして true を返すこと。 - // 失敗すればデバイスをクローズしてエラーメッセージを表示し false を返す - // こと。その場合の ifname は不問。 + // 失敗すればデバイスをクローズして putmsg() を出力し false を返すこと。 + // warnx() での出力は行わない(この後他ドライバで成功するかも知れないので)。 + // その場合の ifname は不問。 #if defined(__linux__) // Linux はたぶん固定 r = OpenLinux(); + if (r == false) { + // 単独試行なのでここで終了 + return false; + } #elif defined(__OpenBSD__) if (devpath == "auto") { // 指定されてなければ、tapN を順に試す r = OpenNumbered(); - if (r == false) { - warnx("NetDriverTap.OpenNumbered: %s", errmsg.c_str()); - } + // FALLTHROUGH } else { // ご指名の場合 r = OpenDirect(); - // warnx は出力済み + if (r == false) { + // 単独試行なのでここで終了 + return false; + } } #else @@ -99,39 +97,32 @@ NetDriverTap::Init() r = OpenCloning(); if (r == false) { r = OpenNumbered(); - if (r == false) { - warnx("NetDriverTap.Open: %s", errmsg.c_str()); - } } + // FALLTHROUGH } else { // 指定されている場合 tap か tapN かで対応が違う if (devpath == "/dev/tap") { // tap ご指名の場合 r = OpenCloning(); - if (r == false) { - warnx("NetDriverTap.OpenCloning: %s", errmsg.c_str()); - } } else { // それ以外でご指名の場合 r = OpenDirect(); - // warnx は出力済み + } + if (r == false) { + // 単独試行なのでここで終了 + return false; } } #endif if (r == false) { - // エラーメッセージなどは各呼び出し先で処理済み + // 複数試した結果見付からない時だけこのメッセージを出したい + errmsg = "no tap devices available"; + putmsg(1, "%s", errmsg.c_str()); return false; } // ここで ifname に作成したインタフェース名が入っているはず - putmsg(1, "ifname: %s", ifname.c_str()); - - // オープンできたので受信スレッド起動。 - if (InitRecvThread() == false) { - // ifup 実行前なのでまだ ifdown 不要。 - CloseFd(); - return false; - } + putmsg(1, "opened %s for %s", devpath.c_str(), ifname.c_str()); // 最後に ifup スクリプトがあれば実行 if (!Run_ifup()) { @@ -145,7 +136,7 @@ NetDriverTap::Init() #if defined(__linux__) // tap インタフェースを作成する。Linux の場合。 -// 他の Open*() とは混在しないので、ここで直接エラーメッセージを出力する。 +// エラーの場合は putmsg ログを出力し、errmsg をセットして false を返す。 bool NetDriverTap::OpenLinux() { @@ -155,12 +146,14 @@ NetDriverTap::OpenLinux() // 指定がなければデフォルトパス if (devpath == "auto") { devpath = "/dev/net/tun"; + putmsg(1, "auto-selected devpath: \"%s\"", devpath.c_str()); } // デバイスオープン fd = open(devpath.c_str(), O_RDWR); if (fd < 0) { - warn("NetDriverTap.OpenLinux: %s", devpath.c_str()); + errmsg = string_format("%s: %s", devpath.c_str(), strerror(errno)); + putmsg(1, "%s: %s", __func__, errmsg.c_str()); return false; } @@ -169,8 +162,15 @@ NetDriverTap::OpenLinux() strlcpy(ifr.ifr_name, "tap%d", sizeof(ifr.ifr_name)); r = ioctl(fd, TUNSETIFF, (void *)&ifr); if (r < 0) { - warn("NetDriverTap.OpenLinux: %s: TUNSETIFF", devpath.c_str()); + errmsg = string_format("%s: %s: %s", + devpath.c_str(), "TUNSETIFF", strerror(errno)); + putmsg(1, "%s: %s", __func__, errmsg.c_str()); if (errno == EPERM) { + // Linux では setcap(8) が必要。分かりづらいのでメッセージを出すが + // その例示より先に errmsg を表示したい。 + warnx("%s", errmsg.c_str()); + errmsg.clear(); + fprintf(stderr, " ** Set CAP_NET_ADMIN capability to the nono executable file." "\n" @@ -189,7 +189,7 @@ NetDriverTap::OpenLinux() #endif // __linux__ // tap インタフェースを作成する。BSD で直接指定の場合。 -// 他の Open*() とは混在しないので、ここで直接エラーメッセージを出力する。 +// エラーの場合は putmsg ログを出力し、errmsg をセットして false を返す。 // devpath がデバイス名で "/dev/tap1" なら tap1 インタフェースが出来るはず? bool NetDriverTap::OpenDirect() @@ -198,8 +198,9 @@ NetDriverTap::OpenDirect() // 取り出せばインタフェース名になるはず。それ以外の運用されたらシラン。 auto pos = devpath.rfind("tap"); if (pos == std::string::npos) { - warnx("NetDriverTap.OpenDirect: %s: device name must contain \"tap\"", + errmsg = string_format("%s: devpath must contain \"tap\"", devpath.c_str()); + putmsg(1, "%s: %s", __func__, errmsg.c_str()); return false; } ifname = devpath.substr(pos); @@ -207,7 +208,8 @@ NetDriverTap::OpenDirect() // tapN デバイスオープン fd = open(devpath.c_str(), O_RDWR); if (fd < 0) { - warn("NetDriverTap.OpenDirect: %s", devpath.c_str()); + errmsg = string_format("%s: %s", devpath.c_str(), strerror(errno)); + putmsg(1, "%s: %s", __func__, errmsg.c_str()); return false; } @@ -215,8 +217,7 @@ NetDriverTap::OpenDirect() } // tap インタフェースを作成する。BSD で /dev/tapN を順に試す場合。 -// エラーの場合はログには直ちに出力して、warn() で表示するメッセージを -// errmsg にセットして返す。 +// エラーの場合は putmsg ログを出力し、errmsg をセットして false を返す。 bool NetDriverTap::OpenNumbered() { @@ -235,22 +236,20 @@ NetDriverTap::OpenNumbered() continue; } // オープンできたら終了 - putmsg(1, "%s: %s: found", __func__, devpath.c_str()); return true; } // 見付からなかった if (fd < 0) { + // ここでは何も表示しなくてもだいたい分かる errmsg = "no tap devices available"; - putmsg(1, "%s: %s", __func__, errmsg.c_str()); return false; } - return false; + return true; } // tap インタフェースを作成する。NetBSD で /dev/tap による cloning の場合。 -// エラーの場合はログには直ちに出力して、warn() で表示するメッセージを -// errmsg にセットして返す。 +// エラーの場合は putmsg ログを出力し、errmsg をセットして false を返す。 bool NetDriverTap::OpenCloning() { @@ -261,6 +260,7 @@ NetDriverTap::OpenCloning() // 割り当てることは合法だが、ここではデバイスファイル名でどの方式を // 使うかを選択しているので /dev/tap という名前以外の選択肢はない。 devpath = "/dev/tap"; + putmsg(1, "%s: use fixed devpath: %s", __func__, devpath.c_str()); // tap デバイスオープン fd = open(devpath.c_str(), O_RDWR); @@ -273,8 +273,8 @@ NetDriverTap::OpenCloning() // オープンされたこのディスクリプタのインタフェース名を取得。 memset(&ifr, 0, sizeof(ifr)); if (ioctl(fd, TAPGIFNAME, &ifr) < 0) { - errmsg = string_format("%s: TAPGIFNAME: %s", - devpath.c_str(), strerror(errno)); + errmsg = string_format("%s: %s: %s", + devpath.c_str(), "TAPGIFNAME", strerror(errno)); putmsg(1, "%s: %s", __func__, errmsg.c_str()); CloseFd(); return false; @@ -311,6 +311,14 @@ NetDriverTap::Close() } } +// モニタ (ドライバ依存情報のみ) +void +NetDriverTap::MonitorUpdateMD(TextScreen& screen) +{ + screen.Print(0, 1, "Device : %s", devpath.c_str()); + screen.Print(0, 2, "Interface: %s", ifname.c_str()); +} + // パケットを送信する bool NetDriverTap::SendPacket(const std::vector& data) @@ -336,77 +344,16 @@ NetDriverTap::SendPacket(const std::vect return true; } -// 受信スレッド -void -NetDriverTap::ThreadRun() -{ - struct kevent kev; - autofd kq; - int r; - - kq = kqueue(); - if (kq == -1) { - warn("kqueue"); - return; - } - - EV_SET(&kev, fd, EVFILT_READ, EV_ADD, 0, 0, (kevent_udata_t)0); - r = kevent_set(kq, &kev, 1); - if (r == -1) { - warn("kevent_set(fd=%d)", fd); - return; - } - - for (;;) { - uint8 buf[1600]; - int n; - - // 何か起きるまで待つ - r = kevent_poll(kq, &kev, 1, NULL); - if (r == -1) { - if (errno == EINTR) { - continue; - } - // XXX ? - warn("kevent_poll"); - break; - } - - // 自動的にデバイスに着信があったことになるはず - assert(r > 0); - - n = read(fd, buf, sizeof(buf)); - if (n == -1) { - if (errno == EINTR) { - continue; - } - warn("read"); - break; - } - if (n == 0) { // EOF - return; - } - - // カーネルが tap(4) に直接書き込んだパケット(というかフレームか)は - // 60バイトパディングされないまま読み出せるので、ここでパディングする。 - if (n < 60) { - memset(buf + n, 0, 60 - n); - n = 60; - } - - // CRC XXX 計算すること - memset(buf + n, 0, 4); - n += 4; - - // 受信処理に引き渡す - // ただしデバッガプロンプトで停止してる状態なら VM に干渉しない。 - if (parent->RecvEnabled && gCVPrompt->IsPrompt() == false) { - putlog(2, "RecvPacket %d bytes", n); - rx_pkts++; - rx_bytes += n; - - qvector recvbuf(buf, buf + n); - parent->RecvPacket(recvbuf); - } +// パケットを受信する +ssize_t +NetDriverTap::RecvPacket() +{ + uint8 buf[10240]; + ssize_t rv; + + rv = read(fd, buf, sizeof(buf)); + if (rv > 0) { + RecvEnqueue(buf, rv); } + return rv; }