diff options
author | Volker Diels-Grabsch <v@njh.eu> | 2025-09-16 21:21:15 +0200 |
---|---|---|
committer | Stefano Brivio <sbrivio@redhat.com> | 2025-09-17 13:51:28 +0200 |
commit | e2920e36f65f333af5d217933dc04f32947bb028 (patch) | |
tree | d6649d3f2beffa79e6f8ea23db62bfaa55f6fa62 | |
parent | 142b3d872b7cd58391f59fa34f6ed399e72aaaf9 (diff) | |
download | passt-e2920e36f65f333af5d217933dc04f32947bb028.tar passt-e2920e36f65f333af5d217933dc04f32947bb028.tar.gz passt-e2920e36f65f333af5d217933dc04f32947bb028.tar.bz2 passt-e2920e36f65f333af5d217933dc04f32947bb028.tar.lz passt-e2920e36f65f333af5d217933dc04f32947bb028.tar.xz passt-e2920e36f65f333af5d217933dc04f32947bb028.tar.zst passt-e2920e36f65f333af5d217933dc04f32947bb028.zip |
Send an initial ARP and NDP request to resolve the guest IP address
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 the 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 and an NDP request to
resolve the guest's MAC address via its IPv4 and IPv6 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, or on the very first start right after qemu started. But in
those cases, we just wouldn't get an ARP/NDP response, and can't do
anything until we receive the guest's DHCP request - just as before.
In other words, in the worst case the ARP/NDP requests would be
harmless.
Signed-off-by: Volker Diels-Grabsch <v@njh.eu>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
-rw-r--r-- | arp.c | 34 | ||||
-rw-r--r-- | arp.h | 1 | ||||
-rw-r--r-- | ndp.c | 20 | ||||
-rw-r--r-- | ndp.h | 1 | ||||
-rw-r--r-- | passt.1 | 4 | ||||
-rw-r--r-- | tap.c | 5 |
6 files changed, 63 insertions, 2 deletions
@@ -112,3 +112,37 @@ int arp(const struct ctx *c, struct iov_tail *data) return 1; } + +/** + * arp_send_init_req() - Send initial ARP request to retrieve guest MAC address + * @c: Execution context + */ +void arp_send_init_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, MAC_BROADCAST, 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, MAC_BROADCAST, sizeof(req.am.tha)); + memcpy(req.am.tip, &c->ip4.addr, sizeof(req.am.tip)); + + debug("Sending initial ARP request for guest MAC address"); + tap_send_single(c, &req, sizeof(req)); +} @@ -21,5 +21,6 @@ struct arpmsg { } __attribute__((__packed__)); int arp(const struct ctx *c, struct iov_tail *data); +void arp_send_init_req(const struct ctx *c); #endif /* ARP_H */ @@ -438,3 +438,23 @@ void ndp_timer(const struct ctx *c, const struct timespec *now) first: next_ra = now->tv_sec + interval; } + +/** + * ndp_send_init_req() - Send initial NDP NS to retrieve guest MAC address + * @c: Execution context + */ +void ndp_send_init_req(const struct ctx *c) +{ + struct ndp_ns ns = { + .ih = { + .icmp6_type = NS, + .icmp6_code = 0, + .icmp6_router = 0, /* Reserved */ + .icmp6_solicited = 0, /* Reserved */ + .icmp6_override = 0, /* Reserved */ + }, + .target_addr = c->ip6.addr + }; + debug("Sending initial NDP NS request for guest MAC address"); + ndp_send(c, &c->ip6.addr, &ns, sizeof(ns)); +} @@ -11,5 +11,6 @@ struct icmp6hdr; int ndp(const struct ctx *c, const struct in6_addr *saddr, struct iov_tail *data); void ndp_timer(const struct ctx *c, const struct timespec *now); +void ndp_send_init_req(const struct ctx *c); #endif /* NDP_H */ @@ -330,8 +330,8 @@ selected IPv4 default route. .TP .BR \-\-no-ndp -Disable NDP responses. NDP messages coming from guest or target namespace will -be ignored. +Disable Neighbor Discovery. NDP messages coming from guest or target +namespace will be ignored. No initial NDP message will be sent. .TP .BR \-\-no-dhcpv6 @@ -1359,6 +1359,11 @@ 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); + + if (c->ifi4) + arp_send_init_req(c); + if (c->ifi6 && !c->no_ndp) + ndp_send_init_req(c); } /** |