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

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: 
1.1.1.6 ! root        7: //
        !             8: // ホストネットワークの tap ドライバ
        !             9: //
        !            10: 
1.1       root       11: #include "netdriver_tap.h"
1.1.1.3   root       12: #include "autofd.h"
1.1.1.6 ! root       13: #include "hostnet.h"
1.1       root       14: #include <fcntl.h>
                     15: #include <sys/ioctl.h>
                     16: #include <sys/socket.h>
                     17: #if defined(__linux__)
                     18: #include <linux/if.h>
                     19: #include <linux/if_tun.h>
1.1.1.3   root       20: #elif defined(__OpenBSD__)
                     21: #include <net/if_tun.h>
1.1       root       22: #else
                     23: #include <ifaddrs.h>
                     24: #include <net/if.h>
                     25: #include <net/if_dl.h>
                     26: #include <net/if_tap.h>
                     27: #endif
                     28: 
1.1.1.3   root       29: // Linux
                     30: //     /dev/net/tun をオープンし、ioctl(TUNSETIFF) を発行すると tap
                     31: //     インタフェースが生えて、そのインタフェース名が返ってくる。
                     32: //
                     33: // OpenBSD
                     34: //     /dev/tapN をオープンする。インタフェース名は対応する tapN のはず。
                     35: //
                     36: // NetBSD (dynamic)
                     37: //     /dev/tap をオープンすると tap インタフェースが生えるので
                     38: //     ioctl(TAPGIFNAME) でインタフェース名を取得する。
                     39: //     /dev/tapN をオープンする方式も使える。
                     40: 
1.1       root       41: // コンストラクタ
1.1.1.6 ! root       42: NetDriverTap::NetDriverTap(HostDevice *hostdev_, const std::string& devpath_)
        !            43:        : inherited(hostdev_, "Tap")
1.1       root       44: {
1.1.1.3   root       45:        devpath = devpath_;
1.1.1.5   root       46:        if (devpath.empty()) {
                     47:                devpath = "auto";
                     48:        }
1.1       root       49: }
                     50: 
                     51: // デストラクタ
                     52: NetDriverTap::~NetDriverTap()
                     53: {
                     54:        Close();
                     55: }
                     56: 
1.1.1.6 ! root       57: // ドライバ初期化
1.1       root       58: bool
1.1.1.5   root       59: NetDriverTap::InitDriver()
1.1       root       60: {
1.1.1.3   root       61:        bool r;
                     62: 
1.1.1.5   root       63:        putmsg(1, "trying tap...");
                     64:        putmsg(1, "argument: devpath=\"%s\"", devpath.c_str());
1.1.1.3   root       65: 
                     66:        // デバイス名や tap の作り方は OS ごとに異なる。
                     67:        // もっと言えば、なんか適当にオープンしてほしいデフォルト挙動も全部違う。
                     68:        //
                     69:        // いずれの Open*() 関数も (必要なら) devpath に応じてデバイスをオープンし
                     70:        // 成功すれば fd にデバイスのディスクリプタ、ifname に作成した
                     71:        // インタフェース名をセットして true を返すこと。
1.1.1.5   root       72:        // 失敗すればデバイスをクローズして putmsg() を出力し false を返すこと。
                     73:        // warnx() での出力は行わない(この後他ドライバで成功するかも知れないので)。
                     74:        // その場合の ifname は不問。
1.1.1.3   root       75: #if defined(__linux__)
                     76:        // Linux はたぶん固定
                     77:        r = OpenLinux();
1.1.1.5   root       78:        if (r == false) {
                     79:                // 単独試行なのでここで終了
                     80:                return false;
                     81:        }
1.1.1.3   root       82: 
                     83: #elif defined(__OpenBSD__)
                     84:        if (devpath == "auto") {
                     85:                // 指定されてなければ、tapN を順に試す
                     86:                r = OpenNumbered();
1.1.1.5   root       87:                // FALLTHROUGH
1.1.1.3   root       88:        } else {
                     89:                // ご指名の場合
                     90:                r = OpenDirect();
1.1.1.5   root       91:                if (r == false) {
                     92:                        // 単独試行なのでここで終了
                     93:                        return false;
                     94:                }
1.1.1.3   root       95:        }
                     96: 
                     97: #else
                     98:        if (devpath == "auto") {
                     99:                // 指定されてなければ、tap、tapN の順で試すか
                    100:                r = OpenCloning();
                    101:                if (r == false) {
                    102:                        r = OpenNumbered();
                    103:                }
1.1.1.5   root      104:                // FALLTHROUGH
1.1.1.3   root      105:        } else {
                    106:                // 指定されている場合 tap か tapN かで対応が違う
                    107:                if (devpath == "/dev/tap") {
                    108:                        // tap ご指名の場合
                    109:                        r = OpenCloning();
                    110:                } else {
                    111:                        // それ以外でご指名の場合
                    112:                        r = OpenDirect();
1.1.1.5   root      113:                }
                    114:                if (r == false) {
                    115:                        // 単独試行なのでここで終了
                    116:                        return false;
1.1.1.3   root      117:                }
                    118:        }
                    119: 
                    120: #endif
                    121:        if (r == false) {
1.1.1.5   root      122:                // 複数試した結果見付からない時だけこのメッセージを出したい
                    123:                errmsg = "no tap devices available";
                    124:                putmsg(1, "%s", errmsg.c_str());
1.1.1.3   root      125:                return false;
                    126:        }
                    127:        // ここで ifname に作成したインタフェース名が入っているはず
1.1.1.5   root      128:        putmsg(1, "opened %s for %s", devpath.c_str(), ifname.c_str());
1.1.1.3   root      129: 
                    130:        // 最後に ifup スクリプトがあれば実行
                    131:        if (!Run_ifup()) {
                    132:                // どこまで実行できたか分からないので念のため ifdown も呼ぶ
                    133:                Close();
                    134:                return false;
                    135:        }
                    136: 
1.1.1.6 ! root      137:        if (hostdev->AddOuter(fd) < 0) {
        !           138:                putmsg(0, "AddOuter(fd=%d): %s", (int)fd, strerror(errno));
        !           139:                return false;
        !           140:        }
        !           141: 
1.1.1.3   root      142:        return true;
                    143: }
                    144: 
                    145: #if defined(__linux__)
                    146: // tap インタフェースを作成する。Linux の場合。
1.1.1.5   root      147: // エラーの場合は putmsg ログを出力し、errmsg をセットして false を返す。
1.1.1.3   root      148: bool
                    149: NetDriverTap::OpenLinux()
                    150: {
1.1       root      151:        struct ifreq ifr;
                    152:        int r;
                    153: 
1.1.1.3   root      154:        // 指定がなければデフォルトパス
1.1.1.4   root      155:        if (devpath == "auto") {
1.1.1.3   root      156:                devpath = "/dev/net/tun";
1.1.1.5   root      157:                putmsg(1, "auto-selected devpath: \"%s\"", devpath.c_str());
1.1.1.3   root      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.5   root      163:                errmsg = string_format("%s: %s", devpath.c_str(), strerror(errno));
                    164:                putmsg(1, "%s: %s", __func__, errmsg.c_str());
1.1       root      165:                return false;
                    166:        }
                    167: 
1.1.1.3   root      168:        // インタフェース作成。これには CAP_NET_ADMIN 権限が必要
1.1       root      169:        ifr.ifr_flags = IFF_TAP | IFF_NO_PI;    // tap として使う
                    170:        strlcpy(ifr.ifr_name, "tap%d", sizeof(ifr.ifr_name));
                    171:        r = ioctl(fd, TUNSETIFF, (void *)&ifr);
1.1.1.3   root      172:        if (r < 0) {
1.1.1.5   root      173:                errmsg = string_format("%s: %s: %s",
                    174:                        devpath.c_str(), "TUNSETIFF", strerror(errno));
                    175:                putmsg(1, "%s: %s", __func__, errmsg.c_str());
1.1       root      176:                if (errno == EPERM) {
1.1.1.5   root      177:                        // Linux では setcap(8) が必要。分かりづらいのでメッセージを出すが
                    178:                        // その例示より先に errmsg を表示したい。
                    179:                        warnx("%s", errmsg.c_str());
                    180:                        errmsg.clear();
                    181: 
1.1       root      182:                        fprintf(stderr,
1.1.1.4   root      183:                                " ** Set CAP_NET_ADMIN capability to the nono executable file."
                    184:                                "\n"
1.1       root      185:                                " **  ex) sudo setcap cap_net_admin=ep path/to/%s\n",
                    186:                                getprogname());
                    187:                }
1.1.1.6 ! root      188:                fd.Close();
1.1.1.3   root      189:                return false;
1.1       root      190:        }
                    191: 
                    192:        // 作成できたインタフェース名を取得
                    193:        ifname = std::string(ifr.ifr_name);
                    194: 
1.1.1.3   root      195:        return true;
                    196: }
                    197: #endif // __linux__
                    198: 
                    199: // tap インタフェースを作成する。BSD で直接指定の場合。
1.1.1.5   root      200: // エラーの場合は putmsg ログを出力し、errmsg をセットして false を返す。
1.1.1.3   root      201: // devpath がデバイス名で "/dev/tap1" なら tap1 インタフェースが出来るはず?
                    202: bool
                    203: NetDriverTap::OpenDirect()
                    204: {
                    205:        // devpath は "/dev/tapNN" という書式のはずなので、tapNN のところを
                    206:        // 取り出せばインタフェース名になるはず。それ以外の運用されたらシラン。
                    207:        auto pos = devpath.rfind("tap");
                    208:        if (pos == std::string::npos) {
1.1.1.5   root      209:                errmsg = string_format("%s: devpath must contain \"tap\"",
1.1.1.3   root      210:                        devpath.c_str());
1.1.1.5   root      211:                putmsg(1, "%s: %s", __func__, errmsg.c_str());
1.1.1.3   root      212:                return false;
1.1       root      213:        }
1.1.1.3   root      214:        ifname = devpath.substr(pos);
1.1       root      215: 
1.1.1.3   root      216:        // tapN デバイスオープン
                    217:        fd = open(devpath.c_str(), O_RDWR);
                    218:        if (fd < 0) {
1.1.1.5   root      219:                errmsg = string_format("%s: %s", devpath.c_str(), strerror(errno));
                    220:                putmsg(1, "%s: %s", __func__, errmsg.c_str());
1.1       root      221:                return false;
                    222:        }
                    223: 
                    224:        return true;
1.1.1.3   root      225: }
1.1       root      226: 
1.1.1.3   root      227: // tap インタフェースを作成する。BSD で /dev/tapN を順に試す場合。
1.1.1.5   root      228: // エラーの場合は putmsg ログを出力し、errmsg をセットして false を返す。
1.1.1.3   root      229: bool
                    230: NetDriverTap::OpenNumbered()
                    231: {
                    232:        // 最大値は適当
                    233:        for (int i = 0; i < 10; i++) {
                    234:                ifname = string_format("tap%d", i);
                    235:                devpath = "/dev/" + ifname;
                    236: 
                    237:                fd = open(devpath.c_str(), O_RDWR);
                    238:                if (fd < 0) {
                    239:                        // ファイルがないのを表示したら結構うるさいので除く
                    240:                        if (errno != ENOENT) {
1.1.1.4   root      241:                                putmsg(1, "%s: %s: %s",
                    242:                                        __func__, devpath.c_str(), strerror(errno));
1.1.1.3   root      243:                        }
                    244:                        continue;
                    245:                }
                    246:                // オープンできたら終了
                    247:                return true;
                    248:        }
                    249: 
                    250:        // 見付からなかった
                    251:        if (fd < 0) {
1.1.1.5   root      252:                // ここでは何も表示しなくてもだいたい分かる
1.1.1.3   root      253:                errmsg = "no tap devices available";
                    254:                return false;
                    255:        }
1.1.1.5   root      256:        return true;
1.1.1.3   root      257: }
                    258: 
                    259: // tap インタフェースを作成する。NetBSD で /dev/tap による cloning の場合。
1.1.1.5   root      260: // エラーの場合は putmsg ログを出力し、errmsg をセットして false を返す。
1.1.1.3   root      261: bool
                    262: NetDriverTap::OpenCloning()
                    263: {
                    264: #if defined(TAPGIFNAME)
                    265:        struct ifreq ifr;
                    266: 
                    267:        // /dev/tap 以外のデバイスファイル名に /dev/tap の major/minor 番号を
                    268:        // 割り当てることは合法だが、ここではデバイスファイル名でどの方式を
                    269:        // 使うかを選択しているので /dev/tap という名前以外の選択肢はない。
                    270:        devpath = "/dev/tap";
1.1.1.5   root      271:        putmsg(1, "%s: use fixed devpath: %s", __func__, devpath.c_str());
1.1.1.3   root      272: 
                    273:        // tap デバイスオープン
                    274:        fd = open(devpath.c_str(), O_RDWR);
                    275:        if (fd < 0) {
                    276:                errmsg = string_format("%s: %s", devpath.c_str(), strerror(errno));
1.1.1.4   root      277:                putmsg(1, "%s: %s", __func__, errmsg.c_str());
1.1.1.3   root      278:                return false;
                    279:        }
                    280: 
                    281:        // オープンされたこのディスクリプタのインタフェース名を取得。
                    282:        memset(&ifr, 0, sizeof(ifr));
                    283:        if (ioctl(fd, TAPGIFNAME, &ifr) < 0) {
1.1.1.5   root      284:                errmsg = string_format("%s: %s: %s",
                    285:                        devpath.c_str(), "TAPGIFNAME", strerror(errno));
1.1.1.4   root      286:                putmsg(1, "%s: %s", __func__, errmsg.c_str());
1.1.1.6 ! root      287:                fd.Close();
1.1.1.3   root      288:                return false;
                    289:        }
                    290: 
                    291:        // 作成できたインタフェース名を取得
                    292:        ifname = std::string(ifr.ifr_name);
                    293: 
                    294:        return true;
                    295: #else
                    296:        errmsg = "TAPGIFNAME not compiled(?)";
1.1.1.4   root      297:        putmsg(1, "%s: %s", __func__, errmsg.c_str());
1.1.1.3   root      298:        return false;
                    299: #endif
                    300: }
                    301: 
1.1       root      302: // クローズ
                    303: // (private)
                    304: void
                    305: NetDriverTap::Close()
                    306: {
1.1.1.6 ! root      307:        if (fd.Valid()) {
1.1       root      308:                Run_ifdown();
                    309: 
1.1.1.6 ! root      310:                hostdev->DelOuter(fd);
        !           311:                fd.Close();
1.1       root      312:        }
                    313: }
                    314: 
1.1.1.5   root      315: // モニタ (ドライバ依存情報のみ)
                    316: void
                    317: NetDriverTap::MonitorUpdateMD(TextScreen& screen)
                    318: {
                    319:        screen.Print(0, 1, "Device   : %s", devpath.c_str());
                    320:        screen.Print(0, 2, "Interface: %s", ifname.c_str());
                    321: }
                    322: 
1.1       root      323: // パケットを送信する
1.1.1.6 ! root      324: void
        !           325: NetDriverTap::Write(const void *buf, int buflen)
1.1       root      326: {
1.1.1.6 ! root      327:        ssize_t n;
1.1       root      328: 
1.1.1.6 ! root      329:        n = write(fd, buf, buflen);
        !           330:        if (n < 0) {
        !           331:                putmsg(0, "write: %s", strerror(errno));
        !           332:                return;
        !           333:        }
        !           334:        if (n < buflen) {
        !           335:                putmsg(0, "write: short");
        !           336:                return;
1.1       root      337:        }
                    338: }
                    339: 
1.1.1.5   root      340: // パケットを受信する
1.1.1.6 ! root      341: int
        !           342: NetDriverTap::Read(NetPacket *p)
1.1.1.5   root      343: {
1.1.1.6 ! root      344:        int n;
1.1.1.5   root      345: 
1.1.1.6 ! root      346:        n = read(fd, p->data(), p->size());
        !           347:        if (n < 0) {
        !           348:                putmsg(0, "read: %s", strerror(errno));
        !           349:                return NODATA;
1.1       root      350:        }
1.1.1.6 ! root      351:        p->length = n;
        !           352:        return 0;
1.1       root      353: }

unix.superglobalmegacorp.com

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