Annotation of nono/host/netdriver_bpf.cpp, revision 1.1.1.4

1.1       root        1: //
                      2: // nono
1.1.1.3   root        3: // Copyright (C) 2020 nono project
                      4: // Licensed under nono-license.txt
1.1       root        5: //
                      6: 
                      7: #include "netdriver_bpf.h"
1.1.1.4 ! root        8: #include "autofd.h"
1.1       root        9: #include "cvprompt.h"
                     10: #include <fcntl.h>
                     11: #include <ifaddrs.h>
                     12: #include <sys/ioctl.h>
                     13: #include <sys/socket.h>
                     14: #if defined(HAVE_SYS_EVENT_H)
                     15: #include <sys/event.h>
                     16: #endif
                     17: #if defined(HAVE_KQUEUE_SYS_EVENT_H)
                     18: #include <kqueue/sys/event.h>
                     19: #endif
                     20: #include <net/bpf.h>
                     21: #include <net/if.h>
                     22: #include <net/if_dl.h>
                     23: #include <net/if_types.h>
                     24: 
                     25: // コンストラクタ
1.1.1.3   root       26: NetDriverBPF::NetDriverBPF(EthernetDevice *parent_,
                     27:        std::mutex *mtx_, std::condition_variable *cv_)
                     28:        : inherited("BPF", parent_, mtx_, cv_)
1.1       root       29: {
                     30:        fd = -1;
                     31: }
                     32: 
                     33: // デストラクタ
                     34: NetDriverBPF::~NetDriverBPF()
                     35: {
                     36:        Close();
                     37: }
                     38: 
                     39: // 初期化 & オープン
                     40: bool
                     41: NetDriverBPF::Init()
                     42: {
                     43:        struct ifreq ifr;
                     44:        struct bpf_program prog;
                     45:        u_int val;
                     46:        struct bpf_insn insn[] = {
                     47:                // すべてのパケットを全長受信
                     48:                BPF_STMT(BPF_RET + BPF_K, (u_int)-1),
                     49:        };
                     50: 
1.1.1.4 ! root       51:        putmsg(1, "NetDriverBPF");
        !            52: 
1.1       root       53:        // XXX tap はデバイスを開くとインタフェースが出来る、という方向だが、
                     54:        //     bpf はインタフェースを指定してデバイスを開く、という方向で、逆…。
                     55: 
                     56:        if (ifname.empty()) {
                     57:                // インタフェース指定がなければそれっぽいのを探すか
                     58:                ifname = FindInterface();
                     59:                if (ifname.empty()) {
1.1.1.4 ! root       60:                        warn("NetDriverBPF.Open: no interface specified");
1.1       root       61:                        return false;
                     62:                }
                     63:        }
                     64: 
                     65:        // 最大値は適当
                     66:        fd = -1;
                     67:        for (int i = -1; i < 10; i++) {
                     68:                // i == -1 なら /dev/bpf、0..10 は /dev/bpfN。
                     69:                // まず /dev/bpf を cloning デバイスだと思って開いてみる。
                     70:                // そうでなければ従来どおり 0 から順番に試してみる。
                     71:                if (i == -1) {
                     72:                        devpath = "/dev/bpf";
                     73:                } else {
                     74:                        devpath = string_format("/dev/bpf%d", i);
                     75:                }
                     76: 
                     77:                fd = open(devpath.c_str(), O_RDWR);
1.1.1.4 ! root       78:                if (fd < 0) {
        !            79:                        // ファイルがないのを表示したら結構うるさいので除く
        !            80:                        if (errno != ENOENT) {
        !            81:                                putmsg(1, "%s: %s", devpath.c_str(), strerror(errno));
        !            82:                        }
1.1       root       83:                        continue;
                     84:                }
1.1.1.4 ! root       85:                putmsg(1, "%s: found", devpath.c_str());
1.1       root       86:                break;
                     87:        }
1.1.1.4 ! root       88:        if (fd < 0) {
        !            89:                putmsg(1, "no bpf devices available");
        !            90:                warnx("NetDriverBPF.Open: no bpf devices available");
1.1       root       91:                return false;
                     92:        }
                     93: 
                     94:        // インタフェースにバインド
                     95:        memset(&ifr, 0, sizeof(ifr));
                     96:        strlcpy(ifr.ifr_name, ifname.c_str(), sizeof(ifr.ifr_name));
                     97:        if (ioctl(fd, BIOCSETIF, &ifr) == -1) {
1.1.1.4 ! root       98:                warn("NetDriverBPF.Open: %s(%s): BIOCSETIF",
1.1       root       99:                        devpath.c_str(), ifname.c_str());
                    100:                goto abort;
                    101:        }
                    102:        // バインドまで出来たところでログを出しておきたい
                    103:        // XXX 本当は warn じゃないよね
                    104:        warnx("NetDriverBPF.Open: %s opened for %s",
                    105:                devpath.c_str(), ifname.c_str());
                    106: 
                    107:        if (ioctl(fd, BIOCPROMISC, (void *)0) == -1) {
1.1.1.4 ! root      108:                warn("NetDriverBPF.Open: %s(%s): BIOCPROMISC",
1.1       root      109:                        devpath.c_str(), ifname.c_str());
                    110:                goto abort;
                    111:        }
                    112: 
                    113:        val = 1;
                    114:        if (ioctl(fd, BIOCIMMEDIATE, &val) == -1) {
1.1.1.4 ! root      115:                warn("NetDriverBPF.Open: %s(%s): BIOCIMMEDIATE",
1.1       root      116:                        devpath.c_str(), ifname.c_str());
                    117:                goto abort;
                    118:        }
                    119: 
                    120:        if (ioctl(fd, BIOCSHDRCMPLT, &val) == -1) {
1.1.1.4 ! root      121:                warn("NetDriverBPF.Open: %s(%s): BIOCSHDRCMPLT",
1.1       root      122:                        devpath.c_str(), ifname.c_str());
                    123:                goto abort;
                    124:        }
                    125: 
                    126:        // XXX BIOCFEEDBACK...
                    127:        warn("NetDriverBPF.Open: %s(%s): ローカルホストとのみ通信はできません",
                    128:                devpath.c_str(), ifname.c_str());
                    129: 
                    130:        // フィルタをセット
                    131:        prog.bf_len = countof(insn);
                    132:        prog.bf_insns = insn;
                    133:        if (ioctl(fd, BIOCSETF, &prog) == -1) {
1.1.1.4 ! root      134:                warn("NetDriverBPF.Open: %s(%s): BIOCSETF",
1.1       root      135:                        devpath.c_str(), ifname.c_str());
                    136:                goto abort;
                    137:        }
                    138: 
                    139:        // バッファサイズを取得
                    140:        if (ioctl(fd, BIOCGBLEN, &bpfbuflen) == -1) {
1.1.1.4 ! root      141:                warn("NetDriverBPF.Open: %s(%s): BIOCGBLEN",
1.1       root      142:                        devpath.c_str(), ifname.c_str());
                    143:                goto abort;
                    144:        }
                    145: 
                    146:        // 受信用バッファを確保
                    147:        try {
1.1.1.2   root      148:                bpfbuf.reset(new char [bpfbuflen]);
1.1       root      149:        } catch (...) {
                    150:                warnx("NetDriverBPF.Open: %s(%s): memory allocation failed",
                    151:                        devpath.c_str(), ifname.c_str());
                    152:                goto abort;
                    153:        }
                    154: 
                    155:        // オープンできたので受信スレッド起動。
                    156:        if (InitRecvThread() == false) {
                    157:                goto abort;
                    158:        }
                    159: 
                    160:        // bpf では ifup スクリプトは実行しない
                    161: 
                    162:        return true;
                    163: 
                    164:  abort:
                    165:        close(fd);
                    166:        fd = -1;
                    167:        return false;
                    168: }
                    169: 
                    170: // クローズ
                    171: // (private)
                    172: void
                    173: NetDriverBPF::Close()
                    174: {
                    175:        if (IsOpen()) {
                    176:                close(fd);
                    177:                fd = -1;
                    178:        }
                    179: }
                    180: 
                    181: // パケットを送信する
                    182: bool
                    183: NetDriverBPF::SendPacket(const std::vector<uint8>& data)
                    184: {
                    185:        int n;
                    186: 
                    187:        assert(IsOpen());
                    188: 
                    189:        putlog(2, "SendPacket %zu bytes", data.size());
                    190:        tx_pkts++;
                    191:        tx_bytes += data.size();
                    192: 
                    193:        n = write(fd, data.data(), data.size());
                    194:        if (n == -1) {
                    195:                warn("NetDriverBPF.SendPacket: write failed: fd=%d datalen=%zu",
                    196:                        fd, data.size());
                    197:                return false;
                    198:        }
                    199: 
                    200:        if (n < (int)data.size()) {
                    201:                return false;
                    202:        }
                    203:        return true;
                    204: }
                    205: 
                    206: // 最初に見付かった使えそうなインタフェース名を返す。
                    207: // 物理インタフェースが明らかに1つしかないところで、
                    208: // インタフェース名の指定を省略できるようにするため。
                    209: std::string
                    210: NetDriverBPF::FindInterface()
                    211: {
                    212:        char name[IFNAMSIZ];
                    213:        struct ifaddrs *ifa_list, *ifa;
                    214:        struct sockaddr_dl *dl;
                    215: 
                    216:        if (getifaddrs(&ifa_list) == -1) {
                    217:                return "";
                    218:        }
                    219: 
                    220:        name[0] = '\0';
                    221:        for (ifa = ifa_list; ifa; ifa = ifa->ifa_next) {
                    222:                dl = (struct sockaddr_dl *)ifa->ifa_addr;
                    223:                if ((ifa->ifa_flags & IFF_UP) != 0 && dl->sdl_type == IFT_ETHER) {
                    224:                        strlcpy(name, ifa->ifa_name, sizeof(name));
                    225:                        break;
                    226:                }
                    227:        }
                    228:        freeifaddrs(ifa_list);
                    229: 
                    230:        return std::string(name);
                    231: }
                    232: 
                    233: // 受信スレッド
                    234: void
                    235: NetDriverBPF::ThreadRun()
                    236: {
                    237:        struct kevent kev;
1.1.1.4 ! root      238:        autofd kq;
1.1       root      239:        int r;
                    240: 
                    241:        kq = kqueue();
                    242:        if (kq == -1) {
                    243:                warn("kqueue failed");
                    244:                return;
                    245:        }
                    246: 
                    247:        EV_SET(&kev, fd, EVFILT_READ, EV_ADD, 0, 0, (kevent_udata_t)0);
                    248:        r = kevent_set(kq, &kev, 1);
                    249:        if (r == -1) {
                    250:                warn("kevent_set(fd=%d) failed", fd);
                    251:                return;
                    252:        }
                    253: 
                    254:        for (;;) {
                    255:                int n;
                    256:                char *bpfptr;
                    257:                int bpfcaplen;
                    258: 
                    259:                // 何か起きるまで待つ
                    260:                r = kevent_poll(kq, &kev, 1, NULL);
                    261:                if (r == -1) {
                    262:                        if (errno == EINTR) {
                    263:                                continue;
                    264:                        }
                    265:                        // XXX ?
                    266:                        warn("NetDriverBPF.ThreadRun: kevent_poll failed");
                    267:                        break;
                    268:                }
                    269: 
                    270:                // 自動的にデバイスに着信があったことになるはず
                    271:                assert(r > 0);
                    272: 
1.1.1.2   root      273:                n = read(fd, bpfbuf.get(), bpfbuflen);
1.1       root      274:                if (n == -1) {
                    275:                        if (errno == EINTR) {
                    276:                                continue;
                    277:                        }
                    278:                        warn("NetDriverBPF.ThreadRun: read(fd=%d) failed", fd);
                    279:                        break;
                    280:                }
                    281:                if (n == 0) {   // EOF
                    282:                        return;
                    283:                }
1.1.1.2   root      284:                bpfptr = bpfbuf.get();
1.1       root      285:                bpfcaplen = n;
                    286: 
1.1.1.2   root      287:                while (bpfptr < bpfbuf.get() + bpfcaplen) {
1.1       root      288:                        uint8 buf[1600];
                    289: 
                    290:                        // BPF ヘッダを除いた部分
                    291:                        struct bpf_hdr *bh = (struct bpf_hdr *)bpfptr;
                    292:                        n = bh->bh_caplen;
                    293:                        memcpy(buf, bpfptr + bh->bh_hdrlen, n);
                    294: 
                    295:                        // 次のフレームの先頭を指す
                    296:                        int framelen = BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen);
                    297:                        bpfptr += framelen;
                    298: 
                    299:                        // 60バイトパディングされないまま読み出せたらここでパディングする。
                    300:                        if (n < 60) {
                    301:                                memset(buf + n, 0, 60 - n);
                    302:                                n = 60;
                    303:                        }
                    304: 
                    305:                        // CRC XXX 計算すること
                    306:                        memset(buf + n, 0, 4);
                    307:                        n += 4;
                    308: 
                    309:                        // 受信処理に引き渡す
                    310:                        // ただしデバッガプロンプトで停止してる状態なら VM に干渉しない。
                    311:                        if (parent->RecvEnabled && gCVPrompt->IsPrompt() == false) {
                    312:                                putlog(2, "RecvPacket %d bytes", n);
                    313:                                rx_pkts++;
                    314:                                rx_bytes += n;
                    315: 
                    316:                                qvector<uint8> recvbuf(buf, buf + n);
                    317:                                parent->RecvPacket(recvbuf);
                    318:                        }
                    319:                }
                    320:        }
                    321: }

unix.superglobalmegacorp.com

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