ndp, dhcpv6, tcp, udp: Always use link-local as source if gateway isn't

This shouldn't happen on any sane configuration, but I just met an
example of that: the default IPv6 gateway on the host is configured
with a global unicast address, we use that as source for RA, DHCPv6
replies, and the guest ignores it. Same later on if we talk TCP or
UDP and the guest has no idea where that address comes from.

Use our link-local address in case the gateway address is global.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
This commit is contained in:
Stefano Brivio 2021-10-20 11:10:23 +02:00
parent 12cfa6444c
commit 9618d24700
4 changed files with 28 additions and 5 deletions

View file

@ -452,6 +452,7 @@ int dhcpv6(struct ctx *c, struct ethhdr *eh, size_t len)
{
struct ipv6hdr *ip6h = (struct ipv6hdr *)(eh + 1);
struct opt_hdr *ia, *bad_ia, *client_id, *server_id;
struct in6_addr *src;
struct msg_hdr *mh;
struct udphdr *uh;
uint8_t proto;
@ -476,6 +477,11 @@ int dhcpv6(struct ctx *c, struct ethhdr *eh, size_t len)
c->addr6_ll_seen = ip6h->saddr;
if (IN6_IS_ADDR_LINKLOCAL(&c->gw6))
src = &c->gw6;
else
src = &c->addr6_ll;
mh = (struct msg_hdr *)(uh + 1);
mlen -= sizeof(struct msg_hdr);
@ -527,7 +533,7 @@ int dhcpv6(struct ctx *c, struct ethhdr *eh, size_t len)
resp_not_on_link.hdr.xid = mh->xid;
tap_ip_send(c, &c->gw6, IPPROTO_UDP,
tap_ip_send(c, src, IPPROTO_UDP,
(char *)&resp_not_on_link, n, mh->xid);
return 1;
@ -577,7 +583,7 @@ int dhcpv6(struct ctx *c, struct ethhdr *eh, size_t len)
resp.hdr.xid = mh->xid;
tap_ip_send(c, &c->gw6, IPPROTO_UDP, (char *)&resp, n, mh->xid);
tap_ip_send(c, src, IPPROTO_UDP, (char *)&resp, n, mh->xid);
c->addr6_seen = c->addr6;
return 1;

6
ndp.c
View file

@ -183,7 +183,11 @@ int ndp(struct ctx *c, struct ethhdr *eh, size_t len)
c->addr6_seen = ip6h->saddr;
ip6hr->daddr = ip6h->saddr;
ip6hr->saddr = c->gw6;
if (IN6_IS_ADDR_LINKLOCAL(&c->gw6))
ip6hr->saddr = c->gw6;
else
ip6hr->saddr = c->addr6_ll;
ip6hr->payload_len = htons(sizeof(*ihr) + len);
ip6hr->hop_limit = IPPROTO_ICMPV6;
ihr->icmp6_cksum = 0;

12
tcp.c
View file

@ -2921,8 +2921,16 @@ static void tcp_conn_from_sock(struct ctx *c, union epoll_ref ref,
if (IN6_IS_ADDR_LOOPBACK(&sa6.sin6_addr) ||
!memcmp(&sa6.sin6_addr, &c->addr6_seen, sizeof(c->gw6)) ||
!memcmp(&sa6.sin6_addr, &c->addr6, sizeof(c->gw6)))
memcpy(&sa6.sin6_addr, &c->gw6, sizeof(c->gw6));
!memcmp(&sa6.sin6_addr, &c->addr6, sizeof(c->gw6))) {
struct in6_addr *src;
if (IN6_IS_ADDR_LINKLOCAL(&c->gw6))
src = &c->gw6;
else
src = &c->addr6_ll;
memcpy(&sa6.sin6_addr, src, sizeof(*src));
}
memcpy(&conn->a.a6, &sa6.sin6_addr, sizeof(conn->a.a6));

5
udp.c
View file

@ -698,6 +698,11 @@ void udp_sock_handler(struct ctx *c, union epoll_ref ref, uint32_t events,
in_port_t src = htons(b->s_in6.sin6_port);
b->ip6h.daddr = c->addr6_ll_seen;
if (IN6_IS_ADDR_LINKLOCAL(&c->gw6))
b->ip6h.saddr = c->gw6;
else
b->ip6h.saddr = c->addr6_ll;
b->ip6h.saddr = c->gw6;
udp_tap_map[V6][src].ts_local = now->tv_sec;