|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2025 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // ホストネットワークの usermode (SLIRP) ドライバ
9: //
10:
11: //
12: // NetDriverSlirp SlirpThread
13: // (HostNet スレッド) : (裏スレッド)
14: //
15: // 送信時のフロー:
16: // NetDriverSlirp() は送信パケットをパイプ txfd (=txfd_w) に書き込む。
17: // SlirpThread 側で txfd_r に着信するので取り出して slirp_input() へ送る。
18: //
19: // NetDriverSlirp::Write() :
20: // |
21: // | txfd(=txfd_w) txfd_r
22: // +--------------->| pipe |------+ … パイプで裏スレッドに送信
23: // | |
24: // <-+ : v
25: // --- kevent @ ThreadRun()
26: // : |
27: // SlirpThread::Write()
28: // : |
29: // +-----> slirp_input()
30: //
31:
32: // 受信時のフロー:
33: // SlirpThread 側のイベントループにて、管理する受信ディスクリプタに
34: // 着信があれば SlirpThread::Read() がそれを読み出してパイプ rxfd_w に
35: // 書き込む。
36: // このパイプの対向は HostNetDevice に AddOuter() で登録したものなので
37: // この書き込みに対する着信で HostDevice::Dispatch() が動き、
38: // その結果 NetDriverSlirp() が呼ばれる。
39: // NetDriverSlirp() はこのパイプからパケットを読み出して親に引き渡す。
40: //
41: // --- kevent @ ThreadRun()
42: // |
43: // SlirpThread::Read()
44: // |
45: // rxfd(=rxfd_r) rxfd_w|
46: // +---------------| pipe |<-----+
47: // | 着信通知 ‖
48: // v ‖
49: // --- kqueue ‖
50: // | ‖
51: // HostNetDevice::Read() ‖
52: // | ‖
53: // NetDriverSlirp::Read() ‖
54: // | ‖
55: // パイプから受信 <==++
56: // |
57: // <--+ VM 側へ
58:
59: #include "netdriver_slirp.h"
60: #include "hostnet.h"
61: #include "scheduler.h"
62: #include <sys/socket.h>
63: #include <sys/uio.h>
64: #include <slirp/libslirp.h>
65:
66: //
67: // NetDriver 側スレッド
68: //
69:
70: // コンストラクタ
71: NetDriverSlirp::NetDriverSlirp(HostDevice *hostdev_)
1.1.1.3 root 72: : inherited(hostdev_, "usermode")
1.1 root 73: {
74: txd = -1;
75: rxd = -1;
76: }
77:
78: // デストラクタ
79: NetDriverSlirp::~NetDriverSlirp()
80: {
81: Close();
82: }
83:
84: // ログレベルを設定。
85: void
86: NetDriverSlirp::SetLogLevel(int loglevel_)
87: {
88: inherited::SetLogLevel(loglevel_);
89:
90: // バックエンドにも伝達。
91: if ((bool)backend) {
92: backend->SetLogLevel(loglevel_);
93: }
94: }
95:
96: // ドライバ初期化
97: bool
1.1.1.3 root 98: NetDriverSlirp::InitDriver(bool startup)
1.1 root 99: {
100: putmsg(1, "trying usermode...");
101:
102: if (slirp_occupied) {
1.1.1.3 root 103: errmsg = "'usermode' is already in use by another hostnet-driver";
104: putmsg(1, "%s", errmsg.c_str());
1.1 root 105: return false;
106: }
107:
1.1.1.2 root 108: try {
109: backend.reset(new SlirpThread(this));
110: } catch (...) { }
1.1 root 111: if ((bool)backend == false) {
1.1.1.3 root 112: errmsg = string_format("Failed to initialize SlirpThread at %s",
113: __method__);
114: putmsg(1, "%s", errmsg.c_str());
1.1 root 115: return false;
116: }
117:
118: // ディスクリプタは裏スレッドが所有しているのでこちらでは解放しないこと。
119: const std::string key = hostdev->GetConfigKey();
120: if (backend->InitBackend(key, &rxd, &txd) == false) {
1.1.1.3 root 121: errmsg = backend->errmsg;
122: putmsg(1, "%s", errmsg.c_str());
1.1 root 123: return false;
124: }
125:
126: // 読み込み側を Slirp からの受信端として親に登録。
127: if (hostdev->AddOuter(rxd) < 0) {
1.1.1.3 root 128: errmsg = string_format("AddOuter(rxd=%d): %s", rxd, strerror(errno));
129: putmsg(1, "%s", errmsg.c_str());
1.1 root 130: return false;
131: }
132:
1.1.1.3 root 133: // 起動時は一斉に StartThread() されて backend のスレッドも起動するが、
134: // 再選択時はここで手動でスレッドを起動する必要がある。
135: if (startup == false) {
136: backend->StartThread();
137: }
138:
1.1 root 139: slirp_occupied = true;
140: putmsg(1, "opened");
141: return true;
142: }
143:
144: // クローズ
145: void
146: NetDriverSlirp::Close()
147: {
148: // rxd がすでに閉じられていたらとかはここでは関係なくて、
149: // この番号を kqueue から外す必要がある。(Linux)
1.1.1.3 root 150: if (rxd >= 0) {
151: hostdev->DelOuter(rxd);
152: }
1.1 root 153:
154: // txd, rxd は裏スレッドの所有なのでこちらでは何もしない。
155:
156: if ((bool)backend) {
157: backend->Terminate();
158: backend->Close();
1.1.1.3 root 159: backend.reset();
1.1 root 160: }
161: slirp_occupied = false;
162: }
163:
164: // モニタ (ドライバ依存情報のみ)
165: void
166: NetDriverSlirp::MonitorUpdateMD(TextScreen& screen, int y)
167: {
168: // なし
169: }
170:
171: // パケットを送信する。
172: // と言っても裏スレッドに送り直すだけ。
173: void
174: NetDriverSlirp::Write(const void *buf, int buflen)
175: {
176: struct iovec iov[2];
177: ssize_t r;
178:
179: // 先頭4バイトに後続パケット長を入れておく。
180: static_assert(sizeof(buflen) == sizeof(uint32), "");
181: iov[0].iov_base = &buflen;
182: iov[0].iov_len = sizeof(buflen);
183: iov[1].iov_base = const_cast<void *>(buf);
184: iov[1].iov_len = buflen;
185: r = writev(txd, &iov[0], countof(iov));
186: if (r < 0) {
187: putmsg(0, "writev failed: %s", strerror(errno));
188: return;
189: }
190: }
191:
192: // パケットを受信する。
193: // これは rxd に着信があってから HostDevice::Dispatch() から呼ばれるほう。
194: int
195: NetDriverSlirp::Read(NetPacket *p)
196: {
197: ssize_t n;
198: uint32 len;
199:
200: // 先頭4バイトに後続パケット長を入れてある。
201: len = 0;
202: n = read(rxd, &len, sizeof(len));
203: if (n < 0) {
204: putmsg(0, "read(len) failed: %s", strerror(errno));
205: return NODATA;
206: }
207:
208: n = read(rxd, p->data(), len);
209: if (n < 0) {
210: putmsg(0, "read(buf) failed: %s", strerror(errno));
211: return NODATA;
212: }
213:
214: p->length = n;
215: return 0;
216: }
217:
218: // usermode を使えるのは同時に1人だけに限定する。
219: /*static*/ bool NetDriverSlirp::slirp_occupied = false;
220:
221:
222: //
223: // 裏スレッド
224: //
225:
226: // おそらく同じはずなので無駄な変換はしない。
227: #if SLIRP_POLL_IN == POLLIN && \
228: SLIRP_POLL_OUT == POLLOUT && \
229: SLIRP_POLL_PRI == POLLPRI && \
230: SLIRP_POLL_ERR == POLLERR && \
231: SLIRP_POLL_HUP == POLLHUP
232: #define POLL_EVENT_to_SLIRP_EVENT(x) (x)
233: #define SLIRP_EVENT_to_POLL_EVENT(x) (x)
234: #else
235: static int POLL_EVENT_to_SLIRP_EVENT(int pollevents)
236: {
237: int slevents = 0;
238: if ((pollevents & POLLIN)) slevents |= SLIRP_POLL_IN;
239: if ((pollevents & POLLOUT)) slevents |= SLIRP_POLL_OUT;
240: if ((pollevents & POLLPRI)) slevents |= SLIRP_POLL_PRI;
241: if ((pollevents & POLLERR)) slevents |= SLIRP_POLL_ERR;
242: if ((pollevents & POLLHUP)) slevents |= SLIRP_POLL_HUP;
243: return slevents;
244: }
245: static int SLIRP_EVENT_to_POLL_EVENT(int slevents)
246: {
247: int pollevents = 0;
248: if ((slevents & SLIRP_POLL_IN)) pollevents |= POLLIN;
249: if ((slevents & SLIRP_POLL_OUT)) pollevents |= POLLOUT;
250: if ((slevents & SLIRP_POLL_PRI)) pollevents |= POLLPRI;
251: if ((slevents & SLIRP_POLL_ERR)) pollevents |= POLLERR;
252: if ((slevents & SLIRP_POLL_HUP)) pollevents |= POLLHUP;
253: return pollevents;
254: }
255: #endif
256:
257: // タイマーは TimerNewCB() コールバックで確保して返す。
258: // 確保した領域そのものはライブラリが管理し、TimerFreeCB() コールバックで
259: // 解放する。
260: // 一方 timerlist は有効なタイマーのリストなので、期限が来たらこのリスト
261: // からは削除してよい。
262: class SlirpTimer
263: {
264: public:
265: SlirpTimer(SlirpTimerCb func_, void *arg_)
266: {
267: func = func_;
268: arg = arg_;
269: }
270:
271: SlirpTimerCb func {};
272: void *arg {};
273: int64 expire_msec {}; // 期限の(仮想?)絶対時刻 [msec]
274: int num {}; // デバッグ用のタイマー番号。
275: };
276:
277: static ssize_t send_packet_cb(const void *, size_t, void *);
278: static void guest_error_cb(const char *msg, void *);
279: static int64 clock_get_ns_cb(void *);
280: static void *timer_new_cb(SlirpTimerCb cb, void *cb_arg, void *);
281: static void timer_free_cb(void *timer, void *);
282: static void timer_mod_cb(void *timer, int64 expire_msec, void *);
283: static void register_poll_fd_cb(int fd, void *);
284: static void unregister_poll_fd_cb(int fd, void *);
285: static void notify_cb(void *);
286: static int add_poll_cb(int fd, int events, void *);
287: static int get_revents_cb(int idx, void *);
288:
289: static SlirpCb slirp_callbacks = {
290: .send_packet = send_packet_cb,
291: .guest_error = guest_error_cb,
292: .clock_get_ns = clock_get_ns_cb,
293: .timer_new = timer_new_cb,
294: .timer_free = timer_free_cb,
295: .timer_mod = timer_mod_cb,
296: .register_poll_fd = register_poll_fd_cb,
297: .unregister_poll_fd = unregister_poll_fd_cb,
298: .notify = notify_cb,
299: };
300:
301: // コンストラクタ
302: SlirpThread::SlirpThread(NetDriverSlirp *parent_)
303: : inherited(OBJ_NONE)
304: {
305: parent = parent_;
306: hostnet = dynamic_cast<HostNetDevice *>(parent->GetHostDev());
307: SetName(parent->GetName());
1.1.1.4 ! root 308: SetThreadName("SlirpThread");
1.1 root 309:
310: // コンストラクト後ただちにログ出力できるようここで一度追従しておく。
311: // 以降は親 (NetDriver側) の SetLogLevel() で変更する。
312: loglevel = parent->loglevel;
313: }
314:
315: // デストラクタ
316: SlirpThread::~SlirpThread()
317: {
318: TerminateThread();
1.1.1.3 root 319: Close();
1.1 root 320: }
321:
322: // 初期化。
323: // Device::Init() とは違い、NetDriver::InitDriver() から呼ばれる独自版。
324: // (表スレッドから見た裏スレッドへの) 受信側(RX)と送信側(TX)の
1.1.1.3 root 325: // ディスクリプタを書き戻す。
326: // 成功すれば true を返す。
327: // 失敗すれば (こちらのメンバ変数の) errmsg をセットして false を返す。
1.1 root 328: // これはスレッド起動前に (VM スレッドで) 呼ばれる。
329: bool
330: SlirpThread::InitBackend(const std::string& key, int *rxdp, int *txdp)
331: {
332: SlirpConfig config;
333: struct in_addr vnetwork;
334: struct in_addr vnetmask;
335: struct in6_addr vprefix_addr6;
336: uint vprefix_len;
337: int fds[2];
338:
1.1.1.3 root 339: const ConfigItem& netitem = gConfig->Find(key + "-usermode-net");
340: if (ParseConfigNet(netitem, &vnetwork, &vnetmask) == false) {
341: return false;
1.1 root 342: }
343:
1.1.1.3 root 344: const ConfigItem& net6item = gConfig->Find(key + "-usermode-net6");
345: if (ParseConfigNet6(net6item, &vprefix_addr6, &vprefix_len) == false) {
346: return false;
1.1 root 347: }
348:
1.1.1.3 root 349: // hostnet-usermode-hostfwd=<entry1>[;<entry2>[;<entry3>...]]
1.1 root 350: const ConfigItem& fwditem = gConfig->Find(key + "-usermode-hostfwd");
351: auto entries = string_split(fwditem.AsString(), ';');
1.1.1.3 root 352: if (entries.size() == 1) {
353: std::string entry = string_trim(entries[0]);
354: if (ParseConfigHostFwdEntry(fwditem, entry, "") == false) {
355: fwdinfo.clear();
1.1 root 356: return false;
357: }
1.1.1.3 root 358: } else {
359: for (const auto& e : entries) {
360: std::string entry = string_trim(e);
361: // エントリが複数ある時はそのうちのどこで起きたか表示したい。
362: std::string where = " at \"" + entry + "\"";
363: if (ParseConfigHostFwdEntry(fwditem, entry, where) == false) {
364: fwdinfo.clear();
365: return false;
366: }
1.1 root 367: }
368: }
369:
370: // Slirp を初期化。
371: memset(&config, 0, sizeof(config));
372: config.version = 4;
373: config.in_enabled = true;
374: config.vnetwork = vnetwork;
375: config.vnetmask = vnetmask;
376: config.vhost.s_addr = htonl(ntohl(vnetwork.s_addr) + 0x01);
377: config.vnameserver.s_addr = htonl(ntohl(vnetwork.s_addr) + 0x02);
378: config.vdhcp_start.s_addr = htonl(ntohl(vnetwork.s_addr) + 0x0a);
379: config.in6_enabled = true;
380: config.vprefix_addr6 = vprefix_addr6;
381: config.vprefix_len = vprefix_len;
382: config.vhost6 = vprefix_addr6;
383: config.vhost6.s6_addr[15] = 1;
384: config.vnameserver6 = vprefix_addr6;
385: config.vnameserver6.s6_addr[15] = 2;
386: gw6 = config.vhost6;
387: dns6 = config.vnameserver6;
388:
389: sl = slirp_new(&config, &slirp_callbacks, this);
390: if (sl == NULL) {
1.1.1.3 root 391: errmsg = "slirp_new() failed";
1.1 root 392: return false;
393: }
394:
395: for (const auto& fwd : fwdinfo) {
396: int r = slirp_add_hostfwd(sl, fwd.is_udp,
397: fwd.host.sin_addr, fwd.host.sin_port,
398: fwd.guest.sin_addr, fwd.guest.sin_port);
399: if (r != 0) {
400: char hname[INET_ADDRSTRLEN];
401: char gname[INET_ADDRSTRLEN];
402: hname[0] = '\0';
403: gname[0] = '\0';
404: inet_ntop(AF_INET, &fwd.host.sin_addr,
405: hname, (socklen_t)sizeof(hname));
406: inet_ntop(AF_INET, &fwd.guest.sin_addr,
407: gname, (socklen_t)sizeof(gname));
1.1.1.3 root 408: errmsg = string_format("slirp_add_hostfwd(%s,%s:%d,%s:%d) failed",
1.1 root 409: (fwd.is_udp ? "udp" : "tcp"),
410: hname, fwd.host.sin_port, gname, fwd.guest.sin_port);
1.1.1.3 root 411: goto abort;
1.1 root 412: }
413: }
414:
415: // libslirp のバージョンをチェック。
1.1.1.3 root 416: {
417: const char *version_str = slirp_version_string();
418: uint major;
419: uint minor;
420: uint patch;
421: if (sscanf(version_str, "%u.%u.%u", &major, &minor, &patch) == 3) {
422: putmsg(1, "libslirp version %u.%u", major, minor);
423: ver = VER(major, minor, patch);
424: }
1.1 root 425: }
426:
427: // 送信用のパイプを用意。
428: if (pipe(fds) < 0) {
1.1.1.3 root 429: errmsg = string_format("pipe(): %s", strerror(errno));
430: goto abort;
1.1 root 431: }
432: txd_r = fds[0];
433: txd_w = fds[1];
434:
435: // 受信用のパイプを用意。
436: if (pipe(fds) < 0) {
1.1.1.3 root 437: errmsg = string_format("pipe(): %s", strerror(errno));
438: goto abort;
1.1 root 439: }
440: rxd_r = fds[0];
441: rxd_w = fds[1];
442:
443: *rxdp = rxd_r;
444: *txdp = txd_w;
445:
1.1.1.3 root 446: // モニタを設定。(登録自体は hostnet で行ってある)
447: monitor = gMonitorManager->Find(ID_MONITOR_SLIRP);
448: assert(monitor);
449: monitor->obj = this;
1.1 root 450: monitor->func = ToMonitorCallback(&SlirpThread::MonitorUpdate);
451: monitor->SetSize(80, 40);
452:
453: return true;
1.1.1.3 root 454:
455: abort:
456: Close();
457: return false;
1.1 root 458: }
459:
1.1.1.3 root 460: // hostnetN-usermode-net の引数 <addr>/<netmask> or <addr>/<netlen> を
461: // パースして *pnetwork, *pnetmask に格納する。
462: // 失敗したら errmsg をセットして false を返す。
1.1 root 463: bool
1.1.1.3 root 464: SlirpThread::ParseConfigNet(const ConfigItem& item,
465: struct in_addr *pnetwork, struct in_addr *pnetmask)
1.1 root 466: {
1.1.1.3 root 467: std::string net = item.AsString();
1.1 root 468:
1.1.1.3 root 469: auto list = string_split(net, '/');
470: if (list.empty()) {
471: syntax_error:
472: errmsg = item.ErrMsg("Syntax is <addr>/<netmask>");
473: return false;
474: }
1.1 root 475:
1.1.1.3 root 476: // アドレス。
477: auto addr = list[0];
478: if (addr.empty()) {
479: goto syntax_error;
480: }
481: if (inet_pton(AF_INET, addr.c_str(), pnetwork) < 1) {
482: errmsg = item.ErrMsg("Invalid network address");
483: return false;
484: }
485:
486: // 区切りの '/'。
487: if (list.size() == 1) {
488: goto syntax_error;
489: }
490:
491: // ここからネットマスク or ネットマスク長。
492: auto mask = list[1];
493: const char *p = mask.c_str();
494:
495: // アドレスでも長さでも必ず1文字目は10進数値。
496: // 特に strtoul() は先頭のスペースを無視して成功してしまうので
497: // 先頭がスペースでないことはチェックする必要がある。
498: if (*p < '0' || *p > '9') {
499: goto syntax_error;
500: }
501: char *end;
502: errno = 0;
503: unsigned long netlen = strtoul(p, &end, 10);
504: if (*end == '\0') {
505: // 数値だけから成っていれば、ネットマスク長かも知れない。
506:
507: if (errno == ERANGE) {
508: out_of_range:
509: errmsg = item.ErrMsg("netmask len is out of range");
1.1 root 510: return false;
1.1.1.3 root 511: } else if (errno != 0) {
512: errmsg = item.ErrMsg("Invalid netmask len: %s", strerror(errno));
513: return false;
514: }
515: // DHCP 配布アドレスまで入れるには最小でも /28 (16-2個) が必要。
516: if (netlen < 1 || netlen > 28) {
517: goto out_of_range;
1.1 root 518: }
1.1.1.3 root 519: pnetmask->s_addr = htonl(((1U << netlen) - 1) << (32 - netlen));
520: } else {
521: // 数値以外も含まれていれば、ネットマスクかも知れない。
522: if (inet_pton(AF_INET, p, pnetmask) < 1) {
523: errmsg = item.ErrMsg("Invalid netmask");
1.1 root 524: return false;
525: }
1.1.1.3 root 526: }
527:
528: // これ以上続くのはエラー。
529: if (list.size() > 2) {
530: goto syntax_error;
531: }
532:
533: return true;
534: }
535:
536: // hostnetN-usermode-net6 の引数 <addr6>/<prefixlen> をパースして
537: // *paddr6, *plen に格納する。
538: // 失敗したら errmsg をセットして false を返す。
539: bool
540: SlirpThread::ParseConfigNet6(const ConfigItem& item,
541: struct in6_addr *paddr6, uint *plen)
542: {
543: std::string net6 = item.AsString();
544:
545: auto list = string_split(net6, '/');
546: if (list.empty()) {
547: syntax_error:
548: errmsg = item.ErrMsg("Syntax is <v6addr>/<len>");
549: return false;
550: }
551:
552: // アドレス。
553: auto addr = list[0];
554: if (addr.empty()) {
555: goto syntax_error;
556: }
557: if (inet_pton(AF_INET6, addr.c_str(), paddr6) < 1) {
558: errmsg = item.ErrMsg("Invalid network address");
559: return false;
560: }
561:
562: // 区切りの '/'。
563: if (list.size() == 1) {
564: goto syntax_error;
565: }
566:
567: // プレフィックス長。
568: auto prefixlen = list[1];
569: const char *p = prefixlen.c_str();
570:
571: // strtoul() は先頭のスペースを無視して成功してしまうので
572: // 先頭がスペースでないことはチェックする必要がある。
573: if (*p < '0' || *p > '9') {
574: goto syntax_error;
575: }
576: char *end;
577: errno = 0;
578: unsigned long len = strtoul(p, &end, 10);
579: if (*end != '\0') {
580: goto syntax_error;
581: }
582: if (errno == ERANGE) {
583: out_of_range:
584: errmsg = item.ErrMsg("prefixlen is out of range");
585: return false;
586: } else if (errno != 0) {
587: errmsg = item.ErrMsg("Invalid netmask len: %s", strerror(errno));
588: return false;
589: }
590: // 適切な範囲がよく分からないのでとりあえず物理限界だけチェック。
591: if (len < 1 || len > 124) {
592: goto out_of_range;
593: }
594: *plen = len;
595:
596: // これ以上続くのはエラー。
597: if (list.size() > 2) {
598: goto syntax_error;
599: }
600:
601: return true;
602: }
603:
604: // hostnetN-usermode-hostfwd の引数のエントリ1つ分をパースして fwdinfo に
605: // 追加する。失敗したら errmsg をセットして false を返す。
606: //
607: // 引数は <entry> を複数ならセミコロンで区切ったもの。
608: // ここに渡される一つの <entry> の書式は次の通り。
609: // <entry> := <proto>,[<hostaddr>:]<hostport>,[<guestaddr>:]<guestport>
610: bool
611: SlirpThread::ParseConfigHostFwdEntry(const ConfigItem& item,
612: const std::string& entry, const std::string& where)
613: {
614: int r;
615:
616: // エントリが空なのは許容するか。
617: if (entry.empty()) {
618: return true;
619: }
620:
621: auto val = string_split(entry, ',');
622: if (val.size() != 3) {
623: syntax_error:
624: errmsg = item.ErrMsg(
625: "Syntax is <proto>,[<hostaddr>:]<port>,[<guestaddr>]:<port>[;...]"
626: + where);
627: return false;
628: }
629:
630: FwdInfo fwd;
631: memset(&fwd, 0, sizeof(fwd));
632:
633: // proto
634: auto proto = string_trim(val[0]);
635: if (strcasecmp(proto.c_str(), "tcp") == 0) {
636: fwd.is_udp = 0;
637: } else if (strcasecmp(proto.c_str(), "udp") == 0) {
638: fwd.is_udp = 1;
1.1 root 639: } else {
1.1.1.3 root 640: errmsg = item.ErrMsg("protocol must be \"tcp\" or \"udp\"" + where);
641: return false;
1.1 root 642: }
643:
1.1.1.3 root 644: // host
645: r = ParseAddrPort(item, val[1], &fwd.host, "host", where);
646: if (r < 0) {
647: goto syntax_error;
648: } else if (r == 0) {
1.1 root 649: return false;
650: }
1.1.1.3 root 651:
652: // guest
653: r = ParseAddrPort(item, val[2], &fwd.guest, "guest", where);
654: if (r < 0) {
655: goto syntax_error;
656: } else if (r == 0) {
657: return false;
658: }
659:
660: fwdinfo.push_back(fwd);
1.1 root 661:
662: return true;
663: }
664:
1.1.1.3 root 665: // "[アドレス:]ポート" 形式の hostport_arg からアドレスとポートを取り出して
666: // sin に格納する。
667: // 戻り値は 1 が成功、
668: // 0 なら失敗で errno はセット済み、
669: // -1 なら失敗で呼び出し元で syntax_error へ分岐。
670: int
671: SlirpThread::ParseAddrPort(const ConfigItem& item,
672: const std::string& hostport_arg, struct sockaddr_in *sin,
673: const char *which, const std::string& where)
674: {
675: std::string hostport = string_trim(hostport_arg);
676: if (hostport.empty()) {
677: return -1;
678: }
679:
680: // ':' で分離。
681: std::string port;
682: auto list = string_split(hostport, ':');
683: if (list.size() == 1) {
684: // ポートのみ。
685: port = list[0];
686: } else {
687: // list.size() == 0 はないので、2以上。
688: std::string host;
689: host = list[0];
690: port = list[1];
691:
692: // アドレス。
693: if (host.empty()) {
694: return -1;
695: }
696: if (inet_pton(AF_INET, host.c_str(), &sin->sin_addr) < 1) {
697: errmsg = item.ErrMsg("Invalid %s address%s", which, where.c_str());
698: return 0;
699: }
700: }
701:
702: // ポート。
703: const char *p = port.c_str();
704: if (*p < '0' || *p > '9') {
705: invalid_port:
706: errmsg = item.ErrMsg("Invalid %s port%s", which, where.c_str());
707: return 0;
708: }
709: char *end;
710: errno = 0;
711: unsigned long portnum = strtoul(p, &end, 10);
712: if (*end != '\0') {
713: goto invalid_port;
714: }
715: if (errno == ERANGE) {
716: out_of_range:
717: errmsg = item.ErrMsg("%s port is out of range%s", which, where.c_str());
718: return 0;
719: }
720: if (errno != 0) {
721: errmsg = item.ErrMsg("%s port: %s%s",
722: which, strerror(errno), where.c_str());
723: return 0;
724: }
725: if (portnum < 1 || portnum > 65535) {
726: goto out_of_range;
727: }
728:
729: // これ以上続くのはエラー。
730: if (list.size() > 2) {
731: return -1;
732: }
733:
734: sin->sin_port = portnum;
735: return 1;
736: }
737:
1.1 root 738: void
739: SlirpThread::Close()
740: {
741: txd_r.Close();
742: txd_w.Close();
743: rxd_r.Close();
744: rxd_w.Close();
745:
746: if (sl) {
747: for (const auto& fwd : fwdinfo) {
748: slirp_remove_hostfwd(sl, fwd.is_udp,
749: fwd.host.sin_addr, fwd.host.sin_port);
750: }
751: slirp_cleanup(sl);
752: sl = NULL;
753: }
754: }
755:
756: // スレッドの終了を指示
757: void
758: SlirpThread::Terminate()
759: {
760: txd_r.Close();
761: }
762:
763: // スレッド
764: void
765: SlirpThread::ThreadRun()
766: {
767: SetThreadAffinityHint(AffinityClass::Light);
768:
769: while (exit_requested == false) {
770: // タイマー処理。
771: int64 cur_msec = ClockGetNsCB() / 1000'000;
772: for (auto it = timerlist.begin(); it != timerlist.end(); ) {
773: SlirpTimer *timer = *it;
774: if (timer->expire_msec <= cur_msec) {
775: // 期限が来ていれば登録されているハンドラを実行。
776: // このハンドラ内から TimerModCB() が呼ばれて
777: // timerlist が書き換わる可能性があるので先に削除。
778: timerlist.erase(it);
779: timer->func(timer->arg);
780: // この場合 it の状態が保証されなくなるので、
781: // もう一度先頭からやり直す。
782: it = timerlist.begin();
783: } else {
784: it++;
785: }
786: }
787:
788: // poll に入る前に内部のテーブル(文字列)を取得しておく。
789: UpdateInfo();
790:
791: // 先頭を txd とする。
792: pollfds.clear();
793: AddPollCB(txd_r, SLIRP_POLL_IN);
794:
795: // slirp ハンドルが持っているディスクリプタを pollfds[] にセット。
796: // コールバックで都度 AddPollCB() が呼ばれてそいつが追加している。
797: //
798: // slirp_pollfds_fill() の &timeout_ms 引数は、timeout_ms を
799: // 書き出す(out パラメータ)ではなく、現行の timeout_ms を必要に
800: // 応じて更新というか短くする(ref パラメータ)。つまり呼び出し前に
801: // 適切な値をセットしておく必要がある。値は必ず 1000 以下にしてくる
802: // ため、初期値も 1000 をセットするのがよさそう。
803: // ドキュメントをちゃんと書け。
804: uint32 timeout_ms = 1000;
805: slirp_pollfds_fill(sl, &timeout_ms, add_poll_cb, this);
806:
807: int err = poll(pollfds.data(), pollfds.size(), timeout_ms);
808: if (err < 0) {
809: if (errno == EINTR) {
810: continue;
811: }
812: // XXX どうする?
813: putmsg(0, "poll failed: %s", strerror(errno));
814: break;
815: }
816:
817: // txd に何か起きた。
818: if (pollfds[0].revents != 0) {
819: // txd のクローズはこのスレッドの終了通知。
820: if ((pollfds[0].revents & (POLLERR | POLLHUP | POLLNVAL))) {
821: exit_requested = true;
822: continue;
823: }
824:
825: // txd への着信は VM (-> HostNet スレッド) からのパケット送信。
826: if ((pollfds[0].revents & POLLIN)) {
827: Write();
828: }
829: }
830:
831: // poll() 完了後に slirp ハンドルの持っている各ディスクリプタの
832: // revents (GetReventsCB() コールバックを通じて取得する) に応じた
833: // 処理をするようだ。
834: slirp_pollfds_poll(sl, (err < 0), get_revents_cb, this);
835: }
836: }
837:
838: // slirp_pollfds_fill() から呼ばれるコールバック。
839: // fd, events をこっちの pollfds[] に追加し、追加したインデックスを返す。
840: int
841: SlirpThread::AddPollCB(int fd, int slevents)
842: {
843: struct pollfd pfd;
844:
845: pfd.fd = fd;
846: pfd.events = SLIRP_EVENT_to_POLL_EVENT(slevents);
847: pfd.revents = 0;
848:
849: int idx = pollfds.size();
850: pollfds.emplace_back(pfd);
851: return idx;
852: }
853:
854: // slirp_pollfds_poll() から呼ばれるコールバック。
855: // pollfds[idx] の revents (SLRIP_POLL_*) を返す。
856: int
857: SlirpThread::GetReventsCB(int idx)
858: {
859: if (idx < 0 || idx >= pollfds.size()) {
860: return 0;
861: }
862:
863: int slevents = POLL_EVENT_to_SLIRP_EVENT(pollfds[idx].revents);
864: return slevents;
865: }
866:
867: // HostNet(NetDriver) スレッドから txd に着信したパケットを Slirp に送信する。
868: void
869: SlirpThread::Write()
870: {
871: uint32 len;
872: int r;
873:
874: // 先頭4バイトに後続パケット長を入れてある。
875: r = read(txd_r, &len, sizeof(len));
876: if (r < 0) {
877: putmsg(0, "Write: read(len) failed: %s", strerror(errno));
878: return;
879: }
880:
881: std::vector<uint8> buf(len);
882: r = read(txd_r, buf.data(), buf.size());
883: if (r < 0) {
884: putmsg(0, "Write: read(buf) failed: %s", strerror(errno));
885: return;
886: }
887:
888: // 一部のフレームを libslirp に渡す前にこちらで処理する。
889: if (__predict_false(WriteHook(buf))) {
890: return;
891: }
892:
893: if (__predict_false(loglevel >= 2)) {
894: putmsgn("Send to SLIRP:");
895: DumpFrame(buf.data(), buf.size());
896: }
897: slirp_input(sl, buf.data(), buf.size());
898: }
899:
900: // 一部の frame を libslirp に渡す前にこちらで処理する。
901: // 処理した (libslirp に渡さない) 場合は true を返す。
902: // 処理しない (libslirp に渡す) 場合は false を返す。
903: //
904: // 現状、自分(ルータと DNS サーバ)以外への ICMPv6 Echo Request が該当。
905: bool
906: SlirpThread::WriteHook(const std::vector<uint8>& frame) const
907: {
908: const uint8 *payload;
909: size_t payloadlen;
910:
911: // XXX libslirp-4.7.0 は、自身が管理するルータ・DNS サーバ以外の
912: // アドレスへの ICMPv6 Echo Request の送出に対する処理が未実装。
913: // それ自体はいいのだが、slirp_input() がそのパケットを受け取ると
914: // GLib の機構でコンソールにログを出してしまい、このログはこちらで制御
915: // 出来ない (しにくい) のにユーザに目立つ形で表示されるので大変困る。
916: // そこで、このパケットを事前にここで検出して、自分で処理というか
917: // ドロップして putmsg() で控えめにログを出すことにする。
918: // libslirp-4.8.0 で修正された。
919: if (ver >= VER(4, 8, 0)) {
920: return false;
921: }
922:
923: // イーサネットフレームが IPv6。
924: if (__predict_false(frame.size() < 14)) {
925: return false;
926: }
927: if (frame[12] != 0x86 || frame[13] != 0xdd) {
928: return false;
929: }
930:
931: struct ipv6_hdr {
932: uint8 ver_cls; // 上位4ビットが version
933: uint8 clf_flw;
934: uint16 flowlabel;
935: uint16 payloadlen;
936: uint8 nexthdr;
937: uint8 hoplimit;
938: uint8 srcip[16];
939: uint8 dstip[16];
940: } __packed;
941: payload = frame.data() + 14;
942: payloadlen = frame.size() - 14;
943: const ipv6_hdr& ipv6 = *(const ipv6_hdr *)payload;
944: if (__predict_false(payloadlen < sizeof(ipv6_hdr))) {
945: return false;
946: }
947: // 先に NextHdr をチェック。途中に別ヘッダが挟まるケースは諦める。
948: if (ipv6.nexthdr != 58) {
949: return false;
950: }
951:
952: struct icmp_hdr {
953: uint8 type;
954: uint8 code;
955: uint16 cksum;
956: } __packed;
957: payload += sizeof(ipv6_hdr);
958: payloadlen -= sizeof(ipv6_hdr);
959: const icmp_hdr& icmp = *(const icmp_hdr *)payload;
960: if (__predict_false(payloadlen < sizeof(icmp_hdr))) {
961: return false;
962: }
963: if (icmp.type != 128) { // ICMPV6 Echo Request
964: return false;
965: }
966:
967: // ICMPv6 Echo Reuqest は確定したのでここで宛先をチェック。
968: if (0) {
969: char srcname[100];
970: char dstname[100];
971: inet_ntop(AF_INET6, ipv6.srcip, srcname, (socklen_t)sizeof(srcname));
972: inet_ntop(AF_INET6, ipv6.dstip, dstname, (socklen_t)sizeof(dstname));
973: printf("ICMPv6 src=%s dst=%s\n", srcname, dstname);
974: }
975:
976: // 宛先が自分のルータか DNS サーバなら処理可能なので何もしない。
977: if (memcmp(ipv6.dstip, &gw6, sizeof(gw6)) == 0) {
978: return false;
979: }
980: if (memcmp(ipv6.dstip, &dns6, sizeof(dns6)) == 0) {
981: return false;
982: }
1.1.1.2 root 983: // リンクローカルはここで作って比較。
984: struct in6_addr linklocal;
985: memset(&linklocal, 0, sizeof(linklocal));
986: linklocal.s6_addr[0] = 0xfe;
987: linklocal.s6_addr[1] = 0x80;
988: memcpy(&linklocal.s6_addr[8], &gw6.s6_addr[8], 8);
989: if (memcmp(ipv6.dstip, &linklocal, sizeof(linklocal)) == 0) {
990: return false;
991: }
992: memcpy(&linklocal.s6_addr[8], &dns6.s6_addr[8], 8);
993: if (memcmp(ipv6.dstip, &linklocal, sizeof(linklocal)) == 0) {
994: return false;
995: }
1.1 root 996:
997: // ここから未実装ログが出る組み合わせ。
998:
999: // 統計情報。
1000: hostnet->CountTXUnsupp(frame.size());
1001:
1002: return true;
1003: }
1004:
1005: // Slirp ライブラリがパケットを "送信した" 時に呼ばれるコールバック。
1006: // なのでこちらから見れば HUB から NIC への着信。
1007: ssize_t
1008: SlirpThread::SendPacketCB(const void *src, size_t srclen)
1009: {
1010: if (__predict_false(loglevel >= 2)) {
1011: putmsgn("Recv from SLIRP");
1012: DumpFrame(src, srclen);
1013: }
1014:
1015: // 受信ディスクリプタ (の書き込み端) に書き込む。
1016: // 先頭4バイトはホストバイトオーダーでの後続のパケット長。
1017: uint32 lenbuf = srclen;
1018:
1019: struct iovec iov[2];
1020: iov[0].iov_base = &lenbuf;
1021: iov[0].iov_len = sizeof(lenbuf);
1022: iov[1].iov_base = const_cast<void *>(src);
1023: iov[1].iov_len = srclen;
1024:
1025: ssize_t r = writev(rxd_w, &iov[0], countof(iov));
1026: if (r < 0) {
1027: putmsg(0, "writev failed: %s", strerror(errno));
1028: return -1;
1029: }
1030: if (r < sizeof(lenbuf) + srclen) {
1031: putmsg(0, "writev failed: too short");
1032: return -1;
1033: }
1034:
1035: return srclen;
1036: }
1037:
1038: // 現在の仮想時間を返すコールバック。
1039: int64
1040: SlirpThread::ClockGetNsCB() const
1041: {
1042: // XXX scheduler がセットされる前から呼ばれるのでとりあえず。
1043: if (__predict_false(scheduler == NULL)) {
1044: return 0;
1045: }
1046: uint64 time = scheduler->GetVirtTime();
1047: return (int64)time;
1048: }
1049:
1050: // 新しいタイマー(イベント)を返すコールバック。
1051: // 引数は時間が来たら呼ぶハンドラと引数。
1052: void *
1053: SlirpThread::TimerNewCB(SlirpTimerCb func, void *arg)
1054: {
1055: SlirpTimer *newtimer = new SlirpTimer(func, arg);
1056: newtimer->num = latest_timer_num++;
1057: return newtimer;
1058: }
1059:
1060: // タイマーを削除する。
1061: void
1062: SlirpThread::TimerFreeCB(SlirpTimer *timer)
1063: {
1064: // まだアクティブならそれも削除する。
1065: timerlist.remove(timer);
1066: delete timer;
1067: }
1068:
1069: // タイマーの期限を新しく設定する?。
1070: void
1071: SlirpThread::TimerModCB(SlirpTimer *timer, int64 expire_msec)
1072: {
1073: timer->expire_msec = expire_msec;
1074:
1075: // リストから一旦削除してから、適切なところに挿入し直す。
1076: timerlist.remove(timer);
1077: if (timerlist.empty()) {
1078: timerlist.push_back(timer);
1079: } else {
1080: for (auto it = timerlist.begin(); it != timerlist.end(); ++it) {
1081: auto *t = *it;
1082: if (t->expire_msec > timer->expire_msec) {
1083: timerlist.insert(it, timer);
1084: break;
1085: }
1086: }
1087: }
1088: }
1089:
1090: // ゲスト (VM) から送信しようとしたパケットのエラーメッセージ。
1091: // とりあえずログに出しておく。
1092: void
1093: SlirpThread::GuestErrorCB(const char *msg)
1094: {
1095: putmsg(1, "libslirp: %s", msg);
1096: }
1097:
1098: // Slirp の内部テーブルのこちら側のコピーを更新する。
1099: // メインループ内から呼ばれる。
1100: void
1101: SlirpThread::UpdateInfo()
1102: {
1103: // 取得。
1104: char *newconn = slirp_connection_info(sl);
1105: char *newnbr = slirp_neighbor_info(sl);
1106:
1107: // 更新が必要か。前回と同じなら今取得したほうを解放。
1108: if (conninfo && strcmp(conninfo, newconn) == 0) {
1109: free(newconn);
1110: newconn = NULL;
1111: }
1112: if (nbrinfo && strcmp(nbrinfo, newnbr) == 0) {
1113: free(newnbr);
1114: newnbr = NULL;
1115: }
1116: // どちらも更新なしならここで終了。
1117: if (newconn == NULL && newnbr == NULL) {
1118: return;
1119: }
1120:
1121: // 更新。
1122: char *oldconn = NULL;
1123: char *oldnbr = NULL;
1124: {
1.1.1.4 ! root 1125: std::lock_guard<std::mutex> lock(info_mtx);
1.1 root 1126:
1127: if (newconn) {
1128: oldconn = conninfo;
1129: conninfo = newconn;
1130: }
1131: if (newnbr) {
1132: oldnbr = nbrinfo;
1133: nbrinfo = newnbr;
1134: }
1135: }
1136:
1137: free(oldconn);
1138: free(oldnbr);
1139: }
1140:
1141: void
1142: SlirpThread::MonitorUpdate(Monitor *, TextScreen& screen)
1143: {
1144: std::string local_conn;
1145: std::string local_nbr;
1146: int y;
1147:
1148: // スレッド越しにコピーを取る。
1149: {
1.1.1.4 ! root 1150: std::lock_guard<std::mutex> lock(info_mtx);
1.1 root 1151: local_conn = std::string(conninfo);
1152: local_nbr = std::string(nbrinfo);
1153: }
1154:
1.1.1.3 root 1155: // Connection State の UDP 欄は [0 sec] の次に一瞬だけ [42949?? sec]
1156: // くらいになる。これは slirp_connection_info() が文字列を返してきた時点で
1.1 root 1157: // こうなっているのでこっちの話ではないのだが、あまりに幼稚すぎて
1158: // 自分のソフトでこれが表示されるのは恥ずかしいのでパッチしておく。
1.1.1.3 root 1159: // UDP[4294967 sec]
1160: static const char udphdr[] = "UDP[";
1161: static const char patch[] = "expired] ";
1162: for (char *s = &local_conn[0]; (s = strstr(s, udphdr)) != NULL; ) {
1163: const char *end;
1164: s += 4;
1165: for (end = s; '0' <= *end && *end <= '9'; end++)
1166: ; // nop
1167: if (__predict_false(end - s >= 7)) {
1168: for (const char *p = patch; *s && *p; ) {
1169: *s++ = *p++;
1170: }
1171: }
1.1 root 1172: }
1173:
1174: screen.Clear();
1175: y = 0;
1176:
1177: auto lines = string_split(local_conn, '\n');
1178: for (auto& buf : lines) {
1179: screen.Puts(0, y++, buf.c_str());
1180: }
1181:
1182: lines = string_split(local_nbr, '\n');
1183: for (auto& buf : lines) {
1184: screen.Puts(0, y++, buf.c_str());
1185: }
1186: // あふれたら諦める…。
1187: }
1188:
1189: // src から srclen バイトの16進ダンプをログに(無条件に)出力する。
1190: void
1191: SlirpThread::DumpHex(const void *src, size_t srclen) const
1192: {
1193: std::vector<std::string> lines = HostNetDevice::DumpHex(src, srclen);
1194: for (auto& line : lines) {
1195: putmsgn("%s", line.c_str());
1196: }
1197: }
1198:
1199: // イーサネットフレームを整形してログに(無条件に)出力する。
1200: void
1201: SlirpThread::DumpFrame(const void *src, size_t srclen) const
1202: {
1203: std::vector<std::string> lines = HostNetDevice::DumpFrame(src, srclen);
1204: for (auto& line : lines) {
1205: putmsgn("%s", line.c_str());
1206: }
1207: }
1208:
1209: // コールバックのトランポリン関数たち
1210:
1211: static ssize_t
1212: send_packet_cb(const void *buf, size_t buflen, void *opaque)
1213: {
1214: auto *backend = reinterpret_cast<SlirpThread *>(opaque);
1215: return backend->SendPacketCB(buf, buflen);
1216: }
1217:
1218: static void
1219: guest_error_cb(const char *msg, void *opaque)
1220: {
1221: auto *backend = reinterpret_cast<SlirpThread *>(opaque);
1222: backend->GuestErrorCB(msg);
1223: }
1224:
1225: static int64
1226: clock_get_ns_cb(void *opaque)
1227: {
1228: auto *backend = reinterpret_cast<SlirpThread *>(opaque);
1229: return backend->ClockGetNsCB();
1230: }
1231:
1232: static void *
1233: timer_new_cb(SlirpTimerCb func, void *arg, void *opaque)
1234: {
1235: auto *backend = reinterpret_cast<SlirpThread *>(opaque);
1236: return backend->TimerNewCB(func, arg);
1237: }
1238:
1239: static void
1240: timer_free_cb(void *timer, void *opaque)
1241: {
1242: auto *backend = reinterpret_cast<SlirpThread *>(opaque);
1243: auto *sltimer = reinterpret_cast<SlirpTimer *>(timer);
1244: return backend->TimerFreeCB(sltimer);
1245: }
1246:
1247: static void
1248: timer_mod_cb(void *timer, int64 expire_msec, void *opaque)
1249: {
1250: auto *backend = reinterpret_cast<SlirpThread *>(opaque);
1251: auto *sltimer = reinterpret_cast<SlirpTimer *>(timer);
1252: backend->TimerModCB(sltimer, expire_msec);
1253: }
1254:
1255: // おそらく不要で何もしなくていいのだが、コールバックを NULL に出来ないため。
1256: static void
1257: register_poll_fd_cb(int fd, void *)
1258: {
1259: }
1260:
1261: // おそらく不要で何もしなくていいのだが、コールバックを NULL に出来ないため。
1262: static void
1263: unregister_poll_fd_cb(int fd, void *)
1264: {
1265: }
1266:
1267: // シングルスレッド動作なので不要なはずだが、コールバックを NULL に出来ない。
1268: static void
1269: notify_cb(void *)
1270: {
1271: }
1272:
1273: static int
1274: add_poll_cb(int fd, int events, void *opaque)
1275: {
1276: auto *backend = reinterpret_cast<SlirpThread *>(opaque);
1277: return backend->AddPollCB(fd, events);
1278: }
1279:
1280: static int
1281: get_revents_cb(int idx, void *opaque)
1282: {
1283: auto *backend = reinterpret_cast<SlirpThread *>(opaque);
1284: return backend->GetReventsCB(idx);
1285: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.