|
|
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.