|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.