aboutgitcodebugslistschat
diff options
context:
space:
mode:
authorDavid Gibson <david@gibson.dropbear.id.au>2025-04-17 11:55:43 +1000
committerStefano Brivio <sbrivio@redhat.com>2025-04-22 12:42:05 +0200
commit436afc30447c6f0ce516f2b38c769833114bb5f8 (patch)
tree6cd652f49c7516583598f8051d8f8b6b570b02e1
parent08e617ec2ba916d8250a41d3ac68183124a6ec3e (diff)
downloadpasst-436afc30447c6f0ce516f2b38c769833114bb5f8.tar
passt-436afc30447c6f0ce516f2b38c769833114bb5f8.tar.gz
passt-436afc30447c6f0ce516f2b38c769833114bb5f8.tar.bz2
passt-436afc30447c6f0ce516f2b38c769833114bb5f8.tar.lz
passt-436afc30447c6f0ce516f2b38c769833114bb5f8.tar.xz
passt-436afc30447c6f0ce516f2b38c769833114bb5f8.tar.zst
passt-436afc30447c6f0ce516f2b38c769833114bb5f8.zip
udp: Translate offender addresses for ICMP messages
We've recently added support for propagating ICMP errors related to a UDP flow from the host to the guest, by handling the extended UDP error on the socket and synthesizing a suitable ICMP on the tap interface. Currently we create that ICMP with a source address of the "offender" from the extended error information - the source of the ICMP error received on the host. However, we don't translate this address for cases where we NAT between host and guest. This means (amongst other things) that we won't get a "Connection refused" error as expected if send data from the guest to the --map-host-loopback address. The error comes from 127.0.0.1 on the host, which doesn't make sense on the tap interface and will be discarded by the guest. Because ICMP errors can be sent by an intermediate host, not just by the endpoints of the flow, we can't handle this translation purely with the information in the flow table entry. We need to explicitly translate this address by our NAT rules, which we can do with the nat_inbound() helper. Signed-off-by: David Gibson <david@gibson.dropbear.id.au> Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
-rw-r--r--fwd.c4
-rw-r--r--fwd.h3
-rw-r--r--udp.c18
3 files changed, 19 insertions, 6 deletions
diff --git a/fwd.c b/fwd.c
index 5c70e83..b73c2c8 100644
--- a/fwd.c
+++ b/fwd.c
@@ -450,8 +450,8 @@ uint8_t fwd_nat_from_splice(const struct ctx *c, uint8_t proto,
* Only handles translations that depend *only* on the address. Anything
* related to specific ports or flows is handled elsewhere.
*/
-static bool nat_inbound(const struct ctx *c, const union inany_addr *addr,
- union inany_addr *translated)
+bool nat_inbound(const struct ctx *c, const union inany_addr *addr,
+ union inany_addr *translated)
{
if (!IN4_IS_ADDR_UNSPECIFIED(&c->ip4.map_host_loopback) &&
inany_equals4(addr, &in4addr_loopback)) {
diff --git a/fwd.h b/fwd.h
index 3562f3c..0458a3c 100644
--- a/fwd.h
+++ b/fwd.h
@@ -7,6 +7,7 @@
#ifndef FWD_H
#define FWD_H
+union inany_addr;
struct flowside;
/* Number of ports for both TCP and UDP */
@@ -47,6 +48,8 @@ void fwd_scan_ports_udp(struct fwd_ports *fwd, const struct fwd_ports *rev,
const struct fwd_ports *tcp_rev);
void fwd_scan_ports_init(struct ctx *c);
+bool nat_inbound(const struct ctx *c, const union inany_addr *addr,
+ union inany_addr *translated);
uint8_t fwd_nat_from_tap(const struct ctx *c, uint8_t proto,
const struct flowside *ini, struct flowside *tgt);
uint8_t fwd_nat_from_splice(const struct ctx *c, uint8_t proto,
diff --git a/udp.c b/udp.c
index d09b3eb..f5a5cd1 100644
--- a/udp.c
+++ b/udp.c
@@ -539,10 +539,10 @@ static int udp_sock_recverr(const struct ctx *c, int s, flow_sidx_t sidx,
.msg_control = buf,
.msg_controllen = sizeof(buf),
};
- const struct flowside *toside;
+ const struct flowside *fromside, *toside;
+ union inany_addr offender, otap;
char astr[INANY_ADDRSTRLEN];
char sastr[SOCKADDR_STRLEN];
- union inany_addr offender;
const struct in_addr *o4;
in_port_t offender_port;
struct udp_flow *uflow;
@@ -602,6 +602,7 @@ static int udp_sock_recverr(const struct ctx *c, int s, flow_sidx_t sidx,
uflow = udp_at_sidx(sidx);
ASSERT(uflow);
+ fromside = &uflow->f.side[sidx.sidei];
toside = &uflow->f.side[!sidx.sidei];
topif = uflow->f.pif[!sidx.sidei];
dlen = rc;
@@ -614,15 +615,24 @@ static int udp_sock_recverr(const struct ctx *c, int s, flow_sidx_t sidx,
/* XXX Can we support any other cases? */
goto fail;
+ /* If the offender *is* the endpoint, make sure our translation is
+ * consistent with the flow's translation. This matters if the flow
+ * endpoint has a port specific translation (like --dns-match).
+ */
+ if (inany_equals(&offender, &fromside->eaddr))
+ otap = toside->oaddr;
+ else if (!nat_inbound(c, &offender, &otap))
+ goto fail;
+
if (hdr->cmsg_level == IPPROTO_IP &&
- (o4 = inany_v4(&offender)) && inany_v4(&toside->eaddr)) {
+ (o4 = inany_v4(&otap)) && inany_v4(&toside->eaddr)) {
dlen = MIN(dlen, ICMP4_MAX_DLEN);
udp_send_tap_icmp4(c, ee, toside, *o4, data, dlen);
return 1;
}
if (hdr->cmsg_level == IPPROTO_IPV6 && !inany_v4(&toside->eaddr)) {
- udp_send_tap_icmp6(c, ee, toside, &offender.a6, data, dlen,
+ udp_send_tap_icmp6(c, ee, toside, &otap.a6, data, dlen,
FLOW_IDX(uflow));
return 1;
}