Depending on several possible NAT situations, udp_update_hdr6() has a few different paths for setting the destination IPv6 address. However, this isn't really separate from the selection of the IPv6 source address: if the adjusted source address is link-local then we need to use a link local destination, otherwise we need the global destination address. This fixes a small bug: it's theoretically possible, although unlikely, for the dns_match address to be link-local, in which case the previous code would have used the wrong destination. This does do slightly more work in the case of NATting packets originating on the host. Currently we always NAT those to a link-local address so we could statically set a link-local destination address, but we now recheck. However, as well as being simpler, the new approach is more robust if we want to allow non-link-local NAT-to-host addresses, which we do plan to in future. Signed-off-by: David Gibson <david(a)gibson.dropbear.id.au> --- udp.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/udp.c b/udp.c index 361f24c..9c96c0f 100644 --- a/udp.c +++ b/udp.c @@ -640,18 +640,13 @@ static size_t udp_update_hdr6(const struct ctx *c, int n, in_port_t dstport, b->ip6h.payload_len = htons(udp6_l2_mh_sock[n].msg_len + sizeof(b->uh)); - if (IN6_IS_ADDR_LINKLOCAL(src)) { - b->ip6h.daddr = c->ip6.addr_ll_seen; - } else if (!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns_match) && + if (!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns_match) && IN6_ARE_ADDR_EQUAL(src, &c->ip6.dns_host) && src_port == 53) { - b->ip6h.daddr = c->ip6.addr_seen; src = &c->ip6.dns_match; } else if (IN6_IS_ADDR_LOOPBACK(src) || IN6_ARE_ADDR_EQUAL(src, &c->ip6.addr_seen) || IN6_ARE_ADDR_EQUAL(src, &c->ip6.addr)) { - b->ip6h.daddr = c->ip6.addr_ll_seen; - udp_tap_map[V6][src_port].ts = now->tv_sec; udp_tap_map[V6][src_port].flags |= PORT_LOCAL; @@ -671,11 +666,13 @@ static size_t udp_update_hdr6(const struct ctx *c, int n, in_port_t dstport, src = &c->ip6.gw; else src = &c->ip6.addr_ll; - } else { - b->ip6h.daddr = c->ip6.addr_seen; } b->ip6h.saddr = *src; + if (IN6_IS_ADDR_LINKLOCAL(src)) + b->ip6h.daddr = c->ip6.addr_ll_seen; + else + b->ip6h.daddr = c->ip6.addr_seen; b->uh.source = b->s_in6.sin6_port; b->uh.dest = htons(dstport); -- 2.40.1