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