|
|
1.1 ! root 1: // ! 2: // nono ! 3: // Copyright (C) 2019 [email protected] ! 4: // ! 5: ! 6: #include "header.h" ! 7: #include "netdriver_tap.h" ! 8: #include "cvprompt.h" ! 9: #include <fcntl.h> ! 10: #include <poll.h> ! 11: #include <sys/ioctl.h> ! 12: #include <sys/socket.h> ! 13: #if defined(HAVE_SYS_EVENT_H) ! 14: #include <sys/event.h> ! 15: #endif ! 16: #if defined(HAVE_KQUEUE_SYS_EVENT_H) ! 17: #include <kqueue/sys/event.h> ! 18: #endif ! 19: #if defined(__linux__) ! 20: #include <linux/if.h> ! 21: #include <linux/if_tun.h> ! 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: ! 29: // コンストラクタ ! 30: NetDriverTap::NetDriverTap(EthernetDevice *p, ! 31: std::mutex *m, std::condition_variable *c) ! 32: : inherited("BSD tap", p, m, c) ! 33: { ! 34: #if defined(__linux__) ! 35: devpath = "/dev/net/tun"; ! 36: #else ! 37: devpath = "/dev/tap"; ! 38: #endif ! 39: } ! 40: ! 41: // デストラクタ ! 42: NetDriverTap::~NetDriverTap() ! 43: { ! 44: Close(); ! 45: } ! 46: ! 47: // 初期化 & オープン ! 48: bool ! 49: NetDriverTap::Init() ! 50: { ! 51: struct ifreq ifr; ! 52: int r; ! 53: ! 54: // tap デバイスオープン ! 55: fd = open(devpath.c_str(), O_RDWR); ! 56: if (fd == -1) { ! 57: warn("open \"%s\" failed", devpath.c_str()); ! 58: return false; ! 59: } ! 60: ! 61: memset(&ifr, 0, sizeof(ifr)); ! 62: #if defined(__linux__) ! 63: // Linux なら、open() したデバイスに対して tap デバイス作成を指示、 ! 64: // その応答にインタフェース名が入っている。 ! 65: ifr.ifr_flags = IFF_TAP | IFF_NO_PI; // tap として使う ! 66: strlcpy(ifr.ifr_name, "tap%d", sizeof(ifr.ifr_name)); ! 67: // インタフェース作成。これには CAP_NET_ADMIN 権限が必要 ! 68: r = ioctl(fd, TUNSETIFF, (void *)&ifr); ! 69: if (r == -1) { ! 70: warn("Open: TUNSETIFF failed"); ! 71: if (errno == EPERM) { ! 72: fprintf(stderr, ! 73: " ** Set CAP_NET_ADMIN capability to the execute file.\n" ! 74: " ** ex) sudo setcap cap_net_admin=ep path/to/%s\n", ! 75: getprogname()); ! 76: } ! 77: goto abort; ! 78: } ! 79: #else ! 80: // NetBSD なら、open() でインタフェースは作成されているので、 ! 81: // オープンされたこのディスクリプタのインタフェース名を取得。 ! 82: r = ioctl(fd, TAPGIFNAME, &ifr); ! 83: if (r == -1) { ! 84: warn("Open: TAPGIFNAME failed"); ! 85: goto abort; ! 86: } ! 87: #endif ! 88: ! 89: // 作成できたインタフェース名を取得 ! 90: ifname = std::string(ifr.ifr_name); ! 91: ! 92: // オープンできたので受信スレッド起動。 ! 93: // ifup 実行前なので、ここで失敗しても ifdown 不要。 ! 94: if (InitRecvThread() == false) { ! 95: goto abort; ! 96: } ! 97: ! 98: // 最後に ifup スクリプトがあれば実行 ! 99: if (!Run_ifup()) { ! 100: // どこまで実行できたか分からないので念のため ifdown も呼ぶ ! 101: Close(); ! 102: return false; ! 103: } ! 104: ! 105: return true; ! 106: ! 107: // ifdown を実行させないために fd = -1 にして帰る。 ! 108: // (まだ ifup を実行していないので) ! 109: abort: ! 110: close(fd); ! 111: fd = -1; ! 112: return false; ! 113: } ! 114: ! 115: // クローズ ! 116: // (private) ! 117: void ! 118: NetDriverTap::Close() ! 119: { ! 120: if (IsOpen()) { ! 121: Run_ifdown(); ! 122: ! 123: close(fd); ! 124: fd = -1; ! 125: } ! 126: } ! 127: ! 128: // パケットを送信する ! 129: bool ! 130: NetDriverTap::SendPacket(const std::vector<uint8>& data) ! 131: { ! 132: int n; ! 133: ! 134: assert(IsOpen()); ! 135: ! 136: putlog(2, "SendPacket %zd bytes", data.size()); ! 137: tx_pkts++; ! 138: tx_bytes += data.size(); ! 139: ! 140: n = write(fd, data.data(), data.size()); ! 141: if (n == -1) { ! 142: warn("SendPacket: write failed: fd=%d datalen=%zu", ! 143: fd, data.size()); ! 144: return false; ! 145: } ! 146: ! 147: if (n < (int)data.size()) { ! 148: return false; ! 149: } ! 150: return true; ! 151: } ! 152: ! 153: // 受信スレッド ! 154: void ! 155: NetDriverTap::ThreadRun() ! 156: { ! 157: struct kevent kev; ! 158: int kq; ! 159: int r; ! 160: ! 161: kq = kqueue(); ! 162: if (kq == -1) { ! 163: warn("kqueue failed"); ! 164: return; ! 165: } ! 166: ! 167: EV_SET(&kev, fd, EVFILT_READ, EV_ADD, 0, 0, (kevent_udata_t)0); ! 168: r = kevent_set(kq, &kev, 1); ! 169: if (r == -1) { ! 170: warn("kevent_set(fd=%d) failed", fd); ! 171: close(kq); ! 172: return; ! 173: } ! 174: ! 175: for (;;) { ! 176: uint8 buf[1600]; ! 177: int n; ! 178: ! 179: // 何か起きるまで待つ ! 180: r = kevent_poll(kq, &kev, 1, NULL); ! 181: if (r == -1) { ! 182: if (errno == EINTR) { ! 183: continue; ! 184: } ! 185: // XXX ? ! 186: warn("kevent_poll failed"); ! 187: break; ! 188: } ! 189: ! 190: // 自動的にデバイスに着信があったことになるはず ! 191: assert(r > 0); ! 192: ! 193: n = read(fd, buf, sizeof(buf)); ! 194: if (n == -1) { ! 195: if (errno == EINTR) { ! 196: continue; ! 197: } ! 198: warn("read(fd=%d) failed", fd); ! 199: break; ! 200: } ! 201: if (n == 0) { // EOF ! 202: return; ! 203: } ! 204: ! 205: // カーネルが tap(4) に直接書き込んだパケット(というかフレームか)は ! 206: // 60バイトパディングされないまま読み出せるので、ここでパディングする。 ! 207: if (n < 60) { ! 208: memset(buf + n, 0, 60 - n); ! 209: n = 60; ! 210: } ! 211: ! 212: // CRC XXX 計算すること ! 213: memset(buf + n, 0, 4); ! 214: n += 4; ! 215: ! 216: // 受信処理に引き渡す ! 217: // ただしデバッガプロンプトで停止してる状態なら VM に干渉しない。 ! 218: if (parent->RecvEnabled && gCVPrompt->IsPrompt() == false) { ! 219: putlog(2, "RecvPacket %d bytes", n); ! 220: rx_pkts++; ! 221: rx_bytes += n; ! 222: ! 223: qvector<uint8> recvbuf(buf, buf + n); ! 224: parent->RecvPacket(recvbuf); ! 225: } ! 226: } ! 227: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.