From c358c7e38d3faf6438ab1eeea345ec40dabd96bf Mon Sep 17 00:00:00 2001 From: florian Date: Fri, 31 May 2024 16:10:02 +0000 Subject: [PATCH] getifaddrs(3) does a non trivial amount of work. Call it once and pass a pointer to the head of the list around when reconfiguring interfaces. testing Ryan Vogt ok benno testing & OK bket@, jmatthew@ --- usr.sbin/rad/frontend.c | 221 +++++++++++++++++----------------------- 1 file changed, 96 insertions(+), 125 deletions(-) diff --git a/usr.sbin/rad/frontend.c b/usr.sbin/rad/frontend.c index ca8719a4a1b..56bd0a50051 100644 --- a/usr.sbin/rad/frontend.c +++ b/usr.sbin/rad/frontend.c @@ -1,4 +1,4 @@ -/* $OpenBSD: frontend.c,v 1.46 2024/05/17 06:50:14 florian Exp $ */ +/* $OpenBSD: frontend.c,v 1.47 2024/05/31 16:10:02 florian Exp $ */ /* * Copyright (c) 2018 Florian Obser @@ -102,6 +102,7 @@ struct ra_iface { TAILQ_ENTRY(ra_iface) entry; struct icmp6_ev *icmp6ev; struct ra_prefix_conf_head prefixes; + struct ether_addr hw_addr; char name[IF_NAMESIZE]; char conf_name[IF_NAMESIZE]; uint32_t if_index; @@ -135,9 +136,8 @@ void frontend_startup(void); void icmp6_receive(int, short, void *); void join_all_routers_mcast_group(struct ra_iface *); void leave_all_routers_mcast_group(struct ra_iface *); -int get_link_state(char *); int get_ifrdomain(char *); -void merge_ra_interface(char *, char *); +void merge_ra_interface(char *, char *, struct ifaddrs *); void merge_ra_interfaces(void); struct ra_iface *find_ra_iface_by_id(uint32_t); struct ra_iface *find_ra_iface_by_name(char *); @@ -153,8 +153,7 @@ void add_new_prefix_to_ra_iface(struct ra_iface *r, void free_ra_iface(struct ra_iface *); int in6_mask2prefixlen(struct in6_addr *); void get_interface_prefixes(struct ra_iface *, - struct ra_prefix_conf *); -int interface_has_linklocal_address(char *); + struct ra_prefix_conf *, struct ifaddrs *); void build_packet(struct ra_iface *); void build_leaving_packet(struct ra_iface *); void ra_output(struct ra_iface *, struct sockaddr_in6 *); @@ -736,30 +735,6 @@ find_ra_iface_conf(struct ra_iface_conf_head *head, char *if_name) return (NULL); } -int -get_link_state(char *if_name) -{ - struct ifaddrs *ifap, *ifa; - int ls = LINK_STATE_UNKNOWN; - - if (getifaddrs(&ifap) != 0) { - log_warn("getifaddrs"); - return LINK_STATE_UNKNOWN; - } - for (ifa = ifap; ifa; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == NULL || - ifa->ifa_addr->sa_family != AF_LINK) - continue; - if (strcmp(if_name, ifa->ifa_name) != 0) - continue; - - ls = ((struct if_data*)ifa->ifa_data)->ifi_link_state; - break; - } - freeifaddrs(ifap); - return ls; -} - int get_ifrdomain(char *if_name) { @@ -774,27 +749,75 @@ get_ifrdomain(char *if_name) } void -merge_ra_interface(char *name, char *conf_name) +merge_ra_interface(char *if_name, char *conf_name, struct ifaddrs *ifap) { struct ra_iface *ra_iface; + struct ifaddrs *ifa; + struct sockaddr_in6 *sin6; + struct in6_ifreq ifr6; + struct sockaddr_dl *sdl; + struct ether_addr hw_addr; uint32_t if_index; - int link_state, has_linklocal, ifrdomain; + int link_state = LINK_STATE_UNKNOWN; + int has_linklocal = 0, ifrdomain; + int has_hw_addr = 0; + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) + continue; + if (ifa->ifa_addr->sa_family != AF_LINK && + ifa->ifa_addr->sa_family != AF_INET6) + continue; + if (strcmp(if_name, ifa->ifa_name) != 0) + continue; + + if (ifa->ifa_addr->sa_family == AF_LINK) { + link_state = + ((struct if_data*)ifa->ifa_data)->ifi_link_state; + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + if (sdl->sdl_type == IFT_ETHER && + sdl->sdl_alen == ETHER_ADDR_LEN) { + has_hw_addr = 1; + memcpy(&hw_addr, LLADDR(sdl), ETHER_ADDR_LEN); + } + } else if (ifa->ifa_addr->sa_family == AF_INET6) { + sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; - link_state = get_link_state(name); - has_linklocal = interface_has_linklocal_address(name); - ifrdomain = get_ifrdomain(name); + if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) + continue; + + memset(&ifr6, 0, sizeof(ifr6)); + strlcpy(ifr6.ifr_name, if_name, sizeof(ifr6.ifr_name)); + memcpy(&ifr6.ifr_addr, sin6, sizeof(ifr6.ifr_addr)); + if (ioctl(ioctlsock, SIOCGIFAFLAG_IN6, + (caddr_t)&ifr6) == -1) { + log_warn("SIOCGIFAFLAG_IN6"); + continue; + } - if ((ra_iface = find_ra_iface_by_name(name)) != NULL) { + if (ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_TENTATIVE | + IN6_IFF_DUPLICATED)) + continue; + has_linklocal = 1; + } + } + + ifrdomain = get_ifrdomain(if_name); + + if ((ra_iface = find_ra_iface_by_name(if_name)) != NULL) { ra_iface->link_state = link_state; if (!LINK_STATE_IS_UP(link_state)) { - log_debug("%s down, removing", name); + log_debug("%s down, removing", if_name); ra_iface->removed = 1; } else if (!has_linklocal) { log_debug("%s has no IPv6 link-local address, " - "removing", name); + "removing", if_name); ra_iface->removed = 1; } else if (ifrdomain == -1) { - log_debug("can't get rdomain for %s, removing", name); + log_debug("can't get rdomain for %s, removing", if_name); + ra_iface->removed = 1; + } else if (!has_hw_addr) { + log_debug("%s has no mac address, removing", if_name); ra_iface->removed = 1; } else if (ra_iface->rdomain != ifrdomain) { leave_all_routers_mcast_group(ra_iface); @@ -804,37 +827,49 @@ merge_ra_interface(char *name, char *conf_name) join_all_routers_mcast_group(ra_iface); ra_iface->removed = 0; } else { - log_debug("keeping interface %s", name); + log_debug("keeping interface %s", if_name); ra_iface->removed = 0; } + memcpy(&ra_iface->hw_addr, &hw_addr, sizeof(hw_addr)); return; } if (!LINK_STATE_IS_UP(link_state)) { - log_debug("%s down, ignoring", name); + log_debug("%s down, ignoring", if_name); return; } if (!has_linklocal) { - log_debug("%s has no IPv6 link-local address, ignoring", name); + log_debug("%s has no IPv6 link-local address, ignoring", + if_name); return; } - log_debug("new interface %s", name); - if ((if_index = if_nametoindex(name)) == 0) + if (ifrdomain == -1) { + log_debug("can't get rdomain for %s, ignoring", if_name); return; + } - log_debug("adding interface %s", name); + if (!has_hw_addr) { + log_debug("%s has no mac address, ignoring", if_name); + return; + } + + log_debug("new interface %s", if_name); + if ((if_index = if_nametoindex(if_name)) == 0) + return; + + log_debug("adding interface %s", if_name); if ((ra_iface = calloc(1, sizeof(*ra_iface))) == NULL) fatal("%s", __func__); - strlcpy(ra_iface->name, name, sizeof(ra_iface->name)); + strlcpy(ra_iface->name, if_name, sizeof(ra_iface->name)); strlcpy(ra_iface->conf_name, conf_name, sizeof(ra_iface->conf_name)); ra_iface->if_index = if_index; ra_iface->rdomain = ifrdomain; - + memcpy(&ra_iface->hw_addr, &hw_addr, sizeof(hw_addr)); SIMPLEQ_INIT(&ra_iface->prefixes); ra_iface->icmp6ev = get_icmp6ev_by_rdomain(ifrdomain); @@ -850,9 +885,15 @@ merge_ra_interfaces(void) struct ra_iface *ra_iface; struct ifgroupreq ifgr; struct ifg_req *ifg; + struct ifaddrs *ifap; char *conf_name; unsigned int len; + if (getifaddrs(&ifap) != 0) { + log_warn("getifaddrs"); + return; + } + TAILQ_FOREACH(ra_iface, &ra_interfaces, entry) ra_iface->removed = 1; @@ -861,7 +902,7 @@ merge_ra_interfaces(void) /* check if network interface or group */ if (isdigit((unsigned char)conf_name[strlen(conf_name) - 1])) { - merge_ra_interface(conf_name, conf_name); + merge_ra_interface(conf_name, conf_name, ifap); } else { log_debug("interface group %s", conf_name); @@ -888,7 +929,7 @@ merge_ra_interfaces(void) ifg++) { len -= sizeof(struct ifg_req); merge_ra_interface(ifg->ifgrq_member, - conf_name); + conf_name, ifap); } free(ifgr.ifgr_groups); } @@ -925,10 +966,11 @@ merge_ra_interfaces(void) if (ra_iface_conf->autoprefix) get_interface_prefixes(ra_iface, - ra_iface_conf->autoprefix); + ra_iface_conf->autoprefix, ifap); build_packet(ra_iface); } + freeifaddrs(ifap); } void @@ -972,62 +1014,15 @@ in6_mask2prefixlen(struct in6_addr *in6) return (plen); } -int -interface_has_linklocal_address(char *name) -{ - struct ifaddrs *ifap, *ifa; - struct sockaddr_in6 *sin6; - struct in6_ifreq ifr6; - int ret = 0; - - if (getifaddrs(&ifap) != 0) - fatal("getifaddrs"); - - for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { - if (strcmp(name, ifa->ifa_name) != 0) - continue; - if (ifa->ifa_addr == NULL || - ifa->ifa_addr->sa_family != AF_INET6) - continue; - - sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; - - if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) - continue; - - memset(&ifr6, 0, sizeof(ifr6)); - strlcpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name)); - memcpy(&ifr6.ifr_addr, sin6, sizeof(ifr6.ifr_addr)); - if (ioctl(ioctlsock, SIOCGIFAFLAG_IN6, (caddr_t)&ifr6) == -1) { - log_warn("SIOCGIFAFLAG_IN6"); - continue; - } - - if (ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_TENTATIVE | - IN6_IFF_DUPLICATED)) - continue; - - ret = 1; - break; - } - freeifaddrs(ifap); - return (ret); -} - void get_interface_prefixes(struct ra_iface *ra_iface, struct ra_prefix_conf - *autoprefix) + *autoprefix, struct ifaddrs *ifap) { struct in6_ifreq ifr6; - struct ifaddrs *ifap, *ifa; + struct ifaddrs *ifa; struct sockaddr_in6 *sin6; int prefixlen; - log_debug("%s: %s", __func__, ra_iface->name); - - if (getifaddrs(&ifap) != 0) - fatal("getifaddrs"); - for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { if (strcmp(ra_iface->name, ifa->ifa_name) != 0) continue; @@ -1058,7 +1053,6 @@ get_interface_prefixes(struct ra_iface *ra_iface, struct ra_prefix_conf add_new_prefix_to_ra_iface(ra_iface, &sin6->sin6_addr, prefixlen, autoprefix); } - freeifaddrs(ifap); } struct ra_prefix_conf* @@ -1118,8 +1112,6 @@ build_packet(struct ra_iface *ra_iface) struct ra_rdnss_conf *ra_rdnss; struct ra_dnssl_conf *ra_dnssl; struct ra_pref64_conf *pref64; - struct ifaddrs *ifap, *ifa; - struct sockaddr_dl *sdl; size_t len, label_len; uint8_t *p, buf[RA_MAX_SIZE]; char *label_start, *label_end; @@ -1187,30 +1179,9 @@ build_packet(struct ra_iface *ra_iface) ndopt_source_link_addr->nd_opt_source_link_addr_type = ND_OPT_SOURCE_LINKADDR; ndopt_source_link_addr->nd_opt_source_link_addr_len = 1; - if (getifaddrs(&ifap) != 0) { - ifap = NULL; - log_warn("getifaddrs"); - } - for (ifa = ifap; ifa; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == NULL || - ifa->ifa_addr->sa_family != AF_LINK) - continue; - if (strcmp(ra_iface->name, ifa->ifa_name) != 0) - continue; - sdl = (struct sockaddr_dl *)ifa->ifa_addr; - if (sdl->sdl_type != IFT_ETHER || - sdl->sdl_alen != ETHER_ADDR_LEN) - continue; - memcpy(&ndopt_source_link_addr-> - nd_opt_source_link_addr_hw_addr, - LLADDR(sdl), ETHER_ADDR_LEN); - break; - } - if (ifap != NULL) { - freeifaddrs(ifap); - p += sizeof(*ndopt_source_link_addr); - } else - len -= sizeof(*ndopt_source_link_addr); + memcpy(&ndopt_source_link_addr->nd_opt_source_link_addr_hw_addr, + &ra_iface->hw_addr, ETHER_ADDR_LEN); + p += sizeof(*ndopt_source_link_addr); } if (ra_options_conf->mtu > 0) { -- 2.20.1