diff --git a/dhcpv6.c b/dhcpv6.c index cac220e..27d3a9c 100644 --- a/dhcpv6.c +++ b/dhcpv6.c @@ -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; diff --git a/ndp.c b/ndp.c index 10c091c..e779ae0 100644 --- a/ndp.c +++ b/ndp.c @@ -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; diff --git a/tcp.c b/tcp.c index c8815c5..6b5d29b 100644 --- a/tcp.c +++ b/tcp.c @@ -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)); diff --git a/udp.c b/udp.c index bd03036..229c038 100644 --- a/udp.c +++ b/udp.c @@ -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;