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

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 <fcntl.h>
                     10: #include <poll.h>
                     11: #include <sys/ioctl.h>
                     12: #include <sys/socket.h>
                     13: #if defined(__linux__)
                     14: #include <linux/if.h>
                     15: #include <linux/if_tun.h>
1.1.1.3   root       16: #elif defined(__OpenBSD__)
                     17: #include <net/if_tun.h>
1.1       root       18: #else
                     19: #include <ifaddrs.h>
                     20: #include <net/if.h>
                     21: #include <net/if_dl.h>
                     22: #include <net/if_tap.h>
                     23: #endif
                     24: 
1.1.1.3   root       25: // Linux
                     26: //     /dev/net/tun をオープンし、ioctl(TUNSETIFF) を発行すると tap
                     27: //     インタフェースが生えて、そのインタフェース名が返ってくる。
                     28: //
                     29: // OpenBSD
                     30: //     /dev/tapN をオープンする。インタフェース名は対応する tapN のはず。
                     31: //
                     32: // NetBSD (dynamic)
                     33: //     /dev/tap をオープンすると tap インタフェースが生えるので
                     34: //     ioctl(TAPGIFNAME) でインタフェース名を取得する。
                     35: //     /dev/tapN をオープンする方式も使える。
                     36: 
1.1       root       37: // コンストラクタ
1.1.1.5 ! root       38: NetDriverTap::NetDriverTap(EthernetDevice *parent_, int loglevel_,
1.1.1.3   root       39:        const std::string& devpath_)
1.1.1.5 ! root       40:        : inherited("Tap", parent_, loglevel_)
1.1       root       41: {
1.1.1.3   root       42:        devpath = devpath_;
1.1.1.5 ! root       43:        if (devpath.empty()) {
        !            44:                devpath = "auto";
        !            45:        }
1.1       root       46: }
                     47: 
                     48: // デストラクタ
                     49: NetDriverTap::~NetDriverTap()
                     50: {
                     51:        Close();
                     52: }
                     53: 
1.1.1.5 ! root       54: // ドライバ初期化 & オープン
1.1       root       55: bool
1.1.1.5 ! root       56: NetDriverTap::InitDriver()
1.1       root       57: {
1.1.1.3   root       58:        bool r;
                     59: 
1.1.1.5 ! root       60:        putmsg(1, "trying tap...");
        !            61:        putmsg(1, "argument: devpath=\"%s\"", devpath.c_str());
1.1.1.3   root       62: 
                     63:        // デバイス名や tap の作り方は OS ごとに異なる。
                     64:        // もっと言えば、なんか適当にオープンしてほしいデフォルト挙動も全部違う。
                     65:        //
                     66:        // いずれの Open*() 関数も (必要なら) devpath に応じてデバイスをオープンし
                     67:        // 成功すれば fd にデバイスのディスクリプタ、ifname に作成した
                     68:        // インタフェース名をセットして true を返すこと。
1.1.1.5 ! root       69:        // 失敗すればデバイスをクローズして putmsg() を出力し false を返すこと。
        !            70:        // warnx() での出力は行わない(この後他ドライバで成功するかも知れないので)。
        !            71:        // その場合の ifname は不問。
1.1.1.3   root       72: #if defined(__linux__)
                     73:        // Linux はたぶん固定
                     74:        r = OpenLinux();
1.1.1.5 ! root       75:        if (r == false) {
        !            76:                // 単独試行なのでここで終了
        !            77:                return false;
        !            78:        }
1.1.1.3   root       79: 
                     80: #elif defined(__OpenBSD__)
                     81:        if (devpath == "auto") {
                     82:                // 指定されてなければ、tapN を順に試す
                     83:                r = OpenNumbered();
1.1.1.5 ! root       84:                // FALLTHROUGH
1.1.1.3   root       85:        } else {
                     86:                // ご指名の場合
                     87:                r = OpenDirect();
1.1.1.5 ! root       88:                if (r == false) {
        !            89:                        // 単独試行なのでここで終了
        !            90:                        return false;
        !            91:                }
1.1.1.3   root       92:        }
                     93: 
                     94: #else
                     95:        if (devpath == "auto") {
                     96:                // 指定されてなければ、tap、tapN の順で試すか
                     97:                r = OpenCloning();
                     98:                if (r == false) {
                     99:                        r = OpenNumbered();
                    100:                }
1.1.1.5 ! root      101:                // FALLTHROUGH
1.1.1.3   root      102:        } else {
                    103:                // 指定されている場合 tap か tapN かで対応が違う
                    104:                if (devpath == "/dev/tap") {
                    105:                        // tap ご指名の場合
                    106:                        r = OpenCloning();
                    107:                } else {
                    108:                        // それ以外でご指名の場合
                    109:                        r = OpenDirect();
1.1.1.5 ! root      110:                }
        !           111:                if (r == false) {
        !           112:                        // 単独試行なのでここで終了
        !           113:                        return false;
1.1.1.3   root      114:                }
                    115:        }
                    116: 
                    117: #endif
                    118:        if (r == false) {
1.1.1.5 ! root      119:                // 複数試した結果見付からない時だけこのメッセージを出したい
        !           120:                errmsg = "no tap devices available";
        !           121:                putmsg(1, "%s", errmsg.c_str());
1.1.1.3   root      122:                return false;
                    123:        }
                    124:        // ここで ifname に作成したインタフェース名が入っているはず
1.1.1.5 ! root      125:        putmsg(1, "opened %s for %s", devpath.c_str(), ifname.c_str());
1.1.1.3   root      126: 
                    127:        // 最後に ifup スクリプトがあれば実行
                    128:        if (!Run_ifup()) {
                    129:                // どこまで実行できたか分からないので念のため ifdown も呼ぶ
                    130:                Close();
                    131:                return false;
                    132:        }
                    133: 
                    134:        return true;
                    135: }
                    136: 
                    137: #if defined(__linux__)
                    138: // tap インタフェースを作成する。Linux の場合。
