static int __ni_rtevent_process_rdnss_info(ni_netdev_t *dev, const struct nd_opt_hdr *opt, size_t len) { const struct ni_nd_opt_rdnss_info_p *ropt; char buf[INET6_ADDRSTRLEN+1] = {'\0'}; const struct in6_addr* addr; ni_ipv6_devinfo_t *ipv6; unsigned int lifetime; struct timeval acquired; ni_bool_t emit = FALSE; const char *server; if (opt == NULL || len < (sizeof(*ropt) + sizeof(*addr))) { ni_error("%s: unable to parse ipv6 rdnss info event data -- too short", dev->name); return -1; } ipv6 = ni_netdev_get_ipv6(dev); if (!ipv6) { ni_error("%s: unable to allocate device ipv6 structure: %m", dev->name); return -1; } ropt = (const struct ni_nd_opt_rdnss_info_p *)opt; ni_timer_get_time(&acquired); lifetime = ntohl(ropt->nd_opt_rdnss_lifetime); len -= sizeof(*ropt); addr = &ropt->nd_opt_rdnss_addr[0]; for ( ; len >= sizeof(*addr); len -= sizeof(*addr), ++addr) { if (IN6_IS_ADDR_LOOPBACK(addr) || IN6_IS_ADDR_UNSPECIFIED(addr)) { server = inet_ntop(AF_INET6, addr, buf, sizeof(buf)); ni_debug_verbose(NI_LOG_DEBUG2, NI_TRACE_IPV6|NI_TRACE_EVENTS, "%s: ignoring invalid rdnss server address %s", dev->name, server); continue; } if (!ni_ipv6_ra_rdnss_list_update(&ipv6->radv.rdnss, addr, lifetime, &acquired)) { server = inet_ntop(AF_INET6, addr, buf, sizeof(buf)); ni_debug_verbose(NI_LOG_DEBUG, NI_TRACE_IPV6|NI_TRACE_EVENTS, "%s: failed to track ipv6 rnssl server %s", dev->name, server); continue; } emit = TRUE; } if (emit) __ni_netdev_nduseropt_event(dev, NI_EVENT_RDNSS_UPDATE); return 0; }
void ni_netdev_set_ipv6(ni_netdev_t *dev, ni_ipv6_devconf_t *conf) { if (conf != NULL) { ni_netdev_get_ipv6(dev); dev->ipv6->conf = *conf; } else if (dev->ipv6) { ni_ipv6_devinfo_free(dev->ipv6); dev->ipv6 = NULL; } }
int ni_system_ipv6_devinfo_set(ni_netdev_t *dev, const ni_ipv6_devconf_t *conf) { ni_ipv6_devinfo_t *ipv6; if (!conf || !(ipv6 = ni_netdev_get_ipv6(dev))) return -1; if (!ni_ipv6_supported()) { ipv6->conf.enabled = NI_TRISTATE_DISABLE; if (ni_tristate_is_enabled(conf->enabled)) { errno = EAFNOSUPPORT; return -1; } return 0; } if (ni_tristate_is_set(conf->enabled)) { if (__ni_system_ipv6_devinfo_change_int(dev->name, "disable_ipv6", ni_tristate_is_enabled(conf->enabled) ? 0 : 1) < 0) return -1; ni_tristate_set(&ipv6->conf.enabled, conf->enabled); } /* If we're disabling IPv6 on this interface, we're done! */ if (ni_tristate_is_disabled(conf->enabled)) { __ni_ipv6_ra_info_reset(&dev->ipv6->radv); return 0; } if (__ni_system_ipv6_devinfo_change_int(dev->name, "autoconf", conf->autoconf) == 0) ipv6->conf.autoconf = conf->autoconf; if (__ni_system_ipv6_devinfo_change_int(dev->name, "forwarding", conf->forwarding) == 0) ipv6->conf.forwarding = conf->forwarding; if (__ni_system_ipv6_devinfo_change_int(dev->name, "accept_redirects", conf->accept_redirects) == 0) ipv6->conf.accept_redirects = conf->accept_redirects; if (ipv6->conf.privacy != NI_TRISTATE_DEFAULT) { /* kernel is using -1 for loopback, ptp, ... */ if (__ni_system_ipv6_devinfo_change_int(dev->name, "use_tempaddr", conf->privacy) == 0) { ipv6->conf.privacy = conf->privacy; } } return 0; }
/* * Discover current IPv6 device settings */ int ni_system_ipv6_devinfo_get(ni_netdev_t *dev, ni_ipv6_devinfo_t *ipv6) { if (ipv6 == NULL) ipv6 = ni_netdev_get_ipv6(dev); if (!ni_ipv6_supported()) { __ni_ipv6_devconf_reset(&ipv6->conf); __ni_ipv6_ra_info_reset(&ipv6->radv); ipv6->conf.enabled = NI_TRISTATE_DISABLE; return 0; } /* * dhcpcd does something very odd when shutting down an interface; * in addition to removing all IPv4 addresses, it also removes any * IPv6 addresses. The kernel seems to take this as "disable IPv6 * on this interface", and subsequently, /proc/sys/ipv6/conf/<ifname> * is gone. * When we bring the interface back up, everything is fine; but until * then we need to ignore this glitch. */ if (ni_sysctl_ipv6_ifconfig_is_present(dev->name)) { int val; if (ni_sysctl_ipv6_ifconfig_get_int(dev->name, "disable_ipv6", &val) >= 0) ni_tristate_set(&ipv6->conf.enabled, !val); if (ni_sysctl_ipv6_ifconfig_get_int(dev->name, "forwarding", &val) >= 0) ni_tristate_set(&ipv6->conf.forwarding, !!val); if (ni_sysctl_ipv6_ifconfig_get_int(dev->name, "autoconf", &val) >= 0) ni_tristate_set(&ipv6->conf.autoconf, !!val); if (ni_sysctl_ipv6_ifconfig_get_int(dev->name, "accept_redirects", &val) >= 0) ni_tristate_set(&ipv6->conf.accept_redirects, !!val); if (ni_sysctl_ipv6_ifconfig_get_int(dev->name, "use_tempaddr", &val) >= 0) ipv6->conf.privacy = val < -1 ? -1 : (val > 2 ? 2 : val); } else { ni_warn("%s: cannot get ipv6 device attributes", dev->name); /* Reset to defaults */ __ni_ipv6_devconf_reset(&ipv6->conf); __ni_ipv6_ra_info_reset(&ipv6->radv); } return 0; }
static int __ni_rtevent_process_dnssl_info(ni_netdev_t *dev, const struct nd_opt_hdr *opt, size_t len) { const struct ni_nd_opt_dnssl_info_p *dopt; ni_ipv6_devinfo_t *ipv6; unsigned int lifetime; struct timeval acquired; size_t length, cnt, off; ni_bool_t emit = FALSE; char domain[256]; if (opt == NULL || len < sizeof(*dopt)) { ni_error("%s: unable to parse ipv6 dnssl info event data -- too short", dev->name); return -1; } ipv6 = ni_netdev_get_ipv6(dev); if (!ipv6) { ni_error("%s: unable to allocate device ipv6 structure: %m", dev->name); return -1; } dopt = (const struct ni_nd_opt_dnssl_info_p *)opt; len -= sizeof(*dopt); ni_timer_get_time(&acquired); lifetime = ntohl(dopt->nd_opt_dnssl_lifetime); length = 0; domain[length] = '\0'; for (off = 0; off < len ; ) { cnt = dopt->nd_opt_dnssl_list[off++]; if (cnt == 0) { /* just padding */ if (domain[0] == '\0') continue; domain[length] = '\0'; if (!ni_check_domain_name(domain, length, 0)) { ni_debug_verbose(NI_LOG_DEBUG, NI_TRACE_IPV6|NI_TRACE_EVENTS, "%s: ignoring suspect DNSSL domain: %s", dev->name, ni_print_suspect(domain, length)); } else if (!ni_ipv6_ra_dnssl_list_update(&ipv6->radv.dnssl, domain, lifetime, &acquired)) { ni_debug_verbose(NI_LOG_DEBUG, NI_TRACE_IPV6|NI_TRACE_EVENTS, "%s: unable to track ipv6 dnssl domain %s", dev->name, domain); } else emit = TRUE; length = 0; domain[length] = '\0'; continue; } if ((off + cnt >= len) || (length + cnt + 2 > sizeof(domain))) break; if (length) domain[length++] = '.'; memcpy(&domain[length], &dopt->nd_opt_dnssl_list[off], cnt); off += cnt; length += cnt; domain[length] = '\0'; } if (emit) __ni_netdev_nduseropt_event(dev, NI_EVENT_DNSSL_UPDATE); return 0; }
/* * Process NEWPREFIX event. This essentially maps 1:1 to IPv6 router advertisements received * by the kernel. */ int __ni_rtevent_newprefix(ni_netconfig_t *nc, const struct sockaddr_nl *nladdr, struct nlmsghdr *h) { struct prefixmsg *pfx; ni_ipv6_devinfo_t *ipv6; ni_ipv6_ra_pinfo_t *pi, *old = NULL; ni_netdev_t *dev; if (!(pfx = ni_rtnl_prefixmsg(h, RTM_NEWPREFIX))) return -1; dev = ni_netdev_by_index(nc, pfx->prefix_ifindex); if (!dev) { ni_debug_events("ipv6 prefix info event for unknown device index: %u", pfx->prefix_ifindex); return 0; } ipv6 = ni_netdev_get_ipv6(dev); if (!ipv6) { ni_error("%s: unable to allocate device ipv6 structure: %m", dev->name); return -1; } pi = calloc(1, sizeof(*pi)); if (!pi) { ni_error("%s: unable to allocate ipv6 prefix info structure: %m", dev->name); return -1; } ni_timer_get_time(&pi->acquired); if (__ni_rtnl_parse_newprefix(dev->name, h, pfx, pi) < 0) { ni_error("%s: unable to parse ipv6 prefix info event data", dev->name); ni_ipv6_ra_pinfo_free(pi); return -1; } if ((old = ni_ipv6_ra_pinfo_list_remove(&ipv6->radv.pinfo, pi)) != NULL) { if (pi->valid_lft != NI_LIFETIME_EXPIRED) { /* Replace with updated prefix info - most recent in front */ ni_ipv6_ra_pinfo_list_prepend(&ipv6->radv.pinfo, pi); __ni_netdev_prefix_event(dev, NI_EVENT_PREFIX_UPDATE, pi); } else { /* A lifetime of 0 means the router requests a prefix remove; * at least 3.0.x kernel set valid lft to 0 and keep pref. */ ni_ipv6_ra_pinfo_free(pi); __ni_netdev_prefix_event(dev, NI_EVENT_PREFIX_DELETE, old); } free(old); } else if (pi->valid_lft != NI_LIFETIME_EXPIRED) { /* Add prefix info - most recent in front */ ni_ipv6_ra_pinfo_list_prepend(&ipv6->radv.pinfo, pi); __ni_netdev_prefix_event(dev, NI_EVENT_PREFIX_UPDATE, pi); } else { /* Request to remove unhandled prefix (missed event?), ignore it. */ ni_ipv6_ra_pinfo_free(pi); } return 0; }