diff options
author | sfan5 <sfan5@live.de> | 2019-02-02 22:48:18 +0100 |
---|---|---|
committer | Hans Dedecker <dedeckeh@gmail.com> | 2019-09-15 18:54:12 +0200 |
commit | 91a28e4566bdae6532d3801332bef9999f43605c (patch) | |
tree | 1e2cc18379b0edbe2ba54df6c4642bb34a6dcc13 /src/ndp.c | |
parent | fd93e36727bc605f6f860759d7710a43bee5215d (diff) |
ndp: answer global-addressed NS manually
An upstream router may address solicits to the global address of the target,
these will not be answered by the kernel and not routed either due to link-local
source. The NS will eventually be retried as multicast, but we want to avoid this.
see also https://forum.archive.openwrt.org/viewtopic.php?id=40871
Signed-off-by: Stefan Alfers <sfan5@live.de>
Diffstat (limited to 'src/ndp.c')
-rw-r--r-- | src/ndp.c | 38 |
1 files changed, 38 insertions, 0 deletions
@@ -295,6 +295,33 @@ static void ping6(struct in6_addr *addr, netlink_setup_route(addr, 128, iface->ifindex, NULL, 128, false); } +/* Send a Neighbor Advertisement. */ +static void send_na(struct in6_addr *to_addr, + const struct interface *iface, struct in6_addr *for_addr, + const uint8_t *mac) +{ + struct sockaddr_in6 dest = { .sin6_family = AF_INET6, .sin6_addr = *to_addr }; + char pbuf[sizeof(struct nd_neighbor_advert) + sizeof(struct nd_opt_hdr) + 6]; + struct nd_neighbor_advert *adv = (struct nd_neighbor_advert*)pbuf; + struct nd_opt_hdr *opt = (struct nd_opt_hdr*) &pbuf[sizeof(struct nd_neighbor_advert)]; + struct iovec iov = { .iov_base = &pbuf, .iov_len = sizeof(pbuf) }; + char ipbuf[INET6_ADDRSTRLEN]; + + memset(pbuf, 0, sizeof(pbuf)); + adv->nd_na_hdr = (struct icmp6_hdr) { + .icmp6_type = ND_NEIGHBOR_ADVERT, + .icmp6_dataun.icmp6_un_data32 = { 0x40000000L } + }; + adv->nd_na_target = *for_addr; + *opt = (struct nd_opt_hdr) { .nd_opt_type = ND_OPT_TARGET_LINKADDR, .nd_opt_len = 1 }; + memcpy(&pbuf[sizeof(struct nd_neighbor_advert) + sizeof(struct nd_opt_hdr)], mac, 6); + + inet_ntop(AF_INET6, to_addr, ipbuf, sizeof(ipbuf)); + syslog(LOG_DEBUG, "Answering NS to %s on %s", ipbuf, iface->ifname); + + odhcpd_send(iface->ndp_ping_fd, &dest, &iov, 1, iface); +} + /* Handle solicitations */ static void handle_solicit(void *addr, void *data, size_t len, struct interface *iface, _unused void *dest) @@ -335,6 +362,17 @@ static void handle_solicit(void *addr, void *data, size_t len, (ns_is_dad || !c->external)) ping6(&req->nd_ns_target, c); } + + /* Catch global-addressed NS and answer them manually. + * The kernel won't answer these and cannot route them either. */ + if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && + IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) { + bool is_proxy_neigh = netlink_get_interface_proxy_neigh(iface->ifindex, + &req->nd_ns_target) == 1; + + if (is_proxy_neigh) + send_na(&ip6->ip6_src, iface, &req->nd_ns_target, mac); + } } /* Use rtnetlink to modify kernel routes */ |