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

1.1       root        1: //
                      2: // nono
1.1.1.2   root        3: // Copyright (C) 2020 nono project
                      4: // Licensed under nono-license.txt
1.1       root        5: //
                      6: 
                      7: #include "netdriver_tap.h"
1.1.1.3   root        8: #include "autofd.h"
1.1       root        9: #include "cvprompt.h"
                     10: #include <fcntl.h>
                     11: #include <poll.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: #if defined(__linux__)
                     21: #include <linux/if.h>
                     22: #include <linux/if_tun.h>
1.1.1.3   root       23: #elif defined(__OpenBSD__)
                     24: #include <net/if_tun.h>
1.1       root       25: #else
                     26: #include <ifaddrs.h>
                     27: #include <net/if.h>
                     28: #include <net/if_dl.h>
                     29: #include <net/if_tap.h>
                     30: #endif
                     31: 
1.1.1.3   root       32: // Linux
                     33: //     /dev/net/tun をオープンし、ioctl(TUNSETIFF) を発行すると tap
                     34: //     インタフェースが生えて、そのインタフェース名が返ってくる。
                     35: //
                     36: // OpenBSD
                     37: //     /dev/tapN をオープンする。インタフェース名は対応する tapN のはず。
                     38: //
                     39: // NetBSD (dynamic)
                     40: //     /dev/tap をオープンすると tap インタフェースが生えるので
                     41: //     ioctl(TAPGIFNAME) でインタフェース名を取得する。
                     42: //     /dev/tapN をオープンする方式も使える。
                     43: 
1.1       root       44: // コンストラクタ
1.1.1.2   root       45: NetDriverTap::NetDriverTap(EthernetDevice *parent_,
1.1.1.3   root       46:        std::mutex *mtx_, std::condition_variable *cv_,
                     47:        const std::string& devpath_)
1.1.1.2   root       48:        : inherited("BSD tap", parent_, mtx_, cv_)
1.1       root       49: {
1.1.1.3   root       50:        devpath = devpath_;
1.1       root       51: }
                     52: 
                     53: // デストラクタ
                     54: NetDriverTap::~NetDriverTap()
                     55: {
                     56:        Close();
                     57: }
                     58: 
                     59: // 初期化 & オープン
                     60: bool
                     61: NetDriverTap::Init()
                     62: {
1.1.1.3   root       63:        bool r;
                     64: 
                     65:        // 空文字列もデフォルトと同じ "auto" にする。
                     66:        if (devpath.empty()) {
                     67:                devpath = "auto";
                     68:        }
1.1.1.4 ! root       69:        putmsg(1, "NetDriverTap: device \"%s\"", devpath.c_str());
1.1.1.3   root       70: 
                     71:        // デバイス名や tap の作り方は OS ごとに異なる。
                     72:        // もっと言えば、なんか適当にオープンしてほしいデフォルト挙動も全部違う。
                     73:        //
                     74:        // いずれの Open*() 関数も (必要なら) devpath に応じてデバイスをオープンし
                     75:        // 成功すれば fd にデバイスのディスクリプタ、ifname に作成した
                     76:        // インタフェース名をセットして true を返すこと。
                     77:        // 失敗すればデバイスをクローズしてエラーメッセージを表示し false を返す
                     78:        // こと。その場合の ifname は不問。
                     79: #if defined(__linux__)
                     80:        // Linux はたぶん固定
                     81:        r = OpenLinux();
                     82: 
                     83: #elif defined(__OpenBSD__)
                     84:        if (devpath == "auto") {
                     85:                // 指定されてなければ、tapN を順に試す
                     86:                r = OpenNumbered();
                     87:                if (r == false) {
                     88:                        warnx("NetDriverTap.OpenNumbered: %s", errmsg.c_str());
                     89:                }
                     90:        } else {
                     91:                // ご指名の場合
                     92:                r = OpenDirect();
                     93:                // warnx は出力済み
                     94:        }
                     95: 
                     96: #else
                     97:        if (devpath == "auto") {
                     98:                // 指定されてなければ、tap、tapN の順で試すか
                     99:                r = OpenCloning();
                    100:                if (r == false) {
                    101:                        r = OpenNumbered();
                    102:                        if (r == false) {
                    103:                                warnx("NetDriverTap.Open: %s", errmsg.c_str());
                    104:                        }
                    105:                }
                    106:        } else {
                    107:                // 指定されている場合 tap か tapN かで対応が違う
                    108:                if (devpath == "/dev/tap") {
                    109:                        // tap ご指名の場合
                    110:                        r = OpenCloning();
                    111:                        if (r == false) {
                    112:                                warnx("NetDriverTap.OpenCloning: %s", errmsg.c_str());
                    113:                        }
                    114:                } else {
                    115:                        // それ以外でご指名の場合
                    116:                        r = OpenDirect();
                    117:                        // warnx は出力済み
                    118:                }
                    119:        }
                    120: 
                    121: #endif
                    122:        if (r == false) {
                    123:                // エラーメッセージなどは各呼び出し先で処理済み
                    124:                return false;
                    125:        }
                    126:        // ここで ifname に作成したインタフェース名が入っているはず
                    127:        putmsg(1, "ifname: %s", ifname.c_str());
                    128: 
                    129:        // オープンできたので受信スレッド起動。
                    130:        if (InitRecvThread() == false) {
                    131:                // ifup 実行前なのでまだ ifdown 不要。
                    132:                CloseFd();
                    133:                return false;
                    134:        }
                    135: 
                    136:        // 最後に ifup スクリプトがあれば実行
                    137:        if (!Run_ifup()) {
                    138:                // どこまで実行できたか分からないので念のため ifdown も呼ぶ
                    139:                Close();
                    140:                return false;
                    141:        }
                    142: 
                    143:        return true;
                    144: }
                    145: 
                    146: #if defined(__linux__)
                    147: // tap インタフェースを作成する。Linux の場合。
                    148: // 他の Open*() とは混在しないので、ここで直接エラーメッセージを出力する。
                    149: bool
                    150: NetDriverTap::OpenLinux()
                    151: {
1.1       root      152:        struct ifreq ifr;
                    153:        int r;
                    154: 
1.1.1.3   root      155:        // 指定がなければデフォルトパス
1.1.1.4 ! root      156:        if (devpath == "auto") {
1.1.1.3   root      157:                devpath = "/dev/net/tun";
                    158:        }
                    159: 
                    160:        // デバイスオープン
1.1       root      161:        fd = open(devpath.c_str(), O_RDWR);
1.1.1.3   root      162:        if (fd < 0) {
1.1.1.4 ! root      163:                warn("NetDriverTap.OpenLinux: %s", devpath.c_str());
1.1       root      164:                return false;
                    165:        }
                    166: 
1.1.1.3   root      167:        // インタフェース作成。これには CAP_NET_ADMIN 権限が必要
1.1       root      168:        ifr.ifr_flags = IFF_TAP | IFF_NO_PI;    // tap として使う
                    169:        strlcpy(ifr.ifr_name, "tap%d", sizeof(ifr.ifr_name));
                    170:        r = ioctl(fd, TUNSETIFF, (void *)&ifr);
1.1.1.3   root      171:        if (r < 0) {
1.1.1.4 ! root      172:                warn("NetDriverTap.OpenLinux: %s: TUNSETIFF", devpath.c_str());
1.1       root      173:                if (errno == EPERM) {
                    174:                        fprintf(stderr,
1.1.1.4 ! root      175:                                " ** Set CAP_NET_ADMIN capability to the nono executable file."
        !           176:                                "\n"
1.1       root      177:                                " **  ex) sudo setcap cap_net_admin=ep path/to/%s\n",
                    178:                                getprogname());
                    179:                }
1.1.1.3   root      180:                CloseFd();
                    181:                return false;
1.1       root      182:        }
                    183: 
                    184:        // 作成できたインタフェース名を取得
                    185:        ifname = std::string(ifr.ifr_name);
                    186: 
1.1.1.3   root      187:        return true;
                    188: }
                    189: #endif // __linux__
                    190: 
                    191: // tap インタフェースを作成する。BSD で直接指定の場合。
                    192: // 他の Open*() とは混在しないので、ここで直接エラーメッセージを出力する。
                    193: // devpath がデバイス名で "/dev/tap1" なら tap1 インタフェースが出来るはず?
                    194: bool
                    195: NetDriverTap::OpenDirect()
                    196: {
                    197:        // devpath は "/dev/tapNN" という書式のはずなので、tapNN のところを
                    198:        // 取り出せばインタフェース名になるはず。それ以外の運用されたらシラン。
                    199:        auto pos = devpath.rfind("tap");
                    200:        if (pos == std::string::npos) {
                    201:                warnx("NetDriverTap.OpenDirect: %s: device name must contain \"tap\"",
                    202:                        devpath.c_str());
                    203:                return false;
1.1       root      204:        }
1.1.1.3   root      205:        ifname = devpath.substr(pos);
1.1       root      206: 
1.1.1.3   root      207:        // tapN デバイスオープン
                    208:        fd = open(devpath.c_str(), O_RDWR);
                    209:        if (fd < 0) {
                    210:                warn("NetDriverTap.OpenDirect: %s", devpath.c_str());
1.1       root      211:                return false;
                    212:        }
                    213: 
                    214:        return true;
1.1.1.3   root      215: }
1.1       root      216: 
1.1.1.3   root      217: // tap インタフェースを作成する。BSD で /dev/tapN を順に試す場合。
                    218: // エラーの場合はログには直ちに出力して、warn() で表示するメッセージを
                    219: // errmsg にセットして返す。
                    220: bool
                    221: NetDriverTap::OpenNumbered()
                    222: {
                    223:        // 最大値は適当
                    224:        for (int i = 0; i < 10; i++) {
                    225:                ifname = string_format("tap%d", i);
                    226:                devpath = "/dev/" + ifname;
                    227: 
                    228:                fd = open(devpath.c_str(), O_RDWR);
                    229:                if (fd < 0) {
                    230:                        // ファイルがないのを表示したら結構うるさいので除く
                    231:                        if (errno != ENOENT) {
1.1.1.4 ! root      232:                                putmsg(1, "%s: %s: %s",
        !           233:                                        __func__, devpath.c_str(), strerror(errno));
1.1.1.3   root      234:                        }
                    235:                        continue;
                    236:                }
                    237:                // オープンできたら終了
1.1.1.4 ! root      238:                putmsg(1, "%s: %s: found", __func__, devpath.c_str());
1.1.1.3   root      239:                return true;
                    240:        }
                    241: 
                    242:        // 見付からなかった
                    243:        if (fd < 0) {
                    244:                errmsg = "no tap devices available";
1.1.1.4 ! root      245:                putmsg(1, "%s: %s", __func__, errmsg.c_str());
1.1.1.3   root      246:                return false;
                    247:        }
                    248:        return false;
                    249: }
                    250: 
                    251: // tap インタフェースを作成する。NetBSD で /dev/tap による cloning の場合。
                    252: // エラーの場合はログには直ちに出力して、warn() で表示するメッセージを
                    253: // errmsg にセットして返す。
                    254: bool
                    255: NetDriverTap::OpenCloning()
                    256: {
                    257: #if defined(TAPGIFNAME)
                    258:        struct ifreq ifr;
                    259: 
                    260:        // /dev/tap 以外のデバイスファイル名に /dev/tap の major/minor 番号を
                    261:        // 割り当てることは合法だが、ここではデバイスファイル名でどの方式を
                    262:        // 使うかを選択しているので /dev/tap という名前以外の選択肢はない。
                    263:        devpath = "/dev/tap";
                    264: 
                    265:        // tap デバイスオープン
                    266:        fd = open(devpath.c_str(), O_RDWR);
                    267:        if (fd < 0) {
                    268:                errmsg = string_format("%s: %s", devpath.c_str(), strerror(errno));
1.1.1.4 ! root      269:                putmsg(1, "%s: %s", __func__, errmsg.c_str());
1.1.1.3   root      270:                return false;
                    271:        }
                    272: 
                    273:        // オープンされたこのディスクリプタのインタフェース名を取得。
                    274:        memset(&ifr, 0, sizeof(ifr));
                    275:        if (ioctl(fd, TAPGIFNAME, &ifr) < 0) {
1.1.1.4 ! root      276:                errmsg = string_format("%s: TAPGIFNAME: %s",
        !           277:                        devpath.c_str(), strerror(errno));
        !           278:                putmsg(1, "%s: %s", __func__, errmsg.c_str());
1.1.1.3   root      279:                CloseFd();
                    280:                return false;
                    281:        }
                    282: 
                    283:        // 作成できたインタフェース名を取得
                    284:        ifname = std::string(ifr.ifr_name);
                    285: 
                    286:        return true;
                    287: #else
                    288:        errmsg = "TAPGIFNAME not compiled(?)";
1.1.1.4 ! root      289:        putmsg(1, "%s: %s", __func__, errmsg.c_str());
1.1.1.3   root      290:        return false;
                    291: #endif
                    292: }
                    293: 
                    294: // fd のクローズ
                    295: void
                    296: NetDriverTap::CloseFd()
                    297: {
1.1       root      298:        close(fd);
                    299:        fd = -1;
                    300: }
                    301: 
                    302: // クローズ
                    303: // (private)
                    304: void
                    305: NetDriverTap::Close()
                    306: {
                    307:        if (IsOpen()) {
                    308:                Run_ifdown();
                    309: 
1.1.1.3   root      310:                CloseFd();
1.1       root      311:        }
                    312: }
                    313: 
                    314: // パケットを送信する
                    315: bool
                    316: NetDriverTap::SendPacket(const std::vector<uint8>& data)
                    317: {
                    318:        int n;
                    319: 
                    320:        assert(IsOpen());
                    321: 
                    322:        putlog(2, "SendPacket %zd bytes", data.size());
                    323:        tx_pkts++;
                    324:        tx_bytes += data.size();
                    325: 
                    326:        n = write(fd, data.data(), data.size());
                    327:        if (n == -1) {
                    328:                warn("SendPacket: write failed: fd=%d datalen=%zu",
                    329:                        fd, data.size());
                    330:                return false;
                    331:        }
                    332: 
                    333:        if (n < (int)data.size()) {
                    334:                return false;
                    335:        }
                    336:        return true;
                    337: }
                    338: 
                    339: // 受信スレッド
                    340: void
                    341: NetDriverTap::ThreadRun()
                    342: {
                    343:        struct kevent kev;
1.1.1.3   root      344:        autofd kq;
1.1       root      345:        int r;
                    346: 
                    347:        kq = kqueue();
                    348:        if (kq == -1) {
1.1.1.4 ! root      349:                warn("kqueue");
1.1       root      350:                return;
                    351:        }
                    352: 
                    353:        EV_SET(&kev, fd, EVFILT_READ, EV_ADD, 0, 0, (kevent_udata_t)0);
                    354:        r = kevent_set(kq, &kev, 1);
                    355:        if (r == -1) {
1.1.1.4 ! root      356:                warn("kevent_set(fd=%d)", fd);
1.1       root      357:                return;
                    358:        }
                    359: 
                    360:        for (;;) {
                    361:                uint8 buf[1600];
                    362:                int n;
                    363: 
                    364:                // 何か起きるまで待つ
                    365:                r = kevent_poll(kq, &kev, 1, NULL);
                    366:                if (r == -1) {
                    367:                        if (errno == EINTR) {
                    368:                                continue;
                    369:                        }
                    370:                        // XXX ?
1.1.1.4 ! root      371:                        warn("kevent_poll");
1.1       root      372:                        break;
                    373:                }
                    374: 
                    375:                // 自動的にデバイスに着信があったことになるはず
                    376:                assert(r > 0);
                    377: 
                    378:                n = read(fd, buf, sizeof(buf));
                    379:                if (n == -1) {
                    380:                        if (errno == EINTR) {
                    381:                                continue;
                    382:                        }
1.1.1.4 ! root      383:                        warn("read");
1.1       root      384:                        break;
                    385:                }
                    386:                if (n == 0) {   // EOF
                    387:                        return;
                    388:                }
                    389: 
                    390:                // カーネルが tap(4) に直接書き込んだパケット(というかフレームか)は
                    391:                // 60バイトパディングされないまま読み出せるので、ここでパディングする。
                    392:                if (n < 60) {
                    393:                        memset(buf + n, 0, 60 - n);
                    394:                        n = 60;
                    395:                }
                    396: 
                    397:                // CRC XXX 計算すること
                    398:                memset(buf + n, 0, 4);
                    399:                n += 4;
                    400: 
                    401:                // 受信処理に引き渡す
                    402:                // ただしデバッガプロンプトで停止してる状態なら VM に干渉しない。
                    403:                if (parent->RecvEnabled && gCVPrompt->IsPrompt() == false) {
                    404:                        putlog(2, "RecvPacket %d bytes", n);
                    405:                        rx_pkts++;
                    406:                        rx_bytes += n;
                    407: 
                    408:                        qvector<uint8> recvbuf(buf, buf + n);
                    409:                        parent->RecvPacket(recvbuf);
                    410:                }
                    411:        }
                    412: }

unix.superglobalmegacorp.com

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