Hi, the following diff allows rad(8) to watch interface groups. This allows to automatically add/remove interfaces in a given group.
For example, I put "interface tap" into rad.conf and it automatically serves my VM interfaces. You could also configure a custom group in vm.conf and rad.conf. I'm working on IPv6 for vmd that needs it. For reasons that I don't remember, I always missed this feature in rtadvd(8)[RIP]. It was amazingly simple to add it to rad(8) as it already reinitializes itself on interface changes. This diff includes the previous ENXIO fix to prevent it from crashing when a cloner interface such as tap is destroyed. Reyk Index: usr.sbin/rad/frontend.c =================================================================== RCS file: /cvs/src/usr.sbin/rad/frontend.c,v retrieving revision 1.16 diff -u -p -u -p -r1.16 frontend.c --- usr.sbin/rad/frontend.c 15 Aug 2018 16:48:20 -0000 1.16 +++ usr.sbin/rad/frontend.c 16 Nov 2018 15:31:24 -0000 @@ -70,6 +70,7 @@ #include <netinet6/ip6_var.h> #include <netinet/icmp6.h> +#include <ctype.h> #include <errno.h> #include <event.h> #include <ifaddrs.h> @@ -101,6 +102,7 @@ struct ra_iface { TAILQ_ENTRY(ra_iface) entry; struct ra_prefix_conf_head prefixes; char name[IF_NAMESIZE]; + char conf[IF_NAMESIZE]; uint32_t if_index; int removed; int prefix_count; @@ -116,6 +118,7 @@ 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 *); +void merge_ra_interface(struct ra_iface_conf *, char *); void merge_ra_interfaces(void); struct ra_iface *find_ra_iface_by_id(uint32_t); struct ra_iface *find_ra_iface_by_name(char *); @@ -648,12 +651,13 @@ leave_all_routers_mcast_group(struct ra_ log_debug("leaving multicast group on %s", ra_iface->name); all_routers.ipv6mr_interface = ra_iface->if_index; if (setsockopt(icmp6sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, - &all_routers, sizeof(all_routers)) == -1) + &all_routers, sizeof(all_routers)) == -1 && errno != ENXIO) fatal("IPV6_LEAVE_GROUP(%s)", ra_iface->name); } struct ra_iface* -find_ra_iface_by_id(uint32_t if_index) { +find_ra_iface_by_id(uint32_t if_index) +{ struct ra_iface *ra_iface; TAILQ_FOREACH(ra_iface, &ra_interfaces, entry) { @@ -688,36 +692,81 @@ find_ra_iface_conf(struct ra_iface_conf_ } void +merge_ra_interface(struct ra_iface_conf *ra_iface_conf, char *name) +{ + struct ra_iface *ra_iface; + uint32_t if_index; + + ra_iface = find_ra_iface_by_name(name); + if (ra_iface == NULL) { + log_debug("new interface %s", name); + if ((if_index = if_nametoindex(name)) == 0) + return; + log_debug("adding interface %s", 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->conf, ra_iface_conf->name, + sizeof(ra_iface->conf)); + + ra_iface->if_index = if_index; + SIMPLEQ_INIT(&ra_iface->prefixes); + TAILQ_INSERT_TAIL(&ra_interfaces, ra_iface, entry); + join_all_routers_mcast_group(ra_iface); + } else { + log_debug("keeping interface %s", name); + ra_iface->removed = 0; + } +} + +void merge_ra_interfaces(void) { struct ra_iface_conf *ra_iface_conf; struct ra_prefix_conf *ra_prefix_conf; struct ra_iface *ra_iface; - uint32_t if_index; + struct ifgroupreq ifgr; + struct ifg_req *ifg; + char *name; + unsigned int len; TAILQ_FOREACH(ra_iface, &ra_interfaces, entry) ra_iface->removed = 1; SIMPLEQ_FOREACH(ra_iface_conf, &frontend_conf->ra_iface_list, entry) { - ra_iface = find_ra_iface_by_name(ra_iface_conf->name); - if (ra_iface == NULL) { - log_debug("new interface %s", ra_iface_conf->name); - if ((if_index = if_nametoindex(ra_iface_conf->name)) - == 0) - continue; - log_debug("adding interface %s", ra_iface_conf->name); - if ((ra_iface = calloc(1, sizeof(*ra_iface))) == NULL) - fatal("%s", __func__); - - (void) strlcpy(ra_iface->name, ra_iface_conf->name, - sizeof(ra_iface->name)); - ra_iface->if_index = if_index; - SIMPLEQ_INIT(&ra_iface->prefixes); - TAILQ_INSERT_TAIL(&ra_interfaces, ra_iface, entry); - join_all_routers_mcast_group(ra_iface); + name = ra_iface_conf->name; + + /* check if network interface or group */ + if (isdigit((unsigned char)name[strlen(name) - 1])) { + merge_ra_interface(ra_iface_conf, name); } else { - log_debug("keeping interface %s", ra_iface_conf->name); - ra_iface->removed = 0; + log_debug("interface group %s", name); + + memset(&ifgr, 0, sizeof(ifgr)); + strlcpy(ifgr.ifgr_name, name, sizeof(ifgr.ifgr_name)); + if (ioctl(ioctlsock, SIOCGIFGMEMB, + (caddr_t)&ifgr) == -1) + continue; + + len = ifgr.ifgr_len; + if ((ifgr.ifgr_groups = calloc(1, len)) == NULL) + fatal("%s: calloc", __func__); + if (ioctl(ioctlsock, SIOCGIFGMEMB, + (caddr_t)&ifgr) == -1) { + log_debug("group %s without members", name); + free(ifgr.ifgr_groups); + continue; + } + + for (ifg = ifgr.ifgr_groups; + (ifg != NULL) && (len >= sizeof(struct ifg_req)); + ifg++) { + len -= sizeof(struct ifg_req); + merge_ra_interface(ra_iface_conf, + ifg->ifgrq_member); + } + free(ifgr.ifgr_groups); } } @@ -739,7 +788,7 @@ merge_ra_interfaces(void) } ra_iface_conf = find_ra_iface_conf( - &frontend_conf->ra_iface_list, ra_iface->name); + &frontend_conf->ra_iface_list, ra_iface->conf); if (ra_iface_conf->autoprefix) get_interface_prefixes(ra_iface, @@ -903,7 +952,7 @@ build_packet(struct ra_iface *ra_iface) char *label_start, *label_end; ra_iface_conf = find_ra_iface_conf(&frontend_conf->ra_iface_list, - ra_iface->name); + ra_iface->conf); ra_options_conf = &ra_iface_conf->ra_options; len = sizeof(*ra); Index: usr.sbin/rad/rad.conf.5 =================================================================== RCS file: /cvs/src/usr.sbin/rad/rad.conf.5,v retrieving revision 1.12 diff -u -p -u -p -r1.12 rad.conf.5 --- usr.sbin/rad/rad.conf.5 16 Sep 2018 18:58:36 -0000 1.12 +++ usr.sbin/rad/rad.conf.5 16 Nov 2018 15:31:24 -0000 @@ -109,7 +109,7 @@ The default is 1800 seconds. .\" XXX .El .Sh INTERFACES -A list of interfaces to send advertisments on: +A list of interfaces or interface groups to send advertisments on: .Bd -unfilled -offset indent .Ic interface Ar name Op { prefix list } .Ed