1.1.1.5 ! root      139: // エラーの場合は putmsg ログを出力し、errmsg をセットして false を返す。
1.1.1.3   root      140: bool
                    141: NetDriverTap::OpenLinux()
                    142: {
1.1       root      143:        struct ifreq ifr;
                    144:        int r;
                    145: 
1.1.1.3   root      146:        // 指定がなければデフォルトパス
1.1.1.4   root      147:        if (devpath == "auto") {
1.1.1.3   root      148:                devpath = "/dev/net/tun";
1.1.1.5 ! root      149:                putmsg(1, "auto-selected devpath: \"%s\"", devpath.c_str());
1.1.1.3   root      150:        }
                    151: 
                    152:        // デバイスオープン
1.1       root      153:        fd = open(devpath.c_str(), O_RDWR);
1.1.1.3   root      154:        if (fd < 0) {
1.1.1.5 ! root      155:                errmsg = string_format("%s: %s", devpath.c_str(), strerror(errno));
        !           156:                putmsg(1, "%s: %s", __func__, errmsg.c_str());
1.1       root      157:                return false;
                    158:        }
                    159: 
1.1.1.3   root      160:        // インタフェース作成。これには CAP_NET_ADMIN 権限が必要
1.1       root      161:        ifr.ifr_flags = IFF_TAP | IFF_NO_PI;    // tap として使う
                    162:        strlcpy(ifr.ifr_name, "tap%d", sizeof(ifr.ifr_name));
                    163:        r = ioctl(fd, TUNSETIFF, (void *)&ifr);
1.1.1.3   root      164:        if (r < 0) {
1.1.1.5 ! root      165:                errmsg = string_format("%s: %s: %s",
        !           166:                        devpath.c_str(), "TUNSETIFF", strerror(errno));
        !           167:                putmsg(1, "%s: %s", __func__, errmsg.c_str());
1.1       root      168:                if (errno == EPERM) {
1.1.1.5 ! root      169:                        // Linux では setcap(8) が必要。分かりづらいのでメッセージを出すが
        !           170:                        // その例示より先に errmsg を表示したい。
        !           171:                        warnx("%s", errmsg.c_str());
        !           172:                        errmsg.clear();
        !           173: 
1.1       root      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 で直接指定の場合。
1.1.1.5 ! root      192: // エラーの場合は putmsg ログを出力し、errmsg をセットして false を返す。
1.1.1.3   root      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) {
1.1.1.5 ! root      201:                errmsg = string_format("%s: devpath must contain \"tap\"",
1.1.1.3   root      202:                        devpath.c_str());
1.1.1.5 ! root      203:                putmsg(1, "%s: %s", __func__, errmsg.c_str());
1.1.1.3   root      204:                return false;
1.1       root      205:        }
1.1.1.3   root      206:        ifname = devpath.substr(pos);
1.1       root      207: 
1.1.1.3   root      208:        // tapN デバイスオープン
                    209:        fd = open(devpath.c_str(), O_RDWR);
                    210:        if (fd < 0) {
1.1.1.5 ! root      211:                errmsg = string_format("%s: %s", devpath.c_str(), strerror(errno));
        !           212:                putmsg(1, "%s: %s", __func__, errmsg.c_str());
1.1       root      213:                return false;
                    214:        }
                    215: 
                    216:        return true;
1.1.1.3   root      217: }
1.1       root      218: 
1.1.1.3   root      219: // tap インタフェースを作成する。BSD で /dev/tapN を順に試す場合。
1.1.1.5 ! root      220: // エラーの場合は putmsg ログを出力し、errmsg をセットして false を返す。
1.1.1.3   root      221: bool
                    222: NetDriverTap::OpenNumbered()
                    223: {
                    224:        // 最大値は適当
                    225:        for (int i = 0; i < 10; i++) {
                    226:                ifname = string_format("tap%d", i);
                    227:                devpath = "/dev/" + ifname;
                    228: 
                    229:                fd = open(devpath.c_str(), O_RDWR);
                    230:                if (fd < 0) {
                    231:                        // ファイルがないのを表示したら結構うるさいので除く
                    232:                        if (errno != ENOENT) {
1.1.1.4   root      233:                                putmsg(1, "%s: %s: %s",
                    234:                                        __func__, devpath.c_str(), strerror(errno));
1.1.1.3   root      235:                        }
                    236:                        continue;
                    237:                }
                    238:                // オープンできたら終了
                    239:                return true;
                    240:        }
                    241: 
                    242:        // 見付からなかった
                    243:        if (fd < 0) {
1.1.1.5 ! root      244:                // ここでは何も表示しなくてもだいたい分かる
1.1.1.3   root      245:                errmsg = "no tap devices available";
                    246:                return false;
                    247:        }
1.1.1.5 ! root      248:        return true;
1.1.1.3   root      249: }
                    250: 
                    251: // tap インタフェースを作成する。NetBSD で /dev/tap による cloning の場合。
1.1.1.5 ! root      252: // エラーの場合は putmsg ログを出力し、errmsg をセットして false を返す。
1.1.1.3   root      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";
1.1.1.5 ! root      263:        putmsg(1, "%s: use fixed devpath: %s", __func__, devpath.c_str());
1.1.1.3   root      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.5 ! root      276:                errmsg = string_format("%s: %s: %s",
        !           277:                        devpath.c_str(), "TAPGIFNAME", strerror(errno));
1.1.1.4   root      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: 
1.1.1.5 ! root      314: // モニタ (ドライバ依存情報のみ)
        !           315: void
        !           316: NetDriverTap::MonitorUpdateMD(TextScreen& screen)
        !           317: {
        !           318:        screen.Print(0, 1, "Device   : %s", devpath.c_str());
        !           319:        screen.Print(0, 2, "Interface: %s", ifname.c_str());
        !           320: }
        !           321: 
1.1       root      322: // パケットを送信する
                    323: bool
                    324: NetDriverTap::SendPacket(const std::vector<uint8>& data)
                    325: {
                    326:        int n;
                    327: 
                    328:        assert(IsOpen());
                    329: 
                    330:        putlog(2, "SendPacket %zd bytes", data.size());
                    331:        tx_pkts++;
                    332:        tx_bytes += data.size();
                    333: 
                    334:        n = write(fd, data.data(), data.size());
                    335:        if (n == -1) {
                    336:                warn("SendPacket: write failed: fd=%d datalen=%zu",
                    337:                        fd, data.size());
                    338:                return false;
                    339:        }
                    340: 
                    341:        if (n < (int)data.size()) {
                    342:                return false;
                    343:        }
                    344:        return true;
                    345: }
                    346: 
1.1.1.5 ! root      347: // パケットを受信する
        !           348: ssize_t
        !           349: NetDriverTap::RecvPacket()
        !           350: {
        !           351:        uint8 buf[10240];
        !           352:        ssize_t rv;
        !           353: 
        !           354:        rv = read(fd, buf, sizeof(buf));
        !           355:        if (rv > 0) {
        !           356:                RecvEnqueue(buf, rv);
1.1       root      357:        }
1.1.1.5 ! root      358:        return rv;
1.1       root      359: }

unix.superglobalmegacorp.com

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