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