diff options
| author | EJ Campbell <ej.campbell@gmail.com> | 2026-06-11 04:26:19 +0000 |
|---|---|---|
| committer | Stefano Brivio <sbrivio@redhat.com> | 2026-06-19 13:10:24 +0200 |
| commit | 3f57f0382f6a72c0b8ce0c5ff92248b5117ed9b6 (patch) | |
| tree | 971c72eefad2f4d4de70dc930afa71f876548d7c | |
| parent | f072bc07b8251e513823cbb08c69a336187ec56c (diff) | |
| download | passt-3f57f0382f6a72c0b8ce0c5ff92248b5117ed9b6.tar passt-3f57f0382f6a72c0b8ce0c5ff92248b5117ed9b6.tar.gz passt-3f57f0382f6a72c0b8ce0c5ff92248b5117ed9b6.tar.bz2 passt-3f57f0382f6a72c0b8ce0c5ff92248b5117ed9b6.tar.lz passt-3f57f0382f6a72c0b8ce0c5ff92248b5117ed9b6.tar.xz passt-3f57f0382f6a72c0b8ce0c5ff92248b5117ed9b6.tar.zst passt-3f57f0382f6a72c0b8ce0c5ff92248b5117ed9b6.zip | |
tap: don't let overheard traffic move addr_seen when address is explicit
When pasta's tap interface shares an L2 segment with other endpoints
(e.g. bridged in a namespace with a VM behind a second tap), frames
from those endpoints flood to pasta and their source addresses
overwrite addr_seen — inbound flows are then spliced or forwarded to
whichever address spoke last instead of the configured guest.
In such a setup the namespace kernel's own broadcasts (ARP probes,
IGMP joins, sourced from the bridge address) race the guest's traffic
for addr_seen, and inbound port-forwarded connections intermittently
get RST after pasta dials the bridge address, where nothing listens:
Flow 0 (TCP connection (spliced)):
HOST [127.0.0.1]:57280 -> [127.0.0.2]:22844
=> SPLICE [0.0.0.0]:0 -> [10.0.2.1]:80 <- bridge addr, not -a
Flow 0 (TCP connection (spliced)): Error event on socket: Connection refused
When -a / --address is given, the user has explicitly said where the
guest is: track that decision with a new addr_fixed flag (per address
family) and skip the addr_seen updates in the tap handlers. Behaviour
without -a is unchanged, and IPv6 link-local tracking (addr_ll_seen)
is unaffected — a global -a says nothing about which link-local
address the guest chose.
With this change, a reproducer that previously failed one run in three
(99 spliced connections per run) passed six consecutive runs.
Signed-off-by: EJ Campbell <ej.campbell@gmail.com>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
| -rw-r--r-- | conf.c | 2 | ||||
| -rw-r--r-- | passt.h | 6 | ||||
| -rw-r--r-- | tap.c | 9 |
3 files changed, 14 insertions, 3 deletions
@@ -1583,10 +1583,12 @@ void conf(struct ctx *c, int argc, char **argv) if (inany_v4(&addr)) { c->ip4.addr = *inany_v4(&addr); c->ip4.prefix_len = prefix_len - 96; + c->ip4.addr_fixed = true; if (c->mode == MODE_PASTA) c->ip4.no_copy_addrs = true; } else { c->ip6.addr = addr.a6; + c->ip6.addr_fixed = true; if (c->mode == MODE_PASTA) c->ip6.no_copy_addrs = true; } @@ -82,6 +82,8 @@ enum passt_modes { * @ifname_out: Optional interface name to bind outbound sockets to * @no_copy_routes: Don't copy all routes when configuring target namespace * @no_copy_addrs: Don't copy all addresses when configuring namespace + * @addr_fixed: Address was given explicitly (-a): don't update + * addr_seen from traffic observed on tap */ struct ip4_ctx { /* PIF_TAP addresses */ @@ -103,6 +105,7 @@ struct ip4_ctx { bool no_copy_routes; bool no_copy_addrs; + bool addr_fixed; }; /** @@ -123,6 +126,8 @@ struct ip4_ctx { * @ifname_out: Optional interface name to bind outbound sockets to * @no_copy_routes: Don't copy all routes when configuring target namespace * @no_copy_addrs: Don't copy all addresses when configuring namespace + * @addr_fixed: Address was given explicitly (-a): don't update + * addr_seen from traffic observed on tap */ struct ip6_ctx { /* PIF_TAP addresses */ @@ -144,6 +149,7 @@ struct ip6_ctx { bool no_copy_routes; bool no_copy_addrs; + bool addr_fixed; }; #include <netinet/if_ether.h> @@ -771,7 +771,8 @@ resume: continue; } - if (iph->saddr && c->ip4.addr_seen.s_addr != iph->saddr) + if (!c->ip4.addr_fixed && + iph->saddr && c->ip4.addr_seen.s_addr != iph->saddr) c->ip4.addr_seen.s_addr = iph->saddr; if (!iov_drop_header(&data, hlen)) @@ -1014,13 +1015,15 @@ resume: if (IN6_IS_ADDR_LINKLOCAL(saddr)) { c->ip6.addr_ll_seen = *saddr; - if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_seen)) { + if (!c->ip6.addr_fixed && + IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_seen)) { c->ip6.addr_seen = *saddr; } if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr)) c->ip6.addr = *saddr; - } else if (!IN6_IS_ADDR_UNSPECIFIED(saddr)){ + } else if (!c->ip6.addr_fixed && + !IN6_IS_ADDR_UNSPECIFIED(saddr)) { c->ip6.addr_seen = *saddr; } |
