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

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

unix.superglobalmegacorp.com

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