diff options
Diffstat (limited to 'usr.sbin/rtadvd/if.c')
-rw-r--r-- | usr.sbin/rtadvd/if.c | 671 |
1 files changed, 412 insertions, 259 deletions
diff --git a/usr.sbin/rtadvd/if.c b/usr.sbin/rtadvd/if.c index 8722a05..ad42d29 100644 --- a/usr.sbin/rtadvd/if.c +++ b/usr.sbin/rtadvd/if.c @@ -3,6 +3,7 @@ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * Copyright (C) 2011 Hiroki Sato <hrs@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,19 +36,24 @@ #include <sys/sysctl.h> #include <sys/ioctl.h> #include <net/if.h> +#include <net/if_dl.h> #include <net/if_types.h> +#include <net/if_var.h> #include <net/ethernet.h> -#include <ifaddrs.h> #include <net/route.h> -#include <net/if_dl.h> #include <netinet/in.h> +#include <netinet/in_var.h> +#include <netinet/ip6.h> #include <netinet/icmp6.h> +#include <netinet6/nd6.h> #include <unistd.h> #include <errno.h> #include <netdb.h> #include <stdlib.h> #include <string.h> #include <syslog.h> + +#include "pathnames.h" #include "rtadvd.h" #include "if.h" @@ -59,14 +65,34 @@ ((ap)->sa_len ? ROUNDUP((ap)->sa_len, sizeof(u_long)) : \ sizeof(u_long))) -struct if_msghdr **iflist; -int iflist_init_ok; -size_t ifblock_size; -char *ifblock; +struct sockaddr_in6 sin6_linklocal_allnodes = { + .sin6_len = sizeof(sin6_linklocal_allnodes), + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_LINKLOCAL_ALLNODES_INIT, +}; + +struct sockaddr_in6 sin6_linklocal_allrouters = { + .sin6_len = sizeof(sin6_linklocal_allrouters), + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT, +}; + +struct sockaddr_in6 sin6_sitelocal_allrouters = { + .sin6_len = sizeof(sin6_sitelocal_allrouters), + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_SITELOCAL_ALLROUTERS_INIT, +}; + +struct sockinfo sock = { .si_fd = -1, .si_name = NULL }; +struct sockinfo rtsock = { .si_fd = -1, .si_name = NULL }; +struct sockinfo ctrlsock = { .si_fd = -1, .si_name = _PATH_CTRL_SOCK }; + +char *mcastif; -static void get_iflist(char **buf, size_t *size); -static void parse_iflist(struct if_msghdr ***ifmlist_p, - char *buf, size_t bufsize); +static void get_rtaddrs(int, struct sockaddr *, + struct sockaddr **); +static struct if_msghdr *get_next_msghdr(struct if_msghdr *, + struct if_msghdr *); static void get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) @@ -83,133 +109,6 @@ get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) } } -struct sockaddr_dl * -if_nametosdl(char *name) -{ - int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0}; - char *buf, *next, *lim; - size_t len; - struct if_msghdr *ifm; - struct sockaddr *sa, *rti_info[RTAX_MAX]; - struct sockaddr_dl *sdl = NULL, *ret_sdl; - - if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) - return (NULL); - if ((buf = malloc(len)) == NULL) - return (NULL); - if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { - free(buf); - return (NULL); - } - - lim = buf + len; - for (next = buf; next < lim; next += ifm->ifm_msglen) { - ifm = (struct if_msghdr *)next; - if (ifm->ifm_version != RTM_VERSION) { - syslog(LOG_ERR, - "<%s> RTM_VERSION mismatch (%d != %d).", - __func__, ifm->ifm_version, RTM_VERSION); - continue; - } - if (ifm->ifm_type == RTM_IFINFO) { - sa = (struct sockaddr *)(ifm + 1); - get_rtaddrs(ifm->ifm_addrs, sa, rti_info); - if ((sa = rti_info[RTAX_IFP]) != NULL) { - if (sa->sa_family == AF_LINK) { - sdl = (struct sockaddr_dl *)sa; - if (strlen(name) != sdl->sdl_nlen) - continue; /* not same len */ - if (strncmp(&sdl->sdl_data[0], - name, - sdl->sdl_nlen) == 0) { - break; - } - } - } - } - } - if (next == lim) { - /* search failed */ - free(buf); - return (NULL); - } - - if ((ret_sdl = malloc(sdl->sdl_len)) == NULL) - goto end; - memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len); - -end: - free(buf); - return (ret_sdl); -} - -int -if_getmtu(char *name) -{ - struct ifaddrs *ifap, *ifa; - struct if_data *ifd; - u_long mtu = 0; - - if (getifaddrs(&ifap) < 0) - return (0); - for (ifa = ifap; ifa; ifa = ifa->ifa_next) { - if (strcmp(ifa->ifa_name, name) == 0) { - ifd = ifa->ifa_data; - if (ifd) - mtu = ifd->ifi_mtu; - break; - } - } - freeifaddrs(ifap); - -#ifdef SIOCGIFMTU /* XXX: this ifdef may not be necessary */ - if (mtu == 0) { - struct ifreq ifr; - int s; - - if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) - return (0); - - ifr.ifr_addr.sa_family = AF_INET6; - strncpy(ifr.ifr_name, name, - sizeof(ifr.ifr_name)); - if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) < 0) { - close(s); - return (0); - } - close(s); - - mtu = ifr.ifr_mtu; - } -#endif - - return (mtu); -} - -/* give interface index and its old flags, then new flags returned */ -int -if_getflags(int ifindex, int oifflags) -{ - struct ifreq ifr; - int s; - - if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { - syslog(LOG_ERR, "<%s> socket: %s", __func__, - strerror(errno)); - return (oifflags & ~IFF_UP); - } - - if_indextoname(ifindex, ifr.ifr_name); - if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { - syslog(LOG_ERR, "<%s> ioctl:SIOCGIFFLAGS: failed for %s", - __func__, ifr.ifr_name); - close(s); - return (oifflags & ~IFF_UP); - } - close(s); - return (ifr.ifr_flags); -} - #define ROUNDUP8(a) (1 + (((a) - 1) | 7)) int lladdropt_length(struct sockaddr_dl *sdl) @@ -378,30 +277,6 @@ get_rtm_ifindex(char *buf) } int -get_ifm_ifindex(char *buf) -{ - struct if_msghdr *ifm = (struct if_msghdr *)buf; - - return ((int)ifm->ifm_index); -} - -int -get_ifam_ifindex(char *buf) -{ - struct ifa_msghdr *ifam = (struct ifa_msghdr *)buf; - - return ((int)ifam->ifam_index); -} - -int -get_ifm_flags(char *buf) -{ - struct if_msghdr *ifm = (struct if_msghdr *)buf; - - return (ifm->ifm_flags); -} - -int get_prefixlen(char *buf) { struct rt_msghdr *rtm = (struct rt_msghdr *)buf; @@ -458,140 +333,418 @@ prefixlen(u_char *p, u_char *lim) return (masklen); } -int -rtmsg_type(char *buf) +struct ifinfo * +update_persist_ifinfo(struct ifilist_head_t *ifi_head, const char *ifname) { - struct rt_msghdr *rtm = (struct rt_msghdr *)buf; + struct ifinfo *ifi; + int ifindex; + + ifi = NULL; + ifindex = if_nametoindex(ifname); + TAILQ_FOREACH(ifi, ifi_head, ifi_next) { + if (ifindex != 0) { + if (ifindex == ifi->ifi_ifindex) + break; + } else { + if (strncmp(ifname, ifi->ifi_ifname, + sizeof(ifi->ifi_ifname)) == 0) + break; + } + } - return (rtm->rtm_type); -} + if (ifi == NULL) { + /* A new ifinfo element is needed. */ + syslog(LOG_DEBUG, "<%s> new entry: %s", __func__, + ifname); + + ELM_MALLOC(ifi, exit(1)); + ifi->ifi_ifindex = 0; + strncpy(ifi->ifi_ifname, ifname, sizeof(ifi->ifi_ifname)-1); + ifi->ifi_ifname[sizeof(ifi->ifi_ifname)-1] = '\0'; + ifi->ifi_rainfo = NULL; + ifi->ifi_state = IFI_STATE_UNCONFIGURED; + TAILQ_INSERT_TAIL(ifi_head, ifi, ifi_next); + } -int -rtmsg_len(char *buf) -{ - struct rt_msghdr *rtm = (struct rt_msghdr *)buf; + ifi->ifi_persist = 1; - return (rtm->rtm_msglen); + syslog(LOG_DEBUG, "<%s> %s is marked PERSIST", __func__, + ifi->ifi_ifname); + syslog(LOG_DEBUG, "<%s> %s is state = %d", __func__, + ifi->ifi_ifname, ifi->ifi_state); + return (ifi); } int -ifmsg_len(char *buf) +update_ifinfo_nd_flags(struct ifinfo *ifi) { - struct if_msghdr *ifm = (struct if_msghdr *)buf; + struct in6_ndireq nd; + int s; + int error; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, + "<%s> socket() failed.", __func__); + return (1); + } + /* ND flags */ + memset(&nd, 0, sizeof(nd)); + strncpy(nd.ifname, ifi->ifi_ifname, + sizeof(nd.ifname)); + error = ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd); + if (error) { + close(s); + syslog(LOG_ERR, + "<%s> ioctl() failed.", __func__); + return (1); + } + ifi->ifi_nd_flags = nd.ndi.flags; + close(s); - return (ifm->ifm_msglen); + return (0); } -/* - * alloc buffer and get if_msghdrs block from kernel, - * and put them into the buffer - */ -static void -get_iflist(char **buf, size_t *size) +struct ifinfo * +update_ifinfo(struct ifilist_head_t *ifi_head, int ifindex) { - int mib[6]; - - mib[0] = CTL_NET; - mib[1] = PF_ROUTE; - mib[2] = 0; - mib[3] = AF_INET6; - mib[4] = NET_RT_IFLIST; - mib[5] = 0; - - if (sysctl(mib, 6, NULL, size, NULL, 0) < 0) { - syslog(LOG_ERR, "<%s> sysctl: iflist size get failed", - __func__); + struct if_msghdr *ifm; + struct ifinfo *ifi = NULL; + struct sockaddr *sa; + struct sockaddr *rti_info[RTAX_MAX]; + char *msg; + size_t len; + char *lim; + int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET6, NET_RT_IFLIST, 0 }; + int error; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), NULL, &len, NULL, 0) < + 0) { + syslog(LOG_ERR, + "<%s> sysctl: NET_RT_IFLIST size get failed", __func__); exit(1); } - if ((*buf = malloc(*size)) == NULL) { + if ((msg = malloc(len)) == NULL) { syslog(LOG_ERR, "<%s> malloc failed", __func__); exit(1); } - if (sysctl(mib, 6, *buf, size, NULL, 0) < 0) { - syslog(LOG_ERR, "<%s> sysctl: iflist get failed", - __func__); + if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), msg, &len, NULL, 0) < + 0) { + syslog(LOG_ERR, + "<%s> sysctl: NET_RT_IFLIST get failed", __func__); exit(1); } - return; -} -/* - * alloc buffer and parse if_msghdrs block passed as arg, - * and init the buffer as list of pointers ot each of the if_msghdr. - */ -static void -parse_iflist(struct if_msghdr ***ifmlist_p, char *buf, size_t bufsize) -{ - int iflentry_size, malloc_size; - struct if_msghdr *ifm; - struct ifa_msghdr *ifam; - char *lim; + lim = msg + len; + for (ifm = (struct if_msghdr *)msg; + ifm != NULL && ifm < (struct if_msghdr *)lim; + ifm = get_next_msghdr(ifm,(struct if_msghdr *)lim)) { + int ifi_new; - /* - * Estimate least size of an iflist entry, to be obtained from kernel. - * Should add sizeof(sockaddr) ?? - */ - iflentry_size = sizeof(struct if_msghdr); - /* roughly estimate max list size of pointers to each if_msghdr */ - malloc_size = (bufsize/iflentry_size) * sizeof(size_t); - if ((*ifmlist_p = (struct if_msghdr **)malloc(malloc_size)) == NULL) { - syslog(LOG_ERR, "<%s> malloc failed", __func__); - exit(1); - } + syslog(LOG_DEBUG, "<%s> ifm = %p, lim = %p, diff = %d", + __func__, ifm, lim, (char *)lim - (char *)ifm); - lim = buf + bufsize; - for (ifm = (struct if_msghdr *)buf; ifm < (struct if_msghdr *)lim;) { + if (ifm->ifm_version != RTM_VERSION) { + syslog(LOG_ERR, + "<%s> ifm_vesrion mismatch", __func__); + exit(1); + } if (ifm->ifm_msglen == 0) { - syslog(LOG_WARNING, "<%s> ifm_msglen is 0 " - "(buf=%p lim=%p ifm=%p)", __func__, - buf, lim, ifm); - return; + syslog(LOG_WARNING, + "<%s> ifm_msglen is 0", __func__); + free(msg); + return (NULL); } + ifi_new = 0; if (ifm->ifm_type == RTM_IFINFO) { - (*ifmlist_p)[ifm->ifm_index] = ifm; + struct ifreq ifr; + int s; + char ifname[IFNAMSIZ]; + + syslog(LOG_DEBUG, "<%s> RTM_IFINFO found. " + "ifm_index = %d, ifindex = %d", + __func__, ifm->ifm_index, ifindex); + + /* when ifindex is specified */ + if (ifindex != UPDATE_IFINFO_ALL && + ifindex != ifm->ifm_index) + continue; + + /* lookup an entry with the same ifindex */ + TAILQ_FOREACH(ifi, ifi_head, ifi_next) { + if (ifm->ifm_index == ifi->ifi_ifindex) + break; + if_indextoname(ifm->ifm_index, ifname); + if (strncmp(ifname, ifi->ifi_ifname, + sizeof(ifname)) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_DEBUG, + "<%s> new entry for idx=%d", + __func__, ifm->ifm_index); + ELM_MALLOC(ifi, exit(1)); + ifi->ifi_rainfo = NULL; + ifi->ifi_state = IFI_STATE_UNCONFIGURED; + ifi->ifi_persist = 0; + ifi_new = 1; + } + /* ifindex */ + ifi->ifi_ifindex = ifm->ifm_index; + + /* ifname */ + if_indextoname(ifm->ifm_index, ifi->ifi_ifname); + if (ifi->ifi_ifname == NULL) { + syslog(LOG_WARNING, + "<%s> ifname not found (idx=%d)", + __func__, ifm->ifm_index); + if (ifi_new) + free(ifi); + continue; + } + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, + "<%s> socket() failed.", __func__); + if (ifi_new) + free(ifi); + continue; + } + + /* MTU */ + ifi->ifi_phymtu = ifm->ifm_data.ifi_mtu; + if (ifi->ifi_phymtu == 0) { + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_addr.sa_family = AF_INET6; + strncpy(ifr.ifr_name, ifi->ifi_ifname, + sizeof(ifr.ifr_name)); + error = ioctl(s, SIOCGIFMTU, (caddr_t)&ifr); + if (error) { + close(s); + syslog(LOG_ERR, + "<%s> ioctl() failed.", + __func__); + if (ifi_new) + free(ifi); + continue; + } + ifi->ifi_phymtu = ifr.ifr_mtu; + if (ifi->ifi_phymtu == 0) { + syslog(LOG_WARNING, + "<%s> no interface mtu info" + " on %s. %d will be used.", + __func__, ifi->ifi_ifname, + IPV6_MMTU); + ifi->ifi_phymtu = IPV6_MMTU; + } + } + close(s); + + /* ND flags */ + error = update_ifinfo_nd_flags(ifi); + if (error) { + if (ifi_new) + free(ifi); + continue; + } + + /* SDL */ + sa = (struct sockaddr *)(ifm + 1); + get_rtaddrs(ifm->ifm_addrs, sa, rti_info); + if ((sa = rti_info[RTAX_IFP]) != NULL) { + if (sa->sa_family == AF_LINK) { + memcpy(&ifi->ifi_sdl, + (struct sockaddr_dl *)sa, + sizeof(ifi->ifi_sdl)); + } + } else + memset(&ifi->ifi_sdl, 0, + sizeof(ifi->ifi_sdl)); + + /* flags */ + ifi->ifi_flags = ifm->ifm_flags; + + /* type */ + ifi->ifi_type = ifm->ifm_type; } else { - syslog(LOG_ERR, "out of sync parsing NET_RT_IFLIST\n" - "expected %d, got %d\n msglen = %d\n" - "buf:%p, ifm:%p, lim:%p\n", - RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen, - buf, ifm, lim); - exit (1); + syslog(LOG_ERR, + "out of sync parsing NET_RT_IFLIST\n" + "expected %d, got %d\n msglen = %d\n", + RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen); + exit(1); } - for (ifam = (struct ifa_msghdr *) - ((char *)ifm + ifm->ifm_msglen); - ifam < (struct ifa_msghdr *)lim; - ifam = (struct ifa_msghdr *) - ((char *)ifam + ifam->ifam_msglen)) { - /* just for safety */ - if (!ifam->ifam_msglen) { - syslog(LOG_WARNING, "<%s> ifa_msglen is 0 " - "(buf=%p lim=%p ifam=%p)", __func__, - buf, lim, ifam); - return; - } - if (ifam->ifam_type != RTM_NEWADDR) - break; + + if (ifi_new) { + syslog(LOG_DEBUG, + "<%s> adding %s(idx=%d) to ifilist", + __func__, ifi->ifi_ifname, ifi->ifi_ifindex); + TAILQ_INSERT_TAIL(ifi_head, ifi, ifi_next); } - ifm = (struct if_msghdr *)ifam; } + free(msg); + + if (mcastif != NULL) { + error = sock_mc_rr_update(&sock, mcastif); + if (error) + exit(1); + } + + return (ifi); } -void -init_iflist(void) +static struct if_msghdr * +get_next_msghdr(struct if_msghdr *ifm, struct if_msghdr *lim) { + struct ifa_msghdr *ifam; + + for (ifam = (struct ifa_msghdr *)((char *)ifm + ifm->ifm_msglen); + ifam < (struct ifa_msghdr *)lim; + ifam = (struct ifa_msghdr *)((char *)ifam + ifam->ifam_msglen)) { + if (!ifam->ifam_msglen) { + syslog(LOG_WARNING, + "<%s> ifa_msglen is 0", __func__); + return (NULL); + } + if (ifam->ifam_type != RTM_NEWADDR) + break; + } + + return ((struct if_msghdr *)ifam); +} + +int +getinet6sysctl(int code) +{ + int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 }; + int value; + size_t size; + + mib[3] = code; + size = sizeof(value); + if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0) + < 0) { + syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s", + __func__, code, + strerror(errno)); + return (-1); + } + else + return (value); +} + + +int +sock_mc_join(struct sockinfo *s, int ifindex) +{ + struct ipv6_mreq mreq; + char ifname[IFNAMSIZ]; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + if (ifindex == 0) + return (1); + + /* + * join all routers multicast address on each advertising + * interface. + */ + memset(&mreq, 0, sizeof(mreq)); + /* XXX */ + memcpy(&mreq.ipv6mr_multiaddr.s6_addr, + &sin6_linklocal_allrouters.sin6_addr, + sizeof(mreq.ipv6mr_multiaddr.s6_addr)); + + mreq.ipv6mr_interface = ifindex; + if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, + sizeof(mreq)) < 0) { + syslog(LOG_ERR, + "<%s> IPV6_JOIN_GROUP(link) on %s: %s", + __func__, if_indextoname(ifindex, ifname), + strerror(errno)); + return (1); + } syslog(LOG_DEBUG, - "<%s> generate iflist.", __func__); + "<%s> %s: join link-local all-routers MC group", + __func__, if_indextoname(ifindex, ifname)); - if (ifblock) { - free(ifblock); - ifblock_size = 0; + return (0); +} + +int +sock_mc_leave(struct sockinfo *s, int ifindex) +{ + struct ipv6_mreq mreq; + char ifname[IFNAMSIZ]; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + if (ifindex == 0) + return (1); + + /* + * join all routers multicast address on each advertising + * interface. + */ + + memset(&mreq, 0, sizeof(mreq)); + /* XXX */ + memcpy(&mreq.ipv6mr_multiaddr.s6_addr, + &sin6_linklocal_allrouters.sin6_addr, + sizeof(mreq.ipv6mr_multiaddr.s6_addr)); + + mreq.ipv6mr_interface = ifindex; + if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, + sizeof(mreq)) < 0) { + syslog(LOG_ERR, + "<%s> IPV6_JOIN_LEAVE(link) on %s: %s", + __func__, if_indextoname(ifindex, ifname), + strerror(errno)); + return (1); + } + syslog(LOG_DEBUG, + "<%s> %s: leave link-local all-routers MC group", + __func__, if_indextoname(ifindex, ifname)); + + return (0); +} + +int +sock_mc_rr_update(struct sockinfo *s, char *mif) +{ + struct ipv6_mreq mreq; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + if (mif == NULL) + return (1); + /* + * When attending router renumbering, join all-routers site-local + * multicast group. + */ + /* XXX */ + memcpy(&mreq.ipv6mr_multiaddr.s6_addr, + &sin6_sitelocal_allrouters.sin6_addr, + sizeof(mreq.ipv6mr_multiaddr.s6_addr)); + if ((mreq.ipv6mr_interface = if_nametoindex(mif)) == 0) { + syslog(LOG_ERR, + "<%s> invalid interface: %s", + __func__, mif); + return (1); + } + + if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq, sizeof(mreq)) < 0) { + syslog(LOG_ERR, + "<%s> IPV6_JOIN_GROUP(site) on %s: %s", + __func__, mif, strerror(errno)); + return (1); } - if (iflist) - free(iflist); - /* get iflist block from kernel */ - get_iflist(&ifblock, &ifblock_size); - /* make list of pointers to each if_msghdr */ - parse_iflist(&iflist, ifblock, ifblock_size); + syslog(LOG_DEBUG, + "<%s> %s: join site-local all-routers MC group", + __func__, mif); + + return (0); } |