From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=none (p=none dis=none) header.from=njh.eu Received: from mail.notjusthosting.com (mail.notjusthosting.com [IPv6:2a01:4f8:a0:516f:1::1]) by passt.top (Postfix) with ESMTPS id 548905A0271 for ; Sat, 06 Sep 2025 02:22:52 +0200 (CEST) Received: from echelon-telekom.c-base.org ([80.147.140.51] helo=vlap) by mail.notjusthosting.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.89) (envelope-from ) id 1uughf-000237-Nv; Sat, 06 Sep 2025 02:22:51 +0200 From: Volker Diels-Grabsch To: passt-dev@passt.top Subject: [PATCH] Send an initial ARP request to resolve the guest IP address Date: Sat, 6 Sep 2025 02:22:46 +0200 Message-ID: <20250906002246.294473-1-v@njh.eu> X-Mailer: git-send-email 2.47.2 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Message-ID-Hash: 6W4USV4JGLNCWBHGL327DUBETCFHFB23 X-Message-ID-Hash: 6W4USV4JGLNCWBHGL327DUBETCFHFB23 X-MailFrom: v@njh.eu X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Volker Diels-Grabsch X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: When restarting passt while QEMU keeps running with a configured "reconnect-ms" setting, the port forwardings will stop working until the guest sends some outgoing network traffic. Reason: Although QEMU reconnects successfully to the unix domain socket of the new passt process, that one no longer knows the guest's MAC address and uses instead a broadcast MAC address. However, this is ignored by the guest, at least if the guest runs Linux. Only after the guest sends some network package on its own initiative, passt will know the MAC address and will be able to establish forwarded connections. This change fixes this issue by sending an ARP request to resolve the guest's MAC address via its IP address, which we do know, right after the unix domain socket (re)connection. The only case where the IP is "wrong" would be if the configuration changed, and/or on the very first start right after qemu started. But in those cases, we just wouldn't get an ARP response, and can't do anything receive the guest's DHCP request - just as before. In other words, in the worst case an ARP request would be harmless. --- arp.c | 34 ++++++++++++++++++++++++++++++++++ arp.h | 1 + tap.c | 15 +++++++++++++-- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/arp.c b/arp.c index 44677ad..561581a 100644 --- a/arp.c +++ b/arp.c @@ -112,3 +112,37 @@ int arp(const struct ctx *c, struct iov_tail *data) return 1; } + +/** + * send_initial_arp_req() - Send initial ARP request to retrieve guest MAC address + * @c: Execution context + */ +void send_initial_arp_req(const struct ctx *c) +{ + struct { + struct ethhdr eh; + struct arphdr ah; + struct arpmsg am; + } __attribute__((__packed__)) req; + + /* Ethernet header */ + req.eh.h_proto = htons(ETH_P_ARP); + memcpy(req.eh.h_dest, c->guest_mac, sizeof(req.eh.h_dest)); + memcpy(req.eh.h_source, c->our_tap_mac, sizeof(req.eh.h_source)); + + /* ARP header */ + req.ah.ar_op = htons(ARPOP_REQUEST); + req.ah.ar_hrd = htons(ARPHRD_ETHER); + req.ah.ar_pro = htons(ETH_P_IP); + req.ah.ar_hln = ETH_ALEN; + req.ah.ar_pln = 4; + + /* ARP message */ + memcpy(req.am.sha, c->our_tap_mac, sizeof(req.am.sha)); + memcpy(req.am.sip, &c->ip4.our_tap_addr, sizeof(req.am.sip)); + memcpy(req.am.tha, c->guest_mac, sizeof(req.am.tha)); + memcpy(req.am.tip, &c->ip4.addr, sizeof(req.am.tip)); + + info("sending initial ARP request to retrieve guest MAC address after reconnect"); + tap_send_single(c, &req, sizeof(req)); +} diff --git a/arp.h b/arp.h index 86bcbf8..5490144 100644 --- a/arp.h +++ b/arp.h @@ -21,5 +21,6 @@ struct arpmsg { } __attribute__((__packed__)); int arp(const struct ctx *c, struct iov_tail *data); +void send_initial_arp_req(const struct ctx *c); #endif /* ARP_H */ diff --git a/tap.c b/tap.c index 7ba6399..47dedd5 100644 --- a/tap.c +++ b/tap.c @@ -1355,6 +1355,16 @@ static void tap_start_connection(const struct ctx *c) ev.events = EPOLLIN | EPOLLRDHUP; ev.data.u64 = ref.u64; epoll_ctl(c->epollfd, EPOLL_CTL_ADD, c->fd_tap, &ev); + + switch (c->mode) { + case MODE_PASST: + send_initial_arp_req(c); + break; + case MODE_PASTA: + break; + case MODE_VU: + break; + } } /** @@ -1504,8 +1514,9 @@ void tap_backend_init(struct ctx *c) tap_sock_unix_init(c); /* In passt mode, we don't know the guest's MAC address until it - * sends us packets. Use the broadcast address so that our - * first packets will reach it. + * sends us packets (e.g. responds to our initial ARP request). + * Until then, use the broadcast address so that our first + * packets will reach it. */ memset(&c->guest_mac, 0xff, sizeof(c->guest_mac)); break; -- 2.47.2