{.ndm_family = AF_INET6, .ndm_flags = (proxy) ? NTF_PROXY : 0}
};
send(rtnl_event.uloop.fd, &req, sizeof(req), MSG_DONTWAIT);
+ odhcpd_process(&rtnl_event);
}
iface->ndp_event.uloop.fd = -1;
if (!enable || iface->ndp != RELAYD_RELAY)
- write(procfd, "0\n", 2);
+ if (write(procfd, "0\n", 2) < 0) {}
dump_neigh = true;
}
}
if (enable && iface->ndp == RELAYD_RELAY) {
- write(procfd, "1\n", 2);
+ if (write(procfd, "1\n", 2) < 0) {}
close(procfd);
int sock = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IPV6));
static ssize_t ping6(struct in6_addr *addr,
const struct interface *iface)
{
- struct sockaddr_in6 dest = {AF_INET6, 0, 0, *addr, 0};
+ struct sockaddr_in6 dest = {AF_INET6, 0, 0, *addr, iface->ifindex};
struct icmp6_hdr echo = {.icmp6_type = ICMP6_ECHO_REQUEST};
struct iovec iov = {&echo, sizeof(echo)};
}
// Use rtnetlink to modify kernel routes
-static void setup_route(struct in6_addr *addr, struct interface *iface,
- bool add)
+static void setup_route(struct in6_addr *addr, struct interface *iface, bool add)
{
char namebuf[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, addr, namebuf, sizeof(namebuf));
- syslog(LOG_NOTICE, "%s about %s on %s", (add) ? "Learned" : "Forgot",
- namebuf, (iface) ? iface->ifname : "<pending>");
+ syslog(LOG_NOTICE, "%s about %s on %s",
+ (add) ? "Learned" : "Forgot", namebuf, iface->ifname);
- if (!iface || !iface->learn_routes)
- return;
-
- odhcpd_setup_route(addr, 128, iface, NULL, add);
+ if (iface->learn_routes)
+ odhcpd_setup_route(addr, 128, iface, NULL, add);
}
for (struct nlmsghdr *nh = data; NLMSG_OK(nh, len);
nh = NLMSG_NEXT(nh, len)) {
+ struct ndmsg *ndm = NLMSG_DATA(nh);
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);
+ bool is_route = (nh->nlmsg_type == RTM_NEWROUTE
+ || nh->nlmsg_type == RTM_DELROUTE);
+ bool is_neigh = (nh->nlmsg_type == RTM_NEWNEIGH
+ || nh->nlmsg_type == RTM_DELNEIGH);
// Family and ifindex are on the same offset for NEIGH and ADDR
- if (NLMSG_PAYLOAD(nh, 0) < sizeof(*ndm)
+ if ((!is_addr && !is_route && !is_neigh)
+ || NLMSG_PAYLOAD(nh, 0) < sizeof(*ndm)
|| ndm->ndm_family != AF_INET6)
- continue; //
+ continue;
- // Lookup interface
- struct interface *iface;
- if (!(iface = odhcpd_get_interface_by_index(ndm->ndm_ifindex)))
+ // Inform about a change in default route
+ if (is_route && rtm->rtm_dst_len == 0)
+ raise(SIGUSR1);
+ else if (is_route && rtm->rtm_dst_len == 128)
continue;
// Data to retrieve
- size_t rta_offset = (is_addr) ? sizeof(*ifa) : sizeof(*ndm);
- uint16_t atype = (is_addr) ? IFA_ADDRESS : NDA_DST;
+ size_t rta_offset = (is_route) ? sizeof(*rtm) : (is_addr) ?
+ sizeof(*ifa) : sizeof(*ndm);
+ uint16_t atype = (is_route) ? RTA_DST : (is_addr) ? IFA_ADDRESS : NDA_DST;
ssize_t alen = NLMSG_PAYLOAD(nh, rta_offset);
struct in6_addr *addr = NULL;
+ int *ifindex = (!is_route) ? &ndm->ndm_ifindex : NULL;
for (struct rtattr *rta = (void*)(((uint8_t*)ndm) + rta_offset);
- RTA_OK(rta, alen); rta = RTA_NEXT(rta, alen))
+ RTA_OK(rta, alen); rta = RTA_NEXT(rta, alen)) {
if (rta->rta_type == atype &&
- RTA_PAYLOAD(rta) >= sizeof(*addr))
+ RTA_PAYLOAD(rta) >= sizeof(*addr)) {
addr = RTA_DATA(rta);
+ } else if (is_route && rta->rta_type == RTA_OIF &&
+ RTA_PAYLOAD(rta) == sizeof(int)) {
+ ifindex = (int*)RTA_DATA(rta);
+ } else if (is_route && rta->rta_type == RTA_GATEWAY) {
+ ifindex = NULL;
+ break;
+ }
+ }
+
+ // Lookup interface
+ struct interface *iface = ifindex ? odhcpd_get_interface_by_index(*ifindex) : NULL;
+ if (!iface)
+ continue;
+
+ // Keep-alive neighbor entries for RA sending
+ if (nh->nlmsg_type == RTM_DELNEIGH && !(ndm->ndm_state & NUD_FAILED) &&
+ addr && IN6_IS_ADDR_LINKLOCAL(addr) && iface->ra == RELAYD_SERVER)
+ ping6(addr, iface);
// Address not specified or unrelated
if (!addr || IN6_IS_ADDR_LINKLOCAL(addr) ||
(NUD_REACHABLE | NUD_STALE | NUD_DELAY | NUD_PROBE
| NUD_PERMANENT | NUD_NOARP)));
- if (iface->ndp == RELAYD_RELAY) {
+ if (iface->ndp == RELAYD_RELAY && !is_route) {
// Replay change to all neighbor cache
struct {
struct nlmsghdr nh;
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) {
if (iface->dhcpv6 == RELAYD_SERVER)
iface->ia_reconf = true;
-
+ } else if (is_route) {
if (iface->ndp == RELAYD_RELAY && iface->master) {
- // Replay address changes on all slave interfaces
+ // Replay on-link route changes on all slave interfaces
nh->nlmsg_flags = NLM_F_REQUEST;
- if (nh->nlmsg_type == RTM_NEWADDR)
+ if (nh->nlmsg_type == RTM_NEWROUTE)
nh->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
struct interface *c;
list_for_each_entry(c, &interfaces, head) {
if (c->ndp == RELAYD_RELAY && !c->master) {
- ifa->ifa_index = c->ifindex;
+ *ifindex = c->ifindex;
send(rtnl_event.uloop.fd, nh, nh->nlmsg_len, MSG_DONTWAIT);
}
}