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

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:        }
        !            69:        putmsg(1, "NetDriverTap: %s", devpath.c_str());
        !            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:        // 指定がなければデフォルトパス
        !           156:        if (devpath.empty()) {
        !           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) {
        !           163:                warnx("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) {
        !           172:                warn("NetDriverTap.OpenLinux: TUNSETIFF failed");
1.1       root      173:                if (errno == EPERM) {
                    174:                        fprintf(stderr,
                    175:                                " ** Set CAP_NET_ADMIN capability to the execute file.\n"
                    176:                                " **  ex) sudo setcap cap_net_admin=ep path/to/%s\n",
                    177:                                getprogname());
                    178:                }
1.1.1.3 ! root      179:                CloseFd();
        !           180:                return false;
1.1       root      181:        }
                    182: 
                    183:        // 作成できたインタフェース名を取得
                    184:        ifname = std::string(ifr.ifr_name);
                    185: 
1.1.1.3 ! root      186:        return true;
        !           187: }
        !           188: #endif // __linux__
        !           189: 
        !           190: // tap インタフェースを作成する。BSD で直接指定の場合。
        !           191: // 他の Open*() とは混在しないので、ここで直接エラーメッセージを出力する。
        !           192: // devpath がデバイス名で "/dev/tap1" なら tap1 インタフェースが出来るはず?
        !           193: bool
        !           194: NetDriverTap::OpenDirect()
        !           195: {
        !           196:        // devpath は "/dev/tapNN" という書式のはずなので、tapNN のところを
        !           197:        // 取り出せばインタフェース名になるはず。それ以外の運用されたらシラン。
        !           198:        auto pos = devpath.rfind("tap");
        !           199:        if (pos == std::string::npos) {
        !           200:                warnx("NetDriverTap.OpenDirect: %s: device name must contain \"tap\"",
        !           201:                        devpath.c_str());
        !           202:                return false;
1.1       root      203:        }
1.1.1.3 ! root      204:        ifname = devpath.substr(pos);
1.1       root      205: 
1.1.1.3 ! root      206:        // tapN デバイスオープン
        !           207:        fd = open(devpath.c_str(), O_RDWR);
        !           208:        if (fd < 0) {
        !           209:                warn("NetDriverTap.OpenDirect: %s", devpath.c_str());
1.1       root      210:                return false;
                    211:        }
                    212: 
                    213:        return true;
1.1.1.3 ! root      214: }
1.1       root      215: 
1.1.1.3 ! root      216: // tap インタフェースを作成する。BSD で /dev/tapN を順に試す場合。
        !           217: // エラーの場合はログには直ちに出力して、warn() で表示するメッセージを
        !           218: // errmsg にセットして返す。
        !           219: bool
        !           220: NetDriverTap::OpenNumbered()
        !           221: {
        !           222:        // 最大値は適当
        !           223:        for (int i = 0; i < 10; i++) {
        !           224:                ifname = string_format("tap%d", i);
        !           225:                devpath = "/dev/" + ifname;
        !           226: 
        !           227:                fd = open(devpath.c_str(), O_RDWR);
        !           228:                if (fd < 0) {
        !           229:                        // ファイルがないのを表示したら結構うるさいので除く
        !           230:                        if (errno != ENOENT) {
        !           231:                                putmsg(1, "OpenNumbered: %s: %s",
        !           232:                                        devpath.c_str(), strerror(errno));
        !           233:                        }
        !           234:                        continue;
        !           235:                }
        !           236:                // オープンできたら終了
        !           237:                putmsg(1, "OpenNumbered: %s: found", devpath.c_str());
        !           238:                return true;
        !           239:        }
        !           240: 
        !           241:        // 見付からなかった
        !           242:        if (fd < 0) {
        !           243:                errmsg = "no tap devices available";
        !           244:                putmsg(1, "OpenNumbered: %s", errmsg.c_str());
        !           245:                return false;
        !           246:        }
        !           247:        return false;
        !           248: }
        !           249: 
        !           250: // tap インタフェースを作成する。NetBSD で /dev/tap による cloning の場合。
        !           251: // エラーの場合はログには直ちに出力して、warn() で表示するメッセージを
        !           252: // errmsg にセットして返す。
        !           253: bool
        !           254: NetDriverTap::OpenCloning()
        !           255: {
        !           256: #if defined(TAPGIFNAME)
        !           257:        struct ifreq ifr;
        !           258: 
        !           259:        // /dev/tap 以外のデバイスファイル名に /dev/tap の major/minor 番号を
        !           260:        // 割り当てることは合法だが、ここではデバイスファイル名でどの方式を
        !           261:        // 使うかを選択しているので /dev/tap という名前以外の選択肢はない。
        !           262:        devpath = "/dev/tap";
        !           263: 
        !           264:        // tap デバイスオープン
        !           265:        fd = open(devpath.c_str(), O_RDWR);
        !           266:        if (fd < 0) {
        !           267:                errmsg = string_format("%s: %s", devpath.c_str(), strerror(errno));
        !           268:                putmsg(1, "OpenCloning: %s", errmsg.c_str());
        !           269:                return false;
        !           270:        }
        !           271: 
        !           272:        // オープンされたこのディスクリプタのインタフェース名を取得。
        !           273:        memset(&ifr, 0, sizeof(ifr));
        !           274:        if (ioctl(fd, TAPGIFNAME, &ifr) < 0) {
        !           275:                errmsg = devpath + ": TAPGIFNAME failed";
        !           276:                putmsg(1, "OpenCloning: %s", errmsg.c_str());
        !           277:                CloseFd();
        !           278:                return false;
        !           279:        }
        !           280: 
        !           281:        // 作成できたインタフェース名を取得
        !           282:        ifname = std::string(ifr.ifr_name);
        !           283: 
        !           284:        return true;
        !           285: #else
        !           286:        errmsg = "TAPGIFNAME not compiled(?)";
        !           287:        putmsg(1, "OpenCloning: %s", errmsg.c_str());
        !           288:        return false;
        !           289: #endif
        !           290: }
        !           291: 
        !           292: // fd のクローズ
        !           293: void
        !           294: NetDriverTap::CloseFd()
        !           295: {
1.1       root      296:        close(fd);
                    297:        fd = -1;
                    298: }
                    299: 
                    300: // クローズ
                    301: // (private)
                    302: void
                    303: NetDriverTap::Close()
                    304: {
                    305:        if (IsOpen()) {
                    306:                Run_ifdown();
                    307: 
1.1.1.3 ! root      308:                CloseFd();
1.1       root      309:        }
                    310: }
                    311: 
                    312: // パケットを送信する
                    313: bool
                    314: NetDriverTap::SendPacket(const std::vector<uint8>& data)
                    315: {
                    316:        int n;
                    317: 
                    318:        assert(IsOpen());
                    319: 
                    320:        putlog(2, "SendPacket %zd bytes", data.size());
                    321:        tx_pkts++;
                    322:        tx_bytes += data.size();
                    323: 
                    324:        n = write(fd, data.data(), data.size());
                    325:        if (n == -1) {
                    326:                warn("SendPacket: write failed: fd=%d datalen=%zu",
                    327:                        fd, data.size());
                    328:                return false;
                    329:        }
                    330: 
                    331:        if (n < (int)data.size()) {
                    332:                return false;
                    333:        }
                    334:        return true;
                    335: }
                    336: 
                    337: // 受信スレッド
                    338: void
                    339: NetDriverTap::ThreadRun()
                    340: {
                    341:        struct kevent kev;
1.1.1.3 ! root      342:        autofd kq;
1.1       root      343:        int r;
                    344: 
                    345:        kq = kqueue();
                    346:        if (kq == -1) {
                    347:                warn("kqueue failed");
                    348:                return;
                    349:        }
                    350: 
                    351:        EV_SET(&kev, fd, EVFILT_READ, EV_ADD, 0, 0, (kevent_udata_t)0);
                    352:        r = kevent_set(kq, &kev, 1);
                    353:        if (r == -1) {
                    354:                warn("kevent_set(fd=%d) failed", fd);
                    355:                return;
                    356:        }
                    357: 
                    358:        for (;;) {
                    359:                uint8 buf[1600];
                    360:                int n;
                    361: 
                    362:                // 何か起きるまで待つ
                    363:                r = kevent_poll(kq, &kev, 1, NULL);
                    364:                if (r == -1) {
                    365:                        if (errno == EINTR) {
                    366:                                continue;
                    367:                        }
                    368:                        // XXX ?
                    369:                        warn("kevent_poll failed");
                    370:                        break;
                    371:                }
                    372: 
                    373:                // 自動的にデバイスに着信があったことになるはず
                    374:                assert(r > 0);
                    375: 
                    376:                n = read(fd, buf, sizeof(buf));
                    377:                if (n == -1) {
                    378:                        if (errno == EINTR) {
                    379:                                continue;
                    380:                        }
                    381:                        warn("read(fd=%d) failed", fd);
                    382:                        break;
                    383:                }
                    384:                if (n == 0) {   // EOF
                    385:                        return;
                    386:                }
                    387: 
                    388:                // カーネルが tap(4) に直接書き込んだパケット(というかフレームか)は
                    389:                // 60バイトパディングされないまま読み出せるので、ここでパディングする。
                    390:                if (n < 60) {
                    391:                        memset(buf + n, 0, 60 - n);
                    392:                        n = 60;
                    393:                }
                    394: 
                    395:                // CRC XXX 計算すること
                    396:                memset(buf + n, 0, 4);
                    397:                n += 4;
                    398: 
                    399:                // 受信処理に引き渡す
                    400:                // ただしデバッガプロンプトで停止してる状態なら VM に干渉しない。
                    401:                if (parent->RecvEnabled && gCVPrompt->IsPrompt() == false) {
                    402:                        putlog(2, "RecvPacket %d bytes", n);
                    403:                        rx_pkts++;
                    404:                        rx_bytes += n;
                    405: 
                    406:                        qvector<uint8> recvbuf(buf, buf + n);
                    407:                        parent->RecvPacket(recvbuf);
                    408:                }
                    409:        }
                    410: }

unix.superglobalmegacorp.com

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