- bool dump_neigh = false;
- struct in6_addr last_solicited = IN6ADDR_ANY_INIT;
-
- for (struct nlmsghdr *nh = data; NLMSG_OK(nh, len);
- nh = NLMSG_NEXT(nh, len)) {
- struct rtmsg *rtm = NLMSG_DATA(nh);
- if ((nh->nlmsg_type == RTM_NEWROUTE ||
- nh->nlmsg_type == RTM_DELROUTE) &&
- rtm->rtm_dst_len == 0)
- raise(SIGUSR1); // Inform about a change in default route
-
- struct ndmsg *ndm = NLMSG_DATA(nh);
- struct ifaddrmsg *ifa = NLMSG_DATA(nh);
- if (nh->nlmsg_type != RTM_NEWNEIGH
- && nh->nlmsg_type != RTM_DELNEIGH
- && nh->nlmsg_type != RTM_NEWADDR
- && nh->nlmsg_type != RTM_DELADDR)
- continue; // Unrelated message type
- bool is_addr = (nh->nlmsg_type == RTM_NEWADDR
- || nh->nlmsg_type == RTM_DELADDR);
-
- // Family and ifindex are on the same offset for NEIGH and ADDR
- if (NLMSG_PAYLOAD(nh, 0) < sizeof(*ndm)
- || ndm->ndm_family != AF_INET6)
- continue; //
-
- // Lookup interface
- struct interface *iface;
- if (!(iface = odhcpd_get_interface_by_index(ndm->ndm_ifindex)))
- continue;
-
- // Data to retrieve
- size_t rta_offset = (is_addr) ? sizeof(*ifa) : sizeof(*ndm);
- uint16_t atype = (is_addr) ? IFA_ADDRESS : NDA_DST;
- ssize_t alen = NLMSG_PAYLOAD(nh, rta_offset);
- struct in6_addr *addr = NULL;
-
- for (struct rtattr *rta = (void*)(((uint8_t*)ndm) + rta_offset);
- RTA_OK(rta, alen); rta = RTA_NEXT(rta, alen))
- if (rta->rta_type == atype &&
- RTA_PAYLOAD(rta) >= sizeof(*addr))
- addr = RTA_DATA(rta);
-
- // Address not specified or unrelated
- if (!addr || IN6_IS_ADDR_LINKLOCAL(addr) ||
- IN6_IS_ADDR_MULTICAST(addr))
- continue;
-
- // Check for states
- bool add;
- if (is_addr)
- add = (nh->nlmsg_type == RTM_NEWADDR);
- else
- add = (nh->nlmsg_type == RTM_NEWNEIGH && (ndm->ndm_state &
- (NUD_REACHABLE | NUD_STALE | NUD_DELAY | NUD_PROBE
- | NUD_PERMANENT | NUD_NOARP)));
-
- if (iface->ndp == RELAYD_RELAY) {
- // Replay change to all neighbor cache
- struct {
- struct nlmsghdr nh;
- struct ndmsg ndm;
- struct nlattr nla_dst;
- struct in6_addr dst;
- } req = {
- {sizeof(req), RTM_DELNEIGH, NLM_F_REQUEST,
- ++rtnl_seqid, 0},
- {.ndm_family = AF_INET6, .ndm_flags = NTF_PROXY},
- {sizeof(struct nlattr) + sizeof(struct in6_addr), NDA_DST},
- *addr
- };
-
- if (ndm->ndm_flags & NTF_PROXY) {
- // Dump & flush proxy entries
- if (nh->nlmsg_type == RTM_NEWNEIGH) {
- req.ndm.ndm_ifindex = iface->ifindex;
- send(rtnl_event.uloop.fd, &req, sizeof(req), MSG_DONTWAIT);
- setup_route(addr, iface, false);
- dump_neigh = true;
- }
- } else if (add) {
- struct interface *c;
- list_for_each_entry(c, &interfaces, head) {
- if (iface == c)
- continue;
-
- if (c->ndp == RELAYD_RELAY) {
- req.nh.nlmsg_type = RTM_NEWNEIGH;
- req.nh.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
-
- req.ndm.ndm_ifindex = c->ifindex;
- send(rtnl_event.uloop.fd, &req, sizeof(req), MSG_DONTWAIT);
- } else { // Delete NDP cache from interfaces without relay
- req.nh.nlmsg_type = RTM_DELNEIGH;
- req.nh.nlmsg_flags &= ~(NLM_F_CREATE | NLM_F_REPLACE);
-
- req.ndm.ndm_ifindex = c->ifindex;
- send(rtnl_event.uloop.fd, &req, sizeof(req), MSG_DONTWAIT);
- }
- }
-
- setup_route(addr, iface, true);
- } else {
- if (nh->nlmsg_type == RTM_NEWNEIGH) {
- // might be locally originating
- if (!IN6_ARE_ADDR_EQUAL(&last_solicited, addr)) {
- last_solicited = *addr;
-
- struct interface *c;
- list_for_each_entry(c, &interfaces, head)
- if (iface->ndp == RELAYD_RELAY && iface != c &&
- !c->external == false)
- ping6(addr, c);
- }
- } else {
- struct interface *c;
- list_for_each_entry(c, &interfaces, head) {
- if (c->ndp == RELAYD_RELAY && iface != c) {
- req.ndm.ndm_ifindex = c->ifindex;
- send(rtnl_event.uloop.fd, &req, sizeof(req), MSG_DONTWAIT);
- }
- }
- setup_route(addr, iface, false);
-
- // also: dump to add proxies back in case it moved elsewhere
- dump_neigh = true;
- }
- }
- }
-
- if (is_addr) {
- if (iface->ra == RELAYD_SERVER)
- raise(SIGUSR1); // Inform about a change in addresses