udp: Translate source address of resolver only for DNS remapped queries

Paul reports that if pasta is configured with --dns-forward, and the
container queries a resolver which is configured on the host directly,
without using the address given for --dns-forward, we'll translate
the source address of the response pretending it's coming from the
address passed as --dns-forward, and the client will discard the
reply.

That is,

  $ cat /etc/resolv.conf
  198.51.100.1
  $ pasta --config-net --dns-forward 192.0.2.1 nslookup passt.top

will not work, because we change the source address of the reply from
198.51.100.1 to 192.0.2.1. But the client contacted 198.51.100.1, and
it's from that address that it expects an answer.

Add a PORT_DNS_FWD flag for tap-facing ports, which is triggered by
activity in the opposite direction as the other flags. If the
tap-facing port was seen sending a DNS query that was remapped, we'll
remap the source address of the response, otherwise we'll leave it
unaffected.

Reported-by: Paul Holzinger <pholzing@redhat.com>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
This commit is contained in:
Stefano Brivio 2024-03-15 15:17:08 +01:00
parent f919dc7a4b
commit d989eae308

21
udp.c
View file

@ -127,15 +127,16 @@
/** /**
* struct udp_tap_port - Port tracking based on tap-facing source port * struct udp_tap_port - Port tracking based on tap-facing source port
* @sock: Socket bound to source port used as index * @sock: Socket bound to source port used as index
* @flags: Flags for local bind, loopback address/unicast address as source * @flags: Flags for recent activity type seen from/to port
* @ts: Activity timestamp from tap, used for socket aging * @ts: Activity timestamp from tap, used for socket aging
*/ */
struct udp_tap_port { struct udp_tap_port {
int sock; int sock;
uint8_t flags; uint8_t flags;
#define PORT_LOCAL BIT(0) #define PORT_LOCAL BIT(0) /* Port was contacted from local address */
#define PORT_LOOPBACK BIT(1) #define PORT_LOOPBACK BIT(1) /* Port was contacted from loopback address */
#define PORT_GUA BIT(2) #define PORT_GUA BIT(2) /* Port was contacted from global unicast */
#define PORT_DNS_FWD BIT(3) /* Port used as source for DNS remapped query */
time_t ts; time_t ts;
}; };
@ -579,7 +580,8 @@ static size_t udp_update_hdr4(const struct ctx *c, struct udp4_l2_buf_t *b,
struct in_addr src = b->s_in.sin_addr; struct in_addr src = b->s_in.sin_addr;
if (!IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns_match) && if (!IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns_match) &&
IN4_ARE_ADDR_EQUAL(&src, &c->ip4.dns_host) && srcport == 53) { IN4_ARE_ADDR_EQUAL(&src, &c->ip4.dns_host) && srcport == 53 &&
(udp_tap_map[V4][dstport].flags & PORT_DNS_FWD)) {
src = c->ip4.dns_match; src = c->ip4.dns_match;
} else if (IN4_IS_ADDR_LOOPBACK(&src) || } else if (IN4_IS_ADDR_LOOPBACK(&src) ||
IN4_ARE_ADDR_EQUAL(&src, &c->ip4.addr_seen)) { IN4_ARE_ADDR_EQUAL(&src, &c->ip4.addr_seen)) {
@ -632,7 +634,8 @@ static size_t udp_update_hdr6(const struct ctx *c, struct udp6_l2_buf_t *b,
dst = &c->ip6.addr_ll_seen; dst = &c->ip6.addr_ll_seen;
} else if (!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns_match) && } else if (!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns_match) &&
IN6_ARE_ADDR_EQUAL(src, &c->ip6.dns_host) && IN6_ARE_ADDR_EQUAL(src, &c->ip6.dns_host) &&
srcport == 53) { srcport == 53 &&
(udp_tap_map[V4][dstport].flags & PORT_DNS_FWD)) {
src = &c->ip6.dns_match; src = &c->ip6.dns_match;
} else if (IN6_IS_ADDR_LOOPBACK(src) || } else if (IN6_IS_ADDR_LOOPBACK(src) ||
IN6_ARE_ADDR_EQUAL(src, &c->ip6.addr_seen) || IN6_ARE_ADDR_EQUAL(src, &c->ip6.addr_seen) ||
@ -841,6 +844,9 @@ int udp_tap_handler(struct ctx *c, uint8_t pif,
if (IN4_ARE_ADDR_EQUAL(&s_in.sin_addr, &c->ip4.dns_match) && if (IN4_ARE_ADDR_EQUAL(&s_in.sin_addr, &c->ip4.dns_match) &&
ntohs(s_in.sin_port) == 53) { ntohs(s_in.sin_port) == 53) {
s_in.sin_addr = c->ip4.dns_host; s_in.sin_addr = c->ip4.dns_host;
udp_tap_map[V4][src].ts = now->tv_sec;
udp_tap_map[V4][src].flags |= PORT_DNS_FWD;
bitmap_set(udp_act[V4][UDP_ACT_TAP], src);
} else if (IN4_ARE_ADDR_EQUAL(&s_in.sin_addr, &c->ip4.gw) && } else if (IN4_ARE_ADDR_EQUAL(&s_in.sin_addr, &c->ip4.gw) &&
!c->no_map_gw) { !c->no_map_gw) {
if (!(udp_tap_map[V4][dst].flags & PORT_LOCAL) || if (!(udp_tap_map[V4][dst].flags & PORT_LOCAL) ||
@ -890,6 +896,9 @@ int udp_tap_handler(struct ctx *c, uint8_t pif,
if (IN6_ARE_ADDR_EQUAL(daddr, &c->ip6.dns_match) && if (IN6_ARE_ADDR_EQUAL(daddr, &c->ip6.dns_match) &&
ntohs(s_in6.sin6_port) == 53) { ntohs(s_in6.sin6_port) == 53) {
s_in6.sin6_addr = c->ip6.dns_host; s_in6.sin6_addr = c->ip6.dns_host;
udp_tap_map[V6][src].ts = now->tv_sec;
udp_tap_map[V6][src].flags |= PORT_DNS_FWD;
bitmap_set(udp_act[V6][UDP_ACT_TAP], src);
} else if (IN6_ARE_ADDR_EQUAL(daddr, &c->ip6.gw) && } else if (IN6_ARE_ADDR_EQUAL(daddr, &c->ip6.gw) &&
!c->no_map_gw) { !c->no_map_gw) {
if (!(udp_tap_map[V6][dst].flags & PORT_LOCAL) || if (!(udp_tap_map[V6][dst].flags & PORT_LOCAL) ||