+ time_t now = time(NULL);
+
+ struct ndp_neighbor *n = find_neighbor(&req->nd_ns_target, false);
+ if (n && (n->iface || abs(n->timeout - now) < 5)) {
+ syslog(LOG_DEBUG, "%s is on %s", ipbuf,
+ (n->iface) ? n->iface->ifname : "<pending>");
+ if (!n->iface || n->iface == iface)
+ return;
+
+ // Found on other interface, answer with advertisement
+ struct {
+ struct nd_neighbor_advert body;
+ struct nd_opt_hdr opt_ll_hdr;
+ uint8_t mac[6];
+ } advert = {
+ .body = {
+ .nd_na_hdr = {ND_NEIGHBOR_ADVERT,
+ 0, 0, {{0}}},
+ .nd_na_target = req->nd_ns_target,
+ },
+ .opt_ll_hdr = {ND_OPT_TARGET_LINKADDR, 1},
+ };
+
+ memcpy(advert.mac, mac, sizeof(advert.mac));
+ advert.body.nd_na_flags_reserved = ND_NA_FLAG_ROUTER |
+ ND_NA_FLAG_SOLICITED;
+
+ struct sockaddr_in6 dest = {AF_INET6, 0, 0, ALL_IPV6_NODES, 0};
+ if (!ns_is_dad) // If not DAD, then unicast to source
+ dest.sin6_addr = ip6->ip6_src;
+
+ // Linux seems to not honor IPV6_PKTINFO on raw-sockets, so work around
+ setsockopt(ping_socket, SOL_SOCKET, SO_BINDTODEVICE,
+ iface->ifname, sizeof(iface->ifname));
+ struct iovec iov = {&advert, sizeof(advert)};
+ odhcpd_send(ping_socket, &dest, &iov, 1, iface);
+ } else {
+ // Send echo to all other interfaces to see where target is on
+ // This will trigger neighbor discovery which is what we want.
+ // We will observe the neighbor cache to see results.
+
+ ssize_t sent = 0;
+ struct interface *c;
+ list_for_each_entry(c, &interfaces, head)
+ if (iface->ndp == RELAYD_RELAY && iface != c &&
+ (!ns_is_dad || !c->external == false))
+ sent += ping6(&req->nd_ns_target, c);
+
+ if (sent > 0) // Sent a ping, add pending neighbor entry
+ modify_neighbor(&req->nd_ns_target, NULL, true);
+ }