--- nono/host/netdriver_bpf.cpp 2026/04/29 17:04:28 1.1 +++ nono/host/netdriver_bpf.cpp 2026/04/29 17:05:11 1.1.1.6 @@ -1,67 +1,65 @@ // // nono -// Copyright (C) 2019 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt +// + +// +// ホストネットワークの BPF ドライバ // -#include "header.h" #include "netdriver_bpf.h" -#include "cvprompt.h" -#include "mystring.h" +#include "autofd.h" +#include "hostnet.h" #include #include #include #include -#if defined(HAVE_SYS_EVENT_H) -#include -#endif -#if defined(HAVE_KQUEUE_SYS_EVENT_H) -#include -#endif #include #include #include #include // コンストラクタ -NetDriverBPF::NetDriverBPF(EthernetDevice *p, - std::mutex *m, std::condition_variable *c) - : inherited("BPF", p, m, c) +NetDriverBPF::NetDriverBPF(HostDevice *hostdev_, const std::string& ifname_) + : inherited(hostdev_, "BPF") { - fd = -1; + ifname = ifname_; + if (ifname.empty()) { + ifname = "auto"; + } } // デストラクタ NetDriverBPF::~NetDriverBPF() { Close(); - - if (bpfbuf) { - delete[] bpfbuf; - } } -// 初期化 & オープン +// ドライバ初期化 bool -NetDriverBPF::Init() +NetDriverBPF::InitDriver() { struct ifreq ifr; struct bpf_program prog; u_int val; + u_int bpfbuflen; struct bpf_insn insn[] = { // すべてのパケットを全長受信 BPF_STMT(BPF_RET + BPF_K, (u_int)-1), }; - // XXX tap はデバイスを開くとインタフェースが出来る、という方向だが、 - // bpf はインタフェースを指定してデバイスを開く、という方向で、逆…。 + putmsg(1, "trying bpf..."); + putmsg(1, "argument: ifname=\"%s\"", ifname.c_str()); - if (ifname.empty()) { + if (ifname == "auto") { // インタフェース指定がなければそれっぽいのを探すか ifname = FindInterface(); if (ifname.empty()) { - warn("NetDriverBPF: no interface specified"); - return false; + errmsg = "auto could not find a suitable interface"; + goto abort; } + putmsg(1, "auto-selected ifname: \"%s\"", ifname.c_str()); } // 最大値は適当 @@ -77,93 +75,83 @@ NetDriverBPF::Init() } fd = open(devpath.c_str(), O_RDWR); - if (fd == -1) { - warn("NetDriverBPF.Open: skip %s", devpath.c_str()); + if (fd < 0) { + // ファイルがないのを表示したら結構うるさいので除く + if (errno != ENOENT) { + putmsg(1, "open %s: %s", devpath.c_str(), strerror(errno)); + } continue; } + // 成功したら抜ける break; } - if (fd == -1) { - warnx("NetDriverBPF.Open: no bpf device available"); - return false; + if (fd < 0) { + errmsg = "no bpf devices available"; + goto abort; } // インタフェースにバインド memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifname.c_str(), sizeof(ifr.ifr_name)); if (ioctl(fd, BIOCSETIF, &ifr) == -1) { - warn("NetDriverBPF.Open: %s(%s) BIOCSETIF failed", - devpath.c_str(), ifname.c_str()); + errmsg = string_format("%s(%s): BIOCSETIF: %s", + devpath.c_str(), ifname.c_str(), strerror(errno)); goto abort; } - // バインドまで出来たところでログを出しておきたい - // XXX 本当は warn じゃないよね - warnx("NetDriverBPF.Open: %s opened for %s", - devpath.c_str(), ifname.c_str()); if (ioctl(fd, BIOCPROMISC, (void *)0) == -1) { - warn("NetDriverBPF.Open: %s(%s): BIOCPROMISC failed", - devpath.c_str(), ifname.c_str()); + errmsg = string_format("%s(%s): %s: %s", + devpath.c_str(), ifname.c_str(), "BIOCPROMISC", strerror(errno)); goto abort; } val = 1; if (ioctl(fd, BIOCIMMEDIATE, &val) == -1) { - warn("NetDriverBPF.Open: %s(%s): BIOCIMMEDIATE failed", - devpath.c_str(), ifname.c_str()); + errmsg = string_format("%s(%s): %s: %s", + devpath.c_str(), ifname.c_str(), "BIOCIMMEDIATE", strerror(errno)); goto abort; } if (ioctl(fd, BIOCSHDRCMPLT, &val) == -1) { - warn("NetDriverBPF.Open: %s(%s): BIOCSHDRCMPLT failed", - devpath.c_str(), ifname.c_str()); + errmsg = string_format("%s(%s): %s: %s", + devpath.c_str(), ifname.c_str(), "BIOCSHDRCMPLT", strerror(errno)); goto abort; } - // XXX BIOCFEEDBACK... - warn("NetDriverBPF.Open: %s(%s): ローカルホストとのみ通信はできません", - devpath.c_str(), ifname.c_str()); - // フィルタをセット prog.bf_len = countof(insn); prog.bf_insns = insn; if (ioctl(fd, BIOCSETF, &prog) == -1) { - warn("NetDriverBPF.Open: %s(%s): BIOCSETF failed", - devpath.c_str(), ifname.c_str()); + errmsg = string_format("%s(%s): %s: %s", + devpath.c_str(), ifname.c_str(), "BIOCSETF", strerror(errno)); goto abort; } // バッファサイズを取得 if (ioctl(fd, BIOCGBLEN, &bpfbuflen) == -1) { - warn("NetDriverBPF.Open: %s(%s): BIOCGBLEN failed", - devpath.c_str(), ifname.c_str()); + errmsg = string_format("%s(%s): %s: %s", + devpath.c_str(), ifname.c_str(), "BIOCGBLEN", strerror(errno)); goto abort; } // 受信用バッファを確保 - try { - bpfbuf = new char [bpfbuflen]; - } catch (...) { - bpfbuf = NULL; - } - if (bpfbuf == NULL) { - warnx("NetDriverBPF.Open: %s(%s): memory allocation failed", - devpath.c_str(), ifname.c_str()); - goto abort; - } + bpfbuf.resize(bpfbuflen); - // オープンできたので受信スレッド起動。 - if (InitRecvThread() == false) { - goto abort; - } + putmsg(1, "opened %s for %s", devpath.c_str(), ifname.c_str()); + putmsg(1, "warning: communicating with the localhost is not possible"); // bpf では ifup スクリプトは実行しない + if (hostdev->AddOuter(fd) < 0) { + putmsg(0, "AddOuter(fd=%d): %s", (int)fd, strerror(errno)); + return false; + } + return true; abort: - close(fd); - fd = -1; + putmsg(1, "%s", errmsg.c_str()); + Close(); return false; } @@ -172,42 +160,79 @@ NetDriverBPF::Init() void NetDriverBPF::Close() { - if (IsOpen()) { - close(fd); - fd = -1; + if (fd.Valid()) { + hostdev->DelOuter(fd); + fd.Close(); } } +// モニタ (ドライバ依存情報のみ) +void +NetDriverBPF::MonitorUpdateMD(TextScreen& screen) +{ + screen.Print(0, 1, "Device : %s", devpath.c_str()); + screen.Print(0, 2, "Interface: %s", ifname.c_str()); +} + // パケットを送信する -bool -NetDriverBPF::SendPacket(const std::vector& data) +void +NetDriverBPF::Write(const void *buf, int buflen) { - int n; + ssize_t n; - assert(IsOpen()); + n = write(fd, buf, buflen); + if (n < 0) { + putmsg(0, "write: %s", strerror(errno)); + return; + } + if (n < buflen) { + putmsg(0, "write: short"); + return; + } +} - putlog(2, "SendPacket %zu bytes", data.size()); - tx_pkts++; - tx_bytes += data.size(); - - n = write(fd, data.data(), data.size()); - if (n == -1) { - warn("NetDriverBPF.SendPacket: write failed: fd=%d datalen=%zu", - fd, data.size()); - return false; +// パケットを受信する +int +NetDriverBPF::Read(NetPacket *p) +{ + uint8 *buf = bpfbuf.data(); + + if (bpfptr == NULL) { + bpflen = read(fd, bpfbuf.data(), bpfbuf.size()); + if (bpflen < 0) { + putmsg(0, "read: %s", strerror(errno)); + return NODATA; + } + + bpfptr = buf; } - if (n < (int)data.size()) { - return false; + // BPF ヘッダを除いた部分が 1 パケット + struct bpf_hdr *bh = (struct bpf_hdr *)bpfptr; + + NetPacket& packet = *p; + memcpy(packet.data(), bpfptr + bh->bh_hdrlen, bh->bh_caplen); + packet.length = bh->bh_caplen; + + // 次のフレームの先頭を指す + int framelen = BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen); + bpfptr += framelen; + + if (bpfptr < buf + bpflen) { + // 次がある + return 1; + } else { + // 次はない + bpfptr = NULL; + return 0; } - return true; } // 最初に見付かった使えそうなインタフェース名を返す。 // 物理インタフェースが明らかに1つしかないところで、 // インタフェース名の指定を省略できるようにするため。 std::string -NetDriverBPF::FindInterface() +NetDriverBPF::FindInterface() const { char name[IFNAMSIZ]; struct ifaddrs *ifa_list, *ifa; @@ -229,94 +254,3 @@ NetDriverBPF::FindInterface() return std::string(name); } - -// 受信スレッド -void -NetDriverBPF::ThreadRun() -{ - struct kevent kev; - int kq; - int r; - - kq = kqueue(); - if (kq == -1) { - warn("kqueue failed"); - 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) failed", fd); - close(kq); - return; - } - - for (;;) { - int n; - char *bpfptr; - int bpfcaplen; - - // 何か起きるまで待つ - r = kevent_poll(kq, &kev, 1, NULL); - if (r == -1) { - if (errno == EINTR) { - continue; - } - // XXX ? - warn("NetDriverBPF.ThreadRun: kevent_poll failed"); - break; - } - - // 自動的にデバイスに着信があったことになるはず - assert(r > 0); - - n = read(fd, bpfbuf, bpfbuflen); - if (n == -1) { - if (errno == EINTR) { - continue; - } - warn("NetDriverBPF.ThreadRun: read(fd=%d) failed", fd); - break; - } - if (n == 0) { // EOF - return; - } - bpfptr = bpfbuf; - bpfcaplen = n; - - while (bpfptr < bpfbuf + bpfcaplen) { - uint8 buf[1600]; - - // BPF ヘッダを除いた部分 - struct bpf_hdr *bh = (struct bpf_hdr *)bpfptr; - n = bh->bh_caplen; - memcpy(buf, bpfptr + bh->bh_hdrlen, n); - - // 次のフレームの先頭を指す - int framelen = BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen); - bpfptr += framelen; - - // 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); - } - } - } -}