-
-static void handle_rtnl_event(struct odhcpd_event *e)
-{
- struct event_socket *ev_sock = container_of(e, struct event_socket, ev);
-
- nl_recvmsgs_default(ev_sock->sock);
-}
-
-
-// Handler for neighbor cache entries from the kernel. This is our source
-// to learn and unlearn hosts on interfaces.
-static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg)
-{
- struct nlmsghdr *hdr = nlmsg_hdr(msg);
- struct in6_addr *addr = NULL;
- struct interface *iface = NULL;
- bool add = false;
- char ipbuf[INET6_ADDRSTRLEN];
-
- switch (hdr->nlmsg_type) {
- case RTM_NEWROUTE:
- case RTM_DELROUTE: {
- struct rtmsg *rtm = nlmsg_data(hdr);
-
- if (!nlmsg_valid_hdr(hdr, sizeof(*rtm)) ||
- rtm->rtm_family != AF_INET6)
- return NL_SKIP;
-
- if (rtm->rtm_dst_len == 0) {
- syslog(LOG_INFO, "Raising SIGUSR1 due to default route change");
- raise(SIGUSR1);
- }
- return NL_OK;
- }
-
- case RTM_NEWADDR:
- add = true;
- /* fall through */
- case RTM_DELADDR: {
- struct ifaddrmsg *ifa = nlmsg_data(hdr);
- struct nlattr *nla[__IFA_MAX];
-
- if (!nlmsg_valid_hdr(hdr, sizeof(*ifa)) ||
- ifa->ifa_family != AF_INET6)
- return NL_SKIP;
-
- iface = odhcpd_get_interface_by_index(ifa->ifa_index);
- if (!iface)
- return NL_SKIP;
-
- nlmsg_parse(hdr, sizeof(*ifa), nla, __IFA_MAX - 1, NULL);
- if (!nla[IFA_ADDRESS])
- return NL_SKIP;
-
- addr = nla_data(nla[IFA_ADDRESS]);
- if (!addr || IN6_IS_ADDR_LINKLOCAL(addr) ||
- IN6_IS_ADDR_MULTICAST(addr))
- return NL_SKIP;
-
- inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf));
- syslog(LOG_DEBUG, "Netlink %s %s%%%s", add ? "newaddr" : "deladdr",
- ipbuf, iface->ifname);
-
- check_addr6_updates(iface);
-
- if (iface->ndp != RELAYD_RELAY)
- break;
-
- /* handle the relay logic below */
- setup_addr_for_relaying(addr, iface, add);
-
- if (!add)
- dump_neigh_table(false);
- break;
- }
-
- case RTM_NEWNEIGH:
- add = true;
- /* fall through */
- case RTM_DELNEIGH: {
- struct ndmsg *ndm = nlmsg_data(hdr);
- struct nlattr *nla[__NDA_MAX];
-
- if (!nlmsg_valid_hdr(hdr, sizeof(*ndm)) ||
- ndm->ndm_family != AF_INET6)
- return NL_SKIP;
-
- iface = odhcpd_get_interface_by_index(ndm->ndm_ifindex);
- if (!iface || iface->ndp != RELAYD_RELAY)
- return (iface ? NL_OK : NL_SKIP);
-
- nlmsg_parse(hdr, sizeof(*ndm), nla, __NDA_MAX - 1, NULL);
- if (!nla[NDA_DST])
- return NL_SKIP;
-
- addr = nla_data(nla[NDA_DST]);
- if (!addr || IN6_IS_ADDR_LINKLOCAL(addr) ||
- IN6_IS_ADDR_MULTICAST(addr))
- return NL_SKIP;
-
- inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf));
- syslog(LOG_DEBUG, "Netlink %s %s%%%s", add ? "newneigh" : "delneigh",
- ipbuf, iface->ifname);
-
- if (ndm->ndm_flags & NTF_PROXY) {
- /* Dump and flush proxy entries */
- if (hdr->nlmsg_type == RTM_NEWNEIGH) {
- odhcpd_setup_proxy_neigh(addr, iface, false);
- setup_route(addr, iface, false);
- dump_neigh_table(false);
- }
-
- return NL_OK;
- }
-
- if (add && !(ndm->ndm_state &
- (NUD_REACHABLE | NUD_STALE | NUD_DELAY | NUD_PROBE |
- NUD_PERMANENT | NUD_NOARP)))
- return NL_OK;
-
- setup_addr_for_relaying(addr, iface, add);
- setup_route(addr, iface, add);
-
- if (!add)
- dump_neigh_table(false);
- break;
- }
-
- default:
- return NL_SKIP;
- }
-
- return NL_OK;
-}
-
-static void catch_rtnl_err(struct odhcpd_event *e, int error)
-{
- struct event_socket *ev_sock = container_of(e, struct event_socket, ev);
-
- if (error != ENOBUFS)
- goto err;
-
- /* Double netlink event buffer size */
- ev_sock->sock_bufsize *= 2;
-
- if (nl_socket_set_buffer_size(ev_sock->sock, ev_sock->sock_bufsize, 0))
- goto err;
-
- dump_addr_table(true);
- return;
-
-err:
- odhcpd_deregister(e);
-}