|
|
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: }
1.1.1.4 ! root 69: putmsg(1, "NetDriverTap: device \"%s\"", devpath.c_str());
1.1.1.3 root 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: // 指定がなければデフォルトパス
1.1.1.4 ! root 156: if (devpath == "auto") {
1.1.1.3 root 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) {
1.1.1.4 ! root 163: warn("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) {
1.1.1.4 ! root 172: warn("NetDriverTap.OpenLinux: %s: TUNSETIFF", devpath.c_str());
1.1 root 173: if (errno == EPERM) {
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 で直接指定の場合。
192: // 他の Open*() とは混在しないので、ここで直接エラーメッセージを出力する。
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) {
201: warnx("NetDriverTap.OpenDirect: %s: device name must contain \"tap\"",
202: devpath.c_str());
203: return false;
1.1 root 204: }
1.1.1.3 root 205: ifname = devpath.substr(pos);
1.1 root 206:
1.1.1.3 root 207: // tapN デバイスオープン
208: fd = open(devpath.c_str(), O_RDWR);
209: if (fd < 0) {
210: warn("NetDriverTap.OpenDirect: %s", devpath.c_str());
1.1 root 211: return false;
212: }
213:
214: return true;
1.1.1.3 root 215: }
1.1 root 216:
1.1.1.3 root 217: // tap インタフェースを作成する。BSD で /dev/tapN を順に試す場合。
218: // エラーの場合はログには直ちに出力して、warn() で表示するメッセージを
219: // errmsg にセットして返す。
220: bool
221: NetDriverTap::OpenNumbered()
222: {
223: // 最大値は適当
224: for (int i = 0; i < 10; i++) {
225: ifname = string_format("tap%d", i);
226: devpath = "/dev/" + ifname;
227:
228: fd = open(devpath.c_str(), O_RDWR);
229: if (fd < 0) {
230: // ファイルがないのを表示したら結構うるさいので除く
231: if (errno != ENOENT) {
1.1.1.4 ! root 232: putmsg(1, "%s: %s: %s",
! 233: __func__, devpath.c_str(), strerror(errno));
1.1.1.3 root 234: }
235: continue;
236: }
237: // オープンできたら終了
1.1.1.4 ! root 238: putmsg(1, "%s: %s: found", __func__, devpath.c_str());
1.1.1.3 root 239: return true;
240: }
241:
242: // 見付からなかった
243: if (fd < 0) {
244: errmsg = "no tap devices available";
1.1.1.4 ! root 245: putmsg(1, "%s: %s", __func__, errmsg.c_str());
1.1.1.3 root 246: return false;
247: }
248: return false;
249: }
250:
251: // tap インタフェースを作成する。NetBSD で /dev/tap による cloning の場合。
252: // エラーの場合はログには直ちに出力して、warn() で表示するメッセージを
253: // errmsg にセットして返す。
254: bool
255: NetDriverTap::OpenCloning()
256: {
257: #if defined(TAPGIFNAME)
258: struct ifreq ifr;
259:
260: // /dev/tap 以外のデバイスファイル名に /dev/tap の major/minor 番号を
261: // 割り当てることは合法だが、ここではデバイスファイル名でどの方式を
262: // 使うかを選択しているので /dev/tap という名前以外の選択肢はない。
263: devpath = "/dev/tap";
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.4 ! root 276: errmsg = string_format("%s: TAPGIFNAME: %s",
! 277: devpath.c_str(), strerror(errno));
! 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:
314: // パケットを送信する
315: bool
316: NetDriverTap::SendPacket(const std::vector<uint8>& data)
317: {
318: int n;
319:
320: assert(IsOpen());
321:
322: putlog(2, "SendPacket %zd bytes", data.size());
323: tx_pkts++;
324: tx_bytes += data.size();
325:
326: n = write(fd, data.data(), data.size());
327: if (n == -1) {
328: warn("SendPacket: write failed: fd=%d datalen=%zu",
329: fd, data.size());
330: return false;
331: }
332:
333: if (n < (int)data.size()) {
334: return false;
335: }
336: return true;
337: }
338:
339: // 受信スレッド
340: void
341: NetDriverTap::ThreadRun()
342: {
343: struct kevent kev;
1.1.1.3 root 344: autofd kq;
1.1 root 345: int r;
346:
347: kq = kqueue();
348: if (kq == -1) {
1.1.1.4 ! root 349: warn("kqueue");
1.1 root 350: return;
351: }
352:
353: EV_SET(&kev, fd, EVFILT_READ, EV_ADD, 0, 0, (kevent_udata_t)0);
354: r = kevent_set(kq, &kev, 1);
355: if (r == -1) {
1.1.1.4 ! root 356: warn("kevent_set(fd=%d)", fd);
1.1 root 357: return;
358: }
359:
360: for (;;) {
361: uint8 buf[1600];
362: int n;
363:
364: // 何か起きるまで待つ
365: r = kevent_poll(kq, &kev, 1, NULL);
366: if (r == -1) {
367: if (errno == EINTR) {
368: continue;
369: }
370: // XXX ?
1.1.1.4 ! root 371: warn("kevent_poll");
1.1 root 372: break;
373: }
374:
375: // 自動的にデバイスに着信があったことになるはず
376: assert(r > 0);
377:
378: n = read(fd, buf, sizeof(buf));
379: if (n == -1) {
380: if (errno == EINTR) {
381: continue;
382: }
1.1.1.4 ! root 383: warn("read");
1.1 root 384: break;
385: }
386: if (n == 0) { // EOF
387: return;
388: }
389:
390: // カーネルが tap(4) に直接書き込んだパケット(というかフレームか)は
391: // 60バイトパディングされないまま読み出せるので、ここでパディングする。
392: if (n < 60) {
393: memset(buf + n, 0, 60 - n);
394: n = 60;
395: }
396:
397: // CRC XXX 計算すること
398: memset(buf + n, 0, 4);
399: n += 4;
400:
401: // 受信処理に引き渡す
402: // ただしデバッガプロンプトで停止してる状態なら VM に干渉しない。
403: if (parent->RecvEnabled && gCVPrompt->IsPrompt() == false) {
404: putlog(2, "RecvPacket %d bytes", n);
405: rx_pkts++;
406: rx_bytes += n;
407:
408: qvector<uint8> recvbuf(buf, buf + n);
409: parent->RecvPacket(recvbuf);
410: }
411: }
412: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.