diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile index 2553e61b5e9e..b777d875f966 100644 --- a/sbin/ifconfig/Makefile +++ b/sbin/ifconfig/Makefile @@ -1,84 +1,80 @@ .include PACKAGE=runtime PROG= ifconfig SRCS= ifconfig.c # base support # # NB: The order here defines the order in which the constructors # are called. This in turn defines the default order in which # status is displayed. Probably should add a priority mechanism # to the registration process so we don't depend on this aspect # of the toolchain. # SRCS+= af_link.c # LLC support .if ${MK_INET_SUPPORT} != "no" SRCS+= af_inet.c # IPv4 support .endif .if ${MK_INET6_SUPPORT} != "no" SRCS+= af_inet6.c # IPv6 support .endif .if ${MK_INET6_SUPPORT} != "no" SRCS+= af_nd6.c # ND6 support SRCS+= ifstf.c # STF configuration options .endif SRCS+= ifclone.c # clone device support SRCS+= ifmac.c # MAC support SRCS+= ifmedia.c # SIOC[GS]IFMEDIA support SRCS+= iffib.c # non-default FIB support SRCS+= ifvlan.c # SIOC[GS]ETVLAN support SRCS+= ifvxlan.c # VXLAN support SRCS+= ifgre.c # GRE keys etc SRCS+= ifgif.c # GIF reversed header workaround SRCS+= ifipsec.c # IPsec VTI SRCS+= sfp.c # SFP/SFP+ information LIBADD+= ifconfig m util CFLAGS+= -I${SRCTOP}/lib/libifconfig -I${OBJTOP}/lib/libifconfig .if ${MK_WIRELESS_SUPPORT} != "no" SRCS+= ifieee80211.c # SIOC[GS]IEEE80211 support LIBADD+= 80211 .endif SRCS+= carp.c # SIOC[GS]VH support SRCS+= ifgroup.c # ... .if ${MK_PF} != "no" SRCS+= ifpfsync.c # pfsync(4) support .endif SRCS+= ifbridge.c # bridge support SRCS+= iflagg.c # lagg support -.if ${MK_EXPERIMENTAL} != "no" -CFLAGS+= -DDRAFT_IETF_6MAN_IPV6ONLY_FLAG -CFLAGS+= -DEXPERIMENTAL -.endif .if ${MK_INET6_SUPPORT} != "no" CFLAGS+= -DINET6 .endif .if ${MK_INET_SUPPORT} != "no" CFLAGS+= -DINET .endif .if ${MK_JAIL} != "no" && !defined(RESCUE) CFLAGS+= -DJAIL LIBADD+= jail .endif LIBADD+= nv .if ${MK_NETLINK_SUPPORT} != "no" SRCS+= ifconfig_netlink.c .else CFLAGS+=-DWITHOUT_NETLINK .endif MAN= ifconfig.8 CFLAGS+= -Wall -Wmissing-prototypes -Wcast-qual -Wwrite-strings -Wnested-externs HAS_TESTS= SUBDIR.${MK_TESTS}+= tests .include diff --git a/sbin/ifconfig/af_inet6.c b/sbin/ifconfig/af_inet6.c index 365f01be9590..492ee5bbbed0 100644 --- a/sbin/ifconfig/af_inet6.c +++ b/sbin/ifconfig/af_inet6.c @@ -1,797 +1,793 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Define ND6_INFINITE_LIFETIME */ #include "ifconfig.h" #include "ifconfig_netlink.h" #ifndef WITHOUT_NETLINK struct in6_px { struct in6_addr addr; int plen; bool set; }; struct in6_pdata { struct in6_px addr; struct in6_px dst_addr; struct in6_addrlifetime lifetime; uint32_t flags; uint32_t vhid; }; static struct in6_pdata in6_del; static struct in6_pdata in6_add = { .lifetime = { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME }, }; #else static struct in6_ifreq in6_ridreq; static struct in6_aliasreq in6_addreq = { .ifra_flags = 0, .ifra_lifetime = { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME } }; #endif static int ip6lifetime; #ifdef WITHOUT_NETLINK static int prefix(void *, int); #endif static char *sec2str(time_t); static int explicit_prefix = 0; extern char *f_inet6, *f_addr; extern void setnd6flags(if_ctx *, const char *, int); extern void setnd6defif(if_ctx *,const char *, int); extern void nd6_status(if_ctx *); static char addr_buf[NI_MAXHOST]; /*for getnameinfo()*/ static void setifprefixlen(if_ctx *ctx __netlink_unused, const char *addr, int dummy __unused) { #ifdef WITHOUT_NETLINK const struct afswtch *afp = ctx->afp; if (afp->af_getprefix != NULL) afp->af_getprefix(addr, MASK); #else int plen = strtol(addr, NULL, 10); if ((plen < 0) || (plen > 128)) errx(1, "%s: bad value", addr); in6_add.addr.plen = plen; #endif explicit_prefix = 1; } static void setip6flags(if_ctx *ctx, const char *dummyaddr __unused, int flag) { const struct afswtch *afp = ctx->afp; if (afp->af_af != AF_INET6) err(1, "address flags can be set only for inet6 addresses"); #ifdef WITHOUT_NETLINK if (flag < 0) in6_addreq.ifra_flags &= ~(-flag); else in6_addreq.ifra_flags |= flag; #else if (flag < 0) in6_add.flags &= ~(-flag); else in6_add.flags |= flag; #endif } static void setip6lifetime(if_ctx *ctx, const char *cmd, const char *val) { const struct afswtch *afp = ctx->afp; struct timespec now; time_t newval; char *ep; #ifdef WITHOUT_NETLINK struct in6_addrlifetime *lifetime = &in6_addreq.ifra_lifetime; #else struct in6_addrlifetime *lifetime = &in6_add.lifetime; #endif clock_gettime(CLOCK_MONOTONIC_FAST, &now); newval = (time_t)strtoul(val, &ep, 0); if (val == ep) errx(1, "invalid %s", cmd); if (afp->af_af != AF_INET6) errx(1, "%s not allowed for the AF", cmd); if (strcmp(cmd, "vltime") == 0) { lifetime->ia6t_expire = now.tv_sec + newval; lifetime->ia6t_vltime = newval; } else if (strcmp(cmd, "pltime") == 0) { lifetime->ia6t_preferred = now.tv_sec + newval; lifetime->ia6t_pltime = newval; } } static void setip6pltime(if_ctx *ctx, const char *seconds, int dummy __unused) { setip6lifetime(ctx, "pltime", seconds); } static void setip6vltime(if_ctx *ctx, const char *seconds, int dummy __unused) { setip6lifetime(ctx, "vltime", seconds); } static void setip6eui64(if_ctx *ctx, const char *cmd, int dummy __unused) { const struct afswtch *afp = ctx->afp; struct ifaddrs *ifap, *ifa; const struct sockaddr_in6 *sin6 = NULL; const struct in6_addr *lladdr = NULL; struct in6_addr *in6; if (afp->af_af != AF_INET6) errx(EXIT_FAILURE, "%s not allowed for the AF", cmd); #ifdef WITHOUT_NETLINK in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr; #else in6 = &in6_add.addr.addr; #endif if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0) errx(EXIT_FAILURE, "interface index is already filled"); if (getifaddrs(&ifap) != 0) err(EXIT_FAILURE, "getifaddrs"); for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family == AF_INET6 && strcmp(ifa->ifa_name, ctx->ifname) == 0) { sin6 = (const struct sockaddr_in6 *)satosin6(ifa->ifa_addr); if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { lladdr = &sin6->sin6_addr; break; } } } if (!lladdr) errx(EXIT_FAILURE, "could not determine link local address"); memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8); freeifaddrs(ifap); } static void print_addr(struct sockaddr_in6 *sin) { int error, n_flags; if (f_addr != NULL && strcmp(f_addr, "fqdn") == 0) n_flags = 0; else if (f_addr != NULL && strcmp(f_addr, "host") == 0) n_flags = NI_NOFQDN; else n_flags = NI_NUMERICHOST; error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf, sizeof(addr_buf), NULL, 0, n_flags); if (error != 0) inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, sizeof(addr_buf)); printf("\tinet6 %s", addr_buf); } static void print_p2p(struct sockaddr_in6 *sin) { int error; error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST); if (error != 0) inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, sizeof(addr_buf)); printf(" --> %s", addr_buf); } static void print_mask(int plen) { if (f_inet6 != NULL && strcmp(f_inet6, "cidr") == 0) printf("/%d", plen); else printf(" prefixlen %d", plen); } static void print_flags(int flags6) { if ((flags6 & IN6_IFF_ANYCAST) != 0) printf(" anycast"); if ((flags6 & IN6_IFF_TENTATIVE) != 0) printf(" tentative"); if ((flags6 & IN6_IFF_DUPLICATED) != 0) printf(" duplicated"); if ((flags6 & IN6_IFF_DETACHED) != 0) printf(" detached"); if ((flags6 & IN6_IFF_DEPRECATED) != 0) printf(" deprecated"); if ((flags6 & IN6_IFF_AUTOCONF) != 0) printf(" autoconf"); if ((flags6 & IN6_IFF_TEMPORARY) != 0) printf(" temporary"); if ((flags6 & IN6_IFF_PREFER_SOURCE) != 0) printf(" prefer_source"); } static void print_lifetime(const char *prepend, time_t px_time, struct timespec *now) { printf(" %s", prepend); if (px_time == 0) printf(" infty"); printf(" %s", px_time < now->tv_sec ? "0" : sec2str(px_time - now->tv_sec)); } #ifdef WITHOUT_NETLINK static void in6_status(if_ctx *ctx, const struct ifaddrs *ifa) { struct sockaddr_in6 *sin, null_sin = {}; struct in6_ifreq ifr6; int s6; u_int32_t flags6; struct in6_addrlifetime lifetime; sin = satosin6(ifa->ifa_addr); if (sin == NULL) return; strlcpy(ifr6.ifr_name, ctx->ifname, sizeof(ifr6.ifr_name)); if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { warn("socket(AF_INET6,SOCK_DGRAM)"); return; } ifr6.ifr_addr = *sin; if (ioctl(s6, SIOCGIFAFLAG_IN6, &ifr6) < 0) { warn("ioctl(SIOCGIFAFLAG_IN6)"); close(s6); return; } flags6 = ifr6.ifr_ifru.ifru_flags6; memset(&lifetime, 0, sizeof(lifetime)); ifr6.ifr_addr = *sin; if (ioctl(s6, SIOCGIFALIFETIME_IN6, &ifr6) < 0) { warn("ioctl(SIOCGIFALIFETIME_IN6)"); close(s6); return; } lifetime = ifr6.ifr_ifru.ifru_lifetime; close(s6); print_addr(sin); if (ifa->ifa_flags & IFF_POINTOPOINT) { sin = satosin6(ifa->ifa_dstaddr); /* * some of the interfaces do not have valid destination * address. */ if (sin != NULL && sin->sin6_family == AF_INET6) print_p2p(sin); } sin = satosin6(ifa->ifa_netmask); if (sin == NULL) sin = &null_sin; print_mask(prefix(&sin->sin6_addr, sizeof(struct in6_addr))); print_flags(flags6); if ((satosin6(ifa->ifa_addr))->sin6_scope_id) printf(" scopeid 0x%x", (satosin6(ifa->ifa_addr))->sin6_scope_id); if (ip6lifetime && (lifetime.ia6t_preferred || lifetime.ia6t_expire)) { struct timespec now; clock_gettime(CLOCK_MONOTONIC_FAST, &now); print_lifetime("pltime", lifetime.ia6t_preferred, &now); print_lifetime("vltime", lifetime.ia6t_expire, &now); } print_vhid(ifa); putchar('\n'); } #else static void show_lifetime(struct ifa_cacheinfo *ci) { struct timespec now; uint32_t pl, vl; if (ci == NULL) return; int count = ci->ifa_prefered != ND6_INFINITE_LIFETIME; count += ci->ifa_valid != ND6_INFINITE_LIFETIME; if (count == 0) return; pl = (ci->ifa_prefered == ND6_INFINITE_LIFETIME) ? 0 : ci->ifa_prefered; vl = (ci->ifa_valid == ND6_INFINITE_LIFETIME) ? 0 : ci->ifa_valid; clock_gettime(CLOCK_MONOTONIC_FAST, &now); print_lifetime("pltime", pl + (ip6lifetime ? ci->tstamp / 1000 : now.tv_sec), &now); print_lifetime("vltime", vl + (ip6lifetime ? ci->tstamp / 1000 : now.tv_sec), &now); } static void in6_status_nl(if_ctx *ctx __unused, if_link_t *link __unused, if_addr_t *ifa) { int plen = ifa->ifa_prefixlen; uint32_t scopeid; if (ifa->ifa_local == NULL) { /* Non-P2P address */ scopeid = satosin6(ifa->ifa_address)->sin6_scope_id; print_addr(satosin6(ifa->ifa_address)); } else { scopeid = satosin6(ifa->ifa_local)->sin6_scope_id; print_addr(satosin6(ifa->ifa_local)); print_p2p(satosin6(ifa->ifa_address)); } print_mask(plen); print_flags(ifa->ifaf_flags); if (scopeid != 0) printf(" scopeid 0x%x", scopeid); show_lifetime(ifa->ifa_cacheinfo); if (ifa->ifaf_vhid != 0) printf(" vhid %d", ifa->ifaf_vhid); putchar('\n'); } static struct in6_px *sin6tab_nl[] = { &in6_del.addr, /* RIDADDR */ &in6_add.addr, /* ADDR */ NULL, /* MASK */ &in6_add.dst_addr, /* DSTADDR*/ }; static void in6_copyaddr(if_ctx *ctx __unused, int to, int from) { sin6tab_nl[to]->addr = sin6tab_nl[from]->addr; sin6tab_nl[to]->set = sin6tab_nl[from]->set; } static void in6_getaddr(const char *addr_str, int which) { struct in6_px *px = sin6tab_nl[which]; if (which == MASK) errx(1, "netmask: invalid option for inet6"); if (which == BRDADDR) errx(1, "broadcast: invalid option for inet6"); px->set = true; px->plen = 128; if (which == ADDR) { char *p = NULL; if((p = strrchr(addr_str, '/')) != NULL) { *p = '\0'; int plen = strtol(p + 1, NULL, 10); if (plen < 0 || plen > 128) errx(1, "%s: bad value", p + 1); px->plen = plen; explicit_prefix = 1; } } struct addrinfo hints = { .ai_family = AF_INET6 }; struct addrinfo *res; int error = getaddrinfo(addr_str, NULL, &hints, &res); if (error != 0) { if (inet_pton(AF_INET6, addr_str, &px->addr) != 1) errx(1, "%s: bad value", addr_str); } else { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)(void *)res->ai_addr; px->addr = sin6->sin6_addr; freeaddrinfo(res); } } static int in6_exec_nl(if_ctx *ctx, unsigned long action, void *data) { struct in6_pdata *pdata = (struct in6_pdata *)data; struct snl_writer nw = {}; snl_init_writer(ctx->io_ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, action); struct ifaddrmsg *ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg); ifahdr->ifa_family = AF_INET6; ifahdr->ifa_prefixlen = pdata->addr.plen; ifahdr->ifa_index = if_nametoindex_nl(ctx->io_ss, ctx->ifname); snl_add_msg_attr_ip6(&nw, IFA_LOCAL, &pdata->addr.addr); if (action == NL_RTM_NEWADDR && pdata->dst_addr.set) snl_add_msg_attr_ip6(&nw, IFA_ADDRESS, &pdata->dst_addr.addr); struct ifa_cacheinfo ci = { .ifa_prefered = pdata->lifetime.ia6t_pltime, .ifa_valid = pdata->lifetime.ia6t_vltime, }; snl_add_msg_attr(&nw, IFA_CACHEINFO, sizeof(ci), &ci); int off = snl_add_msg_attr_nested(&nw, IFA_FREEBSD); snl_add_msg_attr_u32(&nw, IFAF_FLAGS, pdata->flags); if (pdata->vhid != 0) snl_add_msg_attr_u32(&nw, IFAF_VHID, pdata->vhid); snl_end_attr_nested(&nw, off); if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ctx->io_ss, hdr)) return (0); struct snl_errmsg_data e = {}; snl_read_reply_code(ctx->io_ss, hdr->nlmsg_seq, &e); return (e.error); } #endif #ifdef WITHOUT_NETLINK static struct sockaddr_in6 *sin6tab[] = { &in6_ridreq.ifr_addr, &in6_addreq.ifra_addr, &in6_addreq.ifra_prefixmask, &in6_addreq.ifra_dstaddr }; static void in6_copyaddr(if_ctx *ctx __unused, int to, int from) { memcpy(sin6tab[to], sin6tab[from], sizeof(struct sockaddr_in6)); } static void in6_getprefix(const char *plen, int which) { struct sockaddr_in6 *sin = sin6tab[which]; u_char *cp; int len = atoi(plen); if ((len < 0) || (len > 128)) errx(1, "%s: bad value", plen); sin->sin6_len = sizeof(*sin); if (which != MASK) sin->sin6_family = AF_INET6; if ((len == 0) || (len == 128)) { memset(&sin->sin6_addr, 0xff, sizeof(struct in6_addr)); return; } memset((void *)&sin->sin6_addr, 0x00, sizeof(sin->sin6_addr)); for (cp = (u_char *)&sin->sin6_addr; len > 7; len -= 8) *cp++ = 0xff; *cp = 0xff << (8 - len); } static void in6_getaddr(const char *s, int which) { struct sockaddr_in6 *sin = sin6tab[which]; struct addrinfo hints, *res; int error = -1; sin->sin6_len = sizeof(*sin); if (which != MASK) sin->sin6_family = AF_INET6; if (which == ADDR) { char *p = NULL; if((p = strrchr(s, '/')) != NULL) { *p = '\0'; in6_getprefix(p + 1, MASK); explicit_prefix = 1; } } if (sin->sin6_family == AF_INET6) { bzero(&hints, sizeof(struct addrinfo)); hints.ai_family = AF_INET6; error = getaddrinfo(s, NULL, &hints, &res); if (error != 0) { if (inet_pton(AF_INET6, s, &sin->sin6_addr) != 1) errx(1, "%s: bad value", s); } else { bcopy(res->ai_addr, sin, res->ai_addrlen); freeaddrinfo(res); } } } static int prefix(void *val, int size) { u_char *name = (u_char *)val; int byte, bit, plen = 0; for (byte = 0; byte < size; byte++, plen += 8) if (name[byte] != 0xff) break; if (byte == size) return (plen); for (bit = 7; bit != 0; bit--, plen++) if (!(name[byte] & (1 << bit))) break; for (; bit != 0; bit--) if (name[byte] & (1 << bit)) return(0); byte++; for (; byte < size; byte++) if (name[byte]) return(0); return (plen); } #endif static char * sec2str(time_t total) { static char result[256]; int days, hours, mins, secs; int first = 1; char *p = result; if (0) { days = total / 3600 / 24; hours = (total / 3600) % 24; mins = (total / 60) % 60; secs = total % 60; if (days) { first = 0; p += sprintf(p, "%dd", days); } if (!first || hours) { first = 0; p += sprintf(p, "%dh", hours); } if (!first || mins) { first = 0; p += sprintf(p, "%dm", mins); } sprintf(p, "%ds", secs); } else sprintf(result, "%lu", (unsigned long)total); return(result); } static void in6_postproc(if_ctx *ctx, int newaddr __unused, int ifflags __unused) { if (explicit_prefix == 0) { /* Aggregatable address architecture defines all prefixes are 64. So, it is convenient to set prefixlen to 64 if it is not specified. */ setifprefixlen(ctx, "64", 0); /* in6_getprefix("64", MASK) if MASK is available here... */ } } static void in6_status_tunnel(if_ctx *ctx) { char src[NI_MAXHOST]; char dst[NI_MAXHOST]; struct in6_ifreq in6_ifr; const struct sockaddr *sa = (const struct sockaddr *) &in6_ifr.ifr_addr; memset(&in6_ifr, 0, sizeof(in6_ifr)); strlcpy(in6_ifr.ifr_name, ctx->ifname, sizeof(in6_ifr.ifr_name)); if (ioctl_ctx(ctx, SIOCGIFPSRCADDR_IN6, (caddr_t)&in6_ifr) < 0) return; if (sa->sa_family != AF_INET6) return; if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, NI_NUMERICHOST) != 0) src[0] = '\0'; if (ioctl_ctx(ctx, SIOCGIFPDSTADDR_IN6, (caddr_t)&in6_ifr) < 0) return; if (sa->sa_family != AF_INET6) return; if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, NI_NUMERICHOST) != 0) dst[0] = '\0'; printf("\ttunnel inet6 %s --> %s\n", src, dst); } static void in6_set_tunnel(if_ctx *ctx, struct addrinfo *srcres, struct addrinfo *dstres) { struct in6_aliasreq in6_req = {}; strlcpy(in6_req.ifra_name, ctx->ifname, sizeof(in6_req.ifra_name)); memcpy(&in6_req.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len); memcpy(&in6_req.ifra_dstaddr, dstres->ai_addr, dstres->ai_addr->sa_len); if (ioctl_ctx(ctx, SIOCSIFPHYADDR_IN6, &in6_req) < 0) warn("SIOCSIFPHYADDR_IN6"); } static void in6_set_vhid(int vhid) { #ifdef WITHOUT_NETLINK in6_addreq.ifra_vhid = vhid; #else in6_add.vhid = (uint32_t)vhid; #endif } static struct cmd inet6_cmds[] = { DEF_CMD_ARG("prefixlen", setifprefixlen), DEF_CMD("anycast", IN6_IFF_ANYCAST, setip6flags), DEF_CMD("tentative", IN6_IFF_TENTATIVE, setip6flags), DEF_CMD("-tentative", -IN6_IFF_TENTATIVE, setip6flags), DEF_CMD("deprecated", IN6_IFF_DEPRECATED, setip6flags), DEF_CMD("-deprecated", -IN6_IFF_DEPRECATED, setip6flags), DEF_CMD("autoconf", IN6_IFF_AUTOCONF, setip6flags), DEF_CMD("-autoconf", -IN6_IFF_AUTOCONF, setip6flags), DEF_CMD("prefer_source",IN6_IFF_PREFER_SOURCE, setip6flags), DEF_CMD("-prefer_source",-IN6_IFF_PREFER_SOURCE,setip6flags), DEF_CMD("accept_rtadv", ND6_IFF_ACCEPT_RTADV, setnd6flags), DEF_CMD("-accept_rtadv",-ND6_IFF_ACCEPT_RTADV, setnd6flags), DEF_CMD("no_radr", ND6_IFF_NO_RADR, setnd6flags), DEF_CMD("-no_radr", -ND6_IFF_NO_RADR, setnd6flags), DEF_CMD("defaultif", 1, setnd6defif), DEF_CMD("-defaultif", -1, setnd6defif), DEF_CMD("ifdisabled", ND6_IFF_IFDISABLED, setnd6flags), DEF_CMD("-ifdisabled", -ND6_IFF_IFDISABLED, setnd6flags), DEF_CMD("nud", ND6_IFF_PERFORMNUD, setnd6flags), DEF_CMD("-nud", -ND6_IFF_PERFORMNUD, setnd6flags), DEF_CMD("auto_linklocal",ND6_IFF_AUTO_LINKLOCAL,setnd6flags), DEF_CMD("-auto_linklocal",-ND6_IFF_AUTO_LINKLOCAL,setnd6flags), DEF_CMD("no_prefer_iface",ND6_IFF_NO_PREFER_IFACE,setnd6flags), DEF_CMD("-no_prefer_iface",-ND6_IFF_NO_PREFER_IFACE,setnd6flags), DEF_CMD("no_dad", ND6_IFF_NO_DAD, setnd6flags), DEF_CMD("-no_dad", -ND6_IFF_NO_DAD, setnd6flags), DEF_CMD_ARG("pltime", setip6pltime), DEF_CMD_ARG("vltime", setip6vltime), DEF_CMD("eui64", 0, setip6eui64), DEF_CMD("stableaddr", ND6_IFF_STABLEADDR, setnd6flags), DEF_CMD("-stableaddr", -ND6_IFF_STABLEADDR, setnd6flags), -#ifdef EXPERIMENTAL - DEF_CMD("ipv6_only", ND6_IFF_IPV6_ONLY_MANUAL,setnd6flags), - DEF_CMD("-ipv6_only", -ND6_IFF_IPV6_ONLY_MANUAL,setnd6flags), -#endif }; static struct afswtch af_inet6 = { .af_name = "inet6", .af_af = AF_INET6, #ifdef WITHOUT_NETLINK .af_status = in6_status, #else .af_status = in6_status_nl, #endif .af_getaddr = in6_getaddr, .af_copyaddr = in6_copyaddr, #ifdef WITHOUT_NETLINK .af_getprefix = in6_getprefix, #endif .af_other_status = nd6_status, .af_postproc = in6_postproc, .af_status_tunnel = in6_status_tunnel, .af_settunnel = in6_set_tunnel, .af_setvhid = in6_set_vhid, #ifdef WITHOUT_NETLINK .af_difaddr = SIOCDIFADDR_IN6, .af_aifaddr = SIOCAIFADDR_IN6, .af_ridreq = &in6_ridreq, .af_addreq = &in6_addreq, .af_exec = af_exec_ioctl, #else .af_difaddr = NL_RTM_DELADDR, .af_aifaddr = NL_RTM_NEWADDR, .af_ridreq = &in6_del, .af_addreq = &in6_add, .af_exec = in6_exec_nl, #endif }; static void in6_Lopt_cb(const char *arg __unused) { ip6lifetime++; /* print IPv6 address lifetime */ } static struct option in6_Lopt = { .opt = "L", .opt_usage = "[-L]", .cb = in6_Lopt_cb }; static __constructor void inet6_ctor(void) { size_t i; #ifndef RESCUE if (!feature_present("inet6")) return; #endif for (i = 0; i < nitems(inet6_cmds); i++) cmd_register(&inet6_cmds[i]); af_register(&af_inet6); opt_register(&in6_Lopt); } diff --git a/sbin/ifconfig/af_nd6.c b/sbin/ifconfig/af_nd6.c index fb7e72028e2e..199523450dca 100644 --- a/sbin/ifconfig/af_nd6.c +++ b/sbin/ifconfig/af_nd6.c @@ -1,173 +1,169 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009 Hiroki Sato. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" #define MAX_SYSCTL_TRY 5 static const char *ND6BITS[] = { [0] = "PERFORMNUD", [1] = "ACCEPT_RTADV", [2] = "PREFER_SOURCE", [3] = "IFDISABLED", [4] = "DONT_SET_IFROUTE", [5] = "AUTO_LINKLOCAL", [6] = "NO_RADR", [7] = "NO_PREFER_IFACE", [8] = "NO_DAD", -#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG - [9] = "IPV6_ONLY", - [10] = "IPV6_ONLY_MANUAL", -#endif [11] = "STABLEADDR", [15] = "DEFAULTIF", }; static int isnd6defif(if_ctx *ctx, int s); void setnd6flags(if_ctx *, const char *, int); void setnd6defif(if_ctx *,const char *, int); void nd6_status(if_ctx *); void setnd6flags(if_ctx *ctx, const char *dummyaddr __unused, int d) { struct in6_ndireq nd = {}; int error; strlcpy(nd.ifname, ctx->ifname, sizeof(nd.ifname)); error = ioctl_ctx(ctx, SIOCGIFINFO_IN6, &nd); if (error) { warn("ioctl(SIOCGIFINFO_IN6)"); return; } if (d < 0) nd.ndi.flags &= ~(-d); else nd.ndi.flags |= d; error = ioctl_ctx(ctx, SIOCSIFINFO_IN6, (caddr_t)&nd); if (error) warn("ioctl(SIOCSIFINFO_IN6)"); } void setnd6defif(if_ctx *ctx, const char *dummyaddr __unused, int d) { struct in6_ndifreq ndifreq = {}; int ifindex; int error; strlcpy(ndifreq.ifname, ctx->ifname, sizeof(ndifreq.ifname)); if (d < 0) { if (isnd6defif(ctx, ctx->io_s)) { /* ifindex = 0 means to remove default if */ ifindex = 0; } else return; } else if ((ifindex = if_nametoindex(ndifreq.ifname)) == 0) { warn("if_nametoindex(%s)", ndifreq.ifname); return; } ndifreq.ifindex = ifindex; error = ioctl_ctx(ctx, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq); if (error) warn("ioctl(SIOCSDEFIFACE_IN6)"); } static int isnd6defif(if_ctx *ctx, int s) { struct in6_ndifreq ndifreq = {}; unsigned int ifindex; int error; strlcpy(ndifreq.ifname, ctx->ifname, sizeof(ndifreq.ifname)); ifindex = if_nametoindex(ndifreq.ifname); error = ioctl(s, SIOCGDEFIFACE_IN6, (caddr_t)&ndifreq); if (error) { warn("ioctl(SIOCGDEFIFACE_IN6)"); return (error); } return (ndifreq.ifindex == ifindex); } void nd6_status(if_ctx *ctx) { struct in6_ndireq nd = {}; int s6; int error; int isdefif; uint32_t bits; strlcpy(nd.ifname, ctx->ifname, sizeof(nd.ifname)); if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { if (errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT) warn("socket(AF_INET6, SOCK_DGRAM)"); return; } error = ioctl(s6, SIOCGIFINFO_IN6, &nd); if (error) { if (errno != EPFNOSUPPORT) warn("ioctl(SIOCGIFINFO_IN6)"); close(s6); return; } isdefif = isnd6defif(ctx, s6); close(s6); if (nd.ndi.flags == 0 && !isdefif) return; bits = (nd.ndi.flags | (isdefif << 15)); printf("\tnd6 options=%x", bits); print_bits("options", &bits, 1, ND6BITS, nitems(ND6BITS)); putchar('\n'); } diff --git a/share/man/man5/src.conf.5 b/share/man/man5/src.conf.5 index a94c5d6177b4..65fbcf2e69a4 100644 --- a/share/man/man5/src.conf.5 +++ b/share/man/man5/src.conf.5 @@ -1,2053 +1,2051 @@ .\" DO NOT EDIT-- this file is @generated by tools/build/options/makeman. -.Dd April 3, 2026 +.Dd April 10, 2026 .Dt SRC.CONF 5 .Os .Sh NAME .Nm src.conf .Nd "source build options" .Sh DESCRIPTION The .Nm file contains variables that control what components will be generated during the build process of the .Fx source tree; see .Xr build 7 . .Pp The .Nm file uses the standard makefile syntax. However, .Nm should not specify any dependencies to .Xr make 1 . Instead, .Nm is to set .Xr make 1 variables that control the aspects of how the system builds. .Pp The default location of .Nm is the top level of the source tree, or .Pa /etc/src.conf if no .Nm is found in the source tree itself, though an alternative location can be specified in the .Xr make 1 variable .Va SRCCONF . Overriding the location of .Nm may be necessary if the system-wide settings are not suitable for a particular build. For instance, setting .Va SRCCONF to .Pa /dev/null effectively resets all build controls to their defaults. .Pp The only purpose of .Nm is to control the compilation of the .Fx source code, which is usually located in .Pa /usr/src . As a rule, the system administrator creates .Nm when the values of certain control variables need to be changed from their defaults. .Pp In addition, control variables can be specified for a particular build via the .Fl D option of .Xr make 1 or in its environment; see .Xr environ 7 . .Pp The environment of .Xr make 1 for the build can be controlled via the .Va SRC_ENV_CONF variable, which defaults to .Pa /etc/src-env.conf . Some examples that may only be set in this file are .Va WITH_DIRDEPS_BUILD , and .Va WITH_META_MODE , and .Va MAKEOBJDIRPREFIX as they are environment-only variables. .Pp The values of .Va WITH_ and .Va WITHOUT_ variables are ignored regardless of their setting; even if they would be set to .Dq Li FALSE or .Dq Li NO . The presence of an option causes it to be honored by .Xr make 1 . .Pp This list provides a name and short description for variables that can be used for source builds. .Bl -tag -width indent .It Va WITHOUT_ACCT Do not build process accounting tools such as .Xr accton 8 and .Xr sa 8 . .It Va WITHOUT_ACPI Do not build .Xr acpiconf 8 , .Xr acpidump 8 and related programs. .It Va WITHOUT_APM Do not build .Xr apm 8 , .Xr apmd 8 and related programs. .It Va WITH_ASAN Build the base system with Address Sanitizer (ASan) to detect memory corruption bugs such as buffer overflows or use-after-free. Requires that Clang be used as the base system compiler and that the runtime support library is available. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITH_LLVM_BINUTILS .El .It Va WITHOUT_ASSERT_DEBUG Compile programs and libraries without the .Xr assert 3 checks. .It Va WITHOUT_AT Do not build .Xr at 1 and related utilities. .It Va WITHOUT_AUDIT Do not build audit support into system programs. .It Va WITHOUT_AUTHPF Do not build .Xr authpf 8 . .It Va WITHOUT_AUTOFS Do not build .Xr autofs 4 related programs, libraries, and kernel modules. .It Va WITHOUT_AUTO_OBJ Disable automatic creation of objdirs. This is enabled by default if the wanted OBJDIR is writable by the current user. .Pp This must be set in the environment, make command line, or .Pa /etc/src-env.conf , not .Pa /etc/src.conf . .It Va WITH_BEARSSL Build the BearSSL library. .Pp BearSSL is a tiny SSL library suitable for embedded environments. For details see .Lk https://www.BearSSL.org/ .Pp This library is currently only used to perform signature verification and related operations for Verified Exec and .Xr loader 8 . .Pp Due to size constraints in the BIOS environment on x86, one may need to set .Va LOADERSIZE larger than the default 500000, although often loader is under the 500k limit even with this option. Setting .Va LOADERSIZE larger than 500000 may cause .Xr pxeboot 8 to be too large to work. Careful testing of the loader in the target environment when built with a larger limit to establish safe limits is critical because different BIOS environments reserve differing amounts of the low 640k space, making a precise limit for everybody impossible. .Pp See also .Va WITH_LOADER_PXEBOOT for other considerations. When set, these options are also in effect: .Pp .Bl -inset -compact .It Va WITH_LOADER_EFI_SECUREBOOT (unless .Va WITHOUT_LOADER_EFI_SECUREBOOT is set explicitly) .It Va WITH_LOADER_VERIEXEC (unless .Va WITHOUT_LOADER_VERIEXEC is set explicitly) .It Va WITH_LOADER_VERIEXEC_VECTX (unless .Va WITHOUT_LOADER_VERIEXEC_VECTX is set explicitly) .It Va WITH_VERIEXEC (unless .Va WITHOUT_VERIEXEC is set explicitly) .El .It Va WITHOUT_BHYVE Do not build or install .Xr bhyve 8 , associated utilities, and examples. .Pp This option only affects amd64/amd64 and arm64/aarch64. .It Va WITH_BHYVE_SNAPSHOT Include support for save and restore (snapshots) in .Xr bhyve 8 and .Xr bhyvectl 8 . .Pp This option only affects amd64/amd64. .It Va WITH_BIND_NOW Build all binaries with the .Dv DF_BIND_NOW flag set to indicate that the run-time loader should perform all relocation processing at process startup rather than on demand. The combination of the .Va BIND_NOW and .Va RELRO options provide "full" Relocation Read-Only (RELRO) support. With full RELRO the entire GOT is made read-only after performing relocation at startup, avoiding GOT overwrite attacks. .It Va WITHOUT_BLACKLIST This option has been renamed to .Va WITHOUT_BLOCKLIST . When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_BLOCKLIST .El .Pp When set, these options are also in effect: .Pp .Bl -inset -compact .It Va WITHOUT_BLACKLIST_SUPPORT (unless .Va WITH_BLACKLIST_SUPPORT is set explicitly) .It Va WITHOUT_BLOCKLIST_SUPPORT (unless .Va WITH_BLOCKLIST_SUPPORT is set explicitly) .El .It Va WITHOUT_BLACKLIST_SUPPORT This option has been renamed to .Va WITHOUT_BLOCKLIST_SUPPORT . When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_BLOCKLIST_SUPPORT .El .It Va WITHOUT_BLOCKLIST Set this if you do not want to build .Xr blocklistd 8 and .Xr blocklistctl 8 . When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_BLACKLIST .El .Pp When set, these options are also in effect: .Pp .Bl -inset -compact .It Va WITHOUT_BLACKLIST_SUPPORT (unless .Va WITH_BLACKLIST_SUPPORT is set explicitly) .It Va WITHOUT_BLOCKLIST_SUPPORT (unless .Va WITH_BLOCKLIST_SUPPORT is set explicitly) .El .It Va WITHOUT_BLOCKLIST_SUPPORT Build some programs without .Xr libblocklist 3 support, like .Xr fingerd 8 and .Xr sshd 8 . When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_BLACKLIST_SUPPORT .El .It Va WITHOUT_BLUETOOTH Do not build Bluetooth related kernel modules, programs and libraries. .It Va WITHOUT_BOOT Do not build the boot blocks and loader. .It Va WITHOUT_BOOTPARAMD Do not build or install .Xr bootparamd 8 . .It Va WITHOUT_BOOTPD Do not build or install .Xr bootpd 8 . .It Va WITH_BRANCH_PROTECTION Build with branch protection enabled. On arm64 enable the use of pointer authentication and branch target identification instructions on arm64. These can be used to help mitigate some exploit techniques. .It Va WITHOUT_BSDINSTALL Do not build .Xr bsdinstall 8 , .Xr sade 8 , and related programs. .It Va WITHOUT_BSNMP Do not build or install .Xr bsnmpd 1 and related libraries and data files. .It Va WITHOUT_CALENDAR Do not build .Xr calendar 1 . .It Va WITHOUT_CAROOT Do not add the trusted certificates from the Mozilla NSS bundle to base. .It Va WITHOUT_CASPER This option has no effect. .It Va WITH_CCACHE_BUILD Use .Xr ccache 1 for the build. No configuration is required except to install the .Sy devel/ccache or .Sy devel/sccache package. When using with .Xr distcc 1 , set .Sy CCACHE_PREFIX=/usr/local/bin/distcc . When using with sccache set .Sy CCACHE_NAME=sccache in .Xr src.conf 5 . The default cache directory of .Pa $HOME/.ccache will be used, which can be overridden by setting .Sy CCACHE_DIR . The .Sy CCACHE_COMPILERCHECK option defaults to .Sy content when using the in-tree bootstrap compiler, and .Sy mtime when using an external compiler. The .Sy CCACHE_CPP2 option is used for Clang but not GCC. .Pp Sharing a cache between multiple work directories requires using a layout similar to .Pa /some/prefix/src .Pa /some/prefix/obj and an environment such as: .Bd -literal -offset indent CCACHE_BASEDIR='${SRCTOP:H}' MAKEOBJDIRPREFIX='${SRCTOP:H}/obj' .Ed .Pp See .Xr ccache 1 for more configuration options. .It Va WITHOUT_CCD Do not build .Xr geom_ccd 4 and related utilities. .It Va WITHOUT_CDDL Do not build code licensed under Sun's CDDL. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_CTF .It .Va WITHOUT_DTRACE .It .Va WITHOUT_LOADER_ZFS .It .Va WITHOUT_ZFS .It .Va WITHOUT_ZFS_TESTS .El .It Va WITHOUT_CLANG Do not build the Clang C/C++ compiler during the regular phase of the build. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_CLANG_EXTRAS .It .Va WITHOUT_CLANG_FORMAT .It .Va WITHOUT_CLANG_FULL .It .Va WITHOUT_LLVM_COV .El .Pp When set, these options are also in effect: .Pp .Bl -inset -compact .It Va WITHOUT_LLVM_TARGET_AARCH64 (unless .Va WITH_LLVM_TARGET_AARCH64 is set explicitly) .It Va WITHOUT_LLVM_TARGET_ALL (unless .Va WITH_LLVM_TARGET_ALL is set explicitly) .It Va WITHOUT_LLVM_TARGET_ARM (unless .Va WITH_LLVM_TARGET_ARM is set explicitly) .It Va WITHOUT_LLVM_TARGET_POWERPC (unless .Va WITH_LLVM_TARGET_POWERPC is set explicitly) .It Va WITHOUT_LLVM_TARGET_RISCV (unless .Va WITH_LLVM_TARGET_RISCV is set explicitly) .El .It Va WITHOUT_CLANG_BOOTSTRAP Do not build the Clang C/C++ compiler during the bootstrap phase of the build. To be able to build the system, either gcc or clang bootstrap must be enabled unless an alternate compiler is provided via XCC. .It Va WITH_CLANG_EXTRAS Build additional clang and llvm tools, such as bugpoint and clang-format. .It Va WITH_CLANG_FORMAT Build clang-format. .It Va WITHOUT_CLANG_FULL Avoid building the ARCMigrate, Rewriter and StaticAnalyzer components of the Clang C/C++ compiler. .It Va WITH_CLEAN Clean before building world and/or kernel. Note that recording a new epoch in .Pa .clean_build_epoch in the root of the source tree will also force a clean world build. When set, these options are also in effect: .Pp .Bl -inset -compact .It Va WITHOUT_DEPEND_CLEANUP (unless .Va WITH_DEPEND_CLEANUP is set explicitly) .El .It Va WITHOUT_CPP Do not build .Xr cpp 1 . .It Va WITHOUT_CROSS_COMPILER Do not build any cross compiler in the cross-tools stage of buildworld. When compiling a different version of .Fx than what is installed on the system, provide an alternate compiler with XCC to ensure success. When compiling with an identical version of .Fx to the host, this option may be safely used. This option may also be safe when the host version of .Fx is close to the sources being built, but all bets are off if there have been any changes to the toolchain between the versions. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_CLANG_BOOTSTRAP .It .Va WITHOUT_ELFTOOLCHAIN_BOOTSTRAP .It .Va WITHOUT_LLD_BOOTSTRAP .It .Va WITHOUT_LLVM_BINUTILS_BOOTSTRAP .El .It Va WITHOUT_CRYPT Do not build any crypto code. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_DMAGENT .It .Va WITHOUT_KERBEROS .It .Va WITHOUT_LDNS .It .Va WITHOUT_LDNS_UTILS .It .Va WITHOUT_LOADER_ZFS .It .Va WITHOUT_MITKRB5 .It .Va WITHOUT_OPENSSH .It .Va WITHOUT_OPENSSL .It .Va WITHOUT_OPENSSL_KTLS .It .Va WITHOUT_PKGBOOTSTRAP .It .Va WITHOUT_UNBOUND .It .Va WITHOUT_ZFS .It .Va WITHOUT_ZFS_TESTS .El .Pp When set, these options are also in effect: .Pp .Bl -inset -compact .It Va WITHOUT_KERBEROS_SUPPORT (unless .Va WITH_KERBEROS_SUPPORT is set explicitly) .El .It Va WITH_CTF Compile with CTF (Compact C Type Format) data. CTF data encapsulates a reduced form of debugging information similar to DWARF and the venerable stabs and is required for DTrace. .It Va WITHOUT_CUSE Do not build CUSE-related programs and libraries. .It Va WITHOUT_CXGBETOOL Do not build .Xr cxgbetool 8 .Pp This is a default setting on arm/armv7 and riscv/riscv64. .It Va WITH_CXGBETOOL Build .Xr cxgbetool 8 .Pp This is a default setting on amd64/amd64, arm64/aarch64, i386/i386, powerpc/powerpc64 and powerpc/powerpc64le. .It Va WITHOUT_DEBUG_FILES Avoid building or installing standalone debug files for each executable binary and shared library. .It Va WITHOUT_DEPEND_CLEANUP Do not attempt to detect if the object tree needs cleaning in part or in whole before building. This speeds up incremental builds, especially when experimenting with build options, but may cause the build to inexplicably fail or produce non-functioning binaries. .It Va WITH_DETECT_TZ_CHANGES Make the time handling code detect changes to the timezone files. .It Va WITH_DIALOG Do build .Xr dialog 1 , .Xr dialog 3 , .Xr dpv 1 , and .Xr dpv 3 . .It Va WITHOUT_DICT Do not build the Webster dictionary files. .It Va WITH_DIRDEPS_BUILD This is an alternate build system. For details see https://www.crufty.net/sjg/docs/freebsd-meta-mode.htm. Build commands can be seen from the top-level with: .Dl make show-valid-targets The build is driven by dirdeps.mk using .Va DIRDEPS stored in Makefile.depend files found in each directory. .Pp The build can be started from anywhere, and behaves the same. The initial instance of .Xr make 1 recursively reads .Va DIRDEPS from .Pa Makefile.depend , computing a graph of tree dependencies from the current origin. Setting .Va NO_DIRDEPS skips checking dirdep dependencies and will only build in the current and child directories. .Va NO_DIRDEPS_BELOW skips building any dirdeps and only build the current directory. .Pp This also utilizes the .Va WITH_META_MODE logic for incremental builds. .Pp The build hides commands executed unless .Va NO_SILENT is defined. .Pp Note that there is currently no mass install feature for this. This build is designed for producing packages, that can then be installed on a target system. .Pp The implementation in .Fx is incomplete. Completion would require leaf directories for building each kernel and package so that their dependencies can be tracked. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITH_INSTALL_AS_USER .El .Pp When set, these options are also in effect: .Pp .Bl -inset -compact .It Va WITH_META_ERROR_TARGET (unless .Va WITHOUT_META_ERROR_TARGET is set explicitly) .It Va WITH_META_MODE (unless .Va WITHOUT_META_MODE is set explicitly) .It Va WITH_STAGING (unless .Va WITHOUT_STAGING is set explicitly) .It Va WITH_STAGING_MAN (unless .Va WITHOUT_STAGING_MAN is set explicitly) .It Va WITH_STAGING_PROG (unless .Va WITHOUT_STAGING_PROG is set explicitly) .It Va WITH_SYSROOT (unless .Va WITHOUT_SYSROOT is set explicitly) .El .Pp This must be set in the environment, make command line, or .Pa /etc/src-env.conf , not .Pa /etc/src.conf . .It Va WITH_DIRDEPS_CACHE Cache result of dirdeps.mk which can save significant time for subsequent builds. Depends on .Va WITH_DIRDEPS_BUILD . .Pp This must be set in the environment, make command line, or .Pa /etc/src-env.conf , not .Pa /etc/src.conf . .It Va WITH_DISK_IMAGE_TOOLS_BOOTSTRAP Build .Xr etdump 1 , .Xr makefs 8 and .Xr mkimg 1 as bootstrap tools. .It Va WITHOUT_DMAGENT Do not build dma Mail Transport Agent. .It Va WITHOUT_DOCCOMPRESS Do not install compressed system documentation. Only the uncompressed version will be installed. .It Va WITHOUT_DTRACE Do not build DTrace framework kernel modules, libraries, and user commands. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_CTF .El .It Va WITH_DTRACE_ASAN Compile userspace DTrace code (libdtrace, dtrace(1), lockstat(1), plockstat(1)) with address and undefined behavior sanitizers. Requires that Clang be used as the base system compiler and that the runtime support library is available. .It Va WITH_DTRACE_TESTS Build and install the DTrace test suite in .Pa /usr/tests/cddl/usr.sbin/dtrace . This test suite is considered experimental on architectures other than amd64/amd64 and running it may cause system instability. .It Va WITHOUT_DYNAMICROOT Set this if you do not want to link .Pa /bin and .Pa /sbin dynamically. .It Va WITHOUT_EE Do not build and install .Xr edit 1 , .Xr ee 1 , and related programs. .It Va WITHOUT_EFI Set not to build .Xr efivar 3 and .Xr efivar 8 . .Pp This is a default setting on i386/i386, powerpc/powerpc64 and powerpc/powerpc64le. .It Va WITH_EFI Build .Xr efivar 3 and .Xr efivar 8 . .Pp This is a default setting on amd64/amd64, arm/armv7, arm64/aarch64 and riscv/riscv64. .It Va WITHOUT_ELFTOOLCHAIN_BOOTSTRAP Do not build ELF Tool Chain tools (addr2line, nm, size, strings and strip) as part of the bootstrap process. .Bf -symbolic An alternate bootstrap tool chain must be provided. .Ef .It Va WITHOUT_EXAMPLES Avoid installing examples to .Pa /usr/share/examples/ . -.It Va WITH_EXPERIMENTAL -Include experimental features in the build. .It Va WITHOUT_FDT Do not build Flattened Device Tree support as part of the base system. This includes the device tree compiler (dtc) and libfdt support library. .Pp This is a default setting on amd64/amd64 and i386/i386. .It Va WITH_FDT Build Flattened Device Tree support as part of the base system. This includes the device tree compiler (dtc) and libfdt support library. .Pp This is a default setting on arm/armv7, arm64/aarch64, powerpc/powerpc64, powerpc/powerpc64le and riscv/riscv64. .It Va WITHOUT_FILE Do not build .Xr file 1 and related programs. .It Va WITHOUT_FINGER Do not build or install .Xr finger 1 and .Xr fingerd 8 . .It Va WITHOUT_FLOPPY Do not build or install programs for operating floppy disk driver. .It Va WITHOUT_FORMAT_EXTENSIONS Do not enable .Fl fformat-extensions when compiling the kernel. Also disables all format checking. .It Va WITHOUT_FORTH Build bootloaders without Forth support. .It Va WITHOUT_FREEBSD_UPDATE Do not build .Xr freebsd-update 8 . .It Va WITHOUT_FTP Do not build or install .Xr ftp 1 . .It Va WITHOUT_GAMES Do not build games. .It Va WITHOUT_GOOGLETEST Neither build nor install .Lb libgmock , .Lb libgtest , and dependent tests. .It Va WITHOUT_GPIO Do not build .Xr gpioctl 8 as part of the base system. .It Va WITHOUT_HAST Do not build .Xr hastd 8 and related utilities. .It Va WITH_HESIOD Build Hesiod support. .It Va WITHOUT_HTML Do not build HTML docs. .It Va WITHOUT_HYPERV Do not build or install HyperV utilities. .Pp This is a default setting on arm/armv7, powerpc/powerpc64, powerpc/powerpc64le and riscv/riscv64. .It Va WITH_HYPERV Build or install HyperV utilities. .Pp This is a default setting on amd64/amd64, arm64/aarch64 and i386/i386. .It Va WITHOUT_ICONV Do not build iconv as part of libc. .It Va WITHOUT_INCLUDES Do not install header files. This option used to be spelled .Va NO_INCS . .Bf -symbolic The option does not work for build targets. .Ef .It Va WITHOUT_INET Do not build programs and libraries related to IPv4 networking. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_INET_SUPPORT .El .It Va WITHOUT_INET6 Do not build programs and libraries related to IPv6 networking. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_INET6_SUPPORT .El .It Va WITHOUT_INET6_SUPPORT Build libraries, programs, and kernel modules without IPv6 support. .It Va WITHOUT_INETD Do not build .Xr inetd 8 . .It Va WITHOUT_INET_SUPPORT Build libraries, programs, and kernel modules without IPv4 support. .It Va WITHOUT_INSTALLLIB Set this to not install optional libraries. For example, when creating a .Xr nanobsd 8 image. .Bf -symbolic The option does not work for build targets. .Ef .It Va WITH_INSTALL_AS_USER Make install targets succeed for non-root users by installing files with owner and group attributes set to that of the user running the .Xr make 1 command. The user still must set the .Va DESTDIR variable to point to a directory where the user has write permissions. .It Va WITHOUT_IPFILTER Do not build IP Filter package. .It Va WITH_IPFILTER_IPFS Enable building the .Xr ipfs 8 tool to save and restore IPFilter state tables. .It Va WITHOUT_IPFW Do not build IPFW tools. .It Va WITHOUT_IPSEC_SUPPORT Do not build the kernel with .Xr ipsec 4 support. This option is needed for .Xr ipsec 4 and .Xr tcpmd5 4 . .It Va WITHOUT_ISCSI Do not build .Xr iscsid 8 and related utilities. .It Va WITHOUT_JAIL Do not build tools for the support of jails; e.g., .Xr jail 8 . .It Va WITHOUT_JEMALLOC_LG_VADDR_WIDE Disallow programs to use more than 48 address bits on amd64. Incompatible with LA57 mode. Enabling this option might result in a slight reduction in memory consumption for jemalloc metadata, but also requires disabling LA57 (if hardware supports it). .It Va WITHOUT_KDUMP Do not build .Xr kdump 1 and .Xr truss 1 . .It Va WITHOUT_KERBEROS Set this to not build Kerberos. When set, these options are also in effect: .Pp .Bl -inset -compact .It Va WITHOUT_KERBEROS_SUPPORT (unless .Va WITH_KERBEROS_SUPPORT is set explicitly) .El .It Va WITHOUT_KERBEROS_SUPPORT Build some programs without Kerberos support, like .Xr ssh 1 , .Xr telnet 1 , and .Xr sshd 8 . .It Va WITH_KERNEL_BIN Generate and install kernel.bin from kernel as part of the normal build and install processes for the kernel. Available only on arm and arm64. Usually this will be added to the kernel config file with: makeoptions WITH_KERNEL_BIN=1 though it can also be used on the command line. .It Va WITH_KERNEL_RETPOLINE Enable the "retpoline" mitigation for CVE-2017-5715 in the kernel build. .It Va WITHOUT_KERNEL_SYMBOLS Do not install standalone kernel debug symbol files. This option has no effect at build time. .It Va WITHOUT_KVM Do not build the .Nm libkvm library as a part of the base system. .Bf -symbolic The option has no effect yet. .Ef When set, these options are also in effect: .Pp .Bl -inset -compact .It Va WITHOUT_KVM_SUPPORT (unless .Va WITH_KVM_SUPPORT is set explicitly) .El .It Va WITHOUT_KVM_SUPPORT Build some programs without optional .Nm libkvm support. .It Va WITHOUT_LDNS Setting this variable will prevent the LDNS library from being built. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_LDNS_UTILS .It .Va WITHOUT_UNBOUND .El .It Va WITHOUT_LDNS_UTILS Setting this variable will prevent building the LDNS utilities .Xr drill 1 and .Xr host 1 . .It Va WITHOUT_LEGACY_CONSOLE Do not build programs that support a legacy PC console; e.g., .Xr kbdcontrol 1 and .Xr vidcontrol 1 . .It Va WITHOUT_LIB32 On 64-bit platforms, do not build 32-bit library set and a .Nm ld-elf32.so.1 runtime linker. .Pp This is a default setting on arm/armv7, i386/i386, powerpc/powerpc64le and riscv/riscv64. .It Va WITH_LIB32 On 64-bit platforms, build the 32-bit library set and a .Nm ld-elf32.so.1 runtime linker. .Pp This is a default setting on amd64/amd64, arm64/aarch64 and powerpc/powerpc64. .It Va WITHOUT_LLD Do not build LLVM's lld linker. .It Va WITHOUT_LLDB Do not build the LLDB debugger. .Pp This is a default setting on riscv/riscv64. .It Va WITH_LLDB Build the LLDB debugger. .Pp This is a default setting on amd64/amd64, arm/armv7, arm64/aarch64, i386/i386, powerpc/powerpc64 and powerpc/powerpc64le. .It Va WITHOUT_LLD_BOOTSTRAP Do not build the LLD linker during the bootstrap phase of the build. To be able to build the system an alternate linker must be provided via XLD. .It Va WITHOUT_LLVM_ASSERTIONS Disable debugging assertions in LLVM. .It Va WITHOUT_LLVM_BINUTILS Install ELF Tool Chain's binary utilities instead of LLVM's. This includes .Xr addr2line 1 , .Xr ar 1 , .Xr nm 1 , .Xr objcopy 1 , .Xr ranlib 1 , .Xr readelf 1 , .Xr size 1 , and .Xr strip 1 . Regardless of this setting, LLVM tools are used for .Xr c++filt 1 and .Xr objdump 1 . .Xr strings 1 is always provided by ELF Tool Chain. .It Va WITHOUT_LLVM_BINUTILS_BOOTSTRAP Do not build LLVM binary utilities during the bootstrap phase of the build. To be able to build the system alternate binary utilities must be provided via .Ev XAR , .Ev XNM , .Ev XOBJCOPY , .Ev XSIZE , .Ev XSTRINGS , and .Ev XSTRIPBIN . .It Va WITHOUT_LLVM_COV Do not build the .Xr llvm-cov 1 tool. .It Va WITH_LLVM_FULL_DEBUGINFO Generate full debug information for LLVM libraries and tools, which uses more disk space and build resources, but allows for easier debugging. .It Va WITH_LLVM_LINK_STATIC_LIBRARIES Link LLVM libraries (libllvm, libclang, liblldb) statically into each of the binaries that use them. This means that binaries linked against these libraries, such as clang, ld.lld and lldb will be much larger and position dependent, but will start more quickly. .It Va WITHOUT_LLVM_TARGET_AARCH64 Do not build LLVM target support for AArch64. The .Va LLVM_TARGET_ALL option should be used rather than this in most cases. .It Va WITHOUT_LLVM_TARGET_ALL Only build the required LLVM target support. This option is preferred to specific target support options. When set, these options are also in effect: .Pp .Bl -inset -compact .It Va WITHOUT_LLVM_TARGET_AARCH64 (unless .Va WITH_LLVM_TARGET_AARCH64 is set explicitly) .It Va WITHOUT_LLVM_TARGET_ARM (unless .Va WITH_LLVM_TARGET_ARM is set explicitly) .It Va WITHOUT_LLVM_TARGET_POWERPC (unless .Va WITH_LLVM_TARGET_POWERPC is set explicitly) .It Va WITHOUT_LLVM_TARGET_RISCV (unless .Va WITH_LLVM_TARGET_RISCV is set explicitly) .El .It Va WITHOUT_LLVM_TARGET_ARM Do not build LLVM target support for ARM. The .Va LLVM_TARGET_ALL option should be used rather than this in most cases. .It Va WITH_LLVM_TARGET_BPF Build LLVM target support for BPF. The .Va LLVM_TARGET_ALL option should be used rather than this in most cases. .It Va WITH_LLVM_TARGET_MIPS Build LLVM target support for MIPS. The .Va LLVM_TARGET_ALL option should be used rather than this in most cases. .It Va WITHOUT_LLVM_TARGET_POWERPC Do not build LLVM target support for PowerPC. The .Va LLVM_TARGET_ALL option should be used rather than this in most cases. .It Va WITHOUT_LLVM_TARGET_RISCV Do not build LLVM target support for RISC-V. The .Va LLVM_TARGET_ALL option should be used rather than this in most cases. .It Va WITHOUT_LLVM_TARGET_X86 Do not build LLVM target support for X86. The .Va LLVM_TARGET_ALL option should be used rather than this in most cases. .It Va WITHOUT_LOADER_BIOS_TEXTONLY Include graphics, font and video mode support in the i386 and amd64 BIOS boot loader. .It Va WITH_LOADER_EFI_SECUREBOOT Enable building .Xr loader 8 with support for verification based on certificates obtained from UEFI. .It Va WITHOUT_LOADER_GELI Disable inclusion of GELI crypto support in the boot chain binaries. .Pp This is a default setting on powerpc/powerpc64 and powerpc/powerpc64le. .It Va WITH_LOADER_GELI Build GELI bootloader support. .Pp This is a default setting on amd64/amd64, arm/armv7, arm64/aarch64, i386/i386 and riscv/riscv64. .It Va WITHOUT_LOADER_IA32 Do not build the 32-bit UEFI loader. .Pp This is a default setting on arm/armv7, arm64/aarch64, i386/i386, powerpc/powerpc64, powerpc/powerpc64le and riscv/riscv64. .It Va WITH_LOADER_IA32 Build the 32-bit UEFI loader. .Pp This is a default setting on amd64/amd64. .It Va WITHOUT_LOADER_KBOOT Do not build kboot, a linuxboot environment loader .Pp This is a default setting on arm/armv7, i386/i386, powerpc/powerpc64le and riscv/riscv64. .It Va WITH_LOADER_KBOOT Build kboot, a linuxboot environment loader .Pp This is a default setting on amd64/amd64, arm64/aarch64 and powerpc/powerpc64. .It Va WITHOUT_LOADER_LUA Do not build LUA bindings for the boot loader. .Pp This is a default setting on powerpc/powerpc64 and powerpc/powerpc64le. .It Va WITH_LOADER_LUA Build LUA bindings for the boot loader. .Pp This is a default setting on amd64/amd64, arm/armv7, arm64/aarch64, i386/i386 and riscv/riscv64. .It Va WITHOUT_LOADER_OFW Disable building of openfirmware bootloader components. .Pp This is a default setting on amd64/amd64, arm/armv7, arm64/aarch64, i386/i386 and riscv/riscv64. .It Va WITH_LOADER_OFW Build openfirmware bootloader components. .Pp This is a default setting on powerpc/powerpc64 and powerpc/powerpc64le. .It Va WITHOUT_LOADER_PXEBOOT Do not build pxeboot on i386/amd64. When the pxeboot is too large, or unneeded, it may be disabled with this option. See .Va WITH_LOADER_PXEBOOT for how to adjust the defaults when you need both a larger .Pa /boot/loader and .Pa /boot/pxeboot .Pp This option only has an effect on x86. .It Va WITHOUT_LOADER_UBOOT Disable building of ubldr. .Pp This is a default setting on amd64/amd64, arm64/aarch64, i386/i386, powerpc/powerpc64le and riscv/riscv64. .It Va WITH_LOADER_UBOOT Build ubldr. .Pp This is a default setting on arm/armv7 and powerpc/powerpc64. .It Va WITH_LOADER_VERBOSE Build with extra verbose debugging in the loader. May explode already nearly too large loader over the limit. Use with care. .It Va WITH_LOADER_VERIEXEC Enable building .Xr loader 8 with support for verification similar to Verified Exec. .Pp Depends on .Va WITH_BEARSSL . May require a larger .Va LOADERSIZE . When set, these options are also in effect: .Pp .Bl -inset -compact .It Va WITH_LOADER_EFI_SECUREBOOT (unless .Va WITHOUT_LOADER_EFI_SECUREBOOT is set explicitly) .It Va WITH_LOADER_VERIEXEC_VECTX (unless .Va WITHOUT_LOADER_VERIEXEC_VECTX is set explicitly) .El .It Va WITH_LOADER_VERIEXEC_PASS_MANIFEST Enable building .Xr loader 8 with support to pass a verified manifest to the kernel. The kernel has to be built with a module to parse the manifest. .Pp Depends on .Va WITH_LOADER_VERIEXEC . .It Va WITH_LOADER_VERIEXEC_VECTX Enable building .Xr loader 8 with support for hashing and verifying kernel and modules as a side effect of loading. .Pp Depends on .Va WITH_LOADER_VERIEXEC . .It Va WITHOUT_LOADER_ZFS Do not build ZFS file system boot loader support. .It Va WITHOUT_LOCALES Do not build localization files; see .Xr locale 1 . .It Va WITHOUT_LOCATE Do not build .Xr locate 1 and related programs. .It Va WITHOUT_LPR Do not build .Xr lpr 1 and related programs. .It Va WITHOUT_LS_COLORS Build .Xr ls 1 without support for colors to distinguish file types. .It Va WITHOUT_MACHDEP_OPTIMIZATIONS Prefer machine-independent non-assembler code in libc and libm. .It Va WITHOUT_MAIL Do not build any mail support (MUA or MTA). When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_DMAGENT .It .Va WITHOUT_MAILWRAPPER .It .Va WITHOUT_SENDMAIL .El .It Va WITHOUT_MAILWRAPPER Do not build the .Xr mailwrapper 8 MTA selector. .It Va WITHOUT_MAKE Do not install .Xr make 1 and related support files. .It Va WITHOUT_MAKE_CHECK_USE_SANDBOX Do not execute .Dq Li "make check" in limited sandbox mode. This option should be paired with .Va WITH_INSTALL_AS_USER if executed as an unprivileged user. See .Xr tests 7 for more details. .It Va WITH_MALLOC_PRODUCTION Disable assertions and statistics gathering in .Xr malloc 3 . The run-time options .Dv opt.abort , .Dv opt.abort_conf , and .Dv opt.junk also default to false. .It Va WITHOUT_MAN Do not build manual pages. When set, these options are also in effect: .Pp .Bl -inset -compact .It Va WITHOUT_MAN_UTILS (unless .Va WITH_MAN_UTILS is set explicitly) .El .It Va WITHOUT_MANCOMPRESS Do not install compressed man pages. Only the uncompressed versions will be installed. .It Va WITH_MANSPLITPKG Split man pages into their own packages during make package. .It Va WITHOUT_MAN_UTILS Do not build utilities for manual pages, .Xr apropos 1 , .Xr makewhatis 1 , .Xr man 1 , .Xr whatis 1 , .Xr manctl 8 , and related support files. .It Va WITH_META_ERROR_TARGET Enable the META_MODE .ERROR target. .Pp This target will copy the meta file of a failed target to .Va ERROR_LOGDIR (default is .Ql ${SRCTOP:H}/error ) to help with failure analysis. Depends on .Va WITH_META_MODE . This default when .Va WITH_DIRDEPS_BUILD is set. .Pp This must be set in the environment, make command line, or .Pa /etc/src-env.conf , not .Pa /etc/src.conf . .It Va WITH_META_MODE Create .Xr make 1 meta files when building, which can provide a reliable incremental build when using .Xr filemon 4 . The meta file is created in OBJDIR as .Pa target.meta . These meta files track the command that was executed, its output, and the current directory. The .Xr filemon 4 module is required unless .Va NO_FILEMON is defined. When the module is loaded, any files used by the commands executed are tracked as dependencies for the target in its meta file. The target is considered out-of-date and rebuilt if any of these conditions are true compared to the last build: .Bl -bullet -compact .It The command to execute changes. .It The current working directory changes. .It The target's meta file is missing. .It The target's meta file is missing filemon data when filemon is loaded and a previous run did not have it loaded. .It [requires .Xr filemon 4 ] Files read, executed or linked to are newer than the target. .It [requires .Xr filemon 4 ] Files read, written, executed or linked are missing. .El The meta files can also be useful for debugging. .Pp The build hides commands that are executed unless .Va NO_SILENT is defined. Errors cause .Xr make 1 to show some of its environment for further debugging. .Pp The build operates as it normally would otherwise. This option originally invoked a different build system but that was renamed to .Va WITH_DIRDEPS_BUILD . .Pp This must be set in the environment, make command line, or .Pa /etc/src-env.conf , not .Pa /etc/src.conf . .It Va WITHOUT_MITKRB5 Set this to build KTH Heimdal instead of MIT Kerberos 5. .It Va WITHOUT_MLX5TOOL Do not build .Xr mlx5tool 8 .Pp This is a default setting on arm/armv7 and riscv/riscv64. .It Va WITH_MLX5TOOL Build .Xr mlx5tool 8 .Pp This is a default setting on amd64/amd64, arm64/aarch64, i386/i386, powerpc/powerpc64 and powerpc/powerpc64le. .It Va WITHOUT_NETCAT Do not build .Xr nc 1 utility. .It Va WITHOUT_NETGRAPH Do not build applications to support .Xr netgraph 4 . When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_BLUETOOTH .El .Pp When set, these options are also in effect: .Pp .Bl -inset -compact .It Va WITHOUT_NETGRAPH_SUPPORT (unless .Va WITH_NETGRAPH_SUPPORT is set explicitly) .El .It Va WITHOUT_NETGRAPH_SUPPORT Build libraries, programs, and kernel modules without netgraph support. .It Va WITHOUT_NETLINK Do not build .Xr genl 1 utility. .It Va WITHOUT_NETLINK_SUPPORT Make libraries and programs use rtsock and .Xr sysctl 3 interfaces instead of .Xr snl 3 . .It Va WITHOUT_NIS Do not build .Xr NIS 8 support and related programs. If set, you might need to adopt your .Xr nsswitch.conf 5 and remove .Sq nis entries. .It Va WITHOUT_NLS Do not build NLS catalogs. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_NLS_CATALOGS .El .It Va WITHOUT_NLS_CATALOGS Do not build NLS catalog support for .Xr csh 1 . .It Va WITHOUT_NS_CACHING Disable name caching in the .Pa nsswitch subsystem. The generic caching daemon, .Xr nscd 8 , will not be built either if this option is set. .It Va WITHOUT_NTP Do not build .Xr ntpd 8 and related programs. .It Va WITHOUT_NUAGEINIT Do not install the limited cloud init support scripts. .It Va WITHOUT_OFED Do not build the .Dq "OpenFabrics Enterprise Distribution" InfiniBand software stack, including kernel modules and userspace libraries. .Pp This is a default setting on arm/armv7. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_OFED_EXTRA .El .It Va WITH_OFED Build the .Dq "OpenFabrics Enterprise Distribution" InfiniBand software stack, including kernel modules and userspace libraries. .Pp This is a default setting on amd64/amd64, arm64/aarch64, i386/i386, powerpc/powerpc64, powerpc/powerpc64le and riscv/riscv64. .It Va WITH_OFED_EXTRA Build the non-essential components of the .Dq "OpenFabrics Enterprise Distribution" Infiniband software stack, mostly examples. .It Va WITH_OPENLDAP Enable building LDAP support for kerberos using an openldap client from ports. .It Va WITHOUT_OPENMP Do not build LLVM's OpenMP runtime. .Pp This is a default setting on arm/armv7. .It Va WITH_OPENMP Build LLVM's OpenMP runtime. .Pp This is a default setting on amd64/amd64, arm64/aarch64, i386/i386, powerpc/powerpc64, powerpc/powerpc64le and riscv/riscv64. .It Va WITHOUT_OPENSSH Do not build OpenSSH. .It Va WITHOUT_OPENSSL Do not build OpenSSL. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_DMAGENT .It .Va WITHOUT_KERBEROS .It .Va WITHOUT_LDNS .It .Va WITHOUT_LDNS_UTILS .It .Va WITHOUT_LOADER_ZFS .It .Va WITHOUT_MITKRB5 .It .Va WITHOUT_OPENSSH .It .Va WITHOUT_OPENSSL_KTLS .It .Va WITHOUT_PKGBOOTSTRAP .It .Va WITHOUT_UNBOUND .It .Va WITHOUT_ZFS .It .Va WITHOUT_ZFS_TESTS .El .Pp When set, these options are also in effect: .Pp .Bl -inset -compact .It Va WITHOUT_KERBEROS_SUPPORT (unless .Va WITH_KERBEROS_SUPPORT is set explicitly) .El .It Va WITHOUT_OPENSSL_KTLS Do not include kernel TLS support in OpenSSL. .Pp This is a default setting on arm/armv7, i386/i386 and riscv/riscv64. .It Va WITH_OPENSSL_KTLS Include kernel TLS support in OpenSSL. .Pp This is a default setting on amd64/amd64, arm64/aarch64, powerpc/powerpc64 and powerpc/powerpc64le. .It Va WITHOUT_PAM Do not build PAM library and modules. .Bf -symbolic This option is deprecated and does nothing. .Ef When set, these options are also in effect: .Pp .Bl -inset -compact .It Va WITHOUT_PAM_SUPPORT (unless .Va WITH_PAM_SUPPORT is set explicitly) .El .It Va WITHOUT_PAM_SUPPORT Build .Xr ppp 8 without PAM support. .It Va WITHOUT_PF Do not build PF firewall package. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_AUTHPF .El .It Va WITHOUT_PIE Do not build dynamically linked binaries as Position-Independent Executable (PIE). .Pp This is a default setting on arm/armv7 and i386/i386. .It Va WITH_PIE Build dynamically linked binaries as Position-Independent Executable (PIE). .Pp This is a default setting on amd64/amd64, arm64/aarch64, powerpc/powerpc64, powerpc/powerpc64le and riscv/riscv64. .It Va WITHOUT_PKGBOOTSTRAP Do not build .Xr pkg 7 bootstrap tool. .It Va WITHOUT_PKGSERVE Do not build or install .Xr pkg-serve 8 . .It Va WITHOUT_PMC Do not build .Xr pmccontrol 8 and related programs. .It Va WITHOUT_PPP Do not build .Xr ppp 8 and related programs. .It Va WITHOUT_PTHREADS_ASSERTIONS Disable debugging assertions in pthreads library. .It Va WITHOUT_QUOTAS Do not build .Xr quota 1 and related programs. .It Va WITHOUT_RADIUS_SUPPORT Do not build radius support into various applications, like .Xr pam_radius 8 and .Xr ppp 8 . .It Va WITH_RATELIMIT Build the system with rate limit support. .Pp This makes .Dv SO_MAX_PACING_RATE effective in .Xr getsockopt 2 , and .Ar txrlimit support in .Xr ifconfig 8 , by proxy. .It Va WITHOUT_RBOOTD Do not build or install .Xr rbootd 8 . .It Va WITHOUT_RELRO Do not apply the Relocation Read-Only (RELRO) vulnerability mitigation. See also the .Va BIND_NOW option. .It Va WITH_REPRODUCIBLE_BUILD Exclude build metadata (such as the build time, user, or host) from the kernel, boot loaders, and .Xr uname 1 output, so that builds produce bit-for-bit identical output. .It Va WITH_REPRODUCIBLE_PATHS Modify the paths encoded in binary artifacts to be standard path Normally, the actual path is encoded in the binary. However, this makes the build differ depending on the path it was built from. With this option enabled, the paths recorded are /usr/src, regardless of the actual path. With this option disabled, the actual paths are recorded. .It Va WITHOUT_RESCUE Do not build .Xr rescue 8 . .It Va WITH_RETPOLINE Build the base system with the retpoline speculative execution vulnerability mitigation for CVE-2017-5715. .It Va WITHOUT_ROUTED Do not build .Xr routed 8 utility. .It Va WITH_RPCBIND_WARMSTART_SUPPORT Build .Xr rpcbind 8 with warmstart support. .It Va WITH_RUN_TESTS Run tests as part of the build. .It Va WITHOUT_SCTP_SUPPORT Disable support in the kernel for the .Xr sctp 4 Stream Control Transmission Protocol loadable kernel module. .It Va WITHOUT_SENDMAIL Do not build .Xr sendmail 8 and related programs. .It Va WITHOUT_SERVICESDB Do not install .Pa /var/db/services.db . .It Va WITHOUT_SETUID_LOGIN Set this to disable the installation of .Xr login 1 as a set-user-ID root program. .It Va WITHOUT_SHAREDOCS Do not build the .Bx 4.4 legacy docs. .It Va WITH_SORT_THREADS Enable threads in .Xr sort 1 . .It Va WITHOUT_SOUND Do not build userland sound utilities such as .Xr beep 1 and .Xr mixer 8 . .It Va WITHOUT_SOURCELESS Do not build kernel modules that include sourceless code (either microcode or native code for host CPU). When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_SOURCELESS_HOST .It .Va WITHOUT_SOURCELESS_UCODE .El .It Va WITHOUT_SOURCELESS_HOST Do not build kernel modules that include sourceless native code for host CPU. .It Va WITHOUT_SOURCELESS_UCODE Do not build kernel modules that include sourceless microcode. .It Va WITHOUT_SPLIT_KERNEL_DEBUG Do not build standalone kernel debug files. Debug data (if enabled by the kernel configuration file) will be included in the kernel and modules. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_KERNEL_SYMBOLS .El .It Va WITHOUT_SSP Do not build world with stack smashing protection. See .Xr mitigations 7 for more information. .It Va WITH_STAGING Enable staging of files to a stage tree. This can be best thought of as auto-install to .Va DESTDIR with some extra meta data to ensure dependencies can be tracked. Depends on .Va WITH_DIRDEPS_BUILD . When set, these options are also in effect: .Pp .Bl -inset -compact .It Va WITH_STAGING_MAN (unless .Va WITHOUT_STAGING_MAN is set explicitly) .It Va WITH_STAGING_PROG (unless .Va WITHOUT_STAGING_PROG is set explicitly) .El .Pp This must be set in the environment, make command line, or .Pa /etc/src-env.conf , not .Pa /etc/src.conf . .It Va WITH_STAGING_MAN Enable staging of man pages to stage tree. .It Va WITH_STAGING_PROG Enable staging of PROGs to stage tree. .It Va WITH_STALE_STAGED Check staged files are not stale. .It Va WITHOUT_STATS Neither build nor install .Lb libstats and dependent binaries. .It Va WITHOUT_SYSCONS Do not build .Xr syscons 4 support files such as keyboard maps, fonts, and screen output maps. .It Va WITH_SYSROOT Enable use of sysroot during build. Depends on .Va WITH_DIRDEPS_BUILD . .Pp This must be set in the environment, make command line, or .Pa /etc/src-env.conf , not .Pa /etc/src.conf . .It Va WITHOUT_SYSTEM_COMPILER Do not opportunistically skip building a cross-compiler during the bootstrap phase of the build. Normally, if the currently installed compiler matches the planned bootstrap compiler type and revision, then it will not be built. This does not prevent a compiler from being built for installation though, only for building one for the build itself. The .Va WITHOUT_CLANG option controls that. .It Va WITHOUT_SYSTEM_LINKER Do not opportunistically skip building a cross-linker during the bootstrap phase of the build. Normally, if the currently installed linker matches the planned bootstrap linker type and revision, then it will not be built. This does not prevent a linker from being built for installation though, only for building one for the build itself. The .Va WITHOUT_LLD option controls that. .Pp This option is only relevant when .Va WITH_LLD_BOOTSTRAP is set. .It Va WITHOUT_TALK Do not build or install .Xr talk 1 and .Xr talkd 8 . .It Va WITHOUT_TCP_WRAPPERS Do not build or install .Xr tcpd 8 , and related utilities. .It Va WITHOUT_TCSH Do not build and install .Pa /bin/csh (which is .Xr tcsh 1 ) . .It Va WITHOUT_TELNET Do not build .Xr telnet 1 and related programs. .It Va WITHOUT_TESTS Do not build nor install the .Fx Test Suite in .Pa /usr/tests/ . See .Xr tests 7 for more details. This also disables the build of all test-related dependencies, including ATF. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_DTRACE_TESTS .It .Va WITHOUT_ZFS_TESTS .El .Pp When set, these options are also in effect: .Pp .Bl -inset -compact .It Va WITHOUT_GOOGLETEST (unless .Va WITH_GOOGLETEST is set explicitly) .It Va WITHOUT_TESTS_SUPPORT (unless .Va WITH_TESTS_SUPPORT is set explicitly) .El .It Va WITHOUT_TESTS_SUPPORT Disable the build of all test-related dependencies, including ATF. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_GOOGLETEST .El .It Va WITHOUT_TEXTPROC Do not build programs used for text processing. .It Va WITHOUT_TFTP Do not build or install .Xr tftp 1 and .Xr tftpd 8 . .It Va WITHOUT_TOOLCHAIN Do not install programs used for program development, compilers, debuggers etc. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_CLANG .It .Va WITHOUT_CLANG_EXTRAS .It .Va WITHOUT_CLANG_FORMAT .It .Va WITHOUT_CLANG_FULL .It .Va WITHOUT_LLD .It .Va WITHOUT_LLDB .It .Va WITHOUT_LLVM_COV .El .Pp When set, these options are also in effect: .Pp .Bl -inset -compact .It Va WITHOUT_LLVM_BINUTILS (unless .Va WITH_LLVM_BINUTILS is set explicitly) .El .It Va WITH_UBSAN Build the base system with Undefined Behavior Sanitizer (UBSan) to detect various kinds of undefined behavior at runtime. Requires that Clang be used as the base system compiler and that the runtime support library is available .It Va WITHOUT_UNBOUND Do not build .Xr unbound 8 and related programs. .It Va WITH_UNDEFINED_VERSION Link libraries with --undefined-version which permits version maps to contain symbols that are not present in the library. If this is necessary to build a particular configuration, a bug is present and the configuration should be reported. .It Va WITHOUT_UNIFIED_OBJDIR Use the historical object directory format for .Xr build 7 targets. For native-builds and builds done directly in sub-directories the format of .Pa ${MAKEOBJDIRPREFIX}/${.CURDIR} is used, while for cross-builds .Pa ${MAKEOBJDIRPREFIX}/${TARGET}.${TARGET_ARCH}/${.CURDIR} is used. .Pp This option is transitional and will be removed in a future version of .Fx , at which time .Va WITH_UNIFIED_OBJDIR will be enabled permanently. .Pp This must be set in the environment, make command line, or .Pa /etc/src-env.conf , not .Pa /etc/src.conf . .It Va WITHOUT_USB Do not build USB-related programs and libraries. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_USB_GADGET_EXAMPLES .El .It Va WITHOUT_USB_GADGET_EXAMPLES Do not build USB gadget kernel modules. .It Va WITHOUT_UTMPX Do not build user accounting tools such as .Xr last 1 , .Xr users 1 , .Xr who 1 , .Xr ac 8 , .Xr lastlogin 8 and .Xr utx 8 . .It Va WITH_VERIEXEC Enable building .Xr veriexec 8 which loads the contents of verified manifests into the kernel for use by .Xr mac_veriexec 4 .Pp Depends on .Va WITH_BEARSSL . .It Va WITHOUT_VI Do not build and install vi, view, ex and related programs. .It Va WITHOUT_VT Do not build .Xr vt 4 support files (fonts and keymaps). .It Va WITHOUT_WARNS Set this to not add warning flags to the compiler invocations. Useful as a temporary workaround when code enters the tree which triggers warnings in environments that differ from the original developer. .It Va WITHOUT_WERROR Set this to not treat compiler warnings as errors. Useful as a temporary workaround when working on fixing compiler warnings. When set, warnings are still printed in the build log but do not fail the build. .It Va WITHOUT_WIRELESS Do not build programs used for 802.11 wireless networks; especially .Xr wpa_supplicant 8 and .Xr hostapd 8 . When set, these options are also in effect: .Pp .Bl -inset -compact .It Va WITHOUT_WIRELESS_SUPPORT (unless .Va WITH_WIRELESS_SUPPORT is set explicitly) .El .It Va WITHOUT_WIRELESS_SUPPORT Build libraries, programs, and kernel modules without 802.11 wireless support. .It Va WITHOUT_WPA_SUPPLICANT_EAPOL Build .Xr wpa_supplicant 8 without support for the IEEE 802.1X protocol and without support for EAP-PEAP, EAP-TLS, EAP-LEAP, and EAP-TTLS protocols (usable only via 802.1X). .It Va WITH_ZEROREGS Build the basesystem with code to zero caller-used register contents on function return. This prevents leaking temporary values for side channel attacks. Additionally this reduces the number of usable ROP gadgets for attackers. .It Va WITHOUT_ZFS Do not build the ZFS file system kernel module, libraries such as .Xr libbe 3 , and user commands such as .Xr zpool 8 or .Xr zfs 8 . Also disable ZFS support in utilities and libraries which implement ZFS-specific functionality. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_ZFS_TESTS .El .It Va WITHOUT_ZFS_TESTS Do not build and install the legacy ZFS test suite. .It Va WITHOUT_ZONEINFO Do not build the timezone database. When set, it enforces these options: .Pp .Bl -item -compact .It .Va WITHOUT_ZONEINFO_LEAPSECONDS_SUPPORT .El .It Va WITH_ZONEINFO_LEAPSECONDS_SUPPORT Build leapsecond information in to the timezone database. This option violates .St -p1003.1 and all other applicable standards, and is known to cause unexpected issues with date/time handling in many applications and programming languages. .El .Pp The following options accept a single value from a list of valid values. .Bl -tag -width indent .It Va INIT_ALL Control default initialization of stack variables in C and C++ code. Options other than .Li none require the Clang compiler or GCC 12.0 or later. The default value is .Li none . Valid values are: .Bl -tag -width indent .It Li none Do not initialize stack variables (standard C/C++ behavior). .It Li pattern Build the base system or kernel with stack variables initialized to .Pq compiler defined debugging patterns on function entry. .It Li zero Build the base system or kernel with stack variables initialized to zero on function entry. This value is converted to .Li none for amd64 kernel builds due to incompatibility with ifunc memset. .El .It Va LIBC_MALLOC Specify the .Xr malloc 3 implementation used by libc. The default value is .Li jemalloc . Valid values are: .Bl -tag -width indent .It Li jemalloc .El .Pp Other implementations are expected in the future in both .Fx and downstream consumers. .El .Sh FILES .Bl -tag -compact -width Pa .It Pa /etc/src.conf .It Pa /etc/src-env.conf .It Pa /usr/share/mk/bsd.own.mk .El .Sh SEE ALSO .Xr make 1 , .Xr make.conf 5 , .Xr build 7 , .Xr ports 7 .Sh HISTORY The .Nm file appeared in .Fx 7.0 . .Sh AUTHORS This manual page was autogenerated by .An tools/build/options/makeman . diff --git a/share/mk/src.opts.mk b/share/mk/src.opts.mk index 5c3e3bb31a7f..ab774d44c283 100644 --- a/share/mk/src.opts.mk +++ b/share/mk/src.opts.mk @@ -1,542 +1,541 @@ # # Option file for FreeBSD /usr/src builds, at least the userland and boot loader # portions of the tree. These options generally chose what parts of the tree to # include or omit and are FreeBSD source tree specific. # # Users define WITH_FOO and WITHOUT_FOO on the command line or in /etc/src.conf # and /etc/make.conf files. These translate in the build system to MK_FOO={yes,no} # with sensible (usually) defaults. # # Makefiles must include bsd.opts.mk after defining specific MK_FOO options that # are applicable for that Makefile (typically there are none, but sometimes there # are exceptions). Recursive makes usually add MK_FOO=no for options that they wish # to omit from that make. # # Makefiles must include bsd.mkopt.mk before they test the value of any MK_FOO # variable. # # Makefiles may also assume that this file is included by src.opts.mk should it # need variables defined there prior to the end of the Makefile where # bsd.{subdir,lib.bin}.mk is traditionally included. # # The old-style YES_FOO and NO_FOO are being phased out. No new instances of them # should be added. Old instances should be removed since they were just to # bridge the gap between FreeBSD 4 and FreeBSD 5. # # Makefiles should never test WITH_FOO or WITHOUT_FOO directly (although an # exception is made for _WITHOUT_SRCONF which turns off this mechanism # completely inside bsd.*.mk files). # .if !target(____) ____: .NOTMAIN .include # # Define MK_* variables (which are either "yes" or "no") for users # to set via WITH_*/WITHOUT_* in /etc/src.conf and override in the # make(1) environment. # These should be tested with `== "no"' or `!= "no"' in makefiles. # The NO_* variables should only be set by makefiles for variables # that haven't been converted over. # # These options are used by the src builds. Those listed in # __DEFAULT_YES_OPTIONS default to 'yes' and will build unless turned # off. __DEFAULT_NO_OPTIONS will default to 'no' and won't build # unless turned on. Any options listed in 'BROKEN_OPTIONS' will be # hard-wired to 'no'. "Broken" here means not working or # not-appropriate and/or not supported. It doesn't imply something is # wrong with the code. There's not a single good word for this, so # BROKEN was selected as the least imperfect one considered at the # time. Options are added to BROKEN_OPTIONS list on a per-arch basis. # At this time, there's no provision for mutually incompatible options. # Options listed in 'REQUIRED_OPTIONS' will be hard-wired to 'yes'; this # is intended as a transitional measure while options are in the process # of being removed. __DEFAULT_YES_OPTIONS = \ ACCT \ ACPI \ APM \ AT \ AUDIT \ AUTHPF \ AUTOFS \ BHYVE \ BLACKLIST \ BLOCKLIST \ BLUETOOTH \ BOOT \ BOOTPARAMD \ BOOTPD \ BSDINSTALL \ BSNMP \ CALENDAR \ CAROOT \ CCD \ CDDL \ CLANG \ CLANG_BOOTSTRAP \ CPP \ CROSS_COMPILER \ CRYPT \ CUSE \ CXGBETOOL \ DEPEND_CLEANUP \ DICT \ DMAGENT \ DTRACE \ DYNAMICROOT \ EE \ EFI \ ELFTOOLCHAIN_BOOTSTRAP \ EXAMPLES \ FILE \ FINGER \ FLOPPY \ FORTH \ FREEBSD_UPDATE \ FTP \ GAMES \ GOOGLETEST \ GPIO \ HAST \ HTML \ HYPERV \ ICONV \ INET \ INET6 \ INETD \ IPFILTER \ IPFW \ ISCSI \ JAIL \ JEMALLOC_LG_VADDR_WIDE \ KDUMP \ KVM \ LDNS \ LDNS_UTILS \ LEGACY_CONSOLE \ LLD \ LLD_BOOTSTRAP \ LLVM_ASSERTIONS \ LLVM_BINUTILS \ LLVM_BINUTILS_BOOTSTRAP \ LLVM_COV \ LOADER_BIOS_TEXTONLY \ LOADER_GELI \ LOADER_KBOOT \ LOADER_LUA \ LOADER_OFW \ LOADER_PXEBOOT \ LOADER_UBOOT \ LOADER_IA32 \ LOCALES \ LOCATE \ LPR \ LS_COLORS \ MACHDEP_OPTIMIZATIONS \ MAIL \ MAILWRAPPER \ MAKE \ MITKRB5 \ MLX5TOOL \ NETCAT \ NETGRAPH \ NETLINK \ NETLINK_SUPPORT \ NLS_CATALOGS \ NS_CACHING \ NTP \ NUAGEINIT \ OFED \ OPENSSL \ PAM \ PF \ PKGBOOTSTRAP \ PKGSERVE \ PMC \ PPP \ PTHREADS_ASSERTIONS \ QUOTAS \ RADIUS_SUPPORT \ RBOOTD \ RESCUE \ ROUTED \ SENDMAIL \ SERVICESDB \ SETUID_LOGIN \ SHAREDOCS \ SOUND \ SOURCELESS \ SOURCELESS_HOST \ SOURCELESS_UCODE \ STATS \ SYSCONS \ SYSTEM_COMPILER \ SYSTEM_LINKER \ TALK \ TCP_WRAPPERS \ TCSH \ TELNET \ TEXTPROC \ TFTP \ UNBOUND \ USB \ UTMPX \ VI \ VT \ WIRELESS \ WPA_SUPPLICANT_EAPOL \ ZFS \ ZFS_TESTS \ LOADER_ZFS \ ZONEINFO __DEFAULT_NO_OPTIONS = \ BEARSSL \ BHYVE_SNAPSHOT \ CLANG_EXTRAS \ CLANG_FORMAT \ CLEAN \ DIALOG \ DETECT_TZ_CHANGES \ DISK_IMAGE_TOOLS_BOOTSTRAP \ DTRACE_ASAN \ DTRACE_TESTS \ - EXPERIMENTAL \ HESIOD \ IPFILTER_IPFS \ LOADER_VERBOSE \ LOADER_VERIEXEC_PASS_MANIFEST \ LLVM_FULL_DEBUGINFO \ LLVM_LINK_STATIC_LIBRARIES \ MALLOC_PRODUCTION \ OFED_EXTRA \ OPENLDAP \ RPCBIND_WARMSTART_SUPPORT \ SORT_THREADS \ ZONEINFO_LEAPSECONDS_SUPPORT \ __REQUIRED_OPTIONS = \ CASPER # LEFT/RIGHT. Left options which default to "yes" unless their corresponding # RIGHT option is disabled. __DEFAULT_DEPENDENT_OPTIONS= \ CLANG_FULL/CLANG \ LLVM_TARGET_ALL/CLANG \ LOADER_VERIEXEC/BEARSSL \ LOADER_EFI_SECUREBOOT/LOADER_VERIEXEC \ LOADER_VERIEXEC_VECTX/LOADER_VERIEXEC \ VERIEXEC/BEARSSL \ __SINGLE_OPTIONS = \ LIBC_MALLOC __LIBC_MALLOC_OPTIONS= jemalloc __LIBC_MALLOC_DEFAULT= jemalloc # MK_*_SUPPORT options which default to "yes" unless their corresponding # MK_* variable is set to "no". # .for var in \ BLACKLIST \ BLOCKLIST \ INET \ INET6 \ KERBEROS \ KVM \ NETGRAPH \ PAM \ TESTS \ WIRELESS __DEFAULT_DEPENDENT_OPTIONS+= ${var}_SUPPORT/${var} .endfor # # Default behaviour of some options depends on the architecture. Unfortunately # this means that we have to test TARGET_ARCH (the buildworld case) as well # as MACHINE_ARCH (the non-buildworld case). Normally TARGET_ARCH is not # used at all in bsd.*.mk, but we have to make an exception here if we want # to allow defaults for some things like clang to vary by target architecture. # Additional, per-target behavior should be rarely added only after much # gnashing of teeth and grinding of gears. # .if defined(TARGET_ARCH) __T=${TARGET_ARCH} .else __T=${MACHINE_ARCH} .endif # All supported backends for LLVM_TARGET_XXX __LLVM_TARGETS= \ aarch64 \ arm \ powerpc \ riscv \ x86 __LLVM_TARGET_FILT= C/(amd64|i386)/x86/:C/powerpc.*/powerpc/:C/armv[67]/arm/:C/riscv.*/riscv/ .for __llt in ${__LLVM_TARGETS} # Default enable the given TARGET's LLVM_TARGET support .if ${__T:${__LLVM_TARGET_FILT}} == ${__llt} __DEFAULT_YES_OPTIONS+= LLVM_TARGET_${__llt:${__LLVM_TARGET_FILT}:tu} # aarch64 needs arm for -m32 support. .elif ${__T} == "aarch64" && ${__llt:Marm*} != "" __DEFAULT_DEPENDENT_OPTIONS+= LLVM_TARGET_ARM/LLVM_TARGET_AARCH64 # Default the rest of the LLVM_TARGETs to the value of MK_LLVM_TARGET_ALL. .else __DEFAULT_DEPENDENT_OPTIONS+= LLVM_TARGET_${__llt:${__LLVM_TARGET_FILT}:tu}/LLVM_TARGET_ALL .endif .endfor __DEFAULT_NO_OPTIONS+=LLVM_TARGET_BPF LLVM_TARGET_MIPS .include .if ${__T} == "i386" || ${__T} == "amd64" __DEFAULT_NO_OPTIONS+=FDT .else __DEFAULT_YES_OPTIONS+=FDT .endif .if ${__T:Mriscv64*} == "" __DEFAULT_YES_OPTIONS+=LLDB .else __DEFAULT_NO_OPTIONS+=LLDB .endif # LIB32 is not supported on all 64-bit architectures. .if (${__T:Maarch64*} != "" && ((defined(X_COMPILER_TYPE) && ${X_COMPILER_TYPE} != "gcc") || (!defined(X_COMPILER_TYPE) && ${COMPILER_TYPE} != "gcc"))) || ${__T} == "amd64" || ${__T} == "powerpc64" __DEFAULT_YES_OPTIONS+=LIB32 .else BROKEN_OPTIONS+=LIB32 .endif # EFI doesn't exist on powerpc (well, officially) and doesn't work on i386 .if ${__T:Mpowerpc*} || ${__T} == "i386" BROKEN_OPTIONS+=EFI .endif # Bad coupling for libsecure stuff with bearssl and efi, so broken on EFI .if ${__T:Mpowerpc*} BROKEN_OPTIONS+=BEARSSL # bearssl brings in secure efi stuff xxx .endif # OFW is only for powerpc, exclude others .if ${__T:Mpowerpc*} == "" BROKEN_OPTIONS+=LOADER_OFW .endif # KBOOT is only for powerpc64 (powerpc64le broken) amd64 and aarch64 .if ${__T} != "powerpc64" && ${__T} != "amd64" && ${__T} != "aarch64" BROKEN_OPTIONS+=LOADER_KBOOT .endif # UBOOT is only for arm, and big-endian powerpc .if (${__T:Marm*} == "" && ${__T:Mpowerpc*} == "") || ${__T} == "powerpc64le" BROKEN_OPTIONS+=LOADER_UBOOT .endif # The 32-bit UEFI loader is only for amd64 .if ${__T} != "amd64" BROKEN_OPTIONS+=LOADER_IA32 .endif # GELI and Lua in loader currently cause boot failures on powerpc. # Further debugging is required -- probably they are just broken on big # endian systems generically (they jump to null pointers or try to read # crazy high addresses, which is typical of endianness problems). .if ${__T:Mpowerpc*} BROKEN_OPTIONS+=LOADER_GELI LOADER_LUA .endif # Kernel TLS is enabled by default on amd64, aarch64 and powerpc64* .if ${__T} == "aarch64" || ${__T} == "amd64" || ${__T:Mpowerpc64*} != "" __DEFAULT_YES_OPTIONS+=OPENSSL_KTLS .else __DEFAULT_NO_OPTIONS+=OPENSSL_KTLS .endif .if ${__T} != "aarch64" && ${__T} != "amd64" && ${__T} != "i386" && \ ${__T:Mpowerpc64*} == "" BROKEN_OPTIONS+=CXGBETOOL BROKEN_OPTIONS+=MLX5TOOL .endif .if ${__T} != "amd64" && ${__T} != "i386" && ${__T} != "aarch64" BROKEN_OPTIONS+=HYPERV .endif .if ${__T} == "aarch64" || ${__T} == "amd64" || ${__T} == "i386" || \ ${__T:Mpowerpc64*} != "" || ${__T:Mriscv64*} != "" __DEFAULT_YES_OPTIONS+=OPENMP .else __DEFAULT_NO_OPTIONS+=OPENMP .endif # Broken on 32-bit arm, kernel module compile errors .if ${__T:Marm*} != "" BROKEN_OPTIONS+= OFED .endif # MK_host_egacy is set by local.sys.mk so is valid here .if ${MACHINE:Nhost*} == "" && ${MK_host_egacy} == "yes" # we cannot expect tests to work BROKEN_OPTIONS+= TESTS .endif .if ${__T} != "amd64" BROKEN_OPTIONS+=BHYVE_SNAPSHOT .endif .-include .include # # Force some options off if their dependencies are off. # Order is somewhat important. # .if ${MK_SOURCELESS} == "no" MK_SOURCELESS_HOST:= no MK_SOURCELESS_UCODE:= no .endif .if ${MK_BLACKLIST} == "no" MK_BLOCKLIST:= no .endif .if ${MK_BLACKLIST_SUPPORT} == "no" MK_BLOCKLIST_SUPPORT:= no .endif .if ${MK_BLOCKLIST} == "no" MK_BLACKLIST:= no .endif .if ${MK_BLOCKLIST_SUPPORT} == "no" MK_BLACKLIST_SUPPORT:= no .endif .if ${MK_CDDL} == "no" MK_CTF:= no MK_DTRACE:= no MK_LOADER_ZFS:= no MK_ZFS:= no .endif .if ${MK_CRYPT} == "no" MK_OPENSSL:= no MK_OPENSSH:= no MK_KERBEROS:= no MK_KERBEROS_SUPPORT:= no MK_MITKRB5:= no .endif .if ${MK_DTRACE} == "no" MK_CTF:= no .endif .if ${MK_MAIL} == "no" MK_MAILWRAPPER:= no MK_SENDMAIL:= no MK_DMAGENT:= no .endif .if ${MK_NETGRAPH} == "no" MK_BLUETOOTH:= no .endif .if ${MK_NLS} == "no" MK_NLS_CATALOGS:= no .endif .if ${MK_OPENSSL} == "no" MK_DMAGENT:= no MK_OPENSSH:= no MK_OPENSSL_KTLS:= no MK_KERBEROS:= no MK_KERBEROS_SUPPORT:= no MK_MITKRB5:= no MK_LDNS:= no MK_PKGBOOTSTRAP:= no MK_LOADER_ZFS:= no MK_ZFS:= no .endif .if ${MK_LDNS} == "no" MK_LDNS_UTILS:= no MK_UNBOUND:= no .endif .if ${MK_PF} == "no" MK_AUTHPF:= no .endif .if ${MK_OFED} == "no" MK_OFED_EXTRA:= no .endif .if ${MK_TESTS} == "no" MK_DTRACE_TESTS:= no MK_ZFS_TESTS:= no .endif .if ${MK_ZFS} == "no" MK_ZFS_TESTS:= no .endif .if ${MK_TESTS_SUPPORT} == "no" MK_GOOGLETEST:= no .endif .if ${MK_ZONEINFO} == "no" MK_ZONEINFO_LEAPSECONDS_SUPPORT:= no .endif .if ${MK_CROSS_COMPILER} == "no" MK_CLANG_BOOTSTRAP:= no MK_ELFTOOLCHAIN_BOOTSTRAP:= no MK_LLD_BOOTSTRAP:= no MK_LLVM_BINUTILS_BOOTSTRAP:= no .endif .if ${MK_TOOLCHAIN} == "no" MK_CLANG:= no MK_LLD:= no MK_LLDB:= no MK_LLVM_BINUTILS:= no .endif .if ${MK_CLANG} == "no" MK_CLANG_EXTRAS:= no MK_CLANG_FORMAT:= no MK_CLANG_FULL:= no MK_LLVM_COV:= no .endif .if ${MK_ASAN} == "yes" # In order to get sensible backtraces from ASAN we have to install # llvm-symbolizer as /usr/bin/addr2line instead of the elftoolchain version. MK_LLVM_BINUTILS:= yes .endif .if ${MK_LOADER_VERIEXEC} == "no" MK_LOADER_VERIEXEC_PASS_MANIFEST := no .endif .if ${MK_CLEAN} == "yes" MK_DEPEND_CLEANUP:= no .endif # # MK_* options whose default value depends on another option. # .for vv in \ KERBEROS_SUPPORT/KERBEROS \ MAN_UTILS/MAN .if defined(WITH_${vv:H}) MK_${vv:H}:= yes .elif defined(WITHOUT_${vv:H}) MK_${vv:H}:= no .else MK_${vv:H}:= ${MK_${vv:T}} .endif .endfor .endif # !target(____) diff --git a/sys/conf/options b/sys/conf/options index 155fbf8e6c8a..54994503ad65 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -1,1017 +1,1016 @@ # # On the handling of kernel options # # All kernel options should be listed in NOTES, with suitable # descriptions. Negative options (options that make some code not # compile) should be commented out; LINT (generated from NOTES) should # compile as much code as possible. Try to structure option-using # code so that a single option only switch code on, or only switch # code off, to make it possible to have a full compile-test. If # necessary, you can check for COMPILING_LINT to get maximum code # coverage. # # All new options shall also be listed in either "conf/options" or # "conf/options.". Options that affect a single source-file # .[c|s] should be directed into "opt_.h", while options # that affect multiple files should either go in "opt_global.h" if # this is a kernel-wide option (used just about everywhere), or in # "opt_.h" if it affects only some files. # Note that the effect of listing only an option without a # header-file-name in conf/options (and cousins) is that the last # convention is followed. # # This handling scheme is not yet fully implemented. # # # Format of this file: # Option name filename # # If filename is missing, the default is # opt_.h AAC_DEBUG opt_aac.h AACRAID_DEBUG opt_aacraid.h AHC_ALLOW_MEMIO opt_aic7xxx.h AHC_TMODE_ENABLE opt_aic7xxx.h AHC_DUMP_EEPROM opt_aic7xxx.h AHC_DEBUG opt_aic7xxx.h AHC_DEBUG_OPTS opt_aic7xxx.h AHC_REG_PRETTY_PRINT opt_aic7xxx.h AHD_DEBUG opt_aic79xx.h AHD_DEBUG_OPTS opt_aic79xx.h AHD_TMODE_ENABLE opt_aic79xx.h AHD_REG_PRETTY_PRINT opt_aic79xx.h # Debugging options ALT_BREAK_TO_DEBUGGER opt_kdb.h BREAK_TO_DEBUGGER opt_kdb.h BUF_TRACKING opt_global.h DDB DDB_BUFR_SIZE opt_ddb.h DDB_CAPTURE_DEFAULTBUFSIZE opt_ddb.h DDB_CAPTURE_MAXBUFSIZE opt_ddb.h DDB_CTF opt_ddb.h DDB_NUMSYM opt_ddb.h EARLY_PRINTF opt_global.h EXTERR_STRINGS opt_global.h FULL_BUF_TRACKING opt_global.h GDB KDB opt_global.h KDB_TRACE opt_kdb.h KDB_UNATTENDED opt_kdb.h KLD_DEBUG opt_kld.h NUM_CORE_FILES opt_global.h QUEUE_MACRO_DEBUG_TRACE opt_global.h QUEUE_MACRO_DEBUG_TRASH opt_global.h SYSCTL_DEBUG opt_sysctl.h TEXTDUMP_PREFERRED opt_ddb.h TEXTDUMP_VERBOSE opt_ddb.h TSLOG opt_global.h TSLOG_PAGEZERO opt_global.h TSLOGSIZE opt_global.h # Miscellaneous options ALQ ATSE_CFI_HACK opt_cfi.h AUDIT opt_global.h BOOTHOWTO opt_global.h BOOTVERBOSE opt_global.h CALLOUT_PROFILING CAPABILITIES opt_capsicum.h CAPABILITY_MODE opt_capsicum.h CC_CDG opt_global.h CC_CHD opt_global.h CC_CUBIC opt_global.h CC_DEFAULT opt_cc.h CC_DCTCP opt_global.h CC_HD opt_global.h CC_HTCP opt_global.h CC_NEWRENO opt_global.h CC_VEGAS opt_global.h COMPAT_43 opt_global.h COMPAT_43TTY opt_global.h COMPAT_FREEBSD4 opt_global.h COMPAT_FREEBSD5 opt_global.h COMPAT_FREEBSD6 opt_global.h COMPAT_FREEBSD7 opt_global.h COMPAT_FREEBSD9 opt_global.h COMPAT_FREEBSD10 opt_global.h COMPAT_FREEBSD11 opt_global.h COMPAT_FREEBSD12 opt_global.h COMPAT_FREEBSD13 opt_global.h COMPAT_FREEBSD14 opt_global.h COMPAT_LINUXKPI opt_dontuse.h COMPILING_LINT opt_global.h CY_PCI_FASTINTR DEADLKRES opt_watchdog.h -EXPERIMENTAL opt_global.h DIRECTIO FFCLOCK FULL_PREEMPTION opt_sched.h GZIO opt_gzio.h IMGACT_BINMISC opt_dontuse.h IPI_PREEMPTION opt_sched.h GEOM_CACHE opt_geom.h GEOM_CONCAT opt_geom.h GEOM_ELI opt_geom.h GEOM_GATE opt_geom.h GEOM_JOURNAL opt_geom.h GEOM_LABEL opt_geom.h GEOM_LABEL_GPT opt_geom.h GEOM_LINUX_LVM opt_geom.h GEOM_MAP opt_geom.h GEOM_MIRROR opt_geom.h GEOM_MOUNTVER opt_geom.h GEOM_MULTIPATH opt_geom.h GEOM_NOP opt_geom.h GEOM_PART_APM opt_geom.h GEOM_PART_BSD opt_geom.h GEOM_PART_BSD64 opt_geom.h GEOM_PART_EBR opt_geom.h GEOM_PART_GPT opt_geom.h GEOM_PART_LDM opt_geom.h GEOM_PART_MBR opt_geom.h GEOM_RAID opt_geom.h GEOM_RAID3 opt_geom.h GEOM_SHSEC opt_geom.h GEOM_STRIPE opt_geom.h GEOM_UZIP opt_geom.h GEOM_UZIP_DEBUG opt_geom.h GEOM_VINUM opt_geom.h GEOM_VIRSTOR opt_geom.h GEOM_ZERO opt_geom.h IFLIB opt_iflib.h KDTRACE_HOOKS opt_global.h KDTRACE_FRAME opt_kdtrace.h KDTRACE_MIB_SDT opt_global.h KN_HASHSIZE opt_kqueue.h KSTACK_MAX_PAGES KSTACK_PAGES KSTACK_USAGE_PROF KTRACE KTRACE_REQUEST_POOL opt_ktrace.h LIBICONV MAC opt_global.h MAC_BIBA opt_dontuse.h MAC_BSDEXTENDED opt_dontuse.h MAC_DDB opt_dontuse.h MAC_DEBUG opt_mac.h MAC_DO opt_dontuse.h MAC_IFOFF opt_dontuse.h MAC_IPACL opt_dontuse.h MAC_LOMAC opt_dontuse.h MAC_MLS opt_dontuse.h MAC_NONE opt_dontuse.h MAC_NTPD opt_dontuse.h MAC_PARTITION opt_dontuse.h MAC_PORTACL opt_dontuse.h MAC_PRIORITY opt_dontuse.h MAC_SEEOTHERUIDS opt_dontuse.h MAC_STATIC opt_mac.h MAC_STUB opt_dontuse.h MAC_TEST opt_dontuse.h MAC_GRANTBYLABEL opt_dontuse.h MAC_VERIEXEC opt_dontuse.h MAC_VERIEXEC_DEBUG opt_mac.h MAC_VERIEXEC_SHA1 opt_dontuse.h MAC_VERIEXEC_SHA256 opt_dontuse.h MAC_VERIEXEC_SHA384 opt_dontuse.h MAC_VERIEXEC_SHA512 opt_dontuse.h MD_ROOT opt_md.h MD_ROOT_FSTYPE opt_md.h MD_ROOT_READONLY opt_md.h MD_ROOT_SIZE opt_md.h MD_ROOT_MEM opt_md.h MFI_DEBUG opt_mfi.h MFI_DECODE_LOG opt_mfi.h MPROF_BUFFERS opt_mprof.h MPROF_HASH_SIZE opt_mprof.h NO_ADAPTIVE_MUTEXES opt_adaptive_mutexes.h NO_ADAPTIVE_RWLOCKS NO_ADAPTIVE_SX NO_OBSOLETE_CODE opt_global.h NO_SYSCTL_DESCR opt_global.h NSWBUF_MIN opt_param.h MBUF_PACKET_ZONE_DISABLE opt_global.h PANIC_REBOOT_WAIT_TIME opt_panic.h PCI_HP opt_pci.h PCI_IOV opt_global.h PPC_DEBUG opt_ppc.h PPC_PROBE_CHIPSET opt_ppc.h PPS_SYNC opt_ntp.h PREEMPTION opt_sched.h QUOTA SCHED_4BSD opt_sched.h SCHED_STATS opt_sched.h SCHED_ULE opt_sched.h SLEEPQUEUE_PROFILING SLHCI_DEBUG opt_slhci.h SOCKET_HHOOK opt_global.h STACK opt_stack.h SUIDDIR MSGMNB opt_sysvipc.h MSGMNI opt_sysvipc.h MSGSEG opt_sysvipc.h MSGSSZ opt_sysvipc.h MSGTQL opt_sysvipc.h SEMMNI opt_sysvipc.h SEMMNS opt_sysvipc.h SEMMNU opt_sysvipc.h SEMMSL opt_sysvipc.h SEMOPM opt_sysvipc.h SEMUME opt_sysvipc.h SHMALL opt_sysvipc.h SHMMAX opt_sysvipc.h SHMMAXPGS opt_sysvipc.h SHMMIN opt_sysvipc.h SHMMNI opt_sysvipc.h SHMSEG opt_sysvipc.h SYSVMSG opt_sysvipc.h SYSVSEM opt_sysvipc.h SYSVSHM opt_sysvipc.h SW_WATCHDOG opt_watchdog.h TCPHPTS TCP_HPTS_KTEST opt_inet.h TCP_REQUEST_TRK opt_global.h TCP_ACCOUNTING opt_global.h TCP_BBR opt_inet.h TCP_RACK opt_inet.h TURNSTILE_PROFILING UMTX_PROFILING UMTX_CHAINS opt_global.h VERBOSE_SYSINIT ZSTDIO opt_zstdio.h # Sanitizers COVERAGE opt_global.h KASAN opt_global.h KCOV KCSAN opt_global.h KMSAN opt_global.h KUBSAN opt_global.h # POSIX kernel options P1003_1B_MQUEUE opt_posix.h P1003_1B_SEMAPHORES opt_posix.h _KPOSIX_PRIORITY_SCHEDULING opt_posix.h # Do we want the config file compiled into the kernel? INCLUDE_CONFIG_FILE opt_config.h # Options for static filesystems. These should only be used at config # time, since the corresponding lkms cannot work if there are any static # dependencies. Unusability is enforced by hiding the defines for the # options in a never-included header. AUTOFS opt_dontuse.h CD9660 opt_dontuse.h EXT2FS opt_dontuse.h FDESCFS opt_dontuse.h FFS opt_dontuse.h FUSEFS opt_dontuse.h MSDOSFS opt_dontuse.h NULLFS opt_dontuse.h PROCFS opt_dontuse.h PSEUDOFS opt_dontuse.h SMBFS opt_dontuse.h TARFS opt_dontuse.h TMPFS opt_dontuse.h UDF opt_dontuse.h UNIONFS opt_dontuse.h ZFS opt_dontuse.h P9FS opt_dontuse.h # Pseudofs debugging PSEUDOFS_TRACE opt_pseudofs.h # Tarfs debugging TARFS_DEBUG opt_tarfs.h # In-kernel GSS-API KGSSAPI opt_kgssapi.h # These static filesystems have one slightly bogus static dependency in # sys/i386/i386/autoconf.c. If any of these filesystems are # statically compiled into the kernel, code for mounting them as root # filesystems will be enabled - but look below. # NFSCL - client # NFSD - server NFSCL opt_nfs.h NFSD opt_nfs.h # filesystems and libiconv bridge CD9660_ICONV opt_dontuse.h MSDOSFS_ICONV opt_dontuse.h UDF_ICONV opt_dontuse.h # If you are following the conditions in the copyright, # you can enable soft-updates which will speed up a lot of thigs # and make the system safer from crashes at the same time. # otherwise a STUB module will be compiled in. SOFTUPDATES opt_ffs.h # On small, embedded systems, it can be useful to turn off support for # snapshots. It saves about 30-40k for a feature that would be lightly # used, if it is used at all. NO_FFS_SNAPSHOT opt_ffs.h # Enabling this option turns on support for Access Control Lists in UFS, # which can be used to support high security configurations. Depends on # UFS_EXTATTR. UFS_ACL opt_ufs.h # Enabling this option turns on support for extended attributes in UFS-based # filesystems, which can be used to support high security configurations # as well as new filesystem features. UFS_EXTATTR opt_ufs.h UFS_EXTATTR_AUTOSTART opt_ufs.h # Enable fast hash lookups for large directories on UFS-based filesystems UFS_DIRHASH opt_ufs.h # Enable gjournal-based UFS journal UFS_GJOURNAL opt_ufs.h # The below sentence is not in English, and neither is this one. # We plan to remove the static dependences above, with a # _ROOT option to control if it usable as root. This list # allows these options to be present in config files already (though # they won't make any difference yet). NFS_ROOT opt_nfsroot.h # SMB/CIFS requester NETSMB opt_netsmb.h # Enable debugnet(4) networking support DEBUGNET opt_global.h # Enable netdump(4) client support NETDUMP opt_global.h # Enable netgdb(4) support NETGDB opt_global.h # Options used only in subr_param.c HZ opt_param.h MAXFILES opt_param.h NBUF opt_param.h NSFBUFS opt_param.h VM_BCACHE_SIZE_MAX opt_param.h VM_SWZONE_SIZE_MAX opt_param.h MAXUSERS DFLDSIZ opt_param.h MAXDSIZ opt_param.h MAXSSIZ opt_param.h # Generic SCSI options CAM_MAX_HIGHPOWER opt_cam.h CAMDEBUG opt_cam.h CAM_DEBUG_COMPILE opt_cam.h CAM_DEBUG_DELAY opt_cam.h CAM_DEBUG_BUS opt_cam.h CAM_DEBUG_TARGET opt_cam.h CAM_DEBUG_LUN opt_cam.h CAM_DEBUG_FLAGS opt_cam.h CAM_BOOT_DELAY opt_cam.h CAM_IOSCHED_DYNAMIC opt_cam.h CAM_IO_STATS opt_cam.h CAM_TEST_FAILURE opt_cam.h SCSI_DELAY opt_scsi.h SCSI_NO_SENSE_STRINGS opt_scsi.h SCSI_NO_OP_STRINGS opt_scsi.h # Options used only in cam/ata/ata_da.c ATA_STATIC_ID opt_ada.h # Options used only in cam/scsi/scsi_cd.c CHANGER_MIN_BUSY_SECONDS opt_cd.h CHANGER_MAX_BUSY_SECONDS opt_cd.h # Options used only in cam/scsi/scsi_da.c DA_TRACK_REFS opt_da.h # Options used only in cam/scsi/scsi_sa.c SA_IO_TIMEOUT opt_sa.h SA_SPACE_TIMEOUT opt_sa.h SA_REWIND_TIMEOUT opt_sa.h SA_ERASE_TIMEOUT opt_sa.h SA_1FM_AT_EOD opt_sa.h # Options used only in cam/scsi/scsi_pt.c SCSI_PT_DEFAULT_TIMEOUT opt_pt.h # Options used only in cam/scsi/scsi_ses.c SES_ENABLE_PASSTHROUGH opt_ses.h # Options used in dev/sym/ (Symbios SCSI driver) SYM_SETUP_SCSI_DIFF opt_sym.h #-HVD support for 825a, 875, 885 # disabled:0 (default), enabled:1 SYM_SETUP_PCI_PARITY opt_sym.h #-PCI parity checking # disabled:0, enabled:1 (default) SYM_SETUP_MAX_LUN opt_sym.h #-Number of LUNs supported # default:8, range:[1..64] # Options used only in dev/isp/* ISP_TARGET_MODE opt_isp.h ISP_FW_CRASH_DUMP opt_isp.h ISP_DEFAULT_ROLES opt_isp.h ISP_INTERNAL_TARGET opt_isp.h ISP_FCTAPE_OFF opt_isp.h # Net stuff ACCEPT_FILTER_DATA ACCEPT_FILTER_DNS ACCEPT_FILTER_HTTP ACCEPT_FILTER_TLS ALTQ opt_global.h ALTQ_CBQ opt_altq.h ALTQ_CDNR opt_altq.h ALTQ_CODEL opt_altq.h ALTQ_DEBUG opt_altq.h ALTQ_HFSC opt_altq.h ALTQ_FAIRQ opt_altq.h ALTQ_NOPCC opt_altq.h ALTQ_PRIQ opt_altq.h ALTQ_RED opt_altq.h ALTQ_RIO opt_altq.h BOOTP opt_bootp.h BOOTP_BLOCKSIZE opt_bootp.h BOOTP_COMPAT opt_bootp.h BOOTP_NFSROOT opt_bootp.h BOOTP_NFSV3 opt_bootp.h BOOTP_WIRED_TO opt_bootp.h DEVICE_POLLING DUMMYNET opt_ipdn.h RATELIMIT opt_ratelimit.h RATELIMIT_DEBUG opt_ratelimit.h INET opt_inet.h INET6 opt_inet6.h STATS opt_global.h IPDIVERT IPFILTER opt_ipfilter.h IPFILTER_DEFAULT_BLOCK opt_ipfilter.h IPFILTER_LOG opt_ipfilter.h IPFILTER_LOOKUP opt_ipfilter.h IPFILTER_IPFS opt_ipfilter.h IPFIREWALL opt_ipfw.h IPFIREWALL_DEFAULT_TO_ACCEPT opt_ipfw.h IPFIREWALL_NAT opt_ipfw.h IPFIREWALL_NAT64 opt_ipfw.h IPFIREWALL_NPTV6 opt_ipfw.h IPFIREWALL_VERBOSE opt_ipfw.h IPFIREWALL_VERBOSE_LIMIT opt_ipfw.h IPFIREWALL_PMOD opt_ipfw.h IPSEC opt_ipsec.h IPSEC_DEBUG opt_ipsec.h IPSEC_SUPPORT opt_ipsec.h IPSEC_OFFLOAD opt_ipsec.h IPSTEALTH KERN_TLS KRPC LIBALIAS LIBMCHAIN MBUF_PROFILING MBUF_STRESS_TEST MROUTING opt_mrouting.h NFSLOCKD NETLINK opt_global.h PF_DEFAULT_TO_DROP opt_pf.h ROUTETABLES opt_route.h FIB_ALGO opt_route.h RSS opt_rss.h SLIP_IFF_OPTS opt_slip.h SIFTR TCP_BLACKBOX opt_global.h TCP_HHOOK opt_global.h TCP_OFFLOAD opt_inet.h # Enable code to dispatch TCP offloading TCP_RFC7413 opt_inet.h TCP_RFC7413_MAX_KEYS opt_inet.h TCP_RFC7413_MAX_PSKS opt_inet.h TCP_SIGNATURE opt_ipsec.h VLAN_ARRAY opt_vlan.h XDR XBONEHACK # # SCTP # SCTP opt_sctp.h SCTP_SUPPORT opt_sctp.h SCTP_DEBUG opt_sctp.h # Enable debug printfs SCTP_LOCK_LOGGING opt_sctp.h # Log to KTR lock activity SCTP_MBUF_LOGGING opt_sctp.h # Log to KTR general mbuf aloc/free SCTP_MBCNT_LOGGING opt_sctp.h # Log to KTR mbcnt activity SCTP_PACKET_LOGGING opt_sctp.h # Log to a packet buffer last N packets SCTP_LTRACE_CHUNKS opt_sctp.h # Log to KTR chunks processed SCTP_LTRACE_ERRORS opt_sctp.h # Log to KTR error returns SCTP_USE_PERCPU_STAT opt_sctp.h # Use per cpu stats SCTP_MCORE_INPUT opt_sctp.h # Have multiple input threads for input mbufs SCTP_LOCAL_TRACE_BUF opt_sctp.h # Use tracebuffer exported via sysctl SCTP_DETAILED_STR_STATS opt_sctp.h # Use per PR-SCTP policy stream stats # # # # Netgraph(4). Use option NETGRAPH to enable the base netgraph code. # Each netgraph node type can be either be compiled into the kernel # or loaded dynamically. To get the former, include the corresponding # option below. Each type has its own man page, e.g. ng_async(4). NETGRAPH NETGRAPH_DEBUG opt_netgraph.h NETGRAPH_ASYNC opt_netgraph.h NETGRAPH_BLUETOOTH opt_netgraph.h NETGRAPH_BLUETOOTH_BT3C opt_netgraph.h NETGRAPH_BLUETOOTH_H4 opt_netgraph.h NETGRAPH_BLUETOOTH_HCI opt_netgraph.h NETGRAPH_BLUETOOTH_L2CAP opt_netgraph.h NETGRAPH_BLUETOOTH_SOCKET opt_netgraph.h NETGRAPH_BLUETOOTH_UBT opt_netgraph.h NETGRAPH_BLUETOOTH_UBTBCMFW opt_netgraph.h NETGRAPH_BPF opt_netgraph.h NETGRAPH_BRIDGE opt_netgraph.h NETGRAPH_CAR opt_netgraph.h NETGRAPH_CHECKSUM opt_netgraph.h NETGRAPH_CISCO opt_netgraph.h NETGRAPH_DEFLATE opt_netgraph.h NETGRAPH_DEVICE opt_netgraph.h NETGRAPH_ECHO opt_netgraph.h NETGRAPH_EIFACE opt_netgraph.h NETGRAPH_ETHER opt_netgraph.h NETGRAPH_ETHER_ECHO opt_netgraph.h NETGRAPH_FEC opt_netgraph.h NETGRAPH_FRAME_RELAY opt_netgraph.h NETGRAPH_GIF opt_netgraph.h NETGRAPH_GIF_DEMUX opt_netgraph.h NETGRAPH_HOLE opt_netgraph.h NETGRAPH_IFACE opt_netgraph.h NETGRAPH_IP_INPUT opt_netgraph.h NETGRAPH_IPFW opt_netgraph.h NETGRAPH_KSOCKET opt_netgraph.h NETGRAPH_L2TP opt_netgraph.h NETGRAPH_LMI opt_netgraph.h NETGRAPH_MPPC_COMPRESSION opt_netgraph.h NETGRAPH_MPPC_ENCRYPTION opt_netgraph.h NETGRAPH_NAT opt_netgraph.h NETGRAPH_NETFLOW opt_netgraph.h NETGRAPH_ONE2MANY opt_netgraph.h NETGRAPH_PATCH opt_netgraph.h NETGRAPH_PIPE opt_netgraph.h NETGRAPH_PPP opt_netgraph.h NETGRAPH_PPPOE opt_netgraph.h NETGRAPH_PPTPGRE opt_netgraph.h NETGRAPH_PRED1 opt_netgraph.h NETGRAPH_RFC1490 opt_netgraph.h NETGRAPH_SOCKET opt_netgraph.h NETGRAPH_SPLIT opt_netgraph.h NETGRAPH_SPPP opt_netgraph.h NETGRAPH_TAG opt_netgraph.h NETGRAPH_TCPMSS opt_netgraph.h NETGRAPH_TEE opt_netgraph.h NETGRAPH_TTY opt_netgraph.h NETGRAPH_UI opt_netgraph.h NETGRAPH_VJC opt_netgraph.h NETGRAPH_VLAN opt_netgraph.h # DRM options DRM_DEBUG opt_drm.h TI_SF_BUF_JUMBO opt_ti.h TI_JUMBO_HDRSPLIT opt_ti.h # Misc debug flags. Most of these should probably be replaced with # 'DEBUG', and then let people recompile just the interesting modules # with 'make CC="cc -DDEBUG"'. DEBUG_1284 opt_ppb_1284.h LPT_DEBUG opt_lpt.h PLIP_DEBUG opt_plip.h LOCKF_DEBUG opt_debug_lockf.h SI_DEBUG opt_debug_si.h IFMEDIA_DEBUG opt_ifmedia.h # Fb options FB_DEBUG opt_fb.h # ppbus related options PERIPH_1284 opt_ppb_1284.h DONTPROBE_1284 opt_ppb_1284.h # smbus related options ENABLE_ALART opt_intpm.h # These cause changes all over the kernel BLKDEV_IOSIZE opt_global.h BURN_BRIDGES opt_global.h DEBUG opt_global.h DEBUG_LOCKS opt_global.h DFLTPHYS opt_global.h DIAGNOSTIC opt_global.h INVARIANT_SUPPORT opt_global.h INVARIANTS opt_global.h KASSERT_PANIC_OPTIONAL opt_global.h MAXCPU opt_global.h MAXMEMDOM opt_global.h MAXPHYS opt_maxphys.h MCLSHIFT opt_global.h MUTEX_NOINLINE opt_global.h LOCK_PROFILING opt_global.h MSIZE opt_global.h REGRESSION opt_global.h RWLOCK_NOINLINE opt_global.h SX_NOINLINE opt_global.h VFS_BIO_DEBUG opt_global.h # These are VM related options VM_KMEM_SIZE opt_vm.h VM_KMEM_SIZE_SCALE opt_vm.h VM_KMEM_SIZE_MAX opt_vm.h VM_NRESERVLEVEL opt_vm.h VM_LEVEL_0_ORDER opt_vm.h MALLOC_MAKE_FAILURES opt_vm.h MALLOC_PROFILE opt_vm.h MALLOC_DEBUG_MAXZONES opt_vm.h # The MemGuard replacement allocator used for tamper-after-free detection DEBUG_MEMGUARD opt_vm.h # The RedZone malloc(9) protection DEBUG_REDZONE opt_vm.h # Standard SMP options EARLY_AP_STARTUP opt_global.h SMP opt_global.h NUMA opt_global.h # Size of the kernel message buffer MSGBUF_SIZE opt_msgbuf.h # NFS options NFS_MINATTRTIMO opt_nfs.h NFS_MAXATTRTIMO opt_nfs.h NFS_MINDIRATTRTIMO opt_nfs.h NFS_MAXDIRATTRTIMO opt_nfs.h NFS_DEBUG opt_nfs.h # TMPFS options TMPFS_PAGES_MINRESERVED opt_tmpfs.h # Options for uart(4) UART_PPS_ON_CTS opt_uart.h UART_POLL_FREQ opt_uart.h UART_DEV_TOLERANCE_PCT opt_uart.h # options for bus/device framework BUS_DEBUG opt_bus.h # options for USB support USB_DEBUG opt_usb.h USB_HOST_ALIGN opt_usb.h USB_REQ_DEBUG opt_usb.h USB_TEMPLATE opt_usb.h USB_VERBOSE opt_usb.h USB_DMA_SINGLE_ALLOC opt_usb.h USB_EHCI_BIG_ENDIAN_DESC opt_usb.h U3G_DEBUG opt_u3g.h UKBD_DFLT_KEYMAP opt_ukbd.h UPLCOM_INTR_INTERVAL opt_uplcom.h UVSCOM_DEFAULT_OPKTSIZE opt_uvscom.h UVSCOM_INTR_INTERVAL opt_uvscom.h # options for the Realtek rtwn driver RTWN_DEBUG opt_rtwn.h RTWN_WITHOUT_UCODE opt_rtwn.h # Embedded system options INIT_PATH ROOTDEVNAME FDC_DEBUG opt_fdc.h PCFCLOCK_VERBOSE opt_pcfclock.h PCFCLOCK_MAX_RETRIES opt_pcfclock.h KTR opt_global.h KTR_ALQ opt_ktr.h KTR_MASK opt_ktr.h KTR_CPUMASK opt_ktr.h KTR_COMPILE opt_global.h KTR_BOOT_ENTRIES opt_global.h KTR_ENTRIES opt_global.h KTR_VERBOSE opt_ktr.h WITNESS opt_global.h WITNESS_KDB opt_witness.h WITNESS_NO_VNODE opt_witness.h WITNESS_SKIPSPIN opt_witness.h WITNESS_COUNT opt_witness.h OPENSOLARIS_WITNESS opt_global.h EPOCH_TRACE opt_global.h # options for ACPI support ACPI_DEBUG opt_acpi.h ACPI_MAX_TASKS opt_acpi.h ACPI_MAX_THREADS opt_acpi.h DEV_ACPI opt_acpi.h ACPI_EARLY_EPYC_WAR opt_acpi.h # options for IOMMU support IOMMU opt_iommu.h # ISA support DEV_ISA opt_isa.h ISAPNP opt_dontuse.h # various 'device presence' options DEV_BPF opt_bpf.h DEV_CARP opt_carp.h DEV_NETMAP opt_global.h DEV_PCI opt_pci.h DEV_PF opt_pf.h DEV_PFLOG opt_pf.h DEV_PFSYNC opt_pf.h DEV_SPLASH opt_splash.h DEV_VLAN opt_vlan.h # bce driver BCE_DEBUG opt_bce.h BCE_NVRAM_WRITE_SUPPORT opt_bce.h SOCKBUF_DEBUG opt_global.h # options for safenet driver SAFE_DEBUG opt_safe.h SAFE_NO_RNG opt_safe.h SAFE_RNDTEST opt_safe.h # syscons/vt options MAXCONS opt_syscons.h SC_ALT_MOUSE_IMAGE opt_syscons.h SC_CUT_SPACES2TABS opt_syscons.h SC_CUT_SEPCHARS opt_syscons.h SC_DEBUG_LEVEL opt_syscons.h SC_DFLT_FONT opt_syscons.h SC_DFLT_TERM opt_syscons.h SC_DISABLE_KDBKEY opt_syscons.h SC_DISABLE_REBOOT opt_syscons.h SC_HISTORY_SIZE opt_syscons.h SC_KERNEL_CONS_ATTR opt_syscons.h SC_KERNEL_CONS_ATTRS opt_syscons.h SC_KERNEL_CONS_REV_ATTR opt_syscons.h SC_MOUSE_CHAR opt_syscons.h SC_NO_CUTPASTE opt_syscons.h SC_NO_FONT_LOADING opt_syscons.h SC_NO_HISTORY opt_syscons.h SC_NO_MODE_CHANGE opt_syscons.h SC_NO_SUSPEND_VTYSWITCH opt_syscons.h SC_NO_SYSMOUSE opt_syscons.h SC_NO_TERM_DUMB opt_syscons.h SC_NO_TERM_SC opt_syscons.h SC_NO_TERM_TEKEN opt_syscons.h SC_NORM_ATTR opt_syscons.h SC_NORM_REV_ATTR opt_syscons.h SC_PIXEL_MODE opt_syscons.h SC_RENDER_DEBUG opt_syscons.h SC_TWOBUTTON_MOUSE opt_syscons.h VT_ALT_TO_ESC_HACK opt_syscons.h VT_FB_MAX_WIDTH opt_syscons.h VT_FB_MAX_HEIGHT opt_syscons.h VT_MAXWINDOWS opt_syscons.h VT_TWOBUTTON_MOUSE opt_syscons.h DEV_SC opt_syscons.h DEV_VT opt_syscons.h # teken terminal emulator options TEKEN_CONS25 opt_teken.h TEKEN_UTF8 opt_teken.h TERMINAL_KERN_ATTR opt_teken.h TERMINAL_NORM_ATTR opt_teken.h # options for printf PRINTF_BUFR_SIZE opt_printf.h BOOT_TAG opt_printf.h BOOT_TAG_SZ opt_printf.h # kbd options KBD_DISABLE_KEYMAP_LOAD opt_kbd.h KBD_INSTALL_CDEV opt_kbd.h KBD_MAXRETRY opt_kbd.h KBD_MAXWAIT opt_kbd.h KBD_RESETDELAY opt_kbd.h KBD_DELAY1 opt_kbd.h KBD_DELAY2 opt_kbd.h KBDIO_DEBUG opt_kbd.h KBDMUX_DFLT_KEYMAP opt_kbdmux.h # options for the Atheros driver ATH_DEBUG opt_ath.h ATH_TXBUF opt_ath.h ATH_RXBUF opt_ath.h ATH_DIAGAPI opt_ath.h ATH_TX99_DIAG opt_ath.h ATH_ENABLE_DFS opt_ath.h ATH_EEPROM_FIRMWARE opt_ath.h ATH_ENABLE_RADIOTAP_VENDOR_EXT opt_ath.h ATH_DEBUG_ALQ opt_ath.h ATH_KTR_INTR_DEBUG opt_ath.h AH_DEBUG opt_ah.h AH_ASSERT opt_ah.h AH_DEBUG_ALQ opt_ah.h AH_REGOPS_FUNC opt_ah.h AH_WRITE_REGDOMAIN opt_ah.h AH_DEBUG_COUNTRY opt_ah.h AH_WRITE_EEPROM opt_ah.h AH_PRIVATE_DIAG opt_ah.h AH_NEED_DESC_SWAP opt_ah.h AH_USE_INIPDGAIN opt_ah.h AH_MAXCHAN opt_ah.h AH_RXCFG_SDMAMW_4BYTES opt_ah.h AH_INTERRUPT_DEBUGGING opt_ah.h # AR5416 and later interrupt mitigation # XXX do not use this for AR9130 AH_AR5416_INTERRUPT_MITIGATION opt_ah.h # options for the Broadcom BCM43xx driver (bwi) BWI_DEBUG opt_bwi.h BWI_DEBUG_VERBOSE opt_bwi.h # options for the Brodacom BCM43xx driver (bwn) BWN_DEBUG opt_bwn.h BWN_GPL_PHY opt_bwn.h BWN_USE_SIBA opt_bwn.h # Options for the SIBA driver SIBA_DEBUG opt_siba.h # options for the Marvell 8335 wireless driver MALO_DEBUG opt_malo.h MALO_TXBUF opt_malo.h MALO_RXBUF opt_malo.h # options for the Marvell wireless driver MWL_DEBUG opt_mwl.h MWL_TXBUF opt_mwl.h MWL_RXBUF opt_mwl.h MWL_DIAGAPI opt_mwl.h MWL_AGGR_SIZE opt_mwl.h MWL_TX_NODROP opt_mwl.h # Options for the Marvell NETA driver MVNETA_MULTIQUEUE opt_mvneta.h MVNETA_KTR opt_mvneta.h # Options for the Intel 802.11ac wireless driver IWM_DEBUG opt_iwm.h # Options for the Intel 802.11n wireless driver IWN_DEBUG opt_iwn.h # Options for the Intel 3945ABG wireless driver WPI_DEBUG opt_wpi.h # dcons options DCONS_BUF_SIZE opt_dcons.h DCONS_POLL_HZ opt_dcons.h DCONS_FORCE_CONSOLE opt_dcons.h DCONS_FORCE_GDB opt_dcons.h # HWPMC options HWPMC_DEBUG opt_global.h HWPMC_HOOKS # Hardware Trace (HWT) framework options HWT_HOOKS # 802.11 support layer IEEE80211_DEBUG opt_wlan.h IEEE80211_DEBUG_REFCNT opt_wlan.h IEEE80211_SUPPORT_MESH opt_wlan.h IEEE80211_SUPPORT_SUPERG opt_wlan.h IEEE80211_SUPPORT_TDMA opt_wlan.h IEEE80211_ALQ opt_wlan.h IEEE80211_DFS_DEBUG opt_wlan.h # 802.11 TDMA support TDMA_SLOTLEN_DEFAULT opt_tdma.h TDMA_SLOTCNT_DEFAULT opt_tdma.h TDMA_BINTVAL_DEFAULT opt_tdma.h TDMA_TXRATE_11B_DEFAULT opt_tdma.h TDMA_TXRATE_11G_DEFAULT opt_tdma.h TDMA_TXRATE_11A_DEFAULT opt_tdma.h TDMA_TXRATE_TURBO_DEFAULT opt_tdma.h TDMA_TXRATE_HALF_DEFAULT opt_tdma.h TDMA_TXRATE_QUARTER_DEFAULT opt_tdma.h TDMA_TXRATE_11NA_DEFAULT opt_tdma.h TDMA_TXRATE_11NG_DEFAULT opt_tdma.h # VideoMode PICKMODE_DEBUG opt_videomode.h # Network stack virtualization options VIMAGE opt_global.h VNET_DEBUG opt_global.h # Common Flash Interface (CFI) options CFI_SUPPORT_STRATAFLASH opt_cfi.h CFI_ARMEDANDDANGEROUS opt_cfi.h CFI_HARDWAREBYTESWAP opt_cfi.h # Sound options SND_DIAGNOSTIC opt_snd.h SND_FEEDER_RATE_HP opt_snd.h SND_PCM_64 opt_snd.h X86BIOS # Flattened device tree options FDT opt_platform.h FDT_DTB_STATIC opt_platform.h # OFED Infiniband stack OFED opt_ofed.h OFED_DEBUG_INIT opt_ofed.h SDP opt_ofed.h SDP_DEBUG opt_ofed.h IPOIB opt_ofed.h IPOIB_DEBUG opt_ofed.h IPOIB_CM opt_ofed.h # Resource Accounting RACCT opt_global.h RACCT_DEFAULT_TO_DISABLED opt_global.h # Resource Limits RCTL opt_global.h # Random number generator(s) # Alternative RNG algorithm RANDOM_FENESTRASX opt_global.h # With this, no entropy processor is loaded, but the entropy # harvesting infrastructure is present. This means an entropy # processor may be loaded as a module. RANDOM_LOADABLE opt_global.h # This turns on high-rate and potentially expensive harvesting in # the uma slab allocator. RANDOM_ENABLE_UMA opt_global.h RANDOM_ENABLE_ETHER opt_global.h RANDOM_ENABLE_KBD opt_global.h RANDOM_ENABLE_MOUSE opt_global.h RANDOM_ENABLE_TPM opt_global.h # This options turns TPM into entropy source. TPM_HARVEST opt_tpm.h # BHND(4) driver BHND_LOGLEVEL opt_global.h # GPIO and child devices GPIO_SPI_DEBUG opt_gpio.h # SPI devices SPIGEN_LEGACY_CDEVNAME opt_spi.h # etherswitch(4) driver RTL8366_SOFT_RESET opt_etherswitch.h # evdev protocol support EVDEV_SUPPORT opt_evdev.h EVDEV_DEBUG opt_evdev.h UINPUT_DEBUG opt_evdev.h # Hyper-V network driver HN_DEBUG opt_hn.h # CAM-based MMC stack MMCCAM # Encrypted kernel crash dumps EKCD opt_ekcd.h # NVME options NVME_USE_NVD opt_nvme.h # amdsbwd options AMDSBWD_DEBUG opt_amdsbwd.h # gcov support GCOV opt_global.h LINDEBUGFS # options for HID support HID_DEBUG opt_hid.h IICHID_DEBUG opt_hid.h IICHID_SAMPLING opt_hid.h HKBD_DFLT_KEYMAP opt_hkbd.h HIDRAW_MAKE_UHID_ALIAS opt_hid.h U2F_DROP_UHID_ALIAS opt_hid.h # kenv options # The early kernel environment (loader environment, config(8)-provided static) # is typically cleared after the dynamic environment comes up to ensure that # we're not inadvertently holding on to 'secret' values in these stale envs. # This option is insecure except in controlled environments where the static # environment's contents are known to be safe. PRESERVE_EARLY_KENV opt_global.h # Options for the Intel QuickAssist (QAT) driver QAT_DISABLE_SAFE_DC_MODE opt_qat.h # EARLY_PRINTF specific options fo NS8250 uart UART_NS8250_EARLY_REG_IO_WIDTH opt_uart.h UART_NS8250_EARLY_REG_SHIFT opt_uart.h diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 812a31595df9..bbaf798b49f2 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -1,1498 +1,1459 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1982, 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "opt_inet.h" #include "opt_inet6.h" #include "opt_netgraph.h" #include "opt_mbuf_profiling.h" #include "opt_rss.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef KDB #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(INET) || defined(INET6) #include #include #include #include #include #endif #ifdef INET6 #include #endif #include #include VNET_DEFINE(pfil_head_t, link_pfil_head); /* Packet filter hooks */ /* netgraph node hooks for ng_ether(4) */ void (*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp); void (*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m); int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp); /* if_bridge(4) support */ void (*bridge_dn_p)(struct mbuf *, struct ifnet *); bool (*bridge_same_p)(const void *, const void *); void *(*bridge_get_softc_p)(struct ifnet *); bool (*bridge_member_ifaddrs_p)(void); /* if_lagg(4) support */ struct mbuf *(*lagg_input_ethernet_p)(struct ifnet *, struct mbuf *); static const u_char etherbroadcastaddr[ETHER_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static int ether_resolvemulti(struct ifnet *, struct sockaddr **, struct sockaddr *); static int ether_requestencap(struct ifnet *, struct if_encap_req *); static inline bool ether_do_pcp(struct ifnet *, struct mbuf *); #define senderr(e) do { error = (e); goto bad;} while (0) static void update_mbuf_csumflags(struct mbuf *src, struct mbuf *dst) { int csum_flags = 0; if (src->m_pkthdr.csum_flags & CSUM_IP) csum_flags |= (CSUM_IP_CHECKED|CSUM_IP_VALID); if (src->m_pkthdr.csum_flags & CSUM_DELAY_DATA) csum_flags |= (CSUM_DATA_VALID|CSUM_PSEUDO_HDR); if (src->m_pkthdr.csum_flags & CSUM_SCTP) csum_flags |= CSUM_SCTP_VALID; dst->m_pkthdr.csum_flags |= csum_flags; if (csum_flags & CSUM_DATA_VALID) dst->m_pkthdr.csum_data = 0xffff; } /* * Handle link-layer encapsulation requests. */ static int ether_requestencap(struct ifnet *ifp, struct if_encap_req *req) { struct ether_header *eh; struct arphdr *ah; uint16_t etype; const u_char *lladdr; if (req->rtype != IFENCAP_LL) return (EOPNOTSUPP); if (req->bufsize < ETHER_HDR_LEN) return (ENOMEM); eh = (struct ether_header *)req->buf; lladdr = req->lladdr; req->lladdr_off = 0; switch (req->family) { case AF_INET: etype = htons(ETHERTYPE_IP); break; case AF_INET6: etype = htons(ETHERTYPE_IPV6); break; case AF_ARP: ah = (struct arphdr *)req->hdata; ah->ar_hrd = htons(ARPHRD_ETHER); switch(ntohs(ah->ar_op)) { case ARPOP_REVREQUEST: case ARPOP_REVREPLY: etype = htons(ETHERTYPE_REVARP); break; case ARPOP_REQUEST: case ARPOP_REPLY: default: etype = htons(ETHERTYPE_ARP); break; } if (req->flags & IFENCAP_FLAG_BROADCAST) lladdr = ifp->if_broadcastaddr; break; default: return (EAFNOSUPPORT); } memcpy(&eh->ether_type, &etype, sizeof(eh->ether_type)); memcpy(eh->ether_dhost, lladdr, ETHER_ADDR_LEN); memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); req->bufsize = sizeof(struct ether_header); return (0); } static int ether_resolve_addr(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, struct route *ro, u_char *phdr, uint32_t *pflags, struct llentry **plle) { uint32_t lleflags = 0; int error = 0; #if defined(INET) || defined(INET6) struct ether_header *eh = (struct ether_header *)phdr; uint16_t etype; #endif if (plle) *plle = NULL; switch (dst->sa_family) { #ifdef INET case AF_INET: if ((m->m_flags & (M_BCAST | M_MCAST)) == 0) error = arpresolve(ifp, 0, m, dst, phdr, &lleflags, plle); else { if (m->m_flags & M_BCAST) memcpy(eh->ether_dhost, ifp->if_broadcastaddr, ETHER_ADDR_LEN); else { const struct in_addr *a; a = &(((const struct sockaddr_in *)dst)->sin_addr); ETHER_MAP_IP_MULTICAST(a, eh->ether_dhost); } etype = htons(ETHERTYPE_IP); memcpy(&eh->ether_type, &etype, sizeof(etype)); memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); } break; #endif #ifdef INET6 case AF_INET6: if ((m->m_flags & M_MCAST) == 0) { int af = RO_GET_FAMILY(ro, dst); error = nd6_resolve(ifp, LLE_SF(af, 0), m, dst, phdr, &lleflags, plle); } else { const struct in6_addr *a6; a6 = &(((const struct sockaddr_in6 *)dst)->sin6_addr); ETHER_MAP_IPV6_MULTICAST(a6, eh->ether_dhost); etype = htons(ETHERTYPE_IPV6); memcpy(&eh->ether_type, &etype, sizeof(etype)); memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); } break; #endif default: if_printf(ifp, "can't handle af%d\n", dst->sa_family); if (m != NULL) m_freem(m); return (EAFNOSUPPORT); } if (error == EHOSTDOWN) { if (ro != NULL && (ro->ro_flags & RT_HAS_GW) != 0) error = EHOSTUNREACH; } if (error != 0) return (error); *pflags = RT_MAY_LOOP; if (lleflags & LLE_IFADDR) *pflags |= RT_L2_ME; return (0); } /* * Ethernet output routine. * Encapsulate a packet of type family for the local net. * Use trailer local net encapsulation if enough data in first * packet leaves a multiple of 512 bytes of data in remainder. */ int ether_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, struct route *ro) { int error = 0; char linkhdr[ETHER_HDR_LEN], *phdr; struct ether_header *eh; struct pf_mtag *t; bool loop_copy; int hlen; /* link layer header length */ uint32_t pflags; struct llentry *lle = NULL; int addref = 0; phdr = NULL; pflags = 0; if (ro != NULL) { /* XXX BPF uses ro_prepend */ if (ro->ro_prepend != NULL) { phdr = ro->ro_prepend; hlen = ro->ro_plen; } else if (!(m->m_flags & (M_BCAST | M_MCAST))) { if ((ro->ro_flags & RT_LLE_CACHE) != 0) { lle = ro->ro_lle; if (lle != NULL && (lle->la_flags & LLE_VALID) == 0) { LLE_FREE(lle); lle = NULL; /* redundant */ ro->ro_lle = NULL; } if (lle == NULL) { /* if we lookup, keep cache */ addref = 1; } else /* * Notify LLE code that * the entry was used * by datapath. */ llentry_provide_feedback(lle); } if (lle != NULL) { phdr = lle->r_linkdata; hlen = lle->r_hdrlen; pflags = lle->r_flags; } } } #ifdef MAC error = mac_ifnet_check_transmit(ifp, m); if (error) senderr(error); #endif M_PROFILE(m); if (ifp->if_flags & IFF_MONITOR) senderr(ENETDOWN); if (!((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))) senderr(ENETDOWN); if (phdr == NULL) { /* No prepend data supplied. Try to calculate ourselves. */ phdr = linkhdr; hlen = ETHER_HDR_LEN; error = ether_resolve_addr(ifp, m, dst, ro, phdr, &pflags, addref ? &lle : NULL); if (addref && lle != NULL) ro->ro_lle = lle; if (error != 0) return (error == EWOULDBLOCK ? 0 : error); } if ((pflags & RT_L2_ME) != 0) { update_mbuf_csumflags(m, m); return (if_simloop(ifp, m, RO_GET_FAMILY(ro, dst), 0)); } loop_copy = (pflags & RT_MAY_LOOP) != 0; /* * Add local net header. If no space in first mbuf, * allocate another. * * Note that we do prepend regardless of RT_HAS_HEADER flag. * This is done because BPF code shifts m_data pointer * to the end of ethernet header prior to calling if_output(). */ M_PREPEND(m, hlen, M_NOWAIT); if (m == NULL) senderr(ENOBUFS); if ((pflags & RT_HAS_HEADER) == 0) { eh = mtod(m, struct ether_header *); memcpy(eh, phdr, hlen); } /* * If a simplex interface, and the packet is being sent to our * Ethernet address or a broadcast address, loopback a copy. * XXX To make a simplex device behave exactly like a duplex * device, we should copy in the case of sending to our own * ethernet address (thus letting the original actually appear * on the wire). However, we don't do that here for security * reasons and compatibility with the original behavior. */ if ((m->m_flags & M_BCAST) && loop_copy && (ifp->if_flags & IFF_SIMPLEX) && ((t = pf_find_mtag(m)) == NULL || !t->routed)) { struct mbuf *n; /* * Because if_simloop() modifies the packet, we need a * writable copy through m_dup() instead of a readonly * one as m_copy[m] would give us. The alternative would * be to modify if_simloop() to handle the readonly mbuf, * but performancewise it is mostly equivalent (trading * extra data copying vs. extra locking). * * XXX This is a local workaround. A number of less * often used kernel parts suffer from the same bug. * See PR kern/105943 for a proposed general solution. */ if ((n = m_dup(m, M_NOWAIT)) != NULL) { update_mbuf_csumflags(m, n); (void)if_simloop(ifp, n, RO_GET_FAMILY(ro, dst), hlen); } else if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); } /* * Bridges require special output handling. */ if (ifp->if_bridge) { BRIDGE_OUTPUT(ifp, m, error); return (error); } #if defined(INET) || defined(INET6) if (ifp->if_carp && (error = (*carp_output_p)(ifp, m, dst))) goto bad; #endif /* Handle ng_ether(4) processing, if any */ if (ifp->if_l2com != NULL) { KASSERT(ng_ether_output_p != NULL, ("ng_ether_output_p is NULL")); if ((error = (*ng_ether_output_p)(ifp, &m)) != 0) { bad: if (m != NULL) m_freem(m); return (error); } if (m == NULL) return (0); } /* Continue with link-layer output */ return ether_output_frame(ifp, m); } static bool ether_set_pcp(struct mbuf **mp, struct ifnet *ifp, uint8_t pcp) { struct ether_8021q_tag qtag; struct ether_header *eh; eh = mtod(*mp, struct ether_header *); if (eh->ether_type == htons(ETHERTYPE_VLAN) || eh->ether_type == htons(ETHERTYPE_QINQ)) { (*mp)->m_flags &= ~M_VLANTAG; return (true); } qtag.vid = 0; qtag.pcp = pcp; qtag.proto = ETHERTYPE_VLAN; if (ether_8021q_frame(mp, ifp, ifp, &qtag)) return (true); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return (false); } /* * Ethernet link layer output routine to send a raw frame to the device. * * This assumes that the 14 byte Ethernet header is present and contiguous * in the first mbuf (if BRIDGE'ing). */ int ether_output_frame(struct ifnet *ifp, struct mbuf *m) { if (ether_do_pcp(ifp, m) && !ether_set_pcp(&m, ifp, ifp->if_pcp)) return (0); if (PFIL_HOOKED_OUT(V_link_pfil_head)) switch (pfil_mbuf_out(V_link_pfil_head, &m, ifp, NULL)) { case PFIL_DROPPED: return (EACCES); case PFIL_CONSUMED: return (0); } -#ifdef EXPERIMENTAL -#if defined(INET6) && defined(INET) - /* draft-ietf-6man-ipv6only-flag */ - /* Catch ETHERTYPE_IP, and ETHERTYPE_[REV]ARP if we are v6-only. */ - if ((ifp->if_inet6->nd_flags & ND6_IFF_IPV6_ONLY_MASK) != 0) { - struct ether_header *eh; - - eh = mtod(m, struct ether_header *); - switch (ntohs(eh->ether_type)) { - case ETHERTYPE_IP: - case ETHERTYPE_ARP: - case ETHERTYPE_REVARP: - m_freem(m); - return (EAFNOSUPPORT); - /* NOTREACHED */ - break; - }; - } -#endif -#endif - /* * Queue message on interface, update output statistics if successful, * and start output if interface not yet active. * * If KMSAN is enabled, use it to verify that the data does not contain * any uninitialized bytes. */ kmsan_check_mbuf(m, "ether_output"); return ((ifp->if_transmit)(ifp, m)); } /* * Process a received Ethernet packet; the packet is in the * mbuf chain m with the ethernet header at the front. */ static void ether_input_internal(struct ifnet *ifp, struct mbuf *m) { struct ether_header *eh; u_short etype; if ((ifp->if_flags & IFF_UP) == 0) { m_freem(m); return; } #ifdef DIAGNOSTIC if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { if_printf(ifp, "discard frame at !IFF_DRV_RUNNING\n"); m_freem(m); return; } #endif if (__predict_false(m->m_len < ETHER_HDR_LEN)) { /* Drivers should pullup and ensure the mbuf is valid */ if_printf(ifp, "discard frame w/o leading ethernet " "header (len %d pkt len %d)\n", m->m_len, m->m_pkthdr.len); if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); m_freem(m); return; } eh = mtod(m, struct ether_header *); etype = ntohs(eh->ether_type); random_harvest_queue_ether(m, sizeof(*m)); -#ifdef EXPERIMENTAL -#if defined(INET6) && defined(INET) - /* draft-ietf-6man-ipv6only-flag */ - /* Catch ETHERTYPE_IP, and ETHERTYPE_[REV]ARP if we are v6-only. */ - if ((ifp->if_inet6->nd_flags & ND6_IFF_IPV6_ONLY_MASK) != 0) { - switch (etype) { - case ETHERTYPE_IP: - case ETHERTYPE_ARP: - case ETHERTYPE_REVARP: - m_freem(m); - return; - /* NOTREACHED */ - break; - }; - } -#endif -#endif - CURVNET_SET_QUIET(ifp->if_vnet); if (ETHER_IS_MULTICAST(eh->ether_dhost)) { if (ETHER_IS_BROADCAST(eh->ether_dhost)) m->m_flags |= M_BCAST; else m->m_flags |= M_MCAST; if_inc_counter(ifp, IFCOUNTER_IMCASTS, 1); } #ifdef MAC /* * Tag the mbuf with an appropriate MAC label before any other * consumers can get to it. */ mac_ifnet_create_mbuf(ifp, m); #endif /* * Give bpf a chance at the packet. */ ETHER_BPF_MTAP(ifp, m); if (!(ifp->if_capenable & IFCAP_HWSTATS)) if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); /* Allow monitor mode to claim this frame, after stats are updated. */ if (ifp->if_flags & IFF_MONITOR) { m_freem(m); CURVNET_RESTORE(); return; } /* Handle input from a lagg(4) port */ if (ifp->if_type == IFT_IEEE8023ADLAG) { KASSERT(lagg_input_ethernet_p != NULL, ("%s: if_lagg not loaded!", __func__)); m = (*lagg_input_ethernet_p)(ifp, m); if (m != NULL) ifp = m->m_pkthdr.rcvif; else { CURVNET_RESTORE(); return; } } /* * If the hardware did not process an 802.1Q tag, do this now, * to allow 802.1P priority frames to be passed to the main input * path correctly. */ if ((m->m_flags & M_VLANTAG) == 0 && ((etype == ETHERTYPE_VLAN) || (etype == ETHERTYPE_QINQ))) { struct ether_vlan_header *evl; if (m->m_len < sizeof(*evl) && (m = m_pullup(m, sizeof(*evl))) == NULL) { #ifdef DIAGNOSTIC if_printf(ifp, "cannot pullup VLAN header\n"); #endif if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); CURVNET_RESTORE(); return; } evl = mtod(m, struct ether_vlan_header *); m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag); m->m_flags |= M_VLANTAG; bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN, ETHER_HDR_LEN - ETHER_TYPE_LEN); m_adj(m, ETHER_VLAN_ENCAP_LEN); eh = mtod(m, struct ether_header *); } M_SETFIB(m, ifp->if_fib); /* Allow ng_ether(4) to claim this frame. */ if (ifp->if_l2com != NULL) { KASSERT(ng_ether_input_p != NULL, ("%s: ng_ether_input_p is NULL", __func__)); m->m_flags &= ~M_PROMISC; (*ng_ether_input_p)(ifp, &m); if (m == NULL) { CURVNET_RESTORE(); return; } eh = mtod(m, struct ether_header *); } /* * Allow if_bridge(4) to claim this frame. * * The BRIDGE_INPUT() macro will update ifp if the bridge changed it * and the frame should be delivered locally. * * If M_BRIDGE_INJECT is set, the packet was received directly by the * bridge via netmap, so "ifp" is the bridge itself and the packet * should be re-examined. */ if (ifp->if_bridge != NULL || (m->m_flags & M_BRIDGE_INJECT) != 0) { m->m_flags &= ~M_PROMISC; BRIDGE_INPUT(ifp, m); if (m == NULL) { CURVNET_RESTORE(); return; } eh = mtod(m, struct ether_header *); } #if defined(INET) || defined(INET6) /* * Clear M_PROMISC on frame so that carp(4) will see it when the * mbuf flows up to Layer 3. * FreeBSD's implementation of carp(4) uses the inprotosw * to dispatch IPPROTO_CARP. carp(4) also allocates its own * Ethernet addresses of the form 00:00:5e:00:01:xx, which * is outside the scope of the M_PROMISC test below. * TODO: Maintain a hash table of ethernet addresses other than * ether_dhost which may be active on this ifp. */ if (ifp->if_carp && (*carp_forus_p)(ifp, eh->ether_dhost)) { m->m_flags &= ~M_PROMISC; } else #endif { /* * If the frame received was not for our MAC address, set the * M_PROMISC flag on the mbuf chain. The frame may need to * be seen by the rest of the Ethernet input path in case of * re-entry (e.g. bridge, vlan, netgraph) but should not be * seen by upper protocol layers. */ if (!ETHER_IS_MULTICAST(eh->ether_dhost) && memcmp(IF_LLADDR(ifp), eh->ether_dhost, ETHER_ADDR_LEN) != 0) m->m_flags |= M_PROMISC; } ether_demux(ifp, m); CURVNET_RESTORE(); } /* * Ethernet input dispatch; by default, direct dispatch here regardless of * global configuration. However, if RSS is enabled, hook up RSS affinity * so that when deferred or hybrid dispatch is enabled, we can redistribute * load based on RSS. * * XXXRW: Would be nice if the ifnet passed up a flag indicating whether or * not it had already done work distribution via multi-queue. Then we could * direct dispatch in the event load balancing was already complete and * handle the case of interfaces with different capabilities better. * * XXXRW: Sort of want an M_DISTRIBUTED flag to avoid multiple distributions * at multiple layers? * * XXXRW: For now, enable all this only if RSS is compiled in, although it * works fine without RSS. Need to characterise the performance overhead * of the detour through the netisr code in the event the result is always * direct dispatch. */ static void ether_nh_input(struct mbuf *m) { M_ASSERTPKTHDR(m); KASSERT(m->m_pkthdr.rcvif != NULL, ("%s: NULL interface pointer", __func__)); ether_input_internal(m->m_pkthdr.rcvif, m); } static struct netisr_handler ether_nh = { .nh_name = "ether", .nh_handler = ether_nh_input, .nh_proto = NETISR_ETHER, #ifdef RSS .nh_policy = NETISR_POLICY_CPU, .nh_dispatch = NETISR_DISPATCH_DIRECT, .nh_m2cpuid = rss_m2cpuid, #else .nh_policy = NETISR_POLICY_SOURCE, .nh_dispatch = NETISR_DISPATCH_DIRECT, #endif }; static void ether_init(__unused void *arg) { netisr_register(ðer_nh); } SYSINIT(ether, SI_SUB_INIT_IF, SI_ORDER_ANY, ether_init, NULL); static void vnet_ether_init(const __unused void *arg) { struct pfil_head_args args; args.pa_version = PFIL_VERSION; args.pa_flags = PFIL_IN | PFIL_OUT; args.pa_type = PFIL_TYPE_ETHERNET; args.pa_headname = PFIL_ETHER_NAME; V_link_pfil_head = pfil_head_register(&args); #ifdef VIMAGE netisr_register_vnet(ðer_nh); #endif } VNET_SYSINIT(vnet_ether_init, SI_SUB_PROTO_IF, SI_ORDER_ANY, vnet_ether_init, NULL); #ifdef VIMAGE static void vnet_ether_pfil_destroy(const __unused void *arg) { pfil_head_unregister(V_link_pfil_head); } VNET_SYSUNINIT(vnet_ether_pfil_uninit, SI_SUB_PROTO_PFIL, SI_ORDER_ANY, vnet_ether_pfil_destroy, NULL); static void vnet_ether_destroy(__unused void *arg) { netisr_unregister_vnet(ðer_nh); } VNET_SYSUNINIT(vnet_ether_uninit, SI_SUB_PROTO_IF, SI_ORDER_ANY, vnet_ether_destroy, NULL); #endif static void ether_input(struct ifnet *ifp, struct mbuf *m) { struct epoch_tracker et; struct mbuf *mn; bool needs_epoch; needs_epoch = (ifp->if_flags & IFF_NEEDSEPOCH); #ifdef INVARIANTS /* * This temporary code is here to prevent epoch unaware and unmarked * drivers to panic the system. Once all drivers are taken care of, * the whole INVARIANTS block should go away. */ if (!needs_epoch && !in_epoch(net_epoch_preempt)) { static bool printedonce; needs_epoch = true; if (!printedonce) { printedonce = true; if_printf(ifp, "called %s w/o net epoch! " "PLEASE file a bug report.", __func__); #ifdef KDB kdb_backtrace(); #endif } } #endif /* * The drivers are allowed to pass in a chain of packets linked with * m_nextpkt. We split them up into separate packets here and pass * them up. This allows the drivers to amortize the receive lock. */ CURVNET_SET_QUIET(ifp->if_vnet); if (__predict_false(needs_epoch)) NET_EPOCH_ENTER(et); while (m) { mn = m->m_nextpkt; m->m_nextpkt = NULL; /* * We will rely on rcvif being set properly in the deferred * context, so assert it is correct here. */ MPASS((m->m_pkthdr.csum_flags & CSUM_SND_TAG) == 0); KASSERT(m->m_pkthdr.rcvif == ifp, ("%s: ifnet mismatch m %p " "rcvif %p ifp %p", __func__, m, m->m_pkthdr.rcvif, ifp)); netisr_dispatch(NETISR_ETHER, m); m = mn; } if (__predict_false(needs_epoch)) NET_EPOCH_EXIT(et); CURVNET_RESTORE(); } /* * Upper layer processing for a received Ethernet packet. */ void ether_demux(struct ifnet *ifp, struct mbuf *m) { struct ether_header *eh; int i, isr; u_short ether_type; NET_EPOCH_ASSERT(); KASSERT(ifp != NULL, ("%s: NULL interface pointer", __func__)); /* Do not grab PROMISC frames in case we are re-entered. */ if (PFIL_HOOKED_IN(V_link_pfil_head) && !(m->m_flags & M_PROMISC)) { i = pfil_mbuf_in(V_link_pfil_head, &m, ifp, NULL); if (i != PFIL_PASS) return; } eh = mtod(m, struct ether_header *); ether_type = ntohs(eh->ether_type); /* * If this frame has a VLAN tag other than 0, call vlan_input() * if its module is loaded. Otherwise, drop. */ if ((m->m_flags & M_VLANTAG) && EVL_VLANOFTAG(m->m_pkthdr.ether_vtag) != 0) { if (ifp->if_vlantrunk == NULL) { if_inc_counter(ifp, IFCOUNTER_NOPROTO, 1); m_freem(m); return; } KASSERT(vlan_input_p != NULL,("%s: VLAN not loaded!", __func__)); /* Clear before possibly re-entering ether_input(). */ m->m_flags &= ~M_PROMISC; (*vlan_input_p)(ifp, m); return; } /* * Pass promiscuously received frames to the upper layer if the user * requested this by setting IFF_PPROMISC. Otherwise, drop them. */ if ((ifp->if_flags & IFF_PPROMISC) == 0 && (m->m_flags & M_PROMISC)) { m_freem(m); return; } /* * Reset layer specific mbuf flags to avoid confusing upper layers. */ m->m_flags &= ~M_VLANTAG; m_clrprotoflags(m); /* * Dispatch frame to upper layer. */ switch (ether_type) { #ifdef INET case ETHERTYPE_IP: isr = NETISR_IP; break; case ETHERTYPE_ARP: if (ifp->if_flags & IFF_NOARP) { /* Discard packet if ARP is disabled on interface */ m_freem(m); return; } isr = NETISR_ARP; break; #endif #ifdef INET6 case ETHERTYPE_IPV6: isr = NETISR_IPV6; break; #endif default: goto discard; } /* Strip off Ethernet header. */ m_adj(m, ETHER_HDR_LEN); netisr_dispatch(isr, m); return; discard: /* * Packet is to be discarded. If netgraph is present, * hand the packet to it for last chance processing; * otherwise dispose of it. */ if (ifp->if_l2com != NULL) { KASSERT(ng_ether_input_orphan_p != NULL, ("ng_ether_input_orphan_p is NULL")); (*ng_ether_input_orphan_p)(ifp, m); return; } m_freem(m); } /* * Convert Ethernet address to printable (loggable) representation. * This routine is for compatibility; it's better to just use * * printf("%6D", , ":"); * * since there's no static buffer involved. */ char * ether_sprintf(const u_char *ap) { static char etherbuf[18]; snprintf(etherbuf, sizeof (etherbuf), "%6D", ap, ":"); return (etherbuf); } /* * Perform common duties while attaching to interface list */ void ether_ifattach(struct ifnet *ifp, const u_int8_t *lla) { int i; struct ifaddr *ifa; struct sockaddr_dl *sdl; ifp->if_addrlen = ETHER_ADDR_LEN; ifp->if_hdrlen = (ifp->if_capabilities & IFCAP_VLAN_MTU) != 0 ? ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN : ETHER_HDR_LEN; ifp->if_mtu = ETHERMTU; if_attach(ifp); ifp->if_output = ether_output; ifp->if_input = ether_input; ifp->if_resolvemulti = ether_resolvemulti; ifp->if_requestencap = ether_requestencap; if (ifp->if_baudrate == 0) ifp->if_baudrate = IF_Mbps(10); /* just a default */ ifp->if_broadcastaddr = etherbroadcastaddr; ifa = ifp->if_addr; KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__)); sdl = (struct sockaddr_dl *)ifa->ifa_addr; sdl->sdl_type = IFT_ETHER; sdl->sdl_alen = ifp->if_addrlen; bcopy(lla, LLADDR(sdl), ifp->if_addrlen); if (ifp->if_hw_addr != NULL) bcopy(lla, ifp->if_hw_addr, ifp->if_addrlen); bpfattach(ifp, DLT_EN10MB, ETHER_HDR_LEN); /* Announce Ethernet MAC address if non-zero. */ for (i = 0; i < ifp->if_addrlen; i++) if (lla[i] != 0) break; if (i != ifp->if_addrlen) if_printf(ifp, "Ethernet address: %6D\n", lla, ":"); uuid_ether_add(LLADDR(sdl)); /* Add necessary bits are setup; announce it now. */ EVENTHANDLER_INVOKE(ether_ifattach_event, ifp); if (IS_DEFAULT_VNET(curvnet)) devctl_notify("ETHERNET", ifp->if_xname, "IFATTACH", NULL); } /* * Perform common duties while detaching an Ethernet interface */ void ether_ifdetach(struct ifnet *ifp) { struct sockaddr_dl *sdl; sdl = (struct sockaddr_dl *)(ifp->if_addr->ifa_addr); uuid_ether_del(LLADDR(sdl)); bpfdetach(ifp); if_detach(ifp); } SYSCTL_DECL(_net_link); SYSCTL_NODE(_net_link, IFT_ETHER, ether, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "Ethernet"); #if 0 /* * This is for reference. We have a table-driven version * of the little-endian crc32 generator, which is faster * than the double-loop. */ uint32_t ether_crc32_le(const uint8_t *buf, size_t len) { size_t i; uint32_t crc; int bit; uint8_t data; crc = 0xffffffff; /* initial value */ for (i = 0; i < len; i++) { for (data = *buf++, bit = 0; bit < 8; bit++, data >>= 1) { carry = (crc ^ data) & 1; crc >>= 1; if (carry) crc = (crc ^ ETHER_CRC_POLY_LE); } } return (crc); } #else uint32_t ether_crc32_le(const uint8_t *buf, size_t len) { static const uint32_t crctab[] = { 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; size_t i; uint32_t crc; crc = 0xffffffff; /* initial value */ for (i = 0; i < len; i++) { crc ^= buf[i]; crc = (crc >> 4) ^ crctab[crc & 0xf]; crc = (crc >> 4) ^ crctab[crc & 0xf]; } return (crc); } #endif uint32_t ether_crc32_be(const uint8_t *buf, size_t len) { size_t i; uint32_t crc, carry; int bit; uint8_t data; crc = 0xffffffff; /* initial value */ for (i = 0; i < len; i++) { for (data = *buf++, bit = 0; bit < 8; bit++, data >>= 1) { carry = ((crc & 0x80000000) ? 1 : 0) ^ (data & 0x01); crc <<= 1; if (carry) crc = (crc ^ ETHER_CRC_POLY_BE) | carry; } } return (crc); } int ether_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct ifaddr *ifa = (struct ifaddr *) data; struct ifreq *ifr = (struct ifreq *) data; int error = 0; switch (command) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: ifp->if_init(ifp->if_softc); /* before arpwhohas */ arp_ifinit(ifp, ifa); break; #endif default: ifp->if_init(ifp->if_softc); break; } break; case SIOCGIFADDR: bcopy(IF_LLADDR(ifp), &ifr->ifr_addr.sa_data[0], ETHER_ADDR_LEN); break; case SIOCSIFMTU: /* * Set the interface MTU. */ if (ifr->ifr_mtu > ETHERMTU) { error = EINVAL; } else { ifp->if_mtu = ifr->ifr_mtu; } break; case SIOCSLANPCP: error = priv_check(curthread, PRIV_NET_SETLANPCP); if (error != 0) break; if (ifr->ifr_lan_pcp > 7 && ifr->ifr_lan_pcp != IFNET_PCP_NONE) { error = EINVAL; } else { ifp->if_pcp = ifr->ifr_lan_pcp; /* broadcast event about PCP change */ EVENTHANDLER_INVOKE(ifnet_event, ifp, IFNET_EVENT_PCP); } break; case SIOCGLANPCP: ifr->ifr_lan_pcp = ifp->if_pcp; break; default: error = EINVAL; /* XXX netbsd has ENOTTY??? */ break; } return (error); } static int ether_resolvemulti(struct ifnet *ifp, struct sockaddr **llsa, struct sockaddr *sa) { struct sockaddr_dl *sdl; #ifdef INET struct sockaddr_in *sin; #endif #ifdef INET6 struct sockaddr_in6 *sin6; #endif u_char *e_addr; switch(sa->sa_family) { case AF_LINK: /* * No mapping needed. Just check that it's a valid MC address. */ sdl = (struct sockaddr_dl *)sa; e_addr = LLADDR(sdl); if (!ETHER_IS_MULTICAST(e_addr)) return EADDRNOTAVAIL; *llsa = NULL; return 0; #ifdef INET case AF_INET: sin = (struct sockaddr_in *)sa; if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) return EADDRNOTAVAIL; sdl = link_init_sdl(ifp, *llsa, IFT_ETHER); sdl->sdl_alen = ETHER_ADDR_LEN; e_addr = LLADDR(sdl); ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr); *llsa = (struct sockaddr *)sdl; return 0; #endif #ifdef INET6 case AF_INET6: sin6 = (struct sockaddr_in6 *)sa; if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { /* * An IP6 address of 0 means listen to all * of the Ethernet multicast address used for IP6. * (This is used for multicast routers.) */ ifp->if_flags |= IFF_ALLMULTI; *llsa = NULL; return 0; } if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) return EADDRNOTAVAIL; sdl = link_init_sdl(ifp, *llsa, IFT_ETHER); sdl->sdl_alen = ETHER_ADDR_LEN; e_addr = LLADDR(sdl); ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr); *llsa = (struct sockaddr *)sdl; return 0; #endif default: /* * Well, the text isn't quite right, but it's the name * that counts... */ return EAFNOSUPPORT; } } static moduledata_t ether_mod = { .name = "ether", }; void ether_vlan_mtap(struct bpf_if *bp, struct mbuf *m, void *data, u_int dlen) { struct ether_vlan_header vlan; struct mbuf mv, mb; KASSERT((m->m_flags & M_VLANTAG) != 0, ("%s: vlan information not present", __func__)); KASSERT(m->m_len >= sizeof(struct ether_header), ("%s: mbuf not large enough for header", __func__)); bcopy(mtod(m, char *), &vlan, sizeof(struct ether_header)); vlan.evl_proto = vlan.evl_encap_proto; vlan.evl_encap_proto = htons(ETHERTYPE_VLAN); vlan.evl_tag = htons(m->m_pkthdr.ether_vtag); m->m_len -= sizeof(struct ether_header); m->m_data += sizeof(struct ether_header); /* * If a data link has been supplied by the caller, then we will need to * re-create a stack allocated mbuf chain with the following structure: * * (1) mbuf #1 will contain the supplied data link * (2) mbuf #2 will contain the vlan header * (3) mbuf #3 will contain the original mbuf's packet data * * Otherwise, submit the packet and vlan header via bpf_mtap2(). */ if (data != NULL) { mv.m_next = m; mv.m_data = (caddr_t)&vlan; mv.m_len = sizeof(vlan); mb.m_next = &mv; mb.m_data = data; mb.m_len = dlen; bpf_mtap(bp, &mb); } else bpf_mtap2(bp, &vlan, sizeof(vlan), m); m->m_len += sizeof(struct ether_header); m->m_data -= sizeof(struct ether_header); } struct mbuf * ether_vlanencap_proto(struct mbuf *m, uint16_t tag, uint16_t proto) { struct ether_vlan_header *evl; M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_NOWAIT); if (m == NULL) return (NULL); /* M_PREPEND takes care of m_len, m_pkthdr.len for us */ if (m->m_len < sizeof(*evl)) { m = m_pullup(m, sizeof(*evl)); if (m == NULL) return (NULL); } /* * Transform the Ethernet header into an Ethernet header * with 802.1Q encapsulation. */ evl = mtod(m, struct ether_vlan_header *); bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN, (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN); evl->evl_encap_proto = htons(proto); evl->evl_tag = htons(tag); return (m); } void ether_bpf_mtap_if(struct ifnet *ifp, struct mbuf *m) { if (bpf_peers_present(ifp->if_bpf)) { M_ASSERTVALID(m); if ((m->m_flags & M_VLANTAG) != 0) ether_vlan_mtap(ifp->if_bpf, m, NULL, 0); else bpf_mtap(ifp->if_bpf, m); } } static SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "IEEE 802.1Q VLAN"); static SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "for consistency"); VNET_DEFINE_STATIC(int, soft_pad); #define V_soft_pad VNET(soft_pad) SYSCTL_INT(_net_link_vlan, OID_AUTO, soft_pad, CTLFLAG_RW | CTLFLAG_VNET, &VNET_NAME(soft_pad), 0, "pad short frames before tagging"); /* * For now, make preserving PCP via an mbuf tag optional, as it increases * per-packet memory allocations and frees. In the future, it would be * preferable to reuse ether_vtag for this, or similar. */ VNET_DEFINE(int, vlan_mtag_pcp) = 0; #define V_vlan_mtag_pcp VNET(vlan_mtag_pcp) SYSCTL_INT(_net_link_vlan, OID_AUTO, mtag_pcp, CTLFLAG_RW | CTLFLAG_VNET, &VNET_NAME(vlan_mtag_pcp), 0, "Retain VLAN PCP information as packets are passed up the stack"); static inline bool ether_do_pcp(struct ifnet *ifp, struct mbuf *m) { if (ifp->if_type == IFT_L2VLAN) return (false); if (ifp->if_pcp != IFNET_PCP_NONE || (m->m_flags & M_VLANTAG) != 0) return (true); if (V_vlan_mtag_pcp && m_tag_locate(m, MTAG_8021Q, MTAG_8021Q_PCP_OUT, NULL) != NULL) return (true); return (false); } bool ether_8021q_frame(struct mbuf **mp, struct ifnet *ife, struct ifnet *p, const struct ether_8021q_tag *qtag) { struct m_tag *mtag; int n; uint16_t tag; uint8_t pcp = qtag->pcp; static const char pad[8]; /* just zeros */ /* * Pad the frame to the minimum size allowed if told to. * This option is in accord with IEEE Std 802.1Q, 2003 Ed., * paragraph C.4.4.3.b. It can help to work around buggy * bridges that violate paragraph C.4.4.3.a from the same * document, i.e., fail to pad short frames after untagging. * E.g., a tagged frame 66 bytes long (incl. FCS) is OK, but * untagging it will produce a 62-byte frame, which is a runt * and requires padding. There are VLAN-enabled network * devices that just discard such runts instead or mishandle * them somehow. */ if (V_soft_pad && p->if_type == IFT_ETHER) { for (n = ETHERMIN + ETHER_HDR_LEN - (*mp)->m_pkthdr.len; n > 0; n -= sizeof(pad)) { if (!m_append(*mp, min(n, sizeof(pad)), pad)) break; } if (n > 0) { m_freem(*mp); *mp = NULL; if_printf(ife, "cannot pad short frame"); return (false); } } /* * If PCP is set in mbuf, use it */ if ((*mp)->m_flags & M_VLANTAG) { pcp = EVL_PRIOFTAG((*mp)->m_pkthdr.ether_vtag); } /* * If underlying interface can do VLAN tag insertion itself, * just pass the packet along. However, we need some way to * tell the interface where the packet came from so that it * knows how to find the VLAN tag to use, so we attach a * packet tag that holds it. */ if (V_vlan_mtag_pcp && (mtag = m_tag_locate(*mp, MTAG_8021Q, MTAG_8021Q_PCP_OUT, NULL)) != NULL) tag = EVL_MAKETAG(qtag->vid, *(uint8_t *)(mtag + 1), 0); else tag = EVL_MAKETAG(qtag->vid, pcp, 0); if ((p->if_capenable & IFCAP_VLAN_HWTAGGING) && (qtag->proto == ETHERTYPE_VLAN)) { (*mp)->m_pkthdr.ether_vtag = tag; (*mp)->m_flags |= M_VLANTAG; } else { *mp = ether_vlanencap_proto(*mp, tag, qtag->proto); if (*mp == NULL) { if_printf(ife, "unable to prepend 802.1Q header"); return (false); } (*mp)->m_flags &= ~M_VLANTAG; } return (true); } /* * Allocate an address from the FreeBSD Foundation OUI. This uses a * cryptographic hash function on the containing jail's name, UUID and the * interface name to attempt to provide a unique but stable address. * Pseudo-interfaces which require a MAC address should use this function to * allocate non-locally-administered addresses. */ void ether_gen_addr_byname(const char *nameunit, struct ether_addr *hwaddr) { SHA1_CTX ctx; char *buf; char uuid[HOSTUUIDLEN + 1]; uint64_t addr; int i, sz; unsigned char digest[SHA1_RESULTLEN]; char jailname[MAXHOSTNAMELEN]; getcredhostuuid(curthread->td_ucred, uuid, sizeof(uuid)); if (strncmp(uuid, DEFAULT_HOSTUUID, sizeof(uuid)) == 0) { /* Fall back to a random mac address. */ goto rando; } /* If each (vnet) jail would also have a unique hostuuid this would not * be necessary. */ getjailname(curthread->td_ucred, jailname, sizeof(jailname)); sz = asprintf(&buf, M_TEMP, "%s-%s-%s", uuid, nameunit, jailname); if (sz < 0) { /* Fall back to a random mac address. */ goto rando; } SHA1Init(&ctx); SHA1Update(&ctx, buf, sz); SHA1Final(digest, &ctx); free(buf, M_TEMP); addr = (digest[0] << 8) | digest[1] | OUI_FREEBSD_GENERATED_LOW; for (i = 0; i < ETHER_ADDR_LEN; ++i) { hwaddr->octet[i] = addr >> ((ETHER_ADDR_LEN - i - 1) * 8) & 0xFF; } return; rando: arc4rand(hwaddr, sizeof(*hwaddr), 0); /* Unicast */ hwaddr->octet[0] &= 0xFE; /* Locally administered. */ hwaddr->octet[0] |= 0x02; } void ether_gen_addr(struct ifnet *ifp, struct ether_addr *hwaddr) { ether_gen_addr_byname(if_name(ifp), hwaddr); } DECLARE_MODULE(ether, ether_mod, SI_SUB_INIT_IF, SI_ORDER_ANY); MODULE_VERSION(ether, 1); diff --git a/sys/netinet/icmp6.h b/sys/netinet/icmp6.h index 9ed39d118c16..9508f221ba10 100644 --- a/sys/netinet/icmp6.h +++ b/sys/netinet/icmp6.h @@ -1,783 +1,779 @@ /* $KAME: icmp6.h,v 1.46 2001/04/27 15:09:48 itojun Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _NETINET_ICMP6_H_ #define _NETINET_ICMP6_H_ #include #define ICMPV6_PLD_MAXLEN 1232 /* IPV6_MMTU - sizeof(struct ip6_hdr) - sizeof(struct icmp6_hdr) */ struct icmp6_hdr { u_int8_t icmp6_type; /* type field */ u_int8_t icmp6_code; /* code field */ u_int16_t icmp6_cksum; /* checksum field */ union { u_int32_t icmp6_un_data32[1]; /* type-specific field */ u_int16_t icmp6_un_data16[2]; /* type-specific field */ u_int8_t icmp6_un_data8[4]; /* type-specific field */ } icmp6_dataun; } __packed; #define icmp6_data32 icmp6_dataun.icmp6_un_data32 #define icmp6_data16 icmp6_dataun.icmp6_un_data16 #define icmp6_data8 icmp6_dataun.icmp6_un_data8 #define icmp6_pptr icmp6_data32[0] /* parameter prob */ #define icmp6_mtu icmp6_data32[0] /* packet too big */ #define icmp6_id icmp6_data16[0] /* echo request/reply */ #define icmp6_seq icmp6_data16[1] /* echo request/reply */ #define icmp6_maxdelay icmp6_data16[0] /* mcast group membership */ #define ICMP6_DST_UNREACH 1 /* dest unreachable, codes: */ #define ICMP6_PACKET_TOO_BIG 2 /* packet too big */ #define ICMP6_TIME_EXCEEDED 3 /* time exceeded, code: */ #define ICMP6_PARAM_PROB 4 /* ip6 header bad */ #define ICMP6_ECHO_REQUEST 128 /* echo service */ #define ICMP6_ECHO_REPLY 129 /* echo reply */ #define MLD_LISTENER_QUERY 130 /* multicast listener query */ #define MLD_LISTENER_REPORT 131 /* multicast listener report */ #define MLD_LISTENER_DONE 132 /* multicast listener done */ #define MLD_LISTENER_REDUCTION MLD_LISTENER_DONE /* RFC3542 definition */ /* RFC2292 decls */ #define ICMP6_MEMBERSHIP_QUERY 130 /* group membership query */ #define ICMP6_MEMBERSHIP_REPORT 131 /* group membership report */ #define ICMP6_MEMBERSHIP_REDUCTION 132 /* group membership termination */ #ifndef _KERNEL /* the followings are for backward compatibility to old KAME apps. */ #define MLD6_LISTENER_QUERY MLD_LISTENER_QUERY #define MLD6_LISTENER_REPORT MLD_LISTENER_REPORT #define MLD6_LISTENER_DONE MLD_LISTENER_DONE #endif #define ND_ROUTER_SOLICIT 133 /* router solicitation */ #define ND_ROUTER_ADVERT 134 /* router advertisement */ #define ND_NEIGHBOR_SOLICIT 135 /* neighbor solicitation */ #define ND_NEIGHBOR_ADVERT 136 /* neighbor advertisement */ #define ND_REDIRECT 137 /* redirect */ #define ICMP6_ROUTER_RENUMBERING 138 /* router renumbering */ #define ICMP6_WRUREQUEST 139 /* who are you request */ #define ICMP6_WRUREPLY 140 /* who are you reply */ #define ICMP6_FQDN_QUERY 139 /* FQDN query */ #define ICMP6_FQDN_REPLY 140 /* FQDN reply */ #define ICMP6_NI_QUERY 139 /* node information request */ #define ICMP6_NI_REPLY 140 /* node information reply */ #define MLDV2_LISTENER_REPORT 143 /* RFC3810 listener report */ /* The definitions below are experimental. TBA */ #define MLD_MTRACE_RESP 200 /* mtrace resp (to sender) */ #define MLD_MTRACE 201 /* mtrace messages */ #ifndef _KERNEL #define MLD6_MTRACE_RESP MLD_MTRACE_RESP #define MLD6_MTRACE MLD_MTRACE #endif #define ICMP6_MAXTYPE 201 #define ICMP6_DST_UNREACH_NOROUTE 0 /* no route to destination */ #define ICMP6_DST_UNREACH_ADMIN 1 /* administratively prohibited */ #define ICMP6_DST_UNREACH_NOTNEIGHBOR 2 /* not a neighbor(obsolete) */ #define ICMP6_DST_UNREACH_BEYONDSCOPE 2 /* beyond scope of source address */ #define ICMP6_DST_UNREACH_ADDR 3 /* address unreachable */ #define ICMP6_DST_UNREACH_NOPORT 4 /* port unreachable */ #define ICMP6_DST_UNREACH_POLICY 5 /* failed ingress/egress policy */ #define ICMP6_DST_UNREACH_REJECT 6 /* Reject route to destination */ #define ICMP6_DST_UNREACH_SRCROUTE 7 /* Error in source routing header */ #define ICMP6_TIME_EXCEED_TRANSIT 0 /* ttl==0 in transit */ #define ICMP6_TIME_EXCEED_REASSEMBLY 1 /* ttl==0 in reass */ #define ICMP6_PARAMPROB_HEADER 0 /* erroneous header field */ #define ICMP6_PARAMPROB_NEXTHEADER 1 /* unrecognized next header */ #define ICMP6_PARAMPROB_OPTION 2 /* unrecognized option */ #define ICMP6_INFOMSG_MASK 0x80 /* all informational messages */ #define ICMP6_NI_SUBJ_IPV6 0 /* Query Subject is an IPv6 address */ #define ICMP6_NI_SUBJ_FQDN 1 /* Query Subject is a Domain name */ #define ICMP6_NI_SUBJ_IPV4 2 /* Query Subject is an IPv4 address */ #define ICMP6_NI_SUCCESS 0 /* node information successful reply */ #define ICMP6_NI_REFUSED 1 /* node information request is refused */ #define ICMP6_NI_UNKNOWN 2 /* unknown Qtype */ #define ICMP6_ROUTER_RENUMBERING_COMMAND 0 /* rr command */ #define ICMP6_ROUTER_RENUMBERING_RESULT 1 /* rr result */ #define ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET 255 /* rr seq num reset */ /* Used in kernel only */ #define ND_REDIRECT_ONLINK 0 /* redirect to an on-link node */ #define ND_REDIRECT_ROUTER 1 /* redirect to a better router */ /* * Multicast Listener Discovery */ struct mld_hdr { struct icmp6_hdr mld_icmp6_hdr; struct in6_addr mld_addr; /* multicast address */ } __packed; /* definitions to provide backward compatibility to old KAME applications */ #ifndef _KERNEL #define mld6_hdr mld_hdr #define mld6_type mld_type #define mld6_code mld_code #define mld6_cksum mld_cksum #define mld6_maxdelay mld_maxdelay #define mld6_reserved mld_reserved #define mld6_addr mld_addr #endif /* shortcut macro definitions */ #define mld_type mld_icmp6_hdr.icmp6_type #define mld_code mld_icmp6_hdr.icmp6_code #define mld_cksum mld_icmp6_hdr.icmp6_cksum #define mld_maxdelay mld_icmp6_hdr.icmp6_data16[0] #define mld_reserved mld_icmp6_hdr.icmp6_data16[1] #define mld_v2_reserved mld_icmp6_hdr.icmp6_data16[0] #define mld_v2_numrecs mld_icmp6_hdr.icmp6_data16[1] /* * Neighbor Discovery */ struct nd_router_solicit { /* router solicitation */ struct icmp6_hdr nd_rs_hdr; /* could be followed by options */ } __packed; #define nd_rs_type nd_rs_hdr.icmp6_type #define nd_rs_code nd_rs_hdr.icmp6_code #define nd_rs_cksum nd_rs_hdr.icmp6_cksum #define nd_rs_reserved nd_rs_hdr.icmp6_data32[0] struct nd_router_advert { /* router advertisement */ struct icmp6_hdr nd_ra_hdr; u_int32_t nd_ra_reachable; /* reachable time */ u_int32_t nd_ra_retransmit; /* retransmit timer */ /* could be followed by options */ } __packed; #define nd_ra_type nd_ra_hdr.icmp6_type #define nd_ra_code nd_ra_hdr.icmp6_code #define nd_ra_cksum nd_ra_hdr.icmp6_cksum #define nd_ra_curhoplimit nd_ra_hdr.icmp6_data8[0] #define nd_ra_flags_reserved nd_ra_hdr.icmp6_data8[1] #define ND_RA_FLAG_MANAGED 0x80 #define ND_RA_FLAG_OTHER 0x40 #define ND_RA_FLAG_HA 0x20 /* * Router preference values based on draft-draves-ipngwg-router-selection-01. * These are non-standard definitions. */ #define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */ #define ND_RA_FLAG_RTPREF_HIGH 0x08 /* 00001000 */ #define ND_RA_FLAG_RTPREF_MEDIUM 0x00 /* 00000000 */ #define ND_RA_FLAG_RTPREF_LOW 0x18 /* 00011000 */ #define ND_RA_FLAG_RTPREF_RSV 0x10 /* 00010000 */ -#ifdef EXPERIMENTAL -#define ND_RA_FLAG_IPV6_ONLY 0x02 /* draft-ietf-6man-ipv6only-flag */ -#endif - #define nd_ra_router_lifetime nd_ra_hdr.icmp6_data16[1] struct nd_neighbor_solicit { /* neighbor solicitation */ struct icmp6_hdr nd_ns_hdr; struct in6_addr nd_ns_target; /*target address */ /* could be followed by options */ } __packed; #define nd_ns_type nd_ns_hdr.icmp6_type #define nd_ns_code nd_ns_hdr.icmp6_code #define nd_ns_cksum nd_ns_hdr.icmp6_cksum #define nd_ns_reserved nd_ns_hdr.icmp6_data32[0] struct nd_neighbor_advert { /* neighbor advertisement */ struct icmp6_hdr nd_na_hdr; struct in6_addr nd_na_target; /* target address */ /* could be followed by options */ } __packed; #define nd_na_type nd_na_hdr.icmp6_type #define nd_na_code nd_na_hdr.icmp6_code #define nd_na_cksum nd_na_hdr.icmp6_cksum #define nd_na_flags_reserved nd_na_hdr.icmp6_data32[0] #if BYTE_ORDER == BIG_ENDIAN #define ND_NA_FLAG_ROUTER 0x80000000 #define ND_NA_FLAG_SOLICITED 0x40000000 #define ND_NA_FLAG_OVERRIDE 0x20000000 #else #if BYTE_ORDER == LITTLE_ENDIAN #define ND_NA_FLAG_ROUTER 0x80 #define ND_NA_FLAG_SOLICITED 0x40 #define ND_NA_FLAG_OVERRIDE 0x20 #endif #endif struct nd_redirect { /* redirect */ struct icmp6_hdr nd_rd_hdr; struct in6_addr nd_rd_target; /* target address */ struct in6_addr nd_rd_dst; /* destination address */ /* could be followed by options */ } __packed; #define nd_rd_type nd_rd_hdr.icmp6_type #define nd_rd_code nd_rd_hdr.icmp6_code #define nd_rd_cksum nd_rd_hdr.icmp6_cksum #define nd_rd_reserved nd_rd_hdr.icmp6_data32[0] struct nd_opt_hdr { /* Neighbor discovery option header */ u_int8_t nd_opt_type; u_int8_t nd_opt_len; /* followed by option specific data*/ } __packed; #define ND_OPT_SOURCE_LINKADDR 1 #define ND_OPT_TARGET_LINKADDR 2 #define ND_OPT_PREFIX_INFORMATION 3 #define ND_OPT_REDIRECTED_HEADER 4 #define ND_OPT_MTU 5 #define ND_OPT_NONCE 14 /* RFC 3971 */ #define ND_OPT_ROUTE_INFO 24 /* RFC 4191 */ #define ND_OPT_RDNSS 25 /* RFC 6106 */ #define ND_OPT_DNSSL 31 /* RFC 6106 */ #define ND_OPT_PREF64 38 /* RFC 8781 */ #define ND_OPT_MAX 38 struct nd_opt_prefix_info { /* prefix information */ u_int8_t nd_opt_pi_type; u_int8_t nd_opt_pi_len; u_int8_t nd_opt_pi_prefix_len; u_int8_t nd_opt_pi_flags_reserved; u_int32_t nd_opt_pi_valid_time; u_int32_t nd_opt_pi_preferred_time; u_int32_t nd_opt_pi_reserved2; struct in6_addr nd_opt_pi_prefix; } __packed; #define ND_OPT_PI_FLAG_ONLINK 0x80 #define ND_OPT_PI_FLAG_AUTO 0x40 struct nd_opt_rd_hdr { /* redirected header */ u_int8_t nd_opt_rh_type; u_int8_t nd_opt_rh_len; u_int16_t nd_opt_rh_reserved1; u_int32_t nd_opt_rh_reserved2; /* followed by IP header and data */ } __packed; struct nd_opt_mtu { /* MTU option */ u_int8_t nd_opt_mtu_type; u_int8_t nd_opt_mtu_len; u_int16_t nd_opt_mtu_reserved; u_int32_t nd_opt_mtu_mtu; } __packed; #define ND_OPT_NONCE_LEN ((1 * 8) - 2) #if ((ND_OPT_NONCE_LEN + 2) % 8) != 0 #error "(ND_OPT_NONCE_LEN + 2) must be a multiple of 8." #endif struct nd_opt_nonce { /* nonce option */ u_int8_t nd_opt_nonce_type; u_int8_t nd_opt_nonce_len; u_int8_t nd_opt_nonce[ND_OPT_NONCE_LEN]; } __packed; struct nd_opt_route_info { /* route info */ u_int8_t nd_opt_rti_type; u_int8_t nd_opt_rti_len; u_int8_t nd_opt_rti_prefixlen; u_int8_t nd_opt_rti_flags; u_int32_t nd_opt_rti_lifetime; /* prefix follows */ } __packed; struct nd_opt_rdnss { /* RDNSS option (RFC 6106) */ u_int8_t nd_opt_rdnss_type; u_int8_t nd_opt_rdnss_len; u_int16_t nd_opt_rdnss_reserved; u_int32_t nd_opt_rdnss_lifetime; /* followed by list of recursive DNS servers */ } __packed; struct nd_opt_dnssl { /* DNSSL option (RFC 6106) */ u_int8_t nd_opt_dnssl_type; u_int8_t nd_opt_dnssl_len; u_int16_t nd_opt_dnssl_reserved; u_int32_t nd_opt_dnssl_lifetime; /* followed by list of DNS search domains */ } __packed; struct nd_opt_pref64 { /* PREF64 option (RFC 8781) */ uint8_t nd_opt_pref64_type; uint8_t nd_opt_pref64_len; /* bits 0-12 are the SL, bits 13-15 are the PLC */ uint16_t nd_opt_pref64_sl_plc; char nd_opt_prefix[12]; } __packed; /* * icmp6 namelookup */ struct icmp6_namelookup { struct icmp6_hdr icmp6_nl_hdr; u_int8_t icmp6_nl_nonce[8]; int32_t icmp6_nl_ttl; #if 0 u_int8_t icmp6_nl_len; u_int8_t icmp6_nl_name[3]; #endif /* could be followed by options */ } __packed; /* * icmp6 node information */ struct icmp6_nodeinfo { struct icmp6_hdr icmp6_ni_hdr; u_int8_t icmp6_ni_nonce[8]; /* could be followed by reply data */ } __packed; #define ni_type icmp6_ni_hdr.icmp6_type #define ni_code icmp6_ni_hdr.icmp6_code #define ni_cksum icmp6_ni_hdr.icmp6_cksum #define ni_qtype icmp6_ni_hdr.icmp6_data16[0] #define ni_flags icmp6_ni_hdr.icmp6_data16[1] #define NI_QTYPE_NOOP 0 /* NOOP */ #define NI_QTYPE_SUPTYPES 1 /* Supported Qtypes */ #define NI_QTYPE_FQDN 2 /* FQDN (draft 04) */ #define NI_QTYPE_DNSNAME 2 /* DNS Name */ #define NI_QTYPE_NODEADDR 3 /* Node Addresses */ #define NI_QTYPE_IPV4ADDR 4 /* IPv4 Addresses */ #if BYTE_ORDER == BIG_ENDIAN #define NI_SUPTYPE_FLAG_COMPRESS 0x1 #define NI_FQDN_FLAG_VALIDTTL 0x1 #elif BYTE_ORDER == LITTLE_ENDIAN #define NI_SUPTYPE_FLAG_COMPRESS 0x0100 #define NI_FQDN_FLAG_VALIDTTL 0x0100 #endif #ifdef NAME_LOOKUPS_04 #if BYTE_ORDER == BIG_ENDIAN #define NI_NODEADDR_FLAG_LINKLOCAL 0x1 #define NI_NODEADDR_FLAG_SITELOCAL 0x2 #define NI_NODEADDR_FLAG_GLOBAL 0x4 #define NI_NODEADDR_FLAG_ALL 0x8 #define NI_NODEADDR_FLAG_TRUNCATE 0x10 #define NI_NODEADDR_FLAG_ANYCAST 0x20 /* just experimental. not in spec */ #elif BYTE_ORDER == LITTLE_ENDIAN #define NI_NODEADDR_FLAG_LINKLOCAL 0x0100 #define NI_NODEADDR_FLAG_SITELOCAL 0x0200 #define NI_NODEADDR_FLAG_GLOBAL 0x0400 #define NI_NODEADDR_FLAG_ALL 0x0800 #define NI_NODEADDR_FLAG_TRUNCATE 0x1000 #define NI_NODEADDR_FLAG_ANYCAST 0x2000 /* just experimental. not in spec */ #endif #else /* draft-ietf-ipngwg-icmp-name-lookups-05 (and later?) */ #if BYTE_ORDER == BIG_ENDIAN #define NI_NODEADDR_FLAG_TRUNCATE 0x1 #define NI_NODEADDR_FLAG_ALL 0x2 #define NI_NODEADDR_FLAG_COMPAT 0x4 #define NI_NODEADDR_FLAG_LINKLOCAL 0x8 #define NI_NODEADDR_FLAG_SITELOCAL 0x10 #define NI_NODEADDR_FLAG_GLOBAL 0x20 #define NI_NODEADDR_FLAG_ANYCAST 0x40 /* just experimental. not in spec */ #elif BYTE_ORDER == LITTLE_ENDIAN #define NI_NODEADDR_FLAG_TRUNCATE 0x0100 #define NI_NODEADDR_FLAG_ALL 0x0200 #define NI_NODEADDR_FLAG_COMPAT 0x0400 #define NI_NODEADDR_FLAG_LINKLOCAL 0x0800 #define NI_NODEADDR_FLAG_SITELOCAL 0x1000 #define NI_NODEADDR_FLAG_GLOBAL 0x2000 #define NI_NODEADDR_FLAG_ANYCAST 0x4000 /* just experimental. not in spec */ #endif #endif struct ni_reply_fqdn { u_int32_t ni_fqdn_ttl; /* TTL */ u_int8_t ni_fqdn_namelen; /* length in octets of the FQDN */ u_int8_t ni_fqdn_name[3]; /* XXX: alignment */ } __packed; /* * Router Renumbering. as router-renum-08.txt */ struct icmp6_router_renum { /* router renumbering header */ struct icmp6_hdr rr_hdr; u_int8_t rr_segnum; u_int8_t rr_flags; u_int16_t rr_maxdelay; u_int32_t rr_reserved; } __packed; #define ICMP6_RR_FLAGS_TEST 0x80 #define ICMP6_RR_FLAGS_REQRESULT 0x40 #define ICMP6_RR_FLAGS_FORCEAPPLY 0x20 #define ICMP6_RR_FLAGS_SPECSITE 0x10 #define ICMP6_RR_FLAGS_PREVDONE 0x08 #define rr_type rr_hdr.icmp6_type #define rr_code rr_hdr.icmp6_code #define rr_cksum rr_hdr.icmp6_cksum #define rr_seqnum rr_hdr.icmp6_data32[0] struct rr_pco_match { /* match prefix part */ u_int8_t rpm_code; u_int8_t rpm_len; u_int8_t rpm_ordinal; u_int8_t rpm_matchlen; u_int8_t rpm_minlen; u_int8_t rpm_maxlen; u_int16_t rpm_reserved; struct in6_addr rpm_prefix; } __packed; #define RPM_PCO_ADD 1 #define RPM_PCO_CHANGE 2 #define RPM_PCO_SETGLOBAL 3 #define RPM_PCO_MAX 4 struct rr_pco_use { /* use prefix part */ u_int8_t rpu_uselen; u_int8_t rpu_keeplen; u_int8_t rpu_ramask; u_int8_t rpu_raflags; u_int32_t rpu_vltime; u_int32_t rpu_pltime; u_int32_t rpu_flags; struct in6_addr rpu_prefix; } __packed; #define ICMP6_RR_PCOUSE_RAFLAGS_ONLINK 0x80 #define ICMP6_RR_PCOUSE_RAFLAGS_AUTO 0x40 #if BYTE_ORDER == BIG_ENDIAN #define ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME 0x80000000 #define ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME 0x40000000 #elif BYTE_ORDER == LITTLE_ENDIAN #define ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME 0x80 #define ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME 0x40 #endif struct rr_result { /* router renumbering result message */ u_int16_t rrr_flags; u_int8_t rrr_ordinal; u_int8_t rrr_matchedlen; u_int32_t rrr_ifid; struct in6_addr rrr_prefix; } __packed; #if BYTE_ORDER == BIG_ENDIAN #define ICMP6_RR_RESULT_FLAGS_OOB 0x0002 #define ICMP6_RR_RESULT_FLAGS_FORBIDDEN 0x0001 #elif BYTE_ORDER == LITTLE_ENDIAN #define ICMP6_RR_RESULT_FLAGS_OOB 0x0200 #define ICMP6_RR_RESULT_FLAGS_FORBIDDEN 0x0100 #endif /* * icmp6 filter structures. */ struct icmp6_filter { u_int32_t icmp6_filt[8]; }; #ifdef _KERNEL #define ICMP6_FILTER_SETPASSALL(filterp) \ do { \ int i; u_char *p; \ p = (u_char *)filterp; \ for (i = 0; i < sizeof(struct icmp6_filter); i++) \ p[i] = 0xff; \ } while (/*CONSTCOND*/ 0) #define ICMP6_FILTER_SETBLOCKALL(filterp) \ bzero(filterp, sizeof(struct icmp6_filter)) #else /* _KERNEL */ #define ICMP6_FILTER_SETPASSALL(filterp) \ memset(filterp, 0xff, sizeof(struct icmp6_filter)) #define ICMP6_FILTER_SETBLOCKALL(filterp) \ memset(filterp, 0x00, sizeof(struct icmp6_filter)) #endif /* _KERNEL */ #define ICMP6_FILTER_SETPASS(type, filterp) \ (((filterp)->icmp6_filt[(type) >> 5]) |= (1 << ((type) & 31))) #define ICMP6_FILTER_SETBLOCK(type, filterp) \ (((filterp)->icmp6_filt[(type) >> 5]) &= ~(1 << ((type) & 31))) #define ICMP6_FILTER_WILLPASS(type, filterp) \ ((((filterp)->icmp6_filt[(type) >> 5]) & (1 << ((type) & 31))) != 0) #define ICMP6_FILTER_WILLBLOCK(type, filterp) \ ((((filterp)->icmp6_filt[(type) >> 5]) & (1 << ((type) & 31))) == 0) /* * Variables related to this implementation * of the internet control message protocol version 6. */ struct icmp6stat { /* statistics related to icmp6 packets generated */ uint64_t icp6s_error; /* # of calls to icmp6_error */ uint64_t icp6s_canterror; /* no error 'cuz old was icmp */ uint64_t icp6s_toofreq; /* no error 'cuz rate limitation */ uint64_t icp6s_outhist[256]; /* statistics related to input message processed */ uint64_t icp6s_badcode; /* icmp6_code out of range */ uint64_t icp6s_tooshort; /* packet < sizeof(struct icmp6_hdr) */ uint64_t icp6s_checksum; /* bad checksum */ uint64_t icp6s_badlen; /* calculated bound mismatch */ uint64_t icp6s_dropped; /* # of packets dropped waiting for a resolution */ /* * number of responses: this member is inherited from netinet code, but * for netinet6 code, it is already available in icp6s_outhist[]. */ uint64_t icp6s_reflect; uint64_t icp6s_inhist[256]; uint64_t icp6s_nd_toomanyopt; /* too many ND options */ uint64_t icp6s_odst_unreach_noroute; uint64_t icp6s_odst_unreach_admin; uint64_t icp6s_odst_unreach_beyondscope; uint64_t icp6s_odst_unreach_addr; uint64_t icp6s_odst_unreach_noport; uint64_t icp6s_opacket_too_big; uint64_t icp6s_otime_exceed_transit; uint64_t icp6s_otime_exceed_reassembly; uint64_t icp6s_oparamprob_header; uint64_t icp6s_oparamprob_nextheader; uint64_t icp6s_oparamprob_option; uint64_t icp6s_oredirect; uint64_t icp6s_ounknown; uint64_t icp6s_pmtuchg; /* path MTU changes */ uint64_t icp6s_nd_badopt; /* bad ND options */ uint64_t icp6s_badns; /* bad neighbor solicitation */ uint64_t icp6s_badna; /* bad neighbor advertisement */ uint64_t icp6s_badrs; /* bad router solicitation */ uint64_t icp6s_badra; /* bad router advertisement */ uint64_t icp6s_badredirect; /* bad redirect message */ uint64_t icp6s_overflowdefrtr; /* Too many default routers. */ uint64_t icp6s_overflowprfx; /* Too many prefixes. */ uint64_t icp6s_overflownndp; /* Too many neighbour entries. */ uint64_t icp6s_overflowredirect;/* Too many redirects. */ uint64_t icp6s_invlhlim; /* Invalid hop limit. */ uint64_t icp6s_spare[32]; }; #ifdef _KERNEL #include #include #ifdef SYSCTL_DECL SYSCTL_DECL(_net_inet6_icmp6); #endif VNET_PCPUSTAT_DECLARE(struct icmp6stat, icmp6stat); /* * In-kernel consumers can use these accessor macros directly to update * stats. */ #define ICMP6STAT_ADD(name, val) \ do { \ MIB_SDT_PROBE1(icmp6, count, name, (val)); \ VNET_PCPUSTAT_ADD(struct icmp6stat, icmp6stat, name, (val)); \ } while (0) #define ICMP6STAT_INC(name) ICMP6STAT_ADD(name, 1) #define ICMP6STAT_INC2(name, type) \ do { \ MIB_SDT_PROBE2(icmp6, count, name, 1, type); \ VNET_PCPUSTAT_ADD(struct icmp6stat, icmp6stat, name[type], 1); \ } while (0) /* * Kernel module consumers must use this accessor macro. */ void kmod_icmp6stat_inc(int statnum); #define KMOD_ICMP6STAT_INC(name) \ do { \ MIB_SDT_PROBE1(icmp6, count, name, 1); \ kmod_icmp6stat_inc( \ offsetof(struct icmp6stat, name) / sizeof(uint64_t)); \ } while (0) #endif /* * Names for ICMP sysctl objects */ #define ICMPV6CTL_STATS 1 #define ICMPV6CTL_REDIRACCEPT 2 /* accept/process redirects */ #define ICMPV6CTL_REDIRTIMEOUT 3 /* redirect cache time */ #if 0 /*obsoleted*/ #define ICMPV6CTL_ERRRATELIMIT 5 /* ICMPv6 error rate limitation */ #endif #define ICMPV6CTL_ND6_PRUNE 6 #define ICMPV6CTL_ND6_DELAY 8 #define ICMPV6CTL_ND6_UMAXTRIES 9 #define ICMPV6CTL_ND6_MMAXTRIES 10 #define ICMPV6CTL_ND6_USELOOPBACK 11 /*#define ICMPV6CTL_ND6_PROXYALL 12 obsoleted, do not reuse here */ #define ICMPV6CTL_NODEINFO 13 #define ICMPV6CTL_ERRPPSLIMIT 14 /* ICMPv6 error pps limitation */ #define ICMPV6CTL_ND6_MAXNUDHINT 15 #define ICMPV6CTL_MTUDISC_HIWAT 16 #define ICMPV6CTL_MTUDISC_LOWAT 17 #define ICMPV6CTL_ND6_DEBUG 18 #define ICMPV6CTL_ND6_DRLIST 19 #define ICMPV6CTL_ND6_PRLIST 20 #define ICMPV6CTL_MLD_MAXSRCFILTER 21 #define ICMPV6CTL_MLD_SOMAXSRC 22 #define ICMPV6CTL_MLD_VERSION 23 #define ICMPV6CTL_ND6_MAXQLEN 24 #define ICMPV6CTL_NODEINFO_OLDMCPREFIX 25 #define ICMPV6CTL_MAXID 26 #ifdef _KERNEL # ifdef __STDC__ struct nhop_object; struct rttimer; struct in6_multi; # endif void icmp6_paramerror(struct mbuf *, int); int icmp6_errmap(const struct icmp6_hdr *); void icmp6_error(struct mbuf *, int, int, int); void icmp6_error2(struct mbuf *, int, int, int, struct ifnet *); int icmp6_input(struct mbuf **, int *, int); void icmp6_prepare(struct mbuf *); void icmp6_redirect_input(struct mbuf *, int); void icmp6_redirect_output(struct mbuf *, struct nhop_object *); int icmp6_ratelimit(const struct in6_addr *, const int, const int); /* XXX: is this the right place for these macros? */ #define icmp6_ifstat_inc(ifp, tag) \ do { \ if (ifp) \ counter_u64_add((ifp)->if_inet6->icmp6_ifstat[ \ offsetof(struct icmp6_ifstat, tag) / sizeof(uint64_t)], 1);\ } while (/*CONSTCOND*/ 0) #define icmp6_ifoutstat_inc(ifp, type, code) \ do { \ icmp6_ifstat_inc(ifp, ifs6_out_msg); \ if (type < ICMP6_INFOMSG_MASK) \ icmp6_ifstat_inc(ifp, ifs6_out_error); \ switch (type) { \ case ICMP6_DST_UNREACH: \ icmp6_ifstat_inc(ifp, ifs6_out_dstunreach); \ if (code == ICMP6_DST_UNREACH_ADMIN) \ icmp6_ifstat_inc(ifp, ifs6_out_adminprohib); \ break; \ case ICMP6_PACKET_TOO_BIG: \ icmp6_ifstat_inc(ifp, ifs6_out_pkttoobig); \ break; \ case ICMP6_TIME_EXCEEDED: \ icmp6_ifstat_inc(ifp, ifs6_out_timeexceed); \ break; \ case ICMP6_PARAM_PROB: \ icmp6_ifstat_inc(ifp, ifs6_out_paramprob); \ break; \ case ICMP6_ECHO_REQUEST: \ icmp6_ifstat_inc(ifp, ifs6_out_echo); \ break; \ case ICMP6_ECHO_REPLY: \ icmp6_ifstat_inc(ifp, ifs6_out_echoreply); \ break; \ case MLD_LISTENER_QUERY: \ icmp6_ifstat_inc(ifp, ifs6_out_mldquery); \ break; \ case MLD_LISTENER_REPORT: \ icmp6_ifstat_inc(ifp, ifs6_out_mldreport); \ break; \ case MLD_LISTENER_DONE: \ icmp6_ifstat_inc(ifp, ifs6_out_mlddone); \ break; \ case ND_ROUTER_SOLICIT: \ icmp6_ifstat_inc(ifp, ifs6_out_routersolicit); \ break; \ case ND_ROUTER_ADVERT: \ icmp6_ifstat_inc(ifp, ifs6_out_routeradvert); \ break; \ case ND_NEIGHBOR_SOLICIT: \ icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit); \ break; \ case ND_NEIGHBOR_ADVERT: \ icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert); \ break; \ case ND_REDIRECT: \ icmp6_ifstat_inc(ifp, ifs6_out_redirect); \ break; \ } \ } while (/*CONSTCOND*/ 0) #define ICMP6_NODEINFO_FQDNOK 0x1 #define ICMP6_NODEINFO_NODEADDROK 0x2 #define ICMP6_NODEINFO_TMPADDROK 0x4 #define ICMP6_NODEINFO_GLOBALOK 0x8 #endif /* _KERNEL */ #endif /* not _NETINET_ICMP6_H_ */ diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h index 8c069c294593..8c8d5a3236ca 100644 --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -1,414 +1,408 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $KAME: nd6.h,v 1.76 2001/12/18 02:10:31 itojun Exp $ */ #ifndef _NETINET6_ND6_H_ #define _NETINET6_ND6_H_ /* see net/route.h, or net/if_inarp.h */ #ifndef RTF_ANNOUNCE #define RTF_ANNOUNCE RTF_PROTO2 #endif #include #include struct llentry; #define ND6_LLINFO_NOSTATE -2 /* * We don't need the WAITDELETE state any more, but we keep the definition * in a comment line instead of removing it. This is necessary to avoid * unintentionally reusing the value for another purpose, which might * affect backward compatibility with old applications. * (20000711 jinmei@kame.net) */ /* #define ND6_LLINFO_WAITDELETE -1 */ #define ND6_LLINFO_INCOMPLETE 0 #define ND6_LLINFO_REACHABLE 1 #define ND6_LLINFO_STALE 2 #define ND6_LLINFO_DELAY 3 #define ND6_LLINFO_PROBE 4 #define ND6_IS_LLINFO_PROBREACH(n) ((n)->ln_state > ND6_LLINFO_INCOMPLETE) #define ND6_LLINFO_PERMANENT(n) (((n)->la_expire == 0) && ((n)->ln_state > ND6_LLINFO_INCOMPLETE)) #define ND6_IFF_PERFORMNUD 0x1 #define ND6_IFF_ACCEPT_RTADV 0x2 #define ND6_IFF_PREFER_SOURCE 0x4 /* Not used in FreeBSD. */ #define ND6_IFF_IFDISABLED 0x8 /* IPv6 operation is disabled due to * DAD failure. (XXX: not ND-specific) */ #define ND6_IFF_DONT_SET_IFROUTE 0x10 #define ND6_IFF_AUTO_LINKLOCAL 0x20 #define ND6_IFF_NO_RADR 0x40 #define ND6_IFF_NO_PREFER_IFACE 0x80 /* XXX: not related to ND. */ #define ND6_IFF_NO_DAD 0x100 #define ND6_IFF_STABLEADDR 0x800 -#ifdef EXPERIMENTAL -/* XXX: not related to ND. */ -#define ND6_IFF_IPV6_ONLY 0x200 /* draft-ietf-6man-ipv6only-flag */ -#define ND6_IFF_IPV6_ONLY_MANUAL 0x400 -#define ND6_IFF_IPV6_ONLY_MASK (ND6_IFF_IPV6_ONLY|ND6_IFF_IPV6_ONLY_MANUAL) -#endif struct in6_nbrinfo { char ifname[IFNAMSIZ]; /* if name, e.g. "en0" */ struct in6_addr addr; /* IPv6 address of the neighbor */ long asked; /* number of queries already sent for this addr */ int isrouter; /* if it acts as a router */ int state; /* reachability state */ int expire; /* lifetime for NDP state transition */ }; /* Sysctls, shared with user space. */ struct in6_defrouter { struct sockaddr_in6 rtaddr; u_char flags; u_short rtlifetime; u_long expire; u_short if_index; }; struct in6_prefix { struct sockaddr_in6 prefix; struct prf_ra raflags; u_char prefixlen; u_char origin; u_int32_t vltime; u_int32_t pltime; time_t expire; u_int32_t flags; int refcnt; u_short if_index; u_short advrtrs; /* number of advertisement routers */ /* struct sockaddr_in6 advrtr[] */ }; struct in6_ndireq { char ifname[IFNAMSIZ]; struct nd_ifinfo { uint32_t linkmtu; /* LinkMTU */ uint32_t maxmtu; /* Upper bound of LinkMTU */ uint32_t basereachable; /* BaseReachableTime */ uint32_t reachable; /* Reachable Time */ uint32_t retrans; /* Retrans Timer */ uint32_t flags; /* Flags */ int recalctm; /* BaseReacable re-calculation timer */ uint8_t chlim; /* CurHopLimit */ /* * The below members are not used. They came from KAME and * are hanging around to preserve ABI compatibility of the * SIOCGIFINFO_IN6 ioctl. * The original comment documented the random* members as a * privacy extension for addrconf. */ uint8_t initialized; /* compat: always 1 */ uint8_t randomseed0[8]; /* upper 64 bits of MD5 digest */ uint8_t randomseed1[8]; /* lower 64 bits (the EUI64 IFID?) */ uint8_t randomid[8]; /* current random ID */ } ndi; }; struct in6_ndifreq { char ifname[IFNAMSIZ]; u_long ifindex; }; /* Prefix status */ #define NDPRF_ONLINK 0x1 #define NDPRF_DETACHED 0x2 /* ND6 NA output flags */ #define ND6_NA_OPT_LLA 0x01 #define ND6_NA_CARP_MASTER 0x02 /* ND6 queue flags */ #define ND6_QUEUE_FLAG_NEWGUA 0x01 /* new global unicast address event */ #define ND6_QUEUE_FLAG_LLADDR 0x02 /* link-layer address change event */ #define ND6_QUEUE_FLAG_ANYCAST 0x04 /* delay NA for anycast address */ #define ND6_QUEUE_FLAG_PROXY 0x08 /* delay NA for proxy address */ /* GRAND specific flags */ #define ND6_QUEUE_GRAND_MASK (ND6_QUEUE_FLAG_NEWGUA|ND6_QUEUE_FLAG_LLADDR) /* protocol constants */ #define MAX_RTR_SOLICITATION_DELAY 1 /* 1sec */ #define RTR_SOLICITATION_INTERVAL 4 /* 4sec */ #define MAX_RTR_SOLICITATIONS 3 #define MAX_ANYCAST_DELAY_TIME 1 /* 1sec */ #define ND6_INFINITE_LIFETIME 0xffffffff #ifdef _KERNEL /* node constants */ #define MAX_REACHABLE_TIME 3600000 /* msec */ #define REACHABLE_TIME 30000 /* msec */ #define RETRANS_TIMER 1000 /* msec */ #define MIN_RANDOM_FACTOR 512 /* 1024 * 0.5 */ #define MAX_RANDOM_FACTOR 1536 /* 1024 * 1.5 */ #define DEF_TEMP_VALID_LIFETIME 172800 /* 2 days */ #define DEF_TEMP_PREFERRED_LIFETIME 86400 /* 1 day */ #define TEMPADDR_REGEN_ADVANCE 5 /* sec */ #define TEMP_MAX_DESYNC_FACTOR_BASE 300 /* 5 min */ #define ND_COMPUTE_RTIME(x) \ (((MIN_RANDOM_FACTOR * (x >> 10)) + (arc4random() & \ ((MAX_RANDOM_FACTOR - MIN_RANDOM_FACTOR) * (x >> 10)))) /1000) #define MAX_NEIGHBOR_ADVERTISEMENT 3 /* RFC4891 Section 10 */ struct nd_defrouter { TAILQ_ENTRY(nd_defrouter) dr_entry; struct in6_addr rtaddr; u_char raflags; /* flags on RA message */ u_short rtlifetime; u_long expire; struct ifnet *ifp; int installed; /* is installed into kernel routing table */ u_int refcnt; }; struct nd_prefixctl { struct ifnet *ndpr_ifp; /* prefix */ struct sockaddr_in6 ndpr_prefix; u_char ndpr_plen; u_int32_t ndpr_vltime; /* advertised valid lifetime */ u_int32_t ndpr_pltime; /* advertised preferred lifetime */ struct prf_ra ndpr_flags; }; LIST_HEAD(nd_prhead, nd_prefix); struct nd_prefix { struct ifnet *ndpr_ifp; LIST_ENTRY(nd_prefix) ndpr_entry; struct sockaddr_in6 ndpr_prefix; /* prefix */ struct in6_addr ndpr_mask; /* netmask derived from the prefix */ u_int32_t ndpr_vltime; /* advertised valid lifetime */ u_int32_t ndpr_pltime; /* advertised preferred lifetime */ time_t ndpr_expire; /* expiration time of the prefix */ time_t ndpr_preferred; /* preferred time of the prefix */ time_t ndpr_lastupdate; /* reception time of last advertisement */ struct prf_ra ndpr_flags; u_int32_t ndpr_stateflags; /* actual state flags */ /* list of routers that advertise the prefix: */ LIST_HEAD(pr_rtrhead, nd_pfxrouter) ndpr_advrtrs; u_char ndpr_plen; int ndpr_addrcnt; /* count of derived addresses */ volatile u_int ndpr_refcnt; }; #define ndpr_raf ndpr_flags #define ndpr_raf_onlink ndpr_flags.onlink #define ndpr_raf_auto ndpr_flags.autonomous #define ndpr_raf_router ndpr_flags.router struct nd_pfxrouter { LIST_ENTRY(nd_pfxrouter) pfr_entry; struct nd_defrouter *router; }; /* nd6.c */ VNET_DECLARE(int, nd6_mmaxtries); VNET_DECLARE(struct nd_prhead, nd_prefix); VNET_DECLARE(int, nd6_debug); #define V_nd6_mmaxtries VNET(nd6_mmaxtries) #define V_nd_prefix VNET(nd_prefix) #define V_nd6_debug VNET(nd6_debug) /* Lock for the prefix and default router lists. */ VNET_DECLARE(struct rwlock, nd6_lock); VNET_DECLARE(uint64_t, nd6_list_genid); #define V_nd6_lock VNET(nd6_lock) #define V_nd6_list_genid VNET(nd6_list_genid) #define ND6_RLOCK() rw_rlock(&V_nd6_lock) #define ND6_RUNLOCK() rw_runlock(&V_nd6_lock) #define ND6_WLOCK() rw_wlock(&V_nd6_lock) #define ND6_WUNLOCK() rw_wunlock(&V_nd6_lock) #define ND6_TRY_UPGRADE() rw_try_upgrade(&V_nd6_lock) #define ND6_WLOCK_ASSERT() rw_assert(&V_nd6_lock, RA_WLOCKED) #define ND6_RLOCK_ASSERT() rw_assert(&V_nd6_lock, RA_RLOCKED) #define ND6_LOCK_ASSERT() rw_assert(&V_nd6_lock, RA_LOCKED) #define ND6_UNLOCK_ASSERT() rw_assert(&V_nd6_lock, RA_UNLOCKED) /* Mutex for prefix onlink/offlink transitions. */ VNET_DECLARE(struct mtx, nd6_onlink_mtx); #define V_nd6_onlink_mtx VNET(nd6_onlink_mtx) #define ND6_ONLINK_LOCK() mtx_lock(&V_nd6_onlink_mtx) #define ND6_ONLINK_TRYLOCK() mtx_trylock(&V_nd6_onlink_mtx) #define ND6_ONLINK_UNLOCK() mtx_unlock(&V_nd6_onlink_mtx) #define ND6_ONLINK_LOCK_ASSERT() mtx_assert(&V_nd6_onlink_mtx, MA_OWNED) #define ND6_ONLINK_UNLOCK_ASSERT() mtx_assert(&V_nd6_onlink_mtx, MA_NOTOWNED) #define nd6log(x) do { if (V_nd6_debug) log x; } while (/*CONSTCOND*/ 0) /* nd6_rtr.c */ VNET_DECLARE(int, nd6_defifindex); VNET_DECLARE(int, ip6_desync_factor); /* seconds */ VNET_DECLARE(uint32_t, ip6_temp_max_desync_factor); /* seconds */ VNET_DECLARE(u_int32_t, ip6_temp_preferred_lifetime); /* seconds */ VNET_DECLARE(u_int32_t, ip6_temp_valid_lifetime); /* seconds */ VNET_DECLARE(int, ip6_temp_regen_advance); /* seconds */ #define V_nd6_defifindex VNET(nd6_defifindex) #define V_ip6_desync_factor VNET(ip6_desync_factor) #define V_ip6_temp_max_desync_factor VNET(ip6_temp_max_desync_factor) #define V_ip6_temp_preferred_lifetime VNET(ip6_temp_preferred_lifetime) #define V_ip6_temp_valid_lifetime VNET(ip6_temp_valid_lifetime) #define V_ip6_temp_regen_advance VNET(ip6_temp_regen_advance) union nd_opts { struct nd_opt_hdr *nd_opt_array[16]; /* max = ND_OPT_NONCE */ struct { struct nd_opt_hdr *zero; struct nd_opt_hdr *src_lladdr; struct nd_opt_hdr *tgt_lladdr; struct nd_opt_prefix_info *pi_beg; /* multiple opts, start */ struct nd_opt_rd_hdr *rh; struct nd_opt_mtu *mtu; struct nd_opt_hdr *__res6; struct nd_opt_hdr *__res7; struct nd_opt_hdr *__res8; struct nd_opt_hdr *__res9; struct nd_opt_hdr *__res10; struct nd_opt_hdr *__res11; struct nd_opt_hdr *__res12; struct nd_opt_hdr *__res13; struct nd_opt_nonce *nonce; struct nd_opt_hdr *__res15; struct nd_opt_hdr *search; /* multiple opts */ struct nd_opt_hdr *last; /* multiple opts */ int done; struct nd_opt_prefix_info *pi_end;/* multiple opts, end */ } nd_opt_each; }; #define nd_opts_src_lladdr nd_opt_each.src_lladdr #define nd_opts_tgt_lladdr nd_opt_each.tgt_lladdr #define nd_opts_pi nd_opt_each.pi_beg #define nd_opts_pi_end nd_opt_each.pi_end #define nd_opts_rh nd_opt_each.rh #define nd_opts_mtu nd_opt_each.mtu #define nd_opts_nonce nd_opt_each.nonce #define nd_opts_search nd_opt_each.search #define nd_opts_last nd_opt_each.last #define nd_opts_done nd_opt_each.done /* XXX: need nd6_var.h?? */ /* nd6.c */ void nd6_init(void); #ifdef VIMAGE void nd6_destroy(void); #endif void nd6_ifattach(struct ifnet *); void nd6_ifdetach(struct ifnet *); int nd6_is_addr_neighbor(const struct sockaddr_in6 *, struct ifnet *); void nd6_option_init(void *, int, union nd_opts *); struct nd_opt_hdr *nd6_option(union nd_opts *); int nd6_options(union nd_opts *); struct llentry *nd6_lookup(const struct in6_addr *, int, struct ifnet *); void nd6_setmtu(struct ifnet *); void nd6_llinfo_setstate(struct llentry *lle, int newstate); void nd6_timer(void *); void nd6_purge(struct ifnet *); int nd6_resolve_addr(struct ifnet *ifp, int flags, const struct sockaddr *dst, char *desten, uint32_t *pflags); int nd6_resolve(struct ifnet *, int, struct mbuf *, const struct sockaddr *, u_char *, uint32_t *, struct llentry **); int nd6_ioctl(u_long, caddr_t, struct ifnet *); void nd6_cache_lladdr(struct ifnet *, struct in6_addr *, char *, int, int, int); bool nd6_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle, char *lladdr); struct mbuf *nd6_grab_holdchain(struct llentry *); int nd6_flush_holdchain(struct ifnet *, struct llentry *, struct mbuf *); void nd6_flush_children_holdchain(struct ifnet *, struct llentry *); int nd6_add_ifa_lle(struct in6_ifaddr *); void nd6_rem_ifa_lle(struct in6_ifaddr *, int); int nd6_output_ifp(struct ifnet *, struct ifnet *, struct mbuf *, struct sockaddr_in6 *, struct route *); struct rib_head; struct rib_cmd_info; void nd6_subscription_cb(struct rib_head *rnh, struct rib_cmd_info *rc, void *arg); /* nd6_nbr.c */ void nd6_na_input(struct mbuf *, int, int); void nd6_na_output(struct ifnet *, const struct in6_addr *, const struct in6_addr *, u_long, int, struct sockaddr *); void nd6_ns_input(struct mbuf *, int, int); void nd6_ns_output(struct ifnet *, const struct in6_addr *, const struct in6_addr *, const struct in6_addr *, uint8_t *); caddr_t nd6_ifptomac(struct ifnet *); void nd6_dad_init(void); void nd6_dad_start(struct ifaddr *, int); void nd6_dad_stop(struct ifaddr *); void nd6_grand_start(struct ifaddr *, uint32_t); void nd6_queue_stop(struct ifaddr *); /* nd6_rtr.c */ void nd6_rs_input(struct mbuf *, int, int); void nd6_ra_input(struct mbuf *, int, int); void nd6_ifnet_link_event(void *, struct ifnet *, int); struct nd_defrouter *defrouter_lookup(const struct in6_addr *, struct ifnet *); struct nd_defrouter *defrouter_lookup_locked(const struct in6_addr *, struct ifnet *); void defrouter_reset(void); void defrouter_select_fib(int fibnum); void defrouter_rele(struct nd_defrouter *); bool defrouter_remove(struct in6_addr *, struct ifnet *); bool nd6_defrouter_list_empty(void); void nd6_defrouter_flush_all(void); void nd6_defrouter_purge(struct ifnet *); void nd6_defrouter_timer(void); void nd6_defrouter_init(void); int nd6_prelist_add(struct nd_prefixctl *, struct nd_defrouter *, struct nd_prefix **); void nd6_prefix_unlink(struct nd_prefix *, struct nd_prhead *); void nd6_prefix_del(struct nd_prefix *); void nd6_prefix_ref(struct nd_prefix *); void nd6_prefix_rele(struct nd_prefix *); int nd6_prefix_offlink(struct nd_prefix *); void pfxlist_onlink_check(void); struct nd_prefix *nd6_prefix_lookup(struct nd_prefixctl *); void rt6_flush(struct in6_addr *, struct ifnet *); int nd6_setdefaultiface(int); int in6_tmpifadd(const struct in6_ifaddr *, int, int); #endif /* _KERNEL */ #endif /* _NETINET6_ND6_H_ */ diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index 6f415408daae..5819370a3011 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -1,2656 +1,2544 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $KAME: nd6_rtr.c,v 1.111 2001/04/27 01:37:15 jinmei Exp $ */ #include "opt_inet.h" #include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_IP6NDP, "ip6ndp", "IPv6 Neighbor Discovery"); static struct nd_defrouter *defrtrlist_update(struct nd_defrouter *); static int prelist_update(struct nd_prefixctl *, struct nd_defrouter *, bool, int); static int nd6_prefix_onlink(struct nd_prefix *); static int in6_get_tmp_ifid(struct in6_aliasreq *); TAILQ_HEAD(nd6_drhead, nd_defrouter); VNET_DEFINE_STATIC(struct nd6_drhead, nd6_defrouter); #define V_nd6_defrouter VNET(nd6_defrouter) VNET_DECLARE(int, nd6_recalc_reachtm_interval); #define V_nd6_recalc_reachtm_interval VNET(nd6_recalc_reachtm_interval) VNET_DEFINE_STATIC(struct ifnet *, nd6_defifp); VNET_DEFINE(int, nd6_defifindex); #define V_nd6_defifp VNET(nd6_defifp) VNET_DEFINE(int, ip6_use_tempaddr) = 0; VNET_DEFINE(bool, ip6_use_stableaddr) = 1; VNET_DEFINE(int, ip6_desync_factor); VNET_DEFINE(uint32_t, ip6_temp_max_desync_factor) = TEMP_MAX_DESYNC_FACTOR_BASE; VNET_DEFINE(u_int32_t, ip6_temp_preferred_lifetime) = DEF_TEMP_PREFERRED_LIFETIME; VNET_DEFINE(u_int32_t, ip6_temp_valid_lifetime) = DEF_TEMP_VALID_LIFETIME; VNET_DEFINE(int, ip6_temp_regen_advance) = TEMPADDR_REGEN_ADVANCE; -#ifdef EXPERIMENTAL -VNET_DEFINE_STATIC(int, nd6_ignore_ipv6_only_ra) = 1; -#define V_nd6_ignore_ipv6_only_ra VNET(nd6_ignore_ipv6_only_ra) -SYSCTL_INT(_net_inet6_icmp6, OID_AUTO, - nd6_ignore_ipv6_only_ra, CTLFLAG_VNET | CTLFLAG_RW, - &VNET_NAME(nd6_ignore_ipv6_only_ra), 0, - "Ignore the 'IPv6-Only flag' in RA messages in compliance with " - "draft-ietf-6man-ipv6only-flag"); -#endif - /* RTPREF_MEDIUM has to be 0! */ #define RTPREF_HIGH 1 #define RTPREF_MEDIUM 0 #define RTPREF_LOW (-1) #define RTPREF_RESERVED (-2) #define RTPREF_INVALID (-3) /* internal */ static void defrouter_ref(struct nd_defrouter *dr) { refcount_acquire(&dr->refcnt); } void defrouter_rele(struct nd_defrouter *dr) { if (refcount_release(&dr->refcnt)) free(dr, M_IP6NDP); } /* * Remove a router from the global list and optionally stash it in a * caller-supplied queue. */ static void defrouter_unlink(struct nd_defrouter *dr, struct nd6_drhead *drq) { ND6_WLOCK_ASSERT(); TAILQ_REMOVE(&V_nd6_defrouter, dr, dr_entry); V_nd6_list_genid++; if (drq != NULL) TAILQ_INSERT_TAIL(drq, dr, dr_entry); } /* * Receive Router Solicitation Message - just for routers. * Router solicitation/advertisement is mostly managed by userland program * (rtadvd) so here we have no function like nd6_ra_output(). * * Based on RFC 2461 */ void nd6_rs_input(struct mbuf *m, int off, int icmp6len) { struct ifnet *ifp; struct ip6_hdr *ip6; struct nd_router_solicit *nd_rs; struct in6_addr saddr6; union nd_opts ndopts; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; char *lladdr; int lladdrlen; ifp = m->m_pkthdr.rcvif; /* * Accept RS only when V_ip6_forwarding=1 and the interface has * no ND6_IFF_ACCEPT_RTADV. */ if (!V_ip6_forwarding || ifp->if_inet6->nd_flags & ND6_IFF_ACCEPT_RTADV) goto freeit; /* RFC 6980: Nodes MUST silently ignore fragments */ if(m->m_flags & M_FRAGMENTED) goto freeit; /* Sanity checks */ ip6 = mtod(m, struct ip6_hdr *); if (__predict_false(ip6->ip6_hlim != 255)) { ICMP6STAT_INC(icp6s_invlhlim); nd6log((LOG_ERR, "%s: invalid hlim (%d) from %s to %s on %s\n", __func__, ip6->ip6_hlim, ip6_sprintf(ip6bufs, &ip6->ip6_src), ip6_sprintf(ip6bufd, &ip6->ip6_dst), if_name(ifp))); goto bad; } /* * Don't update the neighbor cache, if src = ::. * This indicates that the src has no IP address assigned yet. */ saddr6 = ip6->ip6_src; if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) goto freeit; if (m->m_len < off + icmp6len) { m = m_pullup(m, off + icmp6len); if (m == NULL) { IP6STAT_INC(ip6s_exthdrtoolong); return; } } ip6 = mtod(m, struct ip6_hdr *); nd_rs = (struct nd_router_solicit *)((caddr_t)ip6 + off); icmp6len -= sizeof(*nd_rs); nd6_option_init(nd_rs + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { nd6log((LOG_INFO, "%s: invalid ND option, ignored\n", __func__)); /* nd6_options have incremented stats */ goto freeit; } lladdr = NULL; lladdrlen = 0; if (ndopts.nd_opts_src_lladdr) { lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { nd6log((LOG_INFO, "%s: lladdrlen mismatch for %s (if %d, RS packet %d)\n", __func__, ip6_sprintf(ip6bufs, &saddr6), ifp->if_addrlen, lladdrlen - 2)); goto bad; } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0); freeit: m_freem(m); return; bad: ICMP6STAT_INC(icp6s_badrs); m_freem(m); } -#ifdef EXPERIMENTAL -/* - * An initial update routine for draft-ietf-6man-ipv6only-flag. - * We need to iterate over all default routers for the given - * interface to see whether they are all advertising the "S" - * (IPv6-Only) flag. If they do set, otherwise unset, the - * interface flag we later use to filter on. - * - * XXXGL: The use of IF_ADDR_WLOCK (previously it was IF_AFDATA_LOCK) in this - * function is quite strange. - */ -static void -defrtr_ipv6_only_ifp(struct ifnet *ifp) -{ - struct nd_defrouter *dr; - bool ipv6_only, ipv6_only_old; -#ifdef INET - struct epoch_tracker et; - struct ifaddr *ifa; - bool has_ipv4_addr; -#endif - - if (V_nd6_ignore_ipv6_only_ra != 0) - return; - - ipv6_only = true; - ND6_RLOCK(); - TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry) - if (dr->ifp == ifp && - (dr->raflags & ND_RA_FLAG_IPV6_ONLY) == 0) - ipv6_only = false; - ND6_RUNLOCK(); - - IF_ADDR_WLOCK(ifp); - ipv6_only_old = ifp->if_inet6->nd_flags & ND6_IFF_IPV6_ONLY; - IF_ADDR_WUNLOCK(ifp); - - /* If nothing changed, we have an early exit. */ - if (ipv6_only == ipv6_only_old) - return; - -#ifdef INET - /* - * Should we want to set the IPV6-ONLY flag, check if the - * interface has a non-0/0 and non-link-local IPv4 address - * configured on it. If it has we will assume working - * IPv4 operations and will clear the interface flag. - */ - has_ipv4_addr = false; - if (ipv6_only) { - NET_EPOCH_ENTER(et); - CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { - if (ifa->ifa_addr->sa_family != AF_INET) - continue; - if (in_canforward( - satosin(ifa->ifa_addr)->sin_addr)) { - has_ipv4_addr = true; - break; - } - } - NET_EPOCH_EXIT(et); - } - if (ipv6_only && has_ipv4_addr) { - log(LOG_NOTICE, "%s rcvd RA w/ IPv6-Only flag set but has IPv4 " - "configured, ignoring IPv6-Only flag.\n", ifp->if_xname); - ipv6_only = false; - } -#endif - - IF_ADDR_WLOCK(ifp); - if (ipv6_only) - ifp->if_inet6->nd_flags |= ND6_IFF_IPV6_ONLY; - else - ifp->if_inet6->nd_flags &= ~ND6_IFF_IPV6_ONLY; - IF_ADDR_WUNLOCK(ifp); - -#ifdef notyet - /* Send notification of flag change. */ -#endif -} - -static void -defrtr_ipv6_only_ipf_down(struct ifnet *ifp) -{ - - IF_ADDR_WLOCK(ifp); - ifp->if_inet6->nd_flags &= ~ND6_IFF_IPV6_ONLY; - IF_ADDR_WUNLOCK(ifp); -} -#endif /* EXPERIMENTAL */ - void nd6_ifnet_link_event(void *arg __unused, struct ifnet *ifp, int linkstate) { /* * XXX-BZ we might want to trigger re-evaluation of our default router * availability. E.g., on link down the default router might be * unreachable but a different interface might still have connectivity. */ -#ifdef EXPERIMENTAL - if (linkstate == LINK_STATE_DOWN) - defrtr_ipv6_only_ipf_down(ifp); -#endif } static void nd6_ra_opt_pi(struct nd_opt_hdr *pt, struct ifnet *ifp, struct nd_router_advert *nd_ra, struct nd_defrouter *dr, bool auth, bool mcast) { struct nd_opt_prefix_info *pi = NULL; struct nd_prefixctl pr; char ip6bufs[INET6_ADDRSTRLEN]; if (pt->nd_opt_type != ND_OPT_PREFIX_INFORMATION) return; pi = (struct nd_opt_prefix_info *)pt; if (pi->nd_opt_pi_len != 4) { nd6log((LOG_INFO, "%s: invalid option len %d for prefix " "information option, ignored\n", __func__, pi->nd_opt_pi_len)); return; } if (pi->nd_opt_pi_prefix_len > 128) { nd6log((LOG_INFO, "%s: invalid prefix len %d for prefix " "information option, ignored\n", __func__, pi->nd_opt_pi_prefix_len)); return; } if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) || IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) { nd6log((LOG_INFO, "%s: invalid prefix %s, ignored\n", __func__, ip6_sprintf(ip6bufs, &pi->nd_opt_pi_prefix))); return; } bzero(&pr, sizeof(pr)); pr.ndpr_prefix.sin6_family = AF_INET6; pr.ndpr_prefix.sin6_len = sizeof(pr.ndpr_prefix); pr.ndpr_prefix.sin6_addr = pi->nd_opt_pi_prefix; pr.ndpr_ifp = ifp; pr.ndpr_raf_onlink = (pi->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) ? 1 : 0; pr.ndpr_raf_auto = (pi->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) ? 1 : 0; pr.ndpr_plen = pi->nd_opt_pi_prefix_len; pr.ndpr_vltime = ntohl(pi->nd_opt_pi_valid_time); pr.ndpr_pltime = ntohl(pi->nd_opt_pi_preferred_time); (void)prelist_update(&pr, dr, auth, mcast); } static void nd6_ra_opt_mtu(struct nd_opt_mtu *optmtu, struct ifnet *ifp, struct in6_addr saddr6) { struct in6_ifextra *ndi; char ip6bufs[INET6_ADDRSTRLEN]; uint32_t mtu, maxmtu; ndi = ifp->if_inet6; if (optmtu->nd_opt_mtu_len != 1) return; mtu = (uint32_t)ntohl(optmtu->nd_opt_mtu_mtu); /* lower bound */ if (mtu < IPV6_MMTU) { nd6log((LOG_INFO, "%s: bogus mtu option mtu=%u sent from %s, " "ignoring\n", __func__, mtu, ip6_sprintf(ip6bufs, &saddr6))); return; } /* upper bound */ maxmtu = (ndi->nd_maxmtu && ndi->nd_maxmtu < ifp->if_mtu) ? ndi->nd_maxmtu : ifp->if_mtu; if (mtu <= maxmtu) { if (ndi->nd_linkmtu != mtu) { ndi->nd_linkmtu = mtu; rt_updatemtu(ifp); } } else { nd6log((LOG_INFO, "%s: bogus mtu=%u sent from %s; " "exceeds maxmtu %u, ignoring\n", __func__, mtu, ip6_sprintf(ip6bufs, &saddr6), maxmtu)); } } static int nd6_ra_opt_src_lladdr(struct nd_opt_hdr *opthdr, struct ifnet *ifp, struct in6_addr saddr6) { char ip6bufs[INET6_ADDRSTRLEN]; char *lladdr = NULL; int lladdrlen = 0; if (opthdr != NULL) { lladdr = (char *)(opthdr + 1); lladdrlen = opthdr->nd_opt_len << 3; } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { nd6log((LOG_INFO, "%s: lladdrlen mismatch for %s (if %d, RA packet %d)\n", __func__, ip6_sprintf(ip6bufs, &saddr6), ifp->if_addrlen, lladdrlen - 2)); return (-1); } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_ADVERT, 0); /* * Installing a link-layer address might change the state of the * router's neighbor cache, which might also affect our on-link * detection of adveritsed prefixes. */ pfxlist_onlink_check(); return (0); } /* * Receive Router Advertisement Message. * * Based on RFC 2461 * TODO: on-link bit on prefix information * TODO: ND_RA_FLAG_{OTHER,MANAGED} processing */ void nd6_ra_input(struct mbuf *m, int off, int icmp6len) { struct ifnet *ifp; struct in6_ifextra *ndi; struct ip6_hdr *ip6; struct nd_router_advert *nd_ra; struct nd_opt_hdr *pt; struct in6_addr saddr6; struct nd_defrouter dr0, *dr; union nd_opts ndopts; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; uint32_t advreachable; bool mcast, auth; /* * We only accept RAs only when the per-interface flag * ND6_IFF_ACCEPT_RTADV is on the receiving interface. */ ifp = m->m_pkthdr.rcvif; ndi = ifp->if_inet6; if (!(ndi->nd_flags & ND6_IFF_ACCEPT_RTADV)) goto freeit; /* RFC 6980: Nodes MUST silently ignore fragments */ if(m->m_flags & M_FRAGMENTED) goto freeit; ip6 = mtod(m, struct ip6_hdr *); /* RFC 4861 section 6.1.2: hlim must be 255 */ if (__predict_false(ip6->ip6_hlim != 255)) { ICMP6STAT_INC(icp6s_invlhlim); nd6log((LOG_ERR, "%s: invalid hlim (%d) from %s to %s on %s\n", __func__, ip6->ip6_hlim, ip6_sprintf(ip6bufs, &ip6->ip6_src), ip6_sprintf(ip6bufd, &ip6->ip6_dst), if_name(ifp))); goto bad; } saddr6 = ip6->ip6_src; /* RFC 4861 section 6.1.2: source address must be link-local */ if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) { nd6log((LOG_ERR, "%s: src %s is not link-local\n", __func__, ip6_sprintf(ip6bufs, &saddr6))); goto bad; } if (m->m_len < off + icmp6len) { m = m_pullup(m, off + icmp6len); if (m == NULL) { IP6STAT_INC(ip6s_exthdrtoolong); return; } } nd_ra = (struct nd_router_advert *)((caddr_t)ip6 + off); icmp6len -= sizeof(*nd_ra); nd6_option_init(nd_ra + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { nd6log((LOG_INFO, "%s: invalid ND option, ignored\n", __func__)); /* nd6_options have incremented stats */ goto freeit; } dr = NULL; mcast = false; /* remember if this is a multicasted advertisement */ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) mcast = true; bzero(&dr0, sizeof(dr0)); dr0.rtaddr = saddr6; dr0.raflags = nd_ra->nd_ra_flags_reserved; /* * Effectively-disable routes from RA messages when * ND6_IFF_NO_RADR enabled on the receiving interface or * (ip6.forwarding == 1 && ip6.rfc6204w3 != 1). */ if ((ndi->nd_flags & ND6_IFF_NO_RADR) || (V_ip6_forwarding && !V_ip6_rfc6204w3)) dr0.rtlifetime = 0; else dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime); dr0.expire = time_uptime + dr0.rtlifetime; dr0.ifp = ifp; /* * RFC 4861 6.3.4: RA fields such as Cur Hop Limit, * Reachable Time, and Retrans Timer may be unspecified. * In such cases, the parameter should be ignored. */ if (nd_ra->nd_ra_reachable) { advreachable = ntohl(nd_ra->nd_ra_reachable); if (advreachable <= MAX_REACHABLE_TIME && ndi->nd_basereachable != advreachable) { ndi->nd_basereachable = advreachable; ndi->nd_reachable = ND_COMPUTE_RTIME(ndi->nd_basereachable); ndi->nd_recalc_timer = V_nd6_recalc_reachtm_interval; } } if (nd_ra->nd_ra_retransmit) ndi->nd_retrans = ntohl(nd_ra->nd_ra_retransmit); if (nd_ra->nd_ra_curhoplimit) { if (ndi->nd_curhoplimit < nd_ra->nd_ra_curhoplimit) ndi->nd_curhoplimit = nd_ra->nd_ra_curhoplimit; else if (ndi->nd_curhoplimit != nd_ra->nd_ra_curhoplimit) { log(LOG_ERR, "RA with a lower CurHopLimit sent from " "%s on %s (current = %d, received = %d). " "Ignored.\n", ip6_sprintf(ip6bufs, &ip6->ip6_src), if_name(ifp), ndi->nd_curhoplimit, nd_ra->nd_ra_curhoplimit); } } dr = defrtrlist_update(&dr0); -#ifdef EXPERIMENTAL - defrtr_ipv6_only_ifp(ifp); -#endif /* Prefix Information */ if (ndopts.nd_opts_pi != NULL) { /* * Authenticity for NA consists authentication for * both IP header and IP datagrams, doesn't it ? */ auth = ((m->m_flags & M_AUTHIPHDR) && (m->m_flags & M_AUTHIPDGM)); for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi; pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end; pt = (struct nd_opt_hdr *)((caddr_t)pt + (pt->nd_opt_len << 3))) { nd6_ra_opt_pi(pt, ifp, nd_ra, dr, auth, mcast); } } if (dr != NULL) { defrouter_rele(dr); dr = NULL; } /* MTU */ if (ndopts.nd_opts_mtu != NULL) nd6_ra_opt_mtu(ndopts.nd_opts_mtu, ifp, saddr6); /* Source link layer address */ if (nd6_ra_opt_src_lladdr(ndopts.nd_opts_src_lladdr, ifp, saddr6) != 0) goto bad; freeit: m_freem(m); return; bad: ICMP6STAT_INC(icp6s_badra); m_freem(m); } /* PFXRTR */ static struct nd_pfxrouter * pfxrtr_lookup(struct nd_prefix *pr, struct nd_defrouter *dr) { struct nd_pfxrouter *search; ND6_LOCK_ASSERT(); LIST_FOREACH(search, &pr->ndpr_advrtrs, pfr_entry) { if (search->router == dr) break; } return (search); } static void pfxrtr_add(struct nd_prefix *pr, struct nd_defrouter *dr) { struct nd_pfxrouter *new; bool update; ND6_UNLOCK_ASSERT(); ND6_RLOCK(); if (pfxrtr_lookup(pr, dr) != NULL) { ND6_RUNLOCK(); return; } ND6_RUNLOCK(); new = malloc(sizeof(*new), M_IP6NDP, M_NOWAIT | M_ZERO); if (new == NULL) return; defrouter_ref(dr); new->router = dr; ND6_WLOCK(); if (pfxrtr_lookup(pr, dr) == NULL) { LIST_INSERT_HEAD(&pr->ndpr_advrtrs, new, pfr_entry); update = true; } else { /* We lost a race to add the reference. */ defrouter_rele(dr); free(new, M_IP6NDP); update = false; } ND6_WUNLOCK(); if (update) pfxlist_onlink_check(); } static void pfxrtr_del(struct nd_pfxrouter *pfr) { ND6_WLOCK_ASSERT(); LIST_REMOVE(pfr, pfr_entry); defrouter_rele(pfr->router); free(pfr, M_IP6NDP); } /* Default router list processing sub routines. */ static void defrouter_addreq(struct nd_defrouter *new) { uint32_t fibnum = new->ifp->if_fib; struct rib_cmd_info rc = {}; int error = 0; NET_EPOCH_ASSERT(); struct sockaddr_in6 gw = { .sin6_family = AF_INET6, .sin6_len = sizeof(struct sockaddr_in6), .sin6_addr = new->rtaddr, }; error = rib_add_default_route(fibnum, AF_INET6, new->ifp, (struct sockaddr *)&gw, &rc); if (error == 0) { struct nhop_object *nh = nhop_select_func(rc.rc_nh_new, 0); rt_routemsg(RTM_ADD, rc.rc_rt, nh, fibnum); new->installed = 1; } } /* * Remove the default route for a given router. * This is just a subroutine function for defrouter_select_fib(), and * should not be called from anywhere else. */ static void defrouter_delreq(struct nd_defrouter *dr) { uint32_t fibnum = dr->ifp->if_fib; struct epoch_tracker et; struct rib_cmd_info rc; int error; struct sockaddr_in6 dst = { .sin6_family = AF_INET6, .sin6_len = sizeof(struct sockaddr_in6), }; struct sockaddr_in6 gw = { .sin6_family = AF_INET6, .sin6_len = sizeof(struct sockaddr_in6), .sin6_addr = dr->rtaddr, }; NET_EPOCH_ENTER(et); error = rib_del_route_px(fibnum, (struct sockaddr *)&dst, 0, rib_match_gw, (struct sockaddr *)&gw, 0, &rc); if (error == 0) { struct nhop_object *nh = nhop_select_func(rc.rc_nh_old, 0); rt_routemsg(RTM_DELETE, rc.rc_rt, nh, fibnum); } NET_EPOCH_EXIT(et); dr->installed = 0; } static void defrouter_del(struct nd_defrouter *dr) { struct nd_defrouter *deldr = NULL; struct nd_prefix *pr; struct nd_pfxrouter *pfxrtr; ND6_UNLOCK_ASSERT(); /* * Flush all the routing table entries that use the router * as a next hop. */ if (dr->ifp->if_inet6->nd_flags & ND6_IFF_ACCEPT_RTADV) rt6_flush(&dr->rtaddr, dr->ifp); -#ifdef EXPERIMENTAL - defrtr_ipv6_only_ifp(dr->ifp); -#endif - if (dr->installed) { deldr = dr; defrouter_delreq(dr); } /* * Also delete all the pointers to the router in each prefix lists. */ ND6_WLOCK(); LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) { if ((pfxrtr = pfxrtr_lookup(pr, dr)) != NULL) pfxrtr_del(pfxrtr); } ND6_WUNLOCK(); pfxlist_onlink_check(); /* * If the router is the primary one, choose a new one. * Note that defrouter_select_fib() will remove the current * gateway from the routing table. */ if (deldr) defrouter_select_fib(deldr->ifp->if_fib); /* * Release the list reference. */ defrouter_rele(dr); } struct nd_defrouter * defrouter_lookup_locked(const struct in6_addr *addr, struct ifnet *ifp) { struct nd_defrouter *dr; ND6_LOCK_ASSERT(); TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry) if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr)) { defrouter_ref(dr); return (dr); } return (NULL); } struct nd_defrouter * defrouter_lookup(const struct in6_addr *addr, struct ifnet *ifp) { struct nd_defrouter *dr; ND6_RLOCK(); dr = defrouter_lookup_locked(addr, ifp); ND6_RUNLOCK(); return (dr); } /* * Remove all default routes from default router list. */ void defrouter_reset(void) { struct nd_defrouter *dr, **dra; int count, i; count = i = 0; /* * We can't delete routes with the ND lock held, so make a copy of the * current default router list and use that when deleting routes. */ ND6_RLOCK(); TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry) count++; ND6_RUNLOCK(); dra = malloc(count * sizeof(*dra), M_TEMP, M_WAITOK | M_ZERO); ND6_RLOCK(); TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry) { if (i == count) break; defrouter_ref(dr); dra[i++] = dr; } ND6_RUNLOCK(); for (i = 0; i < count && dra[i] != NULL; i++) { defrouter_delreq(dra[i]); defrouter_rele(dra[i]); } free(dra, M_TEMP); /* * XXX should we also nuke any default routers in the kernel, by * going through them by rtalloc1()? */ } /* * Look up a matching default router list entry and remove it. Returns true if a * matching entry was found, false otherwise. */ bool defrouter_remove(struct in6_addr *addr, struct ifnet *ifp) { struct nd_defrouter *dr; ND6_WLOCK(); dr = defrouter_lookup_locked(addr, ifp); if (dr == NULL) { ND6_WUNLOCK(); return (false); } defrouter_unlink(dr, NULL); ND6_WUNLOCK(); defrouter_del(dr); defrouter_rele(dr); return (true); } /* * for default router selection * regards router-preference field as a 2-bit signed integer */ static int rtpref(struct nd_defrouter *dr) { switch (dr->raflags & ND_RA_FLAG_RTPREF_MASK) { case ND_RA_FLAG_RTPREF_HIGH: return (RTPREF_HIGH); case ND_RA_FLAG_RTPREF_MEDIUM: case ND_RA_FLAG_RTPREF_RSV: return (RTPREF_MEDIUM); case ND_RA_FLAG_RTPREF_LOW: return (RTPREF_LOW); default: /* * This case should never happen. If it did, it would mean a * serious bug of kernel internal. We thus always bark here. * Or, can we even panic? */ log(LOG_ERR, "rtpref: impossible RA flag %x\n", dr->raflags); return (RTPREF_INVALID); } /* NOTREACHED */ } static bool is_dr_reachable(const struct nd_defrouter *dr) { struct llentry *ln = NULL; ln = nd6_lookup(&dr->rtaddr, LLE_SF(AF_INET6, 0), dr->ifp); if (ln == NULL) return (false); bool reachable = ND6_IS_LLINFO_PROBREACH(ln); LLE_RUNLOCK(ln); return reachable; } /* * Default Router Selection according to Section 6.3.6 of RFC 2461 and * draft-ietf-ipngwg-router-selection: * 1) Routers that are reachable or probably reachable should be preferred. * If we have more than one (probably) reachable router, prefer ones * with the highest router preference. * 2) When no routers on the list are known to be reachable or * probably reachable, routers SHOULD be selected in a round-robin * fashion, regardless of router preference values. * 3) If the Default Router List is empty, assume that all * destinations are on-link. * * We assume nd_defrouter is sorted by router preference value. * Since the code below covers both with and without router preference cases, * we do not need to classify the cases by ifdef. * * At this moment, we do not try to install more than one default router, * even when the multipath routing is available, because we're not sure about * the benefits for stub hosts comparing to the risk of making the code * complicated and the possibility of introducing bugs. * * We maintain a single list of routers for multiple FIBs, only considering one * at a time based on the receiving interface's FIB. If @fibnum is RT_ALL_FIBS, * we do the whole thing multiple times. */ void defrouter_select_fib(int fibnum) { struct epoch_tracker et; struct nd_defrouter *dr, *selected_dr, *installed_dr; if (fibnum == RT_ALL_FIBS) { for (fibnum = 0; fibnum < rt_numfibs; fibnum++) { defrouter_select_fib(fibnum); } return; } ND6_RLOCK(); /* * Let's handle easy case (3) first: * If default router list is empty, there's nothing to be done. */ if (TAILQ_EMPTY(&V_nd6_defrouter)) { ND6_RUNLOCK(); return; } /* * Search for a (probably) reachable router from the list. * We just pick up the first reachable one (if any), assuming that * the ordering rule of the list described in defrtrlist_update(). */ selected_dr = installed_dr = NULL; NET_EPOCH_ENTER(et); TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry) { if (dr->ifp->if_fib != fibnum) continue; if (selected_dr == NULL && is_dr_reachable(dr)) { selected_dr = dr; defrouter_ref(selected_dr); } if (dr->installed) { if (installed_dr == NULL) { installed_dr = dr; defrouter_ref(installed_dr); } else { /* * this should not happen. * warn for diagnosis. */ log(LOG_ERR, "defrouter_select_fib: more than " "one router is installed\n"); } } } /* * If none of the default routers was found to be reachable, * round-robin the list regardless of preference. * Otherwise, if we have an installed router, check if the selected * (reachable) router should really be preferred to the installed one. * We only prefer the new router when the old one is not reachable * or when the new one has a really higher preference value. */ if (selected_dr == NULL) { if (installed_dr == NULL || TAILQ_NEXT(installed_dr, dr_entry) == NULL) dr = TAILQ_FIRST(&V_nd6_defrouter); else dr = TAILQ_NEXT(installed_dr, dr_entry); /* Ensure we select a router for this FIB. */ TAILQ_FOREACH_FROM(dr, &V_nd6_defrouter, dr_entry) { if (dr->ifp->if_fib == fibnum) { selected_dr = dr; defrouter_ref(selected_dr); break; } } } else if (installed_dr != NULL) { if (is_dr_reachable(installed_dr) && rtpref(selected_dr) <= rtpref(installed_dr)) { defrouter_rele(selected_dr); selected_dr = installed_dr; } } ND6_RUNLOCK(); /* * If we selected a router for this FIB and it's different * than the installed one, remove the installed router and * install the selected one in its place. */ if (installed_dr != selected_dr) { if (installed_dr != NULL) { defrouter_delreq(installed_dr); defrouter_rele(installed_dr); } if (selected_dr != NULL) defrouter_addreq(selected_dr); } if (selected_dr != NULL) defrouter_rele(selected_dr); NET_EPOCH_EXIT(et); } static struct nd_defrouter * defrtrlist_update(struct nd_defrouter *new) { struct nd_defrouter *dr, *n; uint64_t genid; int oldpref; bool writelocked; if (new->rtlifetime == 0) { defrouter_remove(&new->rtaddr, new->ifp); return (NULL); } ND6_RLOCK(); writelocked = false; restart: dr = defrouter_lookup_locked(&new->rtaddr, new->ifp); if (dr != NULL) { oldpref = rtpref(dr); /* override */ dr->raflags = new->raflags; /* XXX flag check */ dr->rtlifetime = new->rtlifetime; dr->expire = new->expire; /* * If the preference does not change, there's no need * to sort the entries. Also make sure the selected * router is still installed in the kernel. */ if (dr->installed && rtpref(new) == oldpref) { if (writelocked) ND6_WUNLOCK(); else ND6_RUNLOCK(); return (dr); } } /* * The router needs to be reinserted into the default router * list, so upgrade to a write lock. If that fails and the list * has potentially changed while the lock was dropped, we'll * redo the lookup with the write lock held. */ if (!writelocked) { writelocked = true; if (!ND6_TRY_UPGRADE()) { genid = V_nd6_list_genid; ND6_RUNLOCK(); ND6_WLOCK(); if (genid != V_nd6_list_genid) goto restart; } } if (dr != NULL) { /* * The preferred router may have changed, so relocate this * router. */ TAILQ_REMOVE(&V_nd6_defrouter, dr, dr_entry); n = dr; } else { n = malloc(sizeof(*n), M_IP6NDP, M_NOWAIT | M_ZERO); if (n == NULL) { ND6_WUNLOCK(); return (NULL); } memcpy(n, new, sizeof(*n)); /* Initialize with an extra reference for the caller. */ refcount_init(&n->refcnt, 2); } /* * Insert the new router in the Default Router List; * The Default Router List should be in the descending order * of router-preferece. Routers with the same preference are * sorted in the arriving time order. */ /* insert at the end of the group */ TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry) { if (rtpref(n) > rtpref(dr)) break; } if (dr != NULL) TAILQ_INSERT_BEFORE(dr, n, dr_entry); else TAILQ_INSERT_TAIL(&V_nd6_defrouter, n, dr_entry); V_nd6_list_genid++; ND6_WUNLOCK(); defrouter_select_fib(new->ifp->if_fib); return (n); } static void in6_init_prefix_ltimes(struct nd_prefix *ndpr) { ndpr->ndpr_preferred = in6_expire_time(ndpr->ndpr_pltime); ndpr->ndpr_expire = in6_expire_time(ndpr->ndpr_vltime); } static void in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6) { lt6->ia6t_preferred = in6_expire_time(lt6->ia6t_pltime); lt6->ia6t_expire = in6_expire_time(lt6->ia6t_vltime); } static struct in6_ifaddr * in6_ifadd(struct nd_prefixctl *pr, int mcast) { struct ifnet *ifp = pr->ndpr_ifp; struct ifaddr *ifa; struct in6_aliasreq ifra; struct in6_ifaddr *ia = NULL, *ib = NULL; int error, plen0; struct in6_addr *ifid_addr = NULL, mask, newaddr; int prefixlen = pr->ndpr_plen; int updateflags; char ip6buf[INET6_ADDRSTRLEN]; in6_prefixlen2mask(&mask, prefixlen); /* * find a link-local address (will be interface ID). * Is it really mandatory? Theoretically, a global or a site-local * address can be configured without a link-local address, if we * have a unique interface identifier... * * it is not mandatory to have a link-local address, we can generate * interface identifier on the fly. we do this because: * (1) it should be the easiest way to find interface identifier. * (2) RFC2462 5.4 suggesting the use of the same interface identifier * for multiple addresses on a single interface, and possible shortcut * of DAD. we omitted DAD for this reason in the past. * (3) a user can prevent autoconfiguration of global address * by removing link-local address by hand (this is partly because we * don't have other way to control the use of IPv6 on an interface. * this has been our design choice - cf. NRL's "ifconfig auto"). * (4) it is easier to manage when an interface has addresses * with the same interface identifier, than to have multiple addresses * with different interface identifiers. * * If using stable privacy generation, generate a new address with * the algorithm specified in RFC 7217 section 5 */ /* make ifaddr */ in6_prepare_ifra(&ifra, &pr->ndpr_prefix.sin6_addr, &mask); if (ifp->if_inet6->nd_flags & ND6_IFF_STABLEADDR) { memcpy(&newaddr, &pr->ndpr_prefix.sin6_addr, sizeof(pr->ndpr_prefix.sin6_addr)); if(!in6_get_stableifid(ifp, &newaddr, prefixlen)) return NULL; } else { ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0); /* 0 is OK? */ if (ifa) { ib = (struct in6_ifaddr *)ifa; ifid_addr = &ib->ia_addr.sin6_addr; /* prefixlen + ifidlen must be equal to 128 */ plen0 = in6_mask2len(&ib->ia_prefixmask.sin6_addr, NULL); if (prefixlen != plen0) { ifa_free(ifa); ifid_addr = NULL; nd6log((LOG_DEBUG, "%s: wrong prefixlen for %s (prefix=%d ifid=%d)\n", __func__, if_name(ifp), prefixlen, 128 - plen0)); } } /* No suitable LL address, get the ifid directly */ if (ifid_addr == NULL) { ifa = ifa_alloc(sizeof(struct in6_ifaddr), M_NOWAIT); if (ifa != NULL) { ib = (struct in6_ifaddr *)ifa; ifid_addr = &ib->ia_addr.sin6_addr; if(in6_get_ifid(ifp, NULL, ifid_addr) != 0) { nd6log((LOG_DEBUG, "%s: failed to get ifid for %s\n", __func__, if_name(ifp))); ifa_free(ifa); ifid_addr = NULL; } } } if (ifid_addr == NULL) { nd6log((LOG_INFO, "%s: could not determine ifid for %s\n", __func__, if_name(ifp))); return NULL; } memcpy(&newaddr, &ib->ia_addr.sin6_addr, sizeof(ib->ia_addr.sin6_addr)); ifa_free(ifa); } IN6_MASK_ADDR(&ifra.ifra_addr.sin6_addr, &mask); /* interface ID */ ifra.ifra_addr.sin6_addr.s6_addr32[0] |= (newaddr.s6_addr32[0] & ~mask.s6_addr32[0]); ifra.ifra_addr.sin6_addr.s6_addr32[1] |= (newaddr.s6_addr32[1] & ~mask.s6_addr32[1]); ifra.ifra_addr.sin6_addr.s6_addr32[2] |= (newaddr.s6_addr32[2] & ~mask.s6_addr32[2]); ifra.ifra_addr.sin6_addr.s6_addr32[3] |= (newaddr.s6_addr32[3] & ~mask.s6_addr32[3]); /* lifetimes. */ ifra.ifra_lifetime.ia6t_vltime = pr->ndpr_vltime; ifra.ifra_lifetime.ia6t_pltime = pr->ndpr_pltime; /* XXX: scope zone ID? */ ifra.ifra_flags |= IN6_IFF_AUTOCONF; /* obey autoconf */ /* * Make sure that we do not have this address already. This should * usually not happen, but we can still see this case, e.g., if we * have manually configured the exact address to be configured. */ ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr); if (ifa != NULL) { ifa_free(ifa); /* this should be rare enough to make an explicit log */ log(LOG_INFO, "in6_ifadd: %s is already configured\n", ip6_sprintf(ip6buf, &ifra.ifra_addr.sin6_addr)); return (NULL); } /* * Allocate ifaddr structure, link into chain, etc. * If we are going to create a new address upon receiving a multicasted * RA, we need to impose a random delay before starting DAD. * [draft-ietf-ipv6-rfc2462bis-02.txt, Section 5.4.2] */ updateflags = 0; if (mcast) updateflags |= IN6_IFAUPDATE_DADDELAY; if ((error = in6_update_ifa(ifp, &ifra, NULL, updateflags)) != 0) { nd6log((LOG_ERR, "%s: failed to make ifaddr %s on %s (errno=%d)\n", __func__, ip6_sprintf(ip6buf, &ifra.ifra_addr.sin6_addr), if_name(ifp), error)); return (NULL); /* ifaddr must not have been allocated. */ } ia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr); /* * XXXRW: Assumption of non-NULLness here might not be true with * fine-grained locking -- should we validate it? Or just return * earlier ifa rather than looking it up again? */ return (ia); /* this is always non-NULL and referenced. */ } static struct nd_prefix * nd6_prefix_lookup_locked(struct nd_prefixctl *key) { struct nd_prefix *search; ND6_LOCK_ASSERT(); LIST_FOREACH(search, &V_nd_prefix, ndpr_entry) { if (key->ndpr_ifp == search->ndpr_ifp && key->ndpr_plen == search->ndpr_plen && in6_are_prefix_equal(&key->ndpr_prefix.sin6_addr, &search->ndpr_prefix.sin6_addr, key->ndpr_plen)) { nd6_prefix_ref(search); break; } } return (search); } struct nd_prefix * nd6_prefix_lookup(struct nd_prefixctl *key) { struct nd_prefix *search; ND6_RLOCK(); search = nd6_prefix_lookup_locked(key); ND6_RUNLOCK(); return (search); } void nd6_prefix_ref(struct nd_prefix *pr) { refcount_acquire(&pr->ndpr_refcnt); } void nd6_prefix_rele(struct nd_prefix *pr) { if (refcount_release(&pr->ndpr_refcnt)) { KASSERT(LIST_EMPTY(&pr->ndpr_advrtrs), ("prefix %p has advertising routers", pr)); free(pr, M_IP6NDP); } } int nd6_prelist_add(struct nd_prefixctl *pr, struct nd_defrouter *dr, struct nd_prefix **newp) { struct nd_prefix *new; char ip6buf[INET6_ADDRSTRLEN]; int error; new = malloc(sizeof(*new), M_IP6NDP, M_NOWAIT | M_ZERO); if (new == NULL) return (ENOMEM); refcount_init(&new->ndpr_refcnt, newp != NULL ? 2 : 1); new->ndpr_ifp = pr->ndpr_ifp; new->ndpr_prefix = pr->ndpr_prefix; new->ndpr_plen = pr->ndpr_plen; new->ndpr_vltime = pr->ndpr_vltime; new->ndpr_pltime = pr->ndpr_pltime; new->ndpr_flags = pr->ndpr_flags; new->ndpr_lastupdate = time_uptime; in6_init_prefix_ltimes(new); /* initialization */ LIST_INIT(&new->ndpr_advrtrs); in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen); /* make prefix in the canonical form */ IN6_MASK_ADDR(&new->ndpr_prefix.sin6_addr, &new->ndpr_mask); ND6_WLOCK(); LIST_INSERT_HEAD(&V_nd_prefix, new, ndpr_entry); V_nd6_list_genid++; ND6_WUNLOCK(); /* ND_OPT_PI_FLAG_ONLINK processing */ if (new->ndpr_raf_onlink) { struct epoch_tracker et; ND6_ONLINK_LOCK(); NET_EPOCH_ENTER(et); if ((error = nd6_prefix_onlink(new)) != 0) { nd6log((LOG_ERR, "%s: failed to make the prefix %s/%d " "on-link on %s (errno=%d)\n", __func__, ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr), pr->ndpr_plen, if_name(pr->ndpr_ifp), error)); /* proceed anyway. XXX: is it correct? */ } NET_EPOCH_EXIT(et); ND6_ONLINK_UNLOCK(); } if (dr != NULL) pfxrtr_add(new, dr); if (newp != NULL) *newp = new; return (0); } /* * Remove a prefix from the prefix list and optionally stash it in a * caller-provided list. * * The ND6 lock must be held. */ void nd6_prefix_unlink(struct nd_prefix *pr, struct nd_prhead *list) { ND6_WLOCK_ASSERT(); LIST_REMOVE(pr, ndpr_entry); V_nd6_list_genid++; if (list != NULL) LIST_INSERT_HEAD(list, pr, ndpr_entry); } /* * Free an unlinked prefix, first marking it off-link if necessary. */ void nd6_prefix_del(struct nd_prefix *pr) { struct nd_pfxrouter *pfr, *next; int e; char ip6buf[INET6_ADDRSTRLEN]; KASSERT(pr->ndpr_addrcnt == 0, ("prefix %p has referencing addresses", pr)); ND6_UNLOCK_ASSERT(); /* * Though these flags are now meaningless, we'd rather keep the value * of pr->ndpr_raf_onlink and pr->ndpr_raf_auto not to confuse users * when executing "ndp -p". */ if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0) { ND6_ONLINK_LOCK(); if ((e = nd6_prefix_offlink(pr)) != 0) { nd6log((LOG_ERR, "%s: failed to make the prefix %s/%d offlink on %s " "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr), pr->ndpr_plen, if_name(pr->ndpr_ifp), e)); /* what should we do? */ } ND6_ONLINK_UNLOCK(); } /* Release references to routers that have advertised this prefix. */ ND6_WLOCK(); LIST_FOREACH_SAFE(pfr, &pr->ndpr_advrtrs, pfr_entry, next) pfxrtr_del(pfr); ND6_WUNLOCK(); nd6_prefix_rele(pr); pfxlist_onlink_check(); } static int prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr, bool auth, int mcast) { struct in6_ifaddr *ia6 = NULL, *ia6_match = NULL; struct ifaddr *ifa; struct ifnet *ifp = new->ndpr_ifp; struct nd_prefix *pr; int error = 0; struct in6_addrlifetime lt6_tmp; char ip6buf[INET6_ADDRSTRLEN]; bool has_temporary = false; NET_EPOCH_ASSERT(); if ((pr = nd6_prefix_lookup(new)) != NULL) { /* * nd6_prefix_lookup() ensures that pr and new have the same * prefix on a same interface. */ /* * Update prefix information. Note that the on-link (L) bit * and the autonomous (A) bit should NOT be changed from 1 * to 0. */ if (new->ndpr_raf_onlink == 1) pr->ndpr_raf_onlink = 1; if (new->ndpr_raf_auto == 1) pr->ndpr_raf_auto = 1; if (new->ndpr_raf_onlink) { pr->ndpr_vltime = new->ndpr_vltime; pr->ndpr_pltime = new->ndpr_pltime; in6_init_prefix_ltimes(pr); pr->ndpr_lastupdate = time_uptime; } if (new->ndpr_raf_onlink && (pr->ndpr_stateflags & NDPRF_ONLINK) == 0) { ND6_ONLINK_LOCK(); if ((error = nd6_prefix_onlink(pr)) != 0) { nd6log((LOG_ERR, "%s: failed to make the prefix %s/%d " "on-link on %s (errno=%d)\n", __func__, ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr), pr->ndpr_plen, if_name(pr->ndpr_ifp), error)); /* proceed anyway. XXX: is it correct? */ } ND6_ONLINK_UNLOCK(); } if (dr != NULL) pfxrtr_add(pr, dr); } else { if (new->ndpr_vltime == 0) goto end; if (new->ndpr_raf_onlink == 0 && new->ndpr_raf_auto == 0) goto end; error = nd6_prelist_add(new, dr, &pr); if (error != 0) { nd6log((LOG_NOTICE, "%s: nd6_prelist_add() failed for " "the prefix %s/%d on %s (errno=%d)\n", __func__, ip6_sprintf(ip6buf, &new->ndpr_prefix.sin6_addr), new->ndpr_plen, if_name(new->ndpr_ifp), error)); goto end; /* we should just give up in this case. */ } /* * XXX: from the ND point of view, we can ignore a prefix * with the on-link bit being zero. However, we need a * prefix structure for references from autoconfigured * addresses. Thus, we explicitly make sure that the prefix * itself expires now. */ if (pr->ndpr_raf_onlink == 0) { pr->ndpr_vltime = 0; pr->ndpr_pltime = 0; in6_init_prefix_ltimes(pr); } } /* * Address autoconfiguration based on Section 5.5.3 of RFC 2462. * Note that pr must be non NULL at this point. */ /* 5.5.3 (a). Ignore the prefix without the A bit set. */ if (!new->ndpr_raf_auto) goto end; /* * 5.5.3 (b). the link-local prefix should have been ignored in * nd6_ra_input. */ /* 5.5.3 (c). Consistency check on lifetimes: pltime <= vltime. */ if (new->ndpr_pltime > new->ndpr_vltime) { error = EINVAL; /* XXX: won't be used */ goto end; } /* * 5.5.3 (d). If the prefix advertised is not equal to the prefix of * an address configured by stateless autoconfiguration already in the * list of addresses associated with the interface, and the Valid * Lifetime is not 0, form an address. We first check if we have * a matching prefix. * Note: we apply a clarification in rfc2462bis-02 here. We only * consider autoconfigured addresses while RFC2462 simply said * "address". */ CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { struct in6_ifaddr *ifa6; u_int32_t remaininglifetime; if (ifa->ifa_addr->sa_family != AF_INET6) continue; ifa6 = (struct in6_ifaddr *)ifa; /* * We only consider autoconfigured addresses as per rfc2462bis. */ if (!(ifa6->ia6_flags & IN6_IFF_AUTOCONF)) continue; /* * Spec is not clear here, but I believe we should concentrate * on unicast (i.e. not anycast) addresses. * XXX: other ia6_flags? detached or duplicated? */ if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0) continue; /* * Ignore the address if it is not associated with a prefix * or is associated with a prefix that is different from this * one. (pr is never NULL here) */ if (ifa6->ia6_ndpr != pr) continue; /* * An already autoconfigured address matched. Now that we * are sure there is at least one matched address, we can * proceed to 5.5.3. (e): update the lifetimes according to the * "two hours" rule and the privacy extension. * We apply some clarifications in rfc2462bis: * - use remaininglifetime instead of storedlifetime as a * variable name * - remove the dead code in the "two-hour" rule */ #define TWOHOUR (120*60) lt6_tmp = ifa6->ia6_lifetime; if (lt6_tmp.ia6t_vltime == ND6_INFINITE_LIFETIME) remaininglifetime = ND6_INFINITE_LIFETIME; else if (time_uptime - ifa6->ia6_updatetime > lt6_tmp.ia6t_vltime) { /* * The case of "invalid" address. We should usually * not see this case. */ remaininglifetime = 0; } else remaininglifetime = lt6_tmp.ia6t_vltime - (time_uptime - ifa6->ia6_updatetime); /* when not updating, keep the current stored lifetime. */ lt6_tmp.ia6t_vltime = remaininglifetime; if (TWOHOUR < new->ndpr_vltime || remaininglifetime < new->ndpr_vltime) { lt6_tmp.ia6t_vltime = new->ndpr_vltime; } else if (remaininglifetime <= TWOHOUR) { if (auth) { lt6_tmp.ia6t_vltime = new->ndpr_vltime; } } else { /* * new->ndpr_vltime <= TWOHOUR && * TWOHOUR < remaininglifetime */ lt6_tmp.ia6t_vltime = TWOHOUR; } /* The 2 hour rule is not imposed for preferred lifetime. */ lt6_tmp.ia6t_pltime = new->ndpr_pltime; in6_init_address_ltimes(pr, <6_tmp); /* * We need to treat lifetimes for temporary addresses * differently, according to * draft-ietf-ipv6-privacy-addrs-v2-01.txt 3.3 (1); * we only update the lifetimes when they are in the maximum * intervals. */ if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0) { u_int32_t maxvltime, maxpltime; /* * if stable addresses (RFC 7217) are enabled, mark that a temporary address has been found * to avoid generating uneeded extra ones. */ if (ifp->if_inet6->nd_flags & ND6_IFF_STABLEADDR) has_temporary = true; if (V_ip6_temp_valid_lifetime > (u_int32_t)((time_uptime - ifa6->ia6_createtime) + V_ip6_desync_factor)) { maxvltime = V_ip6_temp_valid_lifetime - (time_uptime - ifa6->ia6_createtime) - V_ip6_desync_factor; } else maxvltime = 0; if (V_ip6_temp_preferred_lifetime > (u_int32_t)((time_uptime - ifa6->ia6_createtime) + V_ip6_desync_factor)) { maxpltime = V_ip6_temp_preferred_lifetime - (time_uptime - ifa6->ia6_createtime) - V_ip6_desync_factor; } else maxpltime = 0; if (lt6_tmp.ia6t_vltime == ND6_INFINITE_LIFETIME || lt6_tmp.ia6t_vltime > maxvltime) { lt6_tmp.ia6t_vltime = maxvltime; } if (lt6_tmp.ia6t_pltime == ND6_INFINITE_LIFETIME || lt6_tmp.ia6t_pltime > maxpltime) { lt6_tmp.ia6t_pltime = maxpltime; } } ifa6->ia6_lifetime = lt6_tmp; ifa6->ia6_updatetime = time_uptime; /* * If using stable addresses (RFC 7217) and we still have retries to perform, ignore * addresses already marked as duplicated, since a new one will be generated. * Also ignore addresses marked as temporary, since their generation is orthogonal to * opaque stable ones. * * There is a small race condition, in that the dad_counter could be incremented * between here and when a new address is generated, but this will cause that generation * to fail and no further retries should happen. */ if (ifp->if_inet6->nd_flags & ND6_IFF_STABLEADDR && atomic_load_int(&DAD_FAILURES(ifp)) <= V_ip6_stableaddr_maxretries && ifa6->ia6_flags & (IN6_IFF_DUPLICATED | IN6_IFF_TEMPORARY)) continue; if (ia6_match == NULL) /* remember the first one */ ia6_match = ifa6; } if (ia6_match == NULL && new->ndpr_vltime) { int ifidlen; /* * 5.5.3 (d) (continued) * No address matched and the valid lifetime is non-zero. * Create a new address. */ /* * Prefix Length check: * If the sum of the prefix length and interface identifier * length does not equal 128 bits, the Prefix Information * option MUST be ignored. The length of the interface * identifier is defined in a separate link-type specific * document. */ ifidlen = in6_if2idlen(ifp); if (ifidlen < 0) { /* this should not happen, so we always log it. */ log(LOG_ERR, "prelist_update: IFID undefined (%s)\n", if_name(ifp)); goto end; } if (ifidlen + pr->ndpr_plen != 128) { nd6log((LOG_INFO, "%s: invalid prefixlen %d for %s, ignored\n", __func__, pr->ndpr_plen, if_name(ifp))); goto end; } if ((ia6 = in6_ifadd(new, mcast)) != NULL) { /* * note that we should use pr (not new) for reference. */ pr->ndpr_addrcnt++; ia6->ia6_ndpr = pr; /* * RFC 3041 3.3 (2). * When a new public address is created as described * in RFC2462, also create a new temporary address. * * RFC 3041 3.5. * When an interface connects to a new link, a new * randomized interface identifier should be generated * immediately together with a new set of temporary * addresses. Thus, we specifiy 1 as the 2nd arg of * in6_tmpifadd(). * * Skip this if a temporary address has been marked as * found (happens only if stable addresses (RFC 7217) is in use) */ if (V_ip6_use_tempaddr && !has_temporary) { int e; if ((e = in6_tmpifadd(ia6, 1, 1)) != 0) { nd6log((LOG_NOTICE, "%s: failed to " "create a temporary address " "(errno=%d)\n", __func__, e)); } } ifa_free(&ia6->ia_ifa); /* * A newly added address might affect the status * of other addresses, so we check and update it. * XXX: what if address duplication happens? */ pfxlist_onlink_check(); } else { /* just set an error. do not bark here. */ error = EADDRNOTAVAIL; /* XXX: might be unused. */ } } end: if (pr != NULL) nd6_prefix_rele(pr); return (error); } /* * A supplement function used in the on-link detection below; * detect if a given prefix has a (probably) reachable advertising router. * XXX: lengthy function name... */ static struct nd_pfxrouter * find_pfxlist_reachable_router(struct nd_prefix *pr) { struct epoch_tracker et; struct nd_pfxrouter *pfxrtr; ND6_LOCK_ASSERT(); NET_EPOCH_ENTER(et); LIST_FOREACH(pfxrtr, &pr->ndpr_advrtrs, pfr_entry) { if (is_dr_reachable(pfxrtr->router)) break; } NET_EPOCH_EXIT(et); return (pfxrtr); } /* * Check if each prefix in the prefix list has at least one available router * that advertised the prefix (a router is "available" if its neighbor cache * entry is reachable or probably reachable). * If the check fails, the prefix may be off-link, because, for example, * we have moved from the network but the lifetime of the prefix has not * expired yet. So we should not use the prefix if there is another prefix * that has an available router. * But, if there is no prefix that has an available router, we still regard * all the prefixes as on-link. This is because we can't tell if all the * routers are simply dead or if we really moved from the network and there * is no router around us. */ void pfxlist_onlink_check(void) { struct nd_prefix *pr; struct in6_ifaddr *ifa; struct nd_defrouter *dr; struct nd_pfxrouter *pfxrtr = NULL; struct rm_priotracker in6_ifa_tracker; uint64_t genid; uint32_t flags; ND6_ONLINK_LOCK(); ND6_RLOCK(); /* * Check if there is a prefix that has a reachable advertising * router. */ LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) { if (pr->ndpr_raf_onlink && find_pfxlist_reachable_router(pr)) break; } /* * If we have no such prefix, check whether we still have a router * that does not advertise any prefixes. */ if (pr == NULL) { TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry) { struct nd_prefix *pr0; LIST_FOREACH(pr0, &V_nd_prefix, ndpr_entry) { if ((pfxrtr = pfxrtr_lookup(pr0, dr)) != NULL) break; } if (pfxrtr != NULL) break; } } if (pr != NULL || (!TAILQ_EMPTY(&V_nd6_defrouter) && pfxrtr == NULL)) { /* * There is at least one prefix that has a reachable router, * or at least a router which probably does not advertise * any prefixes. The latter would be the case when we move * to a new link where we have a router that does not provide * prefixes and we configure an address by hand. * Detach prefixes which have no reachable advertising * router, and attach other prefixes. */ LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) { /* XXX: a link-local prefix should never be detached */ if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr) || pr->ndpr_raf_onlink == 0 || pr->ndpr_raf_auto == 0) continue; if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 && find_pfxlist_reachable_router(pr) == NULL) pr->ndpr_stateflags |= NDPRF_DETACHED; else if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 && find_pfxlist_reachable_router(pr) != NULL) pr->ndpr_stateflags &= ~NDPRF_DETACHED; } } else { /* there is no prefix that has a reachable router */ LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) { if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr) || pr->ndpr_raf_onlink == 0 || pr->ndpr_raf_auto == 0) continue; pr->ndpr_stateflags &= ~NDPRF_DETACHED; } } /* * Remove each interface route associated with a (just) detached * prefix, and reinstall the interface route for a (just) attached * prefix. Note that all attempt of reinstallation does not * necessarily success, when a same prefix is shared among multiple * interfaces. Such cases will be handled in nd6_prefix_onlink, * so we don't have to care about them. */ restart: LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) { char ip6buf[INET6_ADDRSTRLEN]; int e; if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr) || pr->ndpr_raf_onlink == 0 || pr->ndpr_raf_auto == 0) continue; flags = pr->ndpr_stateflags & (NDPRF_DETACHED | NDPRF_ONLINK); if (flags == 0 || flags == (NDPRF_DETACHED | NDPRF_ONLINK)) { genid = V_nd6_list_genid; ND6_RUNLOCK(); if ((flags & NDPRF_ONLINK) != 0 && (e = nd6_prefix_offlink(pr)) != 0) { nd6log((LOG_ERR, "%s: failed to make %s/%d offlink " "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr), pr->ndpr_plen, e)); } else if ((flags & NDPRF_ONLINK) == 0 && (e = nd6_prefix_onlink(pr)) != 0) { nd6log((LOG_ERR, "%s: failed to make %s/%d onlink " "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr), pr->ndpr_plen, e)); } ND6_RLOCK(); if (genid != V_nd6_list_genid) goto restart; } } /* * Changes on the prefix status might affect address status as well. * Make sure that all addresses derived from an attached prefix are * attached, and that all addresses derived from a detached prefix are * detached. Note, however, that a manually configured address should * always be attached. * The precise detection logic is same as the one for prefixes. */ IN6_IFADDR_RLOCK(&in6_ifa_tracker); CK_STAILQ_FOREACH(ifa, &V_in6_ifaddrhead, ia_link) { if (!(ifa->ia6_flags & IN6_IFF_AUTOCONF)) continue; if (ifa->ia6_ndpr == NULL) { /* * This can happen when we first configure the address * (i.e. the address exists, but the prefix does not). * XXX: complicated relationships... */ continue; } if (find_pfxlist_reachable_router(ifa->ia6_ndpr)) break; } if (ifa) { CK_STAILQ_FOREACH(ifa, &V_in6_ifaddrhead, ia_link) { if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0) continue; if (ifa->ia6_ndpr == NULL) /* XXX: see above. */ continue; if (find_pfxlist_reachable_router(ifa->ia6_ndpr)) { if (ifa->ia6_flags & IN6_IFF_DETACHED) { ifa->ia6_flags &= ~IN6_IFF_DETACHED; ifa->ia6_flags |= IN6_IFF_TENTATIVE; nd6_dad_start((struct ifaddr *)ifa, 0); } } else { ifa->ia6_flags |= IN6_IFF_DETACHED; } } } else { CK_STAILQ_FOREACH(ifa, &V_in6_ifaddrhead, ia_link) { if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0) continue; if (ifa->ia6_flags & IN6_IFF_DETACHED) { ifa->ia6_flags &= ~IN6_IFF_DETACHED; ifa->ia6_flags |= IN6_IFF_TENTATIVE; /* Do we need a delay in this case? */ nd6_dad_start((struct ifaddr *)ifa, 0); } } } IN6_IFADDR_RUNLOCK(&in6_ifa_tracker); ND6_RUNLOCK(); ND6_ONLINK_UNLOCK(); } /* * Add or remove interface route specified by @dst, @netmask and @ifp. * ifa can be NULL. * Returns 0 on success */ static int nd6_prefix_rtrequest(uint32_t fibnum, int cmd, struct sockaddr_in6 *dst, struct sockaddr_in6 *netmask, struct ifnet *ifp, struct ifaddr *ifa) { struct epoch_tracker et; int error; /* Prepare gateway */ struct sockaddr_dl_short sdl = { .sdl_family = AF_LINK, .sdl_len = sizeof(struct sockaddr_dl_short), .sdl_type = ifp->if_type, .sdl_index = ifp->if_index, }; struct rt_addrinfo info = { .rti_ifa = ifa, .rti_ifp = ifp, .rti_flags = RTF_PINNED | ((netmask != NULL) ? 0 : RTF_HOST), .rti_info = { [RTAX_DST] = (struct sockaddr *)dst, [RTAX_NETMASK] = (struct sockaddr *)netmask, [RTAX_GATEWAY] = (struct sockaddr *)&sdl, }, }; /* Don't set additional per-gw filters on removal */ NET_EPOCH_ENTER(et); error = rib_handle_ifaddr_info(fibnum, cmd, &info); NET_EPOCH_EXIT(et); return (error); } static int nd6_prefix_onlink_rtrequest(struct nd_prefix *pr, struct ifaddr *ifa) { int error; struct sockaddr_in6 mask6 = { .sin6_family = AF_INET6, .sin6_len = sizeof(struct sockaddr_in6), .sin6_addr = pr->ndpr_mask, }; struct sockaddr_in6 *pmask6 = (pr->ndpr_plen != 128) ? &mask6 : NULL; error = nd6_prefix_rtrequest(pr->ndpr_ifp->if_fib, RTM_ADD, &pr->ndpr_prefix, pmask6, pr->ndpr_ifp, ifa); if (error == 0) pr->ndpr_stateflags |= NDPRF_ONLINK; return (error); } static int nd6_prefix_onlink(struct nd_prefix *pr) { struct epoch_tracker et; struct ifaddr *ifa; struct ifnet *ifp = pr->ndpr_ifp; struct nd_prefix *opr; char ip6buf[INET6_ADDRSTRLEN]; int error; ND6_ONLINK_LOCK_ASSERT(); ND6_UNLOCK_ASSERT(); if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0) return (EEXIST); /* * Add the interface route associated with the prefix. Before * installing the route, check if there's the same prefix on another * interface, and the prefix has already installed the interface route. * Although such a configuration is expected to be rare, we explicitly * allow it. */ ND6_RLOCK(); LIST_FOREACH(opr, &V_nd_prefix, ndpr_entry) { if (opr == pr) continue; if ((opr->ndpr_stateflags & NDPRF_ONLINK) == 0) continue; if (!V_rt_add_addr_allfibs && opr->ndpr_ifp->if_fib != pr->ndpr_ifp->if_fib) continue; if (opr->ndpr_plen == pr->ndpr_plen && in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, &opr->ndpr_prefix.sin6_addr, pr->ndpr_plen)) { ND6_RUNLOCK(); return (0); } } ND6_RUNLOCK(); /* * We prefer link-local addresses as the associated interface address. */ /* search for a link-local addr */ NET_EPOCH_ENTER(et); ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY | IN6_IFF_ANYCAST); if (ifa == NULL) { /* XXX: freebsd does not have ifa_ifwithaf */ CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family == AF_INET6) { ifa_ref(ifa); break; } } /* should we care about ia6_flags? */ } if (ifa == NULL) { /* * This can still happen, when, for example, we receive an RA * containing a prefix with the L bit set and the A bit clear, * after removing all IPv6 addresses on the receiving * interface. This should, of course, be rare though. */ nd6log((LOG_NOTICE, "%s: failed to find any ifaddr to add route for a " "prefix(%s/%d) on %s\n", __func__, ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr), pr->ndpr_plen, if_name(ifp))); error = 0; } else { error = nd6_prefix_onlink_rtrequest(pr, ifa); ifa_free(ifa); } NET_EPOCH_EXIT(et); return (error); } int nd6_prefix_offlink(struct nd_prefix *pr) { int error = 0; struct ifnet *ifp = pr->ndpr_ifp; struct nd_prefix *opr; char ip6buf[INET6_ADDRSTRLEN]; uint64_t genid; int a_failure; ND6_ONLINK_LOCK_ASSERT(); ND6_UNLOCK_ASSERT(); if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0) return (EEXIST); struct sockaddr_in6 mask6 = { .sin6_family = AF_INET6, .sin6_len = sizeof(struct sockaddr_in6), .sin6_addr = pr->ndpr_mask, }; struct sockaddr_in6 *pmask6 = (pr->ndpr_plen != 128) ? &mask6 : NULL; error = nd6_prefix_rtrequest(ifp->if_fib, RTM_DELETE, &pr->ndpr_prefix, pmask6, ifp, NULL); a_failure = 1; if (error == 0) { pr->ndpr_stateflags &= ~NDPRF_ONLINK; /* * There might be the same prefix on another interface, * the prefix which could not be on-link just because we have * the interface route (see comments in nd6_prefix_onlink). * If there's one, try to make the prefix on-link on the * interface. */ ND6_RLOCK(); restart: LIST_FOREACH(opr, &V_nd_prefix, ndpr_entry) { /* * KAME specific: detached prefixes should not be * on-link. */ if (opr == pr || (opr->ndpr_stateflags & (NDPRF_ONLINK | NDPRF_DETACHED)) != 0) continue; if (opr->ndpr_plen == pr->ndpr_plen && in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, &opr->ndpr_prefix.sin6_addr, pr->ndpr_plen)) { int e; genid = V_nd6_list_genid; ND6_RUNLOCK(); if ((e = nd6_prefix_onlink(opr)) != 0) { nd6log((LOG_ERR, "%s: failed to recover a prefix " "%s/%d from %s to %s (errno=%d)\n", __func__, ip6_sprintf(ip6buf, &opr->ndpr_prefix.sin6_addr), opr->ndpr_plen, if_name(ifp), if_name(opr->ndpr_ifp), e)); } else a_failure = 0; ND6_RLOCK(); if (genid != V_nd6_list_genid) goto restart; } } ND6_RUNLOCK(); } else { /* XXX: can we still set the NDPRF_ONLINK flag? */ nd6log((LOG_ERR, "%s: failed to delete route: %s/%d on %s (errno=%d)\n", __func__, ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr), pr->ndpr_plen, if_name(ifp), error)); } if (a_failure) lltable_prefix_free(AF_INET6, (struct sockaddr *)&pr->ndpr_prefix, (struct sockaddr *)&mask6, LLE_STATIC); return (error); } /* * Get a randomized interface identifier for a temporary address * Based on RFC 8981, Section 3.3.1. */ static int in6_get_tmp_ifid(struct in6_aliasreq *ifra) { struct in6_addr *addr; if(!is_random_seeded()){ return 1; } addr = &(ifra->ifra_addr.sin6_addr); regen: ifra->ifra_addr.sin6_addr.s6_addr32[2] |= (arc4random() & ~(ifra->ifra_prefixmask.sin6_addr.s6_addr32[2])); ifra->ifra_addr.sin6_addr.s6_addr32[3] |= (arc4random() & ~(ifra->ifra_prefixmask.sin6_addr.s6_addr32[3])); /* * Check if generated address is not inappropriate: * * - Reserved IPv6 Interface Identifiers * (https://www.iana.org/assignments/ipv6-interface-ids/) */ /* Subnet-router anycast: 0000:0000:0000:0000 */ if (!(addr->s6_addr32[2] | addr->s6_addr32[3])) goto regen; /* * IANA Ethernet block: 0200:5EFF:FE00:0000-0200:5EFF:FE00:5212 * Proxy Mobile IPv6: 0200:5EFF:FE00:5213 * IANA Ethernet block: 0200:5EFF:FE00:5214-0200:5EFF:FEFF:FFFF */ if (ntohl(addr->s6_addr32[2]) == 0x02005eff && (ntohl(addr->s6_addr32[3]) & 0Xff000000) == 0xfe000000) goto regen; /* Reserved subnet anycast addresses */ if (ntohl(addr->s6_addr32[2]) == 0xfdffffff && ntohl(addr->s6_addr32[3]) >= 0Xffffff80) goto regen; return 0; } /* * ia0 - corresponding public address */ int in6_tmpifadd(const struct in6_ifaddr *ia0, int forcegen, int delay) { struct ifnet *ifp = ia0->ia_ifa.ifa_ifp; struct in6_ifaddr *newia; struct in6_aliasreq ifra; int error; int trylimit = 3; /* XXX: adhoc value */ int updateflags; time_t vltime0, pltime0; in6_prepare_ifra(&ifra, &ia0->ia_addr.sin6_addr, &ia0->ia_prefixmask.sin6_addr); ifra.ifra_addr = ia0->ia_addr; /* XXX: do we need this ? */ /* clear the old IFID */ IN6_MASK_ADDR(&ifra.ifra_addr.sin6_addr, &ifra.ifra_prefixmask.sin6_addr); again: if (in6_get_tmp_ifid(&ifra) != 0) { nd6log((LOG_NOTICE, "%s: failed to find a good random IFID\n", __func__)); return (EINVAL); } /* * in6_get_tmpifid() quite likely provided a unique interface ID. * However, we may still have a chance to see collision, because * there may be a time lag between generation of the ID and generation * of the address. So, we'll do one more sanity check. */ if (in6_localip(&ifra.ifra_addr.sin6_addr) != 0) { if (trylimit-- > 0) { forcegen = 1; goto again; } /* Give up. Something strange should have happened. */ nd6log((LOG_NOTICE, "%s: failed to find a unique random IFID\n", __func__)); return (EEXIST); } /* * The Valid Lifetime is the lower of the Valid Lifetime of the * public address or TEMP_VALID_LIFETIME. * The Preferred Lifetime is the lower of the Preferred Lifetime * of the public address or TEMP_PREFERRED_LIFETIME - * DESYNC_FACTOR. */ if (ia0->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { vltime0 = IFA6_IS_INVALID(ia0) ? 0 : (ia0->ia6_lifetime.ia6t_vltime - (time_uptime - ia0->ia6_updatetime)); if (vltime0 > V_ip6_temp_valid_lifetime) vltime0 = V_ip6_temp_valid_lifetime; } else vltime0 = V_ip6_temp_valid_lifetime; if (ia0->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { pltime0 = IFA6_IS_DEPRECATED(ia0) ? 0 : (ia0->ia6_lifetime.ia6t_pltime - (time_uptime - ia0->ia6_updatetime)); if (pltime0 > V_ip6_temp_preferred_lifetime - V_ip6_desync_factor){ pltime0 = V_ip6_temp_preferred_lifetime - V_ip6_desync_factor; } } else pltime0 = V_ip6_temp_preferred_lifetime - V_ip6_desync_factor; ifra.ifra_lifetime.ia6t_vltime = vltime0; ifra.ifra_lifetime.ia6t_pltime = pltime0; /* * A temporary address is created only if this calculated Preferred * Lifetime is greater than REGEN_ADVANCE time units. */ if (ifra.ifra_lifetime.ia6t_pltime <= V_ip6_temp_regen_advance) return (0); /* XXX: scope zone ID? */ ifra.ifra_flags |= (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY); /* allocate ifaddr structure, link into chain, etc. */ updateflags = 0; if (delay) updateflags |= IN6_IFAUPDATE_DADDELAY; if ((error = in6_update_ifa(ifp, &ifra, NULL, updateflags)) != 0) return (error); newia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr); if (newia == NULL) { /* XXX: can it happen? */ nd6log((LOG_ERR, "%s: ifa update succeeded, but we got no ifaddr\n", __func__)); return (EINVAL); /* XXX */ } newia->ia6_ndpr = ia0->ia6_ndpr; newia->ia6_ndpr->ndpr_addrcnt++; ifa_free(&newia->ia_ifa); /* * A newly added address might affect the status of other addresses. * XXX: when the temporary address is generated with a new public * address, the onlink check is redundant. However, it would be safe * to do the check explicitly everywhere a new address is generated, * and, in fact, we surely need the check when we create a new * temporary address due to deprecation of an old temporary address. */ pfxlist_onlink_check(); return (0); } static int rt6_deleteroute(const struct rtentry *rt, const struct nhop_object *nh, void *arg) { struct in6_addr *gate = (struct in6_addr *)arg; int nh_rt_flags; if (nh->gw_sa.sa_family != AF_INET6) return (0); if (!IN6_ARE_ADDR_EQUAL(gate, &nh->gw6_sa.sin6_addr)) { return (0); } /* * Do not delete a static route. * XXX: this seems to be a bit ad-hoc. Should we consider the * 'cloned' bit instead? */ nh_rt_flags = nhop_get_rtflags(nh); if ((nh_rt_flags & RTF_STATIC) != 0) return (0); /* * We delete only host route. This means, in particular, we don't * delete default route. */ if ((nh_rt_flags & RTF_HOST) == 0) return (0); return (1); #undef SIN6 } /* * Delete all the routing table entries that use the specified gateway. * XXX: this function causes search through all entries of routing table, so * it shouldn't be called when acting as a router. */ void rt6_flush(struct in6_addr *gateway, struct ifnet *ifp) { /* We'll care only link-local addresses */ if (!IN6_IS_ADDR_LINKLOCAL(gateway)) return; /* XXX Do we really need to walk any but the default FIB? */ rib_foreach_table_walk_del(AF_INET6, rt6_deleteroute, (void *)gateway); } int nd6_setdefaultiface(int ifindex) { if (V_nd6_defifindex != ifindex) { V_nd6_defifindex = ifindex; if (V_nd6_defifindex != 0) { struct epoch_tracker et; /* * XXXGL: this function should use ifnet_byindex_ref! */ NET_EPOCH_ENTER(et); V_nd6_defifp = ifnet_byindex(V_nd6_defifindex); NET_EPOCH_EXIT(et); if (V_nd6_defifp == NULL) return (EINVAL); } else V_nd6_defifp = NULL; /* * Our current implementation assumes one-to-one mapping between * interfaces and links, so it would be natural to use the * default interface as the default link. */ scope6_setdefault(V_nd6_defifp); } return (0); } bool nd6_defrouter_list_empty(void) { return (TAILQ_EMPTY(&V_nd6_defrouter)); } void nd6_defrouter_timer(void) { struct nd_defrouter *dr, *ndr; struct nd6_drhead drq; TAILQ_INIT(&drq); ND6_WLOCK(); TAILQ_FOREACH_SAFE(dr, &V_nd6_defrouter, dr_entry, ndr) if (dr->expire && dr->expire < time_uptime) defrouter_unlink(dr, &drq); ND6_WUNLOCK(); while ((dr = TAILQ_FIRST(&drq)) != NULL) { TAILQ_REMOVE(&drq, dr, dr_entry); defrouter_del(dr); } } /* * Nuke default router list entries toward ifp. * We defer removal of default router list entries that is installed in the * routing table, in order to keep additional side effects as small as possible. */ void nd6_defrouter_purge(struct ifnet *ifp) { struct nd_defrouter *dr, *ndr; struct nd6_drhead drq; TAILQ_INIT(&drq); ND6_WLOCK(); TAILQ_FOREACH_SAFE(dr, &V_nd6_defrouter, dr_entry, ndr) { if (dr->installed) continue; if (dr->ifp == ifp) defrouter_unlink(dr, &drq); } TAILQ_FOREACH_SAFE(dr, &V_nd6_defrouter, dr_entry, ndr) { if (!dr->installed) continue; if (dr->ifp == ifp) defrouter_unlink(dr, &drq); } ND6_WUNLOCK(); /* Delete the unlinked router objects. */ while ((dr = TAILQ_FIRST(&drq)) != NULL) { TAILQ_REMOVE(&drq, dr, dr_entry); defrouter_del(dr); } } void nd6_defrouter_flush_all(void) { struct nd_defrouter *dr; struct nd6_drhead drq; TAILQ_INIT(&drq); ND6_WLOCK(); while ((dr = TAILQ_FIRST(&V_nd6_defrouter)) != NULL) defrouter_unlink(dr, &drq); ND6_WUNLOCK(); while ((dr = TAILQ_FIRST(&drq)) != NULL) { TAILQ_REMOVE(&drq, dr, dr_entry); defrouter_del(dr); } } void nd6_defrouter_init(void) { TAILQ_INIT(&V_nd6_defrouter); } static int nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS) { struct in6_defrouter d; struct nd_defrouter *dr; int error; if (req->newptr != NULL) return (EPERM); error = sysctl_wire_old_buffer(req, 0); if (error != 0) return (error); bzero(&d, sizeof(d)); d.rtaddr.sin6_family = AF_INET6; d.rtaddr.sin6_len = sizeof(d.rtaddr); ND6_RLOCK(); TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry) { d.rtaddr.sin6_addr = dr->rtaddr; error = sa6_recoverscope(&d.rtaddr); if (error != 0) break; d.flags = dr->raflags; d.rtlifetime = dr->rtlifetime; d.expire = dr->expire + (time_second - time_uptime); d.if_index = dr->ifp->if_index; error = SYSCTL_OUT(req, &d, sizeof(d)); if (error != 0) break; } ND6_RUNLOCK(); return (error); } SYSCTL_PROC(_net_inet6_icmp6, ICMPV6CTL_ND6_DRLIST, nd6_drlist, CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, nd6_sysctl_drlist, "S,in6_defrouter", "NDP default router list"); diff --git a/tools/build/options/WITH_EXPERIMENTAL b/tools/build/options/WITH_EXPERIMENTAL deleted file mode 100644 index a5f41addee52..000000000000 --- a/tools/build/options/WITH_EXPERIMENTAL +++ /dev/null @@ -1 +0,0 @@ -Include experimental features in the build. diff --git a/usr.sbin/ndp/Makefile b/usr.sbin/ndp/Makefile index 998860d00a69..aad2380bc879 100644 --- a/usr.sbin/ndp/Makefile +++ b/usr.sbin/ndp/Makefile @@ -1,40 +1,35 @@ # Copyright (c) 1996 WIDE Project. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modifications, are permitted provided that the above copyright notice # and this paragraph are duplicated in all such forms and that any # documentation, advertising materials, and other materials related to # such distribution and use acknowledge that the software was developed # by the WIDE Project, Japan. The name of the Project may not be used to # endorse or promote products derived from this software without # specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' # AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT # LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE. .include PROG= ndp MAN= ndp.8 SRCS= ndp.c LIBADD= xo CFLAGS+= -I. -I${.CURDIR} CFLAGS+= -D_U_="" -.if ${MK_EXPERIMENTAL} != "no" -CFLAGS+= -DEXPERIMENTAL -CFLAGS+= -DDRAFT_IETF_6MAN_IPV6ONLY_FLAG -.endif - .if ${MK_NETLINK_SUPPORT} != "no" SRCS+= ndp_netlink.c .else CFLAGS+=-DWITHOUT_NETLINK .endif WARNS?= 3 .include diff --git a/usr.sbin/ndp/ndp.c b/usr.sbin/ndp/ndp.c index cbca8ec20941..7521be752c80 100644 --- a/usr.sbin/ndp/ndp.c +++ b/usr.sbin/ndp/ndp.c @@ -1,1563 +1,1558 @@ /* $KAME: ndp.c,v 1.104 2003/06/27 07:48:39 itojun Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1984, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Sun Microsystems, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Based on: * The Regents of the University of California. All rights reserved.\n"; */ /* * ndp - display, set, delete and flush neighbor cache */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ndp.h" #define NEXTADDR(w, s) \ if (rtm->rtm_addrs & (w)) { \ bcopy((char *)&s, cp, sizeof(s)); \ cp += SA_SIZE(&s); \ } static pid_t pid; static int32_t thiszone; /* time difference with gmt */ static int s = -1; static char host_buf[NI_MAXHOST]; /* getnameinfo() */ static char ifix_buf[IFNAMSIZ]; /* if_indextoname() */ static int file(char *); static int set(int, char **); static void get(char *); static int delete(char *); static int dump(struct sockaddr_in6 *, int); static struct in6_nbrinfo *getnbrinfo(struct in6_addr *, int, int); static int ndp_ether_aton(char *, u_char *); static void usage(void) __dead2; static void ifinfo(char *, int, char **); static void rtrlist(void); static void plist(void); static void pfx_flush(void); static void rtr_flush(void); static void harmonize_rtr(void); #ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */ static void getdefif(void); static void setdefif(char *); #endif #ifdef WITHOUT_NETLINK static void getsocket(void); static int rtmsg(int); #endif static const char *rtpref_str[] = { "medium", /* 00 */ "high", /* 01 */ "rsv", /* 10 */ "low" /* 11 */ }; struct ndp_opts opts = {}; #define NDP_XO_VERSION "1" bool valid_type(int if_type) { switch (if_type) { case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023: case IFT_ISO88024: case IFT_ISO88025: case IFT_L2VLAN: case IFT_BRIDGE: return (true); break; } return (false); } static int32_t utc_offset(void) { time_t t; struct tm *tm; t = time(NULL); tm = localtime(&t); assert(tm->tm_gmtoff > INT32_MIN && tm->tm_gmtoff < INT32_MAX); return (tm->tm_gmtoff); } int main(int argc, char **argv) { int ch, mode = 0; char *arg = NULL; int ret = 0; pid = getpid(); thiszone = utc_offset(); argc = xo_parse_args(argc, argv); if (argc < 0) exit(1); xo_set_version(NDP_XO_VERSION); xo_open_container("ndp"); while ((ch = getopt(argc, argv, "acd:f:Ii:nprstA:HPR")) != -1) switch (ch) { case 'a': case 'c': case 'p': case 'r': case 'H': case 'P': case 'R': case 's': case 'I': if (mode) { usage(); /*NOTREACHED*/ } mode = ch; arg = NULL; break; case 'f': exit(file(optarg) ? 1 : 0); case 'd': case 'i': if (mode) { usage(); /*NOTREACHED*/ } mode = ch; arg = optarg; break; case 'n': opts.nflag = true; break; case 't': opts.tflag = true; break; case 'A': if (mode) { usage(); /*NOTREACHED*/ } mode = 'a'; opts.repeat = atoi(optarg); if (opts.repeat < 0) { usage(); /*NOTREACHED*/ } break; default: usage(); } argc -= optind; argv += optind; switch (mode) { case 'a': case 'c': if (argc != 0) { usage(); /*NOTREACHED*/ } dump(0, mode == 'c'); break; case 'd': if (argc != 0) { usage(); /*NOTREACHED*/ } xo_open_list("neighbor-cache"); ret = delete(arg); xo_close_list("neighbor-cache"); break; case 'I': #ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */ if (argc > 1) { usage(); /*NOTREACHED*/ } else if (argc == 1) { if (strcmp(*argv, "delete") == 0 || if_nametoindex(*argv)) setdefif(*argv); else xo_errx(1, "invalid interface %s", *argv); } getdefif(); /* always call it to print the result */ break; #else xo_errx(1, "not supported yet"); /*NOTREACHED*/ #endif case 'p': if (argc != 0) { usage(); /*NOTREACHED*/ } plist(); break; case 'i': ifinfo(arg, argc, argv); break; case 'r': if (argc != 0) { usage(); /*NOTREACHED*/ } rtrlist(); break; case 's': if (argc < 2 || argc > 4) usage(); exit(set(argc, argv) ? 1 : 0); case 'H': if (argc != 0) { usage(); /*NOTREACHED*/ } harmonize_rtr(); break; case 'P': if (argc != 0) { usage(); /*NOTREACHED*/ } pfx_flush(); break; case 'R': if (argc != 0) { usage(); /*NOTREACHED*/ } rtr_flush(); break; case 0: if (argc != 1) { usage(); /*NOTREACHED*/ } get(argv[0]); break; } xo_close_container("ndp"); if (xo_finish() < 0) xo_err(1, "stdout"); return (ret); } /* * Process a file to set standard ndp entries */ static int file(char *name) { FILE *fp; int i, retval; char line[100], arg[5][50], *args[5], *p; if ((fp = fopen(name, "r")) == NULL) xo_err(1, "cannot open %s", name); args[0] = &arg[0][0]; args[1] = &arg[1][0]; args[2] = &arg[2][0]; args[3] = &arg[3][0]; args[4] = &arg[4][0]; retval = 0; while (fgets(line, sizeof(line), fp) != NULL) { if ((p = strchr(line, '#')) != NULL) *p = '\0'; for (p = line; isblank(*p); p++); if (*p == '\n' || *p == '\0') continue; i = sscanf(line, "%49s %49s %49s %49s %49s", arg[0], arg[1], arg[2], arg[3], arg[4]); if (i < 2) { xo_warnx("bad line: %s", line); retval = 1; continue; } if (set(i, args)) retval = 1; } fclose(fp); return (retval); } static void getsocket(void) { if (s < 0) { s = socket(PF_ROUTE, SOCK_RAW, 0); if (s < 0) { xo_err(1, "socket"); /* NOTREACHED */ } } } static struct sockaddr_in6 so_mask = { .sin6_len = sizeof(so_mask), .sin6_family = AF_INET6 }; static struct sockaddr_in6 blank_sin = { .sin6_len = sizeof(blank_sin), .sin6_family = AF_INET6 }; static struct sockaddr_in6 sin_m; static struct sockaddr_dl blank_sdl = { .sdl_len = sizeof(blank_sdl), .sdl_family = AF_LINK }; static struct sockaddr_dl sdl_m; #ifdef WITHOUT_NETLINK static struct { struct rt_msghdr m_rtm; char m_space[512]; } m_rtmsg; #endif /* * Set an individual neighbor cache entry */ static int set(int argc, char **argv) { struct sockaddr_in6 *sin = &sin_m; int gai_error; u_char *ea; char *host = argv[0], *eaddr = argv[1]; argc -= 2; argv += 2; sdl_m = blank_sdl; sin_m = blank_sin; gai_error = getaddr(host, sin); if (gai_error) { xo_warnx("%s: %s", host, gai_strerror(gai_error)); return 1; } ea = (u_char *)LLADDR(&sdl_m); if (ndp_ether_aton(eaddr, ea) == 0) sdl_m.sdl_alen = 6; while (argc-- > 0) { if (strncmp(argv[0], "temp", 4) == 0) { struct timeval now; gettimeofday(&now, 0); opts.expire_time = now.tv_sec + 20 * 60; } else if (strncmp(argv[0], "proxy", 5) == 0) opts.flags |= RTF_ANNOUNCE; argv++; } #ifndef WITHOUT_NETLINK return (set_nl(0, sin, &sdl_m, host)); #else struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); struct sockaddr_dl *sdl; getsocket(); if (rtmsg(RTM_GET) < 0) { xo_errx(1, "RTM_GET(%s) failed", host); /* NOTREACHED */ } sin = (struct sockaddr_in6 *)(rtm + 1); sdl = (struct sockaddr_dl *)(ALIGN(sin->sin6_len) + (char *)sin); if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) { if (sdl->sdl_family == AF_LINK && !(rtm->rtm_flags & RTF_GATEWAY)) { if (valid_type(sdl->sdl_type)) goto overwrite; } xo_warnx("cannot configure a new entry"); return 1; } overwrite: if (sdl->sdl_family != AF_LINK) { xo_warnx("cannot intuit interface index and type for %s", host); return (1); } sdl_m.sdl_type = sdl->sdl_type; sdl_m.sdl_index = sdl->sdl_index; return (rtmsg(RTM_ADD)); #endif } int getaddr(char *host, struct sockaddr_in6 *sin6) { struct addrinfo hints = { .ai_family = AF_INET6 }; struct addrinfo *res; int gai_error = getaddrinfo(host, NULL, &hints, &res); if (gai_error != 0) return (gai_error); sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(*sin6); sin6->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; sin6->sin6_scope_id = ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; return (0); } /* * Display an individual neighbor cache entry */ static void get(char *host) { struct sockaddr_in6 *sin = &sin_m; int gai_error; sin_m = blank_sin; gai_error = getaddr(host, sin); if (gai_error) { xo_warnx("%s: %s", host, gai_strerror(gai_error)); return; } if (dump(sin, 0) == 0) { getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, sizeof(host_buf), NULL ,0, (opts.nflag ? NI_NUMERICHOST : 0)); xo_errx(1, "%s (%s) -- no entry", host, host_buf); } } #ifdef WITHOUT_NETLINK /* * Delete a neighbor cache entry */ static int delete_rtsock(char *host) { struct sockaddr_in6 *sin = &sin_m; register struct rt_msghdr *rtm = &m_rtmsg.m_rtm; register char *cp = m_rtmsg.m_space; struct sockaddr_dl *sdl; int gai_error; getsocket(); sin_m = blank_sin; gai_error = getaddr(host, sin); if (gai_error) { xo_warnx("%s: %s", host, gai_strerror(gai_error)); return 1; } if (rtmsg(RTM_GET) < 0) { xo_errx(1, "RTM_GET(%s) failed", host); /* NOTREACHED */ } sin = (struct sockaddr_in6 *)(rtm + 1); sdl = (struct sockaddr_dl *)(ALIGN(sin->sin6_len) + (char *)sin); if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) { if (sdl->sdl_family == AF_LINK && !(rtm->rtm_flags & RTF_GATEWAY)) { goto delete; } xo_warnx("delete: cannot delete non-NDP entry"); return 1; } delete: if (sdl->sdl_family != AF_LINK) { xo_warnx("cannot locate %s", host); return (1); } /* * need to reinit the field because it has rt_key * but we want the actual address */ NEXTADDR(RTA_DST, sin_m); rtm->rtm_flags |= RTF_LLDATA; if (rtmsg(RTM_DELETE) == 0) { getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, sizeof(host_buf), NULL, 0, (opts.nflag ? NI_NUMERICHOST : 0)); xo_open_instance("neighbor-cache"); char *ifname = if_indextoname(sdl->sdl_index, ifix_buf); if (ifname == NULL) { strlcpy(ifix_buf, "?", sizeof(ifix_buf)); ifname = ifix_buf; } char abuf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &sin->sin6_addr, abuf, sizeof(abuf)); xo_emit("{:hostname/%s}{d:/ (%s) deleted\n}", host, host_buf); xo_emit("{e:address/%s}{e:interface/%s}", abuf, ifname); xo_close_instance("neighbor-cache"); } return 0; } /* * Dump the entire neighbor cache */ static int dump_rtsock(struct sockaddr_in6 *addr, int cflag) { int mib[6]; size_t needed; char *lim, *buf, *next; struct rt_msghdr *rtm; struct sockaddr_in6 *sin; struct sockaddr_dl *sdl; struct timeval now; time_t expire; int addrwidth; int llwidth; int ifwidth; char flgbuf[8]; char *ifname; /* Print header */ if (!opts.tflag && !cflag) { char xobuf[200]; snprintf(xobuf, sizeof(xobuf), "{T:/%%-%d.%ds} {T:/%%-%d.%ds} {T:/%%%d.%ds} {T:/%%-9.9s} {T:/%%1s} {T:/%%5s}\n", W_ADDR, W_ADDR, W_LL, W_LL, W_IF, W_IF); xo_emit(xobuf, "Neighbor", "Linklayer Address", "Netif", "Expire", "S", "Flags"); } xo_open_list("neighbor-cache"); again: mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET6; mib[4] = NET_RT_FLAGS; #ifdef RTF_LLINFO mib[5] = RTF_LLINFO; #else mib[5] = 0; #endif if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) xo_err(1, "sysctl(PF_ROUTE estimate)"); if (needed > 0) { /* * Add ~2% additional space in case some records * will appear between sysctl() calls. * Round it up so it can fit 4 additional messages at least. */ needed += ((needed >> 6) | (sizeof(m_rtmsg) * 4)); if ((buf = malloc(needed)) == NULL) xo_err(1, "malloc"); if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) xo_err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)"); lim = buf + needed; } else buf = lim = NULL; int count = 0; for (next = buf; next && next < lim; next += rtm->rtm_msglen) { int isrouter = 0, prbs = 0; rtm = (struct rt_msghdr *)next; sin = (struct sockaddr_in6 *)(rtm + 1); sdl = (struct sockaddr_dl *)((char *)sin + ALIGN(sin->sin6_len)); /* * Some OSes can produce a route that has the LINK flag but * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD * and BSD/OS, where xx is not the interface identifier on * lo0). Such routes entry would annoy getnbrinfo() below, * so we skip them. * XXX: such routes should have the GATEWAY flag, not the * LINK flag. However, there is rotten routing software * that advertises all routes that have the GATEWAY flag. * Thus, KAME kernel intentionally does not set the LINK flag. * What is to be fixed is not ndp, but such routing software * (and the kernel workaround)... */ if (sdl->sdl_family != AF_LINK) continue; if (!(rtm->rtm_flags & RTF_HOST)) continue; if (addr) { if (IN6_ARE_ADDR_EQUAL(&addr->sin6_addr, &sin->sin6_addr) == 0 || addr->sin6_scope_id != sin->sin6_scope_id) continue; } else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr)) continue; count++; if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&sin->sin6_addr)) { /* XXX: should scope id be filled in the kernel? */ if (sin->sin6_scope_id == 0) sin->sin6_scope_id = sdl->sdl_index; } getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, sizeof(host_buf), NULL, 0, (opts.nflag ? NI_NUMERICHOST : 0)); if (cflag) { #ifdef RTF_WASCLONED if (rtm->rtm_flags & RTF_WASCLONED) delete(host_buf); #elif defined(RTF_CLONED) if (rtm->rtm_flags & RTF_CLONED) delete(host_buf); #else if (rtm->rtm_flags & RTF_PINNED) continue; delete(host_buf); #endif continue; } gettimeofday(&now, 0); if (opts.tflag) ts_print(&now); addrwidth = strlen(host_buf); if (addrwidth < W_ADDR) addrwidth = W_ADDR; llwidth = strlen(ether_str(sdl)); if (W_ADDR + W_LL - addrwidth > llwidth) llwidth = W_ADDR + W_LL - addrwidth; ifname = if_indextoname(sdl->sdl_index, ifix_buf); if (ifname == NULL) { strlcpy(ifix_buf, "?", sizeof(ifix_buf)); ifname = ifix_buf; } ifwidth = strlen(ifname); if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth) ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth; xo_open_instance("neighbor-cache"); /* Compose format string for libxo, as it doesn't support *.* */ char xobuf[200]; snprintf(xobuf, sizeof(xobuf), "{:address/%%-%d.%ds/%%s} {:mac-address/%%-%d.%ds/%%s} {:interface/%%%d.%ds/%%s}", addrwidth, addrwidth, llwidth, llwidth, ifwidth, ifwidth); xo_emit(xobuf, host_buf, ether_str(sdl), ifname); /* Print neighbor discovery specific information */ expire = rtm->rtm_rmx.rmx_expire; int expire_in = expire - now.tv_sec; if (expire > now.tv_sec) xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", sec2str(expire_in), expire_in); else if (expire == 0) xo_emit("{d:/ %-9.9s}{en:permanent/true}", "permanent"); else xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", "expired", expire_in); char *lle_state = ""; switch (rtm->rtm_rmx.rmx_state) { case ND6_LLINFO_NOSTATE: lle_state = "N"; break; #ifdef ND6_LLINFO_WAITDELETE case ND6_LLINFO_WAITDELETE: lle_state = "W"; break; #endif case ND6_LLINFO_INCOMPLETE: lle_state = "I"; break; case ND6_LLINFO_REACHABLE: lle_state = "R"; break; case ND6_LLINFO_STALE: lle_state = "S"; break; case ND6_LLINFO_DELAY: lle_state = "D"; break; case ND6_LLINFO_PROBE: lle_state = "P"; break; default: lle_state = "?"; break; } xo_emit(" {:neighbor-state/%s}", lle_state); isrouter = rtm->rtm_flags & RTF_GATEWAY; prbs = rtm->rtm_rmx.rmx_pksent; /* * other flags. R: router, P: proxy, W: ?? */ if ((rtm->rtm_addrs & RTA_NETMASK) == 0) { snprintf(flgbuf, sizeof(flgbuf), "%s%s", isrouter ? "R" : "", (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : ""); } else { #if 0 /* W and P are mystery even for us */ sin = (struct sockaddr_in6 *) (sdl->sdl_len + (char *)sdl); snprintf(flgbuf, sizeof(flgbuf), "%s%s%s%s", isrouter ? "R" : "", !IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr) ? "P" : "", (sin->sin6_len != sizeof(struct sockaddr_in6)) ? "W" : "", (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : ""); #else snprintf(flgbuf, sizeof(flgbuf), "%s%s", isrouter ? "R" : "", (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : ""); #endif } xo_emit(" {:nd-flags/%s}", flgbuf); if (prbs) xo_emit("{d:/ %d}", prbs); xo_emit("\n"); xo_close_instance("neighbor-cache"); } if (buf != NULL) free(buf); if (opts.repeat) { xo_emit("\n"); xo_flush(); sleep(opts.repeat); goto again; } xo_close_list("neighbor-cache"); return (count); } #endif static int delete(char *host) { #ifndef WITHOUT_NETLINK return (delete_nl(0, host, true)); /* do warn */ #else return (delete_rtsock(host)); #endif } static int dump(struct sockaddr_in6 *addr, int cflag) { #ifndef WITHOUT_NETLINK return (print_entries_nl(0, addr, cflag)); #else return (dump_rtsock(addr, cflag)); #endif } static struct in6_nbrinfo * getnbrinfo(struct in6_addr *addr, int ifindex, int warning) { static struct in6_nbrinfo nbi; int sock; if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) xo_err(1, "socket"); bzero(&nbi, sizeof(nbi)); if_indextoname(ifindex, nbi.ifname); nbi.addr = *addr; if (ioctl(sock, SIOCGNBRINFO_IN6, (caddr_t)&nbi) < 0) { if (warning) xo_warn("ioctl(SIOCGNBRINFO_IN6)"); close(sock); return(NULL); } close(sock); return(&nbi); } char * ether_str(struct sockaddr_dl *sdl) { static char hbuf[NI_MAXHOST]; if (sdl->sdl_alen == ETHER_ADDR_LEN) { strlcpy(hbuf, ether_ntoa((struct ether_addr *)LLADDR(sdl)), sizeof(hbuf)); } else if (sdl->sdl_alen) { int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; snprintf(hbuf, sizeof(hbuf), "%s", link_ntoa(sdl) + n); } else snprintf(hbuf, sizeof(hbuf), "(incomplete)"); return(hbuf); } static int ndp_ether_aton(char *a, u_char *n) { int i, o[6]; i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2], &o[3], &o[4], &o[5]); if (i != 6) { xo_warnx("invalid Ethernet address '%s'", a); return (1); } for (i = 0; i < 6; i++) n[i] = o[i]; return (0); } static void usage(void) { xo_error("usage: ndp [-nt] hostname\n"); xo_error(" ndp [-nt] -a | -c | -p | -r | -H | -P | -R\n"); xo_error(" ndp [-nt] -A wait\n"); xo_error(" ndp [-nt] -d hostname\n"); xo_error(" ndp [-nt] -f filename\n"); xo_error(" ndp [-nt] -i interface [flags...]\n"); #ifdef SIOCSDEFIFACE_IN6 xo_error(" ndp [-nt] -I [interface|delete]\n"); #endif xo_error(" ndp [-nt] -s nodename etheraddr [temp] [proxy]\n"); exit(1); } #ifdef WITHOUT_NETLINK static int rtmsg(int cmd) { static int seq; int rlen; register struct rt_msghdr *rtm = &m_rtmsg.m_rtm; register char *cp = m_rtmsg.m_space; register int l; errno = 0; if (cmd == RTM_DELETE) goto doit; bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); rtm->rtm_flags = opts.flags; rtm->rtm_version = RTM_VERSION; switch (cmd) { default: xo_errx(1, "internal wrong cmd"); case RTM_ADD: rtm->rtm_addrs |= RTA_GATEWAY; if (opts.expire_time) { rtm->rtm_rmx.rmx_expire = opts.expire_time; rtm->rtm_inits = RTV_EXPIRE; } rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA); /* FALLTHROUGH */ case RTM_GET: rtm->rtm_addrs |= RTA_DST; } NEXTADDR(RTA_DST, sin_m); NEXTADDR(RTA_GATEWAY, sdl_m); rtm->rtm_msglen = cp - (char *)&m_rtmsg; doit: l = rtm->rtm_msglen; rtm->rtm_seq = ++seq; rtm->rtm_type = cmd; if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { if (errno != ESRCH || cmd != RTM_DELETE) { xo_err(1, "writing to routing socket"); /* NOTREACHED */ } } do { l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); } while (l > 0 && (rtm->rtm_type != cmd || rtm->rtm_seq != seq || rtm->rtm_pid != pid)); if (l < 0) xo_warn("read from routing socket"); return (0); } #endif static void ifinfo(char *ifname, int argc, char **argv) { struct in6_ndireq nd; int i, sock; u_int32_t newflags; #ifdef IPV6CTL_USETEMPADDR u_int8_t nullbuf[8]; #endif if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { xo_err(1, "socket"); /* NOTREACHED */ } bzero(&nd, sizeof(nd)); strlcpy(nd.ifname, ifname, sizeof(nd.ifname)); if (ioctl(sock, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) { xo_err(1, "ioctl(SIOCGIFINFO_IN6)"); /* NOTREACHED */ } #define ND nd.ndi newflags = ND.flags; for (i = 0; i < argc; i++) { int clear = 0; char *cp = argv[i]; if (*cp == '-') { clear = 1; cp++; } #define SETFLAG(s, f) do { \ if (strcmp(cp, (s)) == 0) { \ if (clear) \ newflags &= ~(f); \ else \ newflags |= (f); \ } \ } while (0) /* * XXX: this macro is not 100% correct, in that it matches "nud" against * "nudbogus". But we just let it go since this is minor. */ #define SETVALUE(f, v) do { \ char *valptr; \ unsigned long newval; \ v = 0; /* unspecified */ \ if (strncmp(cp, f, strlen(f)) == 0) { \ valptr = strchr(cp, '='); \ if (valptr == NULL) \ xo_err(1, "syntax error in %s field", (f)); \ errno = 0; \ newval = strtoul(++valptr, NULL, 0); \ if (errno) \ xo_err(1, "syntax error in %s's value", (f)); \ v = newval; \ } \ } while (0) SETFLAG("disabled", ND6_IFF_IFDISABLED); SETFLAG("nud", ND6_IFF_PERFORMNUD); #ifdef ND6_IFF_ACCEPT_RTADV SETFLAG("accept_rtadv", ND6_IFF_ACCEPT_RTADV); #endif #ifdef ND6_IFF_AUTO_LINKLOCAL SETFLAG("auto_linklocal", ND6_IFF_AUTO_LINKLOCAL); #endif #ifdef ND6_IFF_NO_PREFER_IFACE SETFLAG("no_prefer_iface", ND6_IFF_NO_PREFER_IFACE); #endif #ifdef ND6_IFF_STABLEADDR SETFLAG("stableaddr", ND6_IFF_STABLEADDR); #endif SETVALUE("basereachable", ND.basereachable); SETVALUE("retrans", ND.retrans); SETVALUE("curhlim", ND.chlim); ND.flags = newflags; if (ioctl(sock, SIOCSIFINFO_IN6, (caddr_t)&nd) < 0) { xo_err(1, "ioctl(SIOCSIFINFO_IN6)"); /* NOTREACHED */ } #undef SETFLAG #undef SETVALUE } if (!ND.initialized) { xo_errx(1, "%s: not initialized yet", ifname); /* NOTREACHED */ } if (ioctl(sock, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) { xo_err(1, "ioctl(SIOCGIFINFO_IN6)"); /* NOTREACHED */ } xo_open_container("ifinfo"); xo_emit("{e:interface/%s}", ifname); xo_emit("linkmtu={:linkmtu/%d}", ND.linkmtu); xo_emit(", maxmtu={:maxmtu/%d}", ND.maxmtu); xo_emit(", curhlim={:curhlim/%d}", ND.chlim); xo_emit("{d:/, basereachable=%ds%dms}{e:basereachable_ms/%u}", ND.basereachable / 1000, ND.basereachable % 1000, ND.basereachable); xo_emit("{d:/, reachable=%ds}{e:reachable_ms/%u}", ND.reachable, ND.reachable * 1000); xo_emit("{d:/, retrans=%ds%dms}{e:retrans_ms/%u}", ND.retrans / 1000, ND.retrans % 1000, ND.retrans); #ifdef IPV6CTL_USETEMPADDR memset(nullbuf, 0, sizeof(nullbuf)); if (memcmp(nullbuf, ND.randomid, sizeof(nullbuf)) != 0) { int j; u_int8_t *rbuf; for (i = 0; i < 3; i++) { const char *txt, *field; switch (i) { case 0: txt = "\nRandom seed(0): "; field = "seed_0"; rbuf = ND.randomseed0; break; case 1: txt = "\nRandom seed(1): "; field = "seed_1"; rbuf = ND.randomseed1; break; case 2: txt = "\nRandom ID: "; field = "random_id"; rbuf = ND.randomid; break; default: xo_errx(1, "impossible case for tempaddr display"); } char abuf[20], xobuf[200]; for (j = 0; j < 8; j++) snprintf(&abuf[j * 2], sizeof(abuf), "%02X", rbuf[j]); snprintf(xobuf, sizeof(xobuf), "%s{:%s/%%s}", txt, field); xo_emit(xobuf, abuf); } } #endif /* IPV6CTL_USETEMPADDR */ if (ND.flags) { xo_emit("\nFlags: {e:flags/%u}", ND.flags); xo_open_list("flags_pretty"); #ifdef ND6_IFF_IFDISABLED if ((ND.flags & ND6_IFF_IFDISABLED)) xo_emit("{l:%s} ", "disabled"); #endif if ((ND.flags & ND6_IFF_PERFORMNUD)) xo_emit("{l:%s} ", "nud"); #ifdef ND6_IFF_ACCEPT_RTADV if ((ND.flags & ND6_IFF_ACCEPT_RTADV)) xo_emit("{l:%s} ", "accept_rtadv"); #endif #ifdef ND6_IFF_AUTO_LINKLOCAL if ((ND.flags & ND6_IFF_AUTO_LINKLOCAL)) xo_emit("{l:%s} ", "auto_linklocal"); #endif #ifdef ND6_IFF_STABLEADDR if ((ND.flags & ND6_IFF_STABLEADDR)) xo_emit("{l:%s} ", "stableaddr"); #endif #ifdef ND6_IFF_NO_PREFER_IFACE if ((ND.flags & ND6_IFF_NO_PREFER_IFACE)) xo_emit("{l:%s} ", "no_prefer_iface"); #endif xo_close_list("flags"); } xo_emit("\n"); #undef ND xo_close_container("ifinfo"); close(sock); } #ifndef ND_RA_FLAG_RTPREF_MASK /* XXX: just for compilation on *BSD release */ #define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */ #endif static void rtrlist(void) { int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_DRLIST }; char *buf; struct in6_defrouter *p, *ep; size_t l; struct timeval now; if (sysctl(mib, nitems(mib), NULL, &l, NULL, 0) < 0) { xo_err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)"); /*NOTREACHED*/ } if (l == 0) return; buf = malloc(l); if (!buf) { xo_err(1, "malloc"); /*NOTREACHED*/ } if (sysctl(mib, nitems(mib), buf, &l, NULL, 0) < 0) { xo_err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)"); /*NOTREACHED*/ } xo_open_list("router-list"); ep = (struct in6_defrouter *)(buf + l); for (p = (struct in6_defrouter *)buf; p < ep; p++) { int rtpref; char abuf[INET6_ADDRSTRLEN], *paddr; if (getnameinfo((struct sockaddr *)&p->rtaddr, p->rtaddr.sin6_len, host_buf, sizeof(host_buf), NULL, 0, (opts.nflag ? NI_NUMERICHOST : 0)) != 0) strlcpy(host_buf, "?", sizeof(host_buf)); if (opts.nflag) paddr = host_buf; else { inet_ntop(AF_INET6, &p->rtaddr.sin6_addr, abuf, sizeof(abuf)); paddr = abuf; } xo_open_instance("router-list"); xo_emit("{:hostname/%s}{e:address/%s} if={:interface/%s}", host_buf, paddr, if_indextoname(p->if_index, ifix_buf)); xo_open_list("flags_pretty"); char rflags[6] = {}, *pflags = rflags; if (p->flags & ND_RA_FLAG_MANAGED) { *pflags++ = 'M'; xo_emit("{el:%s}", "managed"); } if (p->flags & ND_RA_FLAG_OTHER) { *pflags++ = 'O'; xo_emit("{el:%s}", "other"); } -#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG - if (p->flags & ND_RA_FLAG_IPV6_ONLY) { - *pflags++ = 'S'; - xo_emit("{el:%s}", "ipv6only"); - } -#endif + xo_close_list("flags_pretty"); xo_emit(", flags={:flags/%s}", rflags); rtpref = ((p->flags & ND_RA_FLAG_RTPREF_MASK) >> 3) & 0xff; xo_emit(", pref={:preference/%s}", rtpref_str[rtpref]); gettimeofday(&now, 0); if (p->expire == 0) xo_emit(", expire=Never\n{en:permanent/true}"); else xo_emit("{d:/, expire=%s\n}{e:expires_sec/%ld}", sec2str(p->expire - now.tv_sec), (long)p->expire - now.tv_sec); xo_close_instance("router-list"); } free(buf); xo_close_list("router-list"); } static void plist(void) { int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_PRLIST }; char *buf; struct in6_prefix *p, *ep, *n; struct sockaddr_in6 *advrtr; size_t l; struct timeval now; const int niflags = NI_NUMERICHOST; int ninflags = opts.nflag ? NI_NUMERICHOST : 0; char namebuf[NI_MAXHOST]; if (sysctl(mib, nitems(mib), NULL, &l, NULL, 0) < 0) { xo_err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)"); /*NOTREACHED*/ } buf = malloc(l); if (!buf) { xo_err(1, "malloc"); /*NOTREACHED*/ } if (sysctl(mib, nitems(mib), buf, &l, NULL, 0) < 0) { xo_err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)"); /*NOTREACHED*/ } xo_open_list("prefix-list"); ep = (struct in6_prefix *)(buf + l); for (p = (struct in6_prefix *)buf; p < ep; p = n) { advrtr = (struct sockaddr_in6 *)(p + 1); n = (struct in6_prefix *)&advrtr[p->advrtrs]; xo_open_instance("prefix-list"); if (getnameinfo((struct sockaddr *)&p->prefix, p->prefix.sin6_len, namebuf, sizeof(namebuf), NULL, 0, niflags) != 0) strlcpy(namebuf, "?", sizeof(namebuf)); xo_emit("{:prefix/%s%s%d} if={:interface/%s}\n", namebuf, "/", p->prefixlen, if_indextoname(p->if_index, ifix_buf)); gettimeofday(&now, 0); /* * meaning of fields, especially flags, is very different * by origin. notify the difference to the users. */ char flags[10] = {}, *pflags = flags; xo_open_list("flags_pretty"); if (p->raflags.onlink) { *pflags++ = 'L'; xo_emit("{el:%s}", "ra_onlink"); } if (p->raflags.autonomous) { *pflags++ = 'A'; xo_emit("{el:%s}", "ra_autonomous"); } if (p->flags & NDPRF_ONLINK) { *pflags++ = 'O'; xo_emit("{el:%s}", "is_onlink"); } if (p->flags & NDPRF_DETACHED) { *pflags++ = 'D'; xo_emit("{el:%s}", "is_detached"); } #ifdef NDPRF_HOME if (p->flags & NDPRF_HOME) { *pflags++ = 'H'; xo_emit("{el:%s}", "is_home"); } #endif xo_close_list("flags_pretty"); xo_emit("flags={:flags/%s}", flags); int expire_in = p->expire - now.tv_sec; if (p->vltime == ND6_INFINITE_LIFETIME) xo_emit(" vltime=infinity{e:valid-lifetime/%lu}", (unsigned long)p->vltime); else xo_emit(" vltime={:valid-lifetime/%lu}", (unsigned long)p->vltime); if (p->pltime == ND6_INFINITE_LIFETIME) xo_emit(", pltime=infinity{e:preferred-lifetime/%lu}", (unsigned long)p->pltime); else xo_emit(", pltime={:preferred-lifetime/%lu}", (unsigned long)p->pltime); if (p->expire == 0) xo_emit(", expire=Never{en:permanent/true}"); else if (p->expire >= now.tv_sec) xo_emit(", expire={:expires/%s}{e:expires_sec/%d}", sec2str(expire_in), expire_in); else xo_emit(", expired{e:expires_sec/%d}", expire_in); xo_emit(", ref={:refcount/%d}", p->refcnt); xo_emit("\n"); /* * "advertising router" list is meaningful only if the prefix * information is from RA. */ if (p->advrtrs) { int j; struct sockaddr_in6 *sin6; sin6 = advrtr; xo_emit(" advertised by\n"); xo_open_list("advertising-routers"); for (j = 0; j < p->advrtrs; j++) { struct in6_nbrinfo *nbi; xo_open_instance("advertising-routers"); if (getnameinfo((struct sockaddr *)sin6, sin6->sin6_len, namebuf, sizeof(namebuf), NULL, 0, ninflags) != 0) strlcpy(namebuf, "?", sizeof(namebuf)); char abuf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, sizeof(abuf)); xo_emit(" {:hostname/%s}{e:address/%s}", namebuf, abuf); nbi = getnbrinfo(&sin6->sin6_addr, p->if_index, 0); const char *state = ""; if (nbi) { switch (nbi->state) { case ND6_LLINFO_REACHABLE: case ND6_LLINFO_STALE: case ND6_LLINFO_DELAY: case ND6_LLINFO_PROBE: state = "reachable"; break; default: state = "unreachable"; } } else state = "no neighbor state"; xo_emit(" ({:state/%s})\n", state); sin6++; xo_close_instance("advertising-routers"); } xo_close_list("advertising-routers"); } else xo_emit(" No advertising router\n"); xo_close_instance("prefix-list"); } free(buf); xo_close_list("prefix-list"); } static void pfx_flush(void) { char dummyif[IFNAMSIZ+8]; int sock; if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) xo_err(1, "socket"); strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */ if (ioctl(sock, SIOCSPFXFLUSH_IN6, (caddr_t)&dummyif) < 0) xo_err(1, "ioctl(SIOCSPFXFLUSH_IN6)"); close(sock); } static void rtr_flush(void) { char dummyif[IFNAMSIZ+8]; int sock; if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) xo_err(1, "socket"); strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */ if (ioctl(sock, SIOCSRTRFLUSH_IN6, (caddr_t)&dummyif) < 0) xo_err(1, "ioctl(SIOCSRTRFLUSH_IN6)"); close(sock); } static void harmonize_rtr(void) { char dummyif[IFNAMSIZ+8]; int sock; if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) xo_err(1, "socket"); strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */ if (ioctl(sock, SIOCSNDFLUSH_IN6, (caddr_t)&dummyif) < 0) xo_err(1, "ioctl(SIOCSNDFLUSH_IN6)"); close(sock); } #ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */ static void setdefif(char *ifname) { struct in6_ndifreq ndifreq; unsigned int ifindex; int sock; if (strcasecmp(ifname, "delete") == 0) ifindex = 0; else { if ((ifindex = if_nametoindex(ifname)) == 0) xo_err(1, "failed to resolve i/f index for %s", ifname); } if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) xo_err(1, "socket"); strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */ ndifreq.ifindex = ifindex; if (ioctl(sock, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) < 0) xo_err(1, "ioctl(SIOCSDEFIFACE_IN6)"); close(sock); } static void getdefif(void) { struct in6_ndifreq ndifreq; char ifname[IFNAMSIZ+8]; int sock; if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) xo_err(1, "socket"); memset(&ndifreq, 0, sizeof(ndifreq)); strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */ if (ioctl(sock, SIOCGDEFIFACE_IN6, (caddr_t)&ndifreq) < 0) xo_err(1, "ioctl(SIOCGDEFIFACE_IN6)"); if (ndifreq.ifindex == 0) xo_emit("No default interface.\n"); else { if ((if_indextoname(ndifreq.ifindex, ifname)) == NULL) xo_err(1, "failed to resolve ifname for index %lu", ndifreq.ifindex); xo_emit("ND default interface = {:default-interface/%s}\n", ifname); } close(sock); } #endif /* SIOCSDEFIFACE_IN6 */ char * sec2str(time_t total) { static char result[256]; int days, hours, mins, secs; int first = 1; char *p = result; char *ep = &result[sizeof(result)]; int n; days = total / 3600 / 24; hours = (total / 3600) % 24; mins = (total / 60) % 60; secs = total % 60; if (days) { first = 0; n = snprintf(p, ep - p, "%dd", days); if (n < 0 || n >= ep - p) return "?"; p += n; } if (!first || hours) { first = 0; n = snprintf(p, ep - p, "%dh", hours); if (n < 0 || n >= ep - p) return "?"; p += n; } if (!first || mins) { first = 0; n = snprintf(p, ep - p, "%dm", mins); if (n < 0 || n >= ep - p) return "?"; p += n; } snprintf(p, ep - p, "%ds", secs); return(result); } /* * Print the timestamp * from tcpdump/util.c */ void ts_print(const struct timeval *tvp) { int sec; /* Default */ sec = (tvp->tv_sec + thiszone) % 86400; xo_emit("{e:tv_sec/%lld}{e:tv_usec/%lld}{d:/%02d:%02d:%02d.%06u} ", tvp->tv_sec, tvp->tv_usec, sec / 3600, (sec % 3600) / 60, sec % 60, (u_int32_t)tvp->tv_usec); } #undef NEXTADDR diff --git a/usr.sbin/rtadvd/Makefile b/usr.sbin/rtadvd/Makefile index 583f9db88ceb..4ed2d7182a6b 100644 --- a/usr.sbin/rtadvd/Makefile +++ b/usr.sbin/rtadvd/Makefile @@ -1,32 +1,27 @@ # Copyright (c) 1996 WIDE Project. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modifications, are permitted provided that the above copyright notice # and this paragraph are duplicated in all such forms and that any # documentation, advertising materials, and other materials related to # such distribution and use acknowledge that the software was developed # by the WIDE Project, Japan. The name of the Project may not be used to # endorse or promote products derived from this software without # specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' # AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT # LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE. # .include PROG= rtadvd MAN= rtadvd.conf.5 rtadvd.8 SRCS= rtadvd.c rrenum.c advcap.c if.c config.c timer.c timer_subr.c \ control.c control_server.c -.if ${MK_EXPERIMENTAL} != "no" -CFLAGS+= -DEXPERIMENTAL -CFLAGS+= -DDRAFT_IETF_6MAN_IPV6ONLY_FLAG -.endif - LIBADD= util WARNS?= 1 .include diff --git a/usr.sbin/rtadvd/config.c b/usr.sbin/rtadvd/config.c index c0af8f76ca49..70fb2ed7dc28 100644 --- a/usr.sbin/rtadvd/config.c +++ b/usr.sbin/rtadvd/config.c @@ -1,1637 +1,1626 @@ /* $KAME: config.c,v 1.84 2003/08/05 12:34:23 itojun Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 1998 WIDE Project. * Copyright (C) 2011 Hiroki Sato * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rtadvd.h" #include "advcap.h" #include "timer.h" #include "if.h" #include "config.h" /* label of tcapcode + number + domain name + zero octet */ static char entbuf[10 + 3 + NI_MAXHOST + 1]; static char oentbuf[10 + 3 + NI_MAXHOST + 1]; static char abuf[DNAME_LABELENC_MAXLEN]; static time_t prefix_timo = (60 * 120); /* 2 hours. * XXX: should be configurable. */ static struct rtadvd_timer *prefix_timeout(void *); static void makeentry(char *, size_t, int, const char *); static ssize_t dname_labelenc(char *, const char *); /* Encode domain name label encoding in RFC 1035 Section 3.1 */ static ssize_t dname_labelenc(char *dst, const char *src) { char *dst_origin; char *p; size_t len; dst_origin = dst; len = strlen(src); if (len + len / 64 + 1 + 1 > DNAME_LABELENC_MAXLEN) return (-1); /* Length fields per 63 octets + '\0' (<= DNAME_LABELENC_MAXLEN) */ memset(dst, 0, len + len / 64 + 1 + 1); syslog(LOG_DEBUG, "<%s> labelenc = %s", __func__, src); while (src && (len = strlen(src)) != 0) { /* Put a length field with 63 octet limitation first. */ p = strchr(src, '.'); if (p == NULL) *dst = len = MIN(63, len); else *dst = len = MIN(63, p - src); if (dst + 1 + len < dst_origin + DNAME_LABELENC_MAXLEN) dst++; else return (-1); /* Copy 63 octets at most. */ memcpy(dst, src, len); dst += len; if (p == NULL) /* the last label */ break; src = p + 1; } /* Always need a 0-length label at the tail. */ *dst++ = '\0'; syslog(LOG_DEBUG, "<%s> labellen = %td", __func__, dst - dst_origin); return (dst - dst_origin); } #define MUSTHAVE(var, cap) \ do { \ int64_t t; \ if ((t = agetnum(cap)) < 0) { \ fprintf(stderr, "rtadvd: need %s for interface %s\n", \ cap, intface); \ exit(1); \ } \ var = t; \ } while (0) #define MAYHAVE(var, cap, def) \ do { \ if ((var = agetnum(cap)) < 0) \ var = def; \ } while (0) int loadconfig_index(int idx) { char ifname[IFNAMSIZ]; syslog(LOG_DEBUG, "<%s> enter", __func__); if (if_indextoname(idx, ifname) != NULL) return (loadconfig_ifname(ifname)); else return (1); } int loadconfig_ifname(char *ifname) { struct ifinfo *ifi; syslog(LOG_DEBUG, "<%s> enter", __func__); update_ifinfo(&ifilist, UPDATE_IFINFO_ALL); TAILQ_FOREACH(ifi, &ifilist, ifi_next) { /* NULL means all IFs will be processed. */ if (ifname != NULL && strcmp(ifi->ifi_ifname, ifname) != 0) continue; if (!ifi->ifi_persist) { syslog(LOG_INFO, "<%s> %s is not a target interface. " "Ignored at this moment.", __func__, ifi->ifi_ifname); continue; } if (ifi->ifi_ifindex == 0) { syslog(LOG_ERR, "<%s> %s not found. " "Ignored at this moment.", __func__, ifi->ifi_ifname); continue; } if (getconfig(ifi) == NULL) { syslog(LOG_ERR, "<%s> invalid configuration for %s. " "Ignored at this moment.", __func__, ifi->ifi_ifname); continue; } } return (0); } int rm_ifinfo_index(int idx) { struct ifinfo *ifi; ifi = if_indextoifinfo(idx); if (ifi == NULL) { syslog(LOG_ERR, "<%s>: ifinfo not found (idx=%d)", __func__, idx); return (-1); } return (rm_ifinfo(ifi)); } int rm_ifinfo(struct ifinfo *ifi) { int error; syslog(LOG_DEBUG, "<%s> enter (%s).", __func__, ifi->ifi_ifname); switch (ifi->ifi_state) { case IFI_STATE_UNCONFIGURED: return (0); break; default: ifi->ifi_state = IFI_STATE_UNCONFIGURED; syslog(LOG_DEBUG, "<%s> ifname=%s marked as UNCONFIGURED.", __func__, ifi->ifi_ifname); /* XXX: No MC leaving here because index is disappeared */ /* Inactivate timer */ rtadvd_remove_timer(ifi->ifi_ra_timer); ifi->ifi_ra_timer = NULL; break; } /* clean up ifi */ if (!ifi->ifi_persist) { TAILQ_REMOVE(&ifilist, ifi, ifi_next); syslog(LOG_DEBUG, "<%s>: ifinfo (idx=%d) removed.", __func__, ifi->ifi_ifindex); } else { /* recreate an empty entry */ update_persist_ifinfo(&ifilist, ifi->ifi_ifname); syslog(LOG_DEBUG, "<%s>: ifname=%s is persistent.", __func__, ifi->ifi_ifname); } /* clean up rai if any */ switch (ifi->ifi_state) { case IFI_STATE_CONFIGURED: if (ifi->ifi_rainfo != NULL) { error = rm_rainfo(ifi->ifi_rainfo); if (error) return (error); ifi->ifi_rainfo = NULL; } break; case IFI_STATE_TRANSITIVE: if (ifi->ifi_rainfo == ifi->ifi_rainfo_trans) { if (ifi->ifi_rainfo != NULL) { error = rm_rainfo(ifi->ifi_rainfo); if (error) return (error); ifi->ifi_rainfo = NULL; ifi->ifi_rainfo_trans = NULL; } } else { if (ifi->ifi_rainfo != NULL) { error = rm_rainfo(ifi->ifi_rainfo); if (error) return (error); ifi->ifi_rainfo = NULL; } if (ifi->ifi_rainfo_trans != NULL) { error = rm_rainfo(ifi->ifi_rainfo_trans); if (error) return (error); ifi->ifi_rainfo_trans = NULL; } } } syslog(LOG_DEBUG, "<%s> leave (%s).", __func__, ifi->ifi_ifname); if (!ifi->ifi_persist) free(ifi); return (0); } int rm_rainfo(struct rainfo *rai) { struct prefix *pfx; struct soliciter *sol; struct rdnss *rdn; struct rdnss_addr *rdna; struct dnssl *dns; struct pref64 *prf64; struct rtinfo *rti; syslog(LOG_DEBUG, "<%s>: enter", __func__); TAILQ_REMOVE(&railist, rai, rai_next); if (rai->rai_ifinfo != NULL) syslog(LOG_DEBUG, "<%s>: rainfo (idx=%d) removed.", __func__, rai->rai_ifinfo->ifi_ifindex); if (rai->rai_ra_data != NULL) free(rai->rai_ra_data); while ((pfx = TAILQ_FIRST(&rai->rai_prefix)) != NULL) delete_prefix(pfx); while ((sol = TAILQ_FIRST(&rai->rai_soliciter)) != NULL) { TAILQ_REMOVE(&rai->rai_soliciter, sol, sol_next); free(sol); } while ((rdn = TAILQ_FIRST(&rai->rai_rdnss)) != NULL) { TAILQ_REMOVE(&rai->rai_rdnss, rdn, rd_next); while ((rdna = TAILQ_FIRST(&rdn->rd_list)) != NULL) { TAILQ_REMOVE(&rdn->rd_list, rdna, ra_next); free(rdna); } free(rdn); } while ((dns = TAILQ_FIRST(&rai->rai_dnssl)) != NULL) { TAILQ_REMOVE(&rai->rai_dnssl, dns, dn_next); free(dns); } while ((rti = TAILQ_FIRST(&rai->rai_route)) != NULL) { TAILQ_REMOVE(&rai->rai_route, rti, rti_next); free(rti); } while ((prf64 = TAILQ_FIRST(&rai->rai_pref64)) != NULL) { TAILQ_REMOVE(&rai->rai_pref64, prf64, p64_next); free(prf64); } free(rai); syslog(LOG_DEBUG, "<%s>: leave", __func__); return (0); } struct ifinfo * getconfig(struct ifinfo *ifi) { int stat, i; int error; char tbuf[BUFSIZ]; struct rainfo *rai; struct rainfo *rai_old; int32_t val; int64_t val64; char buf[BUFSIZ]; char *bp = buf; char *addr, *flagstr; if (ifi == NULL) /* if does not exist */ return (NULL); if (ifi->ifi_state == IFI_STATE_TRANSITIVE && ifi->ifi_rainfo == NULL) { syslog(LOG_INFO, "<%s> %s is shutting down. Skipped.", __func__, ifi->ifi_ifname); return (NULL); } if ((stat = agetent(tbuf, ifi->ifi_ifname)) <= 0) { memset(tbuf, 0, sizeof(tbuf)); syslog(LOG_INFO, "<%s> %s isn't defined in the configuration file" " or the configuration file doesn't exist." " Treat it as default", __func__, ifi->ifi_ifname); } ELM_MALLOC(rai, exit(1)); TAILQ_INIT(&rai->rai_prefix); TAILQ_INIT(&rai->rai_route); TAILQ_INIT(&rai->rai_rdnss); TAILQ_INIT(&rai->rai_dnssl); TAILQ_INIT(&rai->rai_pref64); TAILQ_INIT(&rai->rai_soliciter); rai->rai_ifinfo = ifi; /* gather on-link prefixes from the network interfaces. */ if (agetflag("noifprefix")) rai->rai_advifprefix = 0; else rai->rai_advifprefix = 1; /* get interface information */ if (agetflag("nolladdr")) rai->rai_advlinkopt = 0; else rai->rai_advlinkopt = 1; if (rai->rai_advlinkopt) { if (ifi->ifi_sdl.sdl_type == 0) { syslog(LOG_ERR, "<%s> can't get information of %s", __func__, ifi->ifi_ifname); goto getconfig_free_rai; } } /* * set router configuration variables. */ MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL); if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) { syslog(LOG_ERR, "<%s> maxinterval (%" PRIu32 ") on %s is invalid " "(must be between %u and %u)", __func__, val, ifi->ifi_ifname, MIN_MAXINTERVAL, MAX_MAXINTERVAL); goto getconfig_free_rai; } rai->rai_maxinterval = (uint16_t)val; MAYHAVE(val, "mininterval", rai->rai_maxinterval/3); if ((uint16_t)val < MIN_MININTERVAL || (uint16_t)val > (rai->rai_maxinterval * 3) / 4) { syslog(LOG_ERR, "<%s> mininterval (%" PRIu32 ") on %s is invalid " "(must be between %d and %d)", __func__, val, ifi->ifi_ifname, MIN_MININTERVAL, (rai->rai_maxinterval * 3) / 4); goto getconfig_free_rai; } rai->rai_mininterval = (uint16_t)val; MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT); rai->rai_hoplimit = val & 0xff; if ((flagstr = (char *)agetstr("raflags", &bp))) { val = 0; if (strchr(flagstr, 'm')) val |= ND_RA_FLAG_MANAGED; if (strchr(flagstr, 'o')) val |= ND_RA_FLAG_OTHER; if (strchr(flagstr, 'h')) val |= ND_RA_FLAG_RTPREF_HIGH; if (strchr(flagstr, 'l')) { if ((val & ND_RA_FLAG_RTPREF_HIGH)) { syslog(LOG_ERR, "<%s> the \'h\' and \'l\'" " router flags are exclusive", __func__); goto getconfig_free_rai; } val |= ND_RA_FLAG_RTPREF_LOW; } -#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG - if (strchr(flagstr, 'S')) - val |= ND_RA_FLAG_IPV6_ONLY; -#endif } else MAYHAVE(val, "raflags", 0); rai->rai_managedflg = val & ND_RA_FLAG_MANAGED; rai->rai_otherflg = val & ND_RA_FLAG_OTHER; #ifndef ND_RA_FLAG_RTPREF_MASK #define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */ #define ND_RA_FLAG_RTPREF_RSV 0x10 /* 00010000 */ #endif rai->rai_rtpref = val & ND_RA_FLAG_RTPREF_MASK; if (rai->rai_rtpref == ND_RA_FLAG_RTPREF_RSV) { syslog(LOG_ERR, "<%s> invalid router preference (%02x) on %s", __func__, rai->rai_rtpref, ifi->ifi_ifname); goto getconfig_free_rai; } -#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG - rai->rai_ipv6onlyflg = val & ND_RA_FLAG_IPV6_ONLY; -#endif MAYHAVE(val, "rltime", rai->rai_maxinterval * 3); if ((uint16_t)val && ((uint16_t)val < rai->rai_maxinterval || (uint16_t)val > MAXROUTERLIFETIME)) { syslog(LOG_ERR, "<%s> router lifetime (%" PRIu32 ") on %s is invalid " "(must be 0 or between %d and %d)", __func__, val, ifi->ifi_ifname, rai->rai_maxinterval, MAXROUTERLIFETIME); goto getconfig_free_rai; } rai->rai_lifetime = val & 0xffff; MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME); if (val < 0 || val > MAXREACHABLETIME) { syslog(LOG_ERR, "<%s> reachable time (%" PRIu32 ") on %s is invalid " "(must be no greater than %d)", __func__, val, ifi->ifi_ifname, MAXREACHABLETIME); goto getconfig_free_rai; } rai->rai_reachabletime = (uint32_t)val; MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER); if (val64 < 0 || val64 > 0xffffffff) { syslog(LOG_ERR, "<%s> retrans time (%" PRIu64 ") on %s out of range", __func__, val64, ifi->ifi_ifname); goto getconfig_free_rai; } rai->rai_retranstimer = (uint32_t)val64; if (agetnum("hapref") != -1 || agetnum("hatime") != -1) { syslog(LOG_ERR, "<%s> mobile-ip6 configuration not supported", __func__); goto getconfig_free_rai; } /* prefix information */ /* * This is an implementation specific parameter to consider * link propagation delays and poorly synchronized clocks when * checking consistency of advertised lifetimes. */ MAYHAVE(val, "clockskew", 0); rai->rai_clockskew = val; rai->rai_pfxs = 0; for (i = -1; i < MAXPREFIX; i++) { struct prefix *pfx; makeentry(entbuf, sizeof(entbuf), i, "addr"); addr = (char *)agetstr(entbuf, &bp); if (addr == NULL) continue; /* allocate memory to store prefix information */ ELM_MALLOC(pfx, exit(1)); pfx->pfx_rainfo = rai; pfx->pfx_origin = PREFIX_FROM_CONFIG; if (inet_pton(AF_INET6, addr, &pfx->pfx_prefix) != 1) { syslog(LOG_ERR, "<%s> inet_pton failed for %s", __func__, addr); goto getconfig_free_pfx; } if (IN6_IS_ADDR_MULTICAST(&pfx->pfx_prefix)) { syslog(LOG_ERR, "<%s> multicast prefix (%s) must " "not be advertised on %s", __func__, addr, ifi->ifi_ifname); goto getconfig_free_pfx; } if (IN6_IS_ADDR_LINKLOCAL(&pfx->pfx_prefix)) syslog(LOG_NOTICE, "<%s> link-local prefix (%s) will be" " advertised on %s", __func__, addr, ifi->ifi_ifname); makeentry(entbuf, sizeof(entbuf), i, "prefixlen"); MAYHAVE(val, entbuf, 64); if (val < 0 || val > 128) { syslog(LOG_ERR, "<%s> prefixlen (%" PRIu32 ") for %s " "on %s out of range", __func__, val, addr, ifi->ifi_ifname); goto getconfig_free_pfx; } pfx->pfx_prefixlen = (int)val; makeentry(entbuf, sizeof(entbuf), i, "pinfoflags"); if ((flagstr = (char *)agetstr(entbuf, &bp))) { val = 0; if (strchr(flagstr, 'l')) val |= ND_OPT_PI_FLAG_ONLINK; if (strchr(flagstr, 'a')) val |= ND_OPT_PI_FLAG_AUTO; } else { MAYHAVE(val, entbuf, (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO)); } pfx->pfx_onlinkflg = val & ND_OPT_PI_FLAG_ONLINK; pfx->pfx_autoconfflg = val & ND_OPT_PI_FLAG_AUTO; makeentry(entbuf, sizeof(entbuf), i, "vltime"); MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME); if (val64 < 0 || val64 > 0xffffffff) { syslog(LOG_ERR, "<%s> vltime (%" PRIu64 ") for " "%s/%d on %s is out of range", __func__, val64, addr, pfx->pfx_prefixlen, ifi->ifi_ifname); goto getconfig_free_pfx; } pfx->pfx_validlifetime = (uint32_t)val64; makeentry(entbuf, sizeof(entbuf), i, "vltimedecr"); if (agetflag(entbuf)) { struct timespec now; clock_gettime(CLOCK_MONOTONIC_FAST, &now); pfx->pfx_vltimeexpire = now.tv_sec + pfx->pfx_validlifetime; } makeentry(entbuf, sizeof(entbuf), i, "pltime"); MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME); if (val64 < 0 || val64 > 0xffffffff) { syslog(LOG_ERR, "<%s> pltime (%" PRIu64 ") for %s/%d on %s " "is out of range", __func__, val64, addr, pfx->pfx_prefixlen, ifi->ifi_ifname); goto getconfig_free_pfx; } pfx->pfx_preflifetime = (uint32_t)val64; makeentry(entbuf, sizeof(entbuf), i, "pltimedecr"); if (agetflag(entbuf)) { struct timespec now; clock_gettime(CLOCK_MONOTONIC_FAST, &now); pfx->pfx_pltimeexpire = now.tv_sec + pfx->pfx_preflifetime; } /* link into chain */ TAILQ_INSERT_TAIL(&rai->rai_prefix, pfx, pfx_next); rai->rai_pfxs++; continue; getconfig_free_pfx: free(pfx); } if (rai->rai_advifprefix && rai->rai_pfxs == 0) get_prefix(rai); MAYHAVE(val64, "mtu", 0); if (val64 < 0 || val64 > 0xffffffff) { syslog(LOG_ERR, "<%s> mtu (%" PRIu64 ") on %s out of range", __func__, val64, ifi->ifi_ifname); goto getconfig_free_rai; } rai->rai_linkmtu = (uint32_t)val64; if (rai->rai_linkmtu == 0) { char *mtustr; if ((mtustr = (char *)agetstr("mtu", &bp)) && strcmp(mtustr, "auto") == 0) rai->rai_linkmtu = ifi->ifi_phymtu; } else if (rai->rai_linkmtu < IPV6_MMTU || rai->rai_linkmtu > ifi->ifi_phymtu) { syslog(LOG_ERR, "<%s> advertised link mtu (%" PRIu32 ") on %s is invalid (must " "be between least MTU (%d) and physical link MTU (%d)", __func__, rai->rai_linkmtu, ifi->ifi_ifname, IPV6_MMTU, ifi->ifi_phymtu); goto getconfig_free_rai; } #ifdef SIOCSIFINFO_IN6 { struct in6_ndireq ndi; int s; if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "<%s> socket: %s", __func__, strerror(errno)); exit(1); } memset(&ndi, 0, sizeof(ndi)); strlcpy(ndi.ifname, ifi->ifi_ifname, sizeof(ndi.ifname)); if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&ndi) < 0) syslog(LOG_INFO, "<%s> ioctl:SIOCGIFINFO_IN6 at %s: %s", __func__, ifi->ifi_ifname, strerror(errno)); /* reflect the RA info to the host variables in kernel */ ndi.ndi.chlim = rai->rai_hoplimit; ndi.ndi.retrans = rai->rai_retranstimer; ndi.ndi.basereachable = rai->rai_reachabletime; if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&ndi) < 0) syslog(LOG_INFO, "<%s> ioctl:SIOCSIFINFO_IN6 at %s: %s", __func__, ifi->ifi_ifname, strerror(errno)); close(s); } #endif /* route information */ rai->rai_routes = 0; for (i = -1; i < MAXROUTE; i++) { struct rtinfo *rti; makeentry(entbuf, sizeof(entbuf), i, "rtprefix"); addr = (char *)agetstr(entbuf, &bp); if (addr == NULL) { makeentry(oentbuf, sizeof(oentbuf), i, "rtrprefix"); addr = (char *)agetstr(oentbuf, &bp); if (addr) fprintf(stderr, "%s was obsoleted. Use %s.\n", oentbuf, entbuf); } if (addr == NULL) continue; /* allocate memory to store prefix information */ ELM_MALLOC(rti, exit(1)); if (inet_pton(AF_INET6, addr, &rti->rti_prefix) != 1) { syslog(LOG_ERR, "<%s> inet_pton failed for %s", __func__, addr); goto getconfig_free_rti; } #if 0 /* * XXX: currently there's no restriction in route information * prefix according to * draft-ietf-ipngwg-router-selection-00.txt. * However, I think the similar restriction be necessary. */ MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME); if (IN6_IS_ADDR_MULTICAST(&rti->prefix)) { syslog(LOG_ERR, "<%s> multicast route (%s) must " "not be advertised on %s", __func__, addr, ifi->ifi_ifname); goto getconfig_free_rti; } if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) { syslog(LOG_NOTICE, "<%s> link-local route (%s) will " "be advertised on %s", __func__, addr, ifi->ifi_ifname); goto getconfig_free_rti; } #endif makeentry(entbuf, sizeof(entbuf), i, "rtplen"); /* XXX: 256 is a magic number for compatibility check. */ MAYHAVE(val, entbuf, 256); if (val == 256) { makeentry(oentbuf, sizeof(oentbuf), i, "rtrplen"); MAYHAVE(val, oentbuf, 256); if (val != 256) fprintf(stderr, "%s was obsoleted. Use %s.\n", oentbuf, entbuf); else val = 64; } if (val < 0 || val > 128) { syslog(LOG_ERR, "<%s> prefixlen (%" PRIu32 ") for %s on %s " "out of range", __func__, val, addr, ifi->ifi_ifname); goto getconfig_free_rti; } rti->rti_prefixlen = (int)val; makeentry(entbuf, sizeof(entbuf), i, "rtflags"); if ((flagstr = (char *)agetstr(entbuf, &bp))) { val = 0; if (strchr(flagstr, 'h')) val |= ND_RA_FLAG_RTPREF_HIGH; if (strchr(flagstr, 'l')) { if ((val & ND_RA_FLAG_RTPREF_HIGH)) { syslog(LOG_ERR, "<%s> the \'h\' and \'l\' route" " preferences are exclusive", __func__); goto getconfig_free_rti; } val |= ND_RA_FLAG_RTPREF_LOW; } } else MAYHAVE(val, entbuf, 256); /* XXX */ if (val == 256) { makeentry(oentbuf, sizeof(oentbuf), i, "rtrflags"); MAYHAVE(val, oentbuf, 256); if (val != 256) { fprintf(stderr, "%s was obsoleted. Use %s.\n", oentbuf, entbuf); } else val = 0; } rti->rti_rtpref = val & ND_RA_FLAG_RTPREF_MASK; if (rti->rti_rtpref == ND_RA_FLAG_RTPREF_RSV) { syslog(LOG_ERR, "<%s> invalid route preference (%02x) " "for %s/%d on %s", __func__, rti->rti_rtpref, addr, rti->rti_prefixlen, ifi->ifi_ifname); goto getconfig_free_rti; } /* * Since the spec does not a default value, we should make * this entry mandatory. However, FreeBSD 4.4 has shipped * with this field being optional, we use the router lifetime * as an ad-hoc default value with a warning message. */ makeentry(entbuf, sizeof(entbuf), i, "rtltime"); MAYHAVE(val64, entbuf, -1); if (val64 == -1) { makeentry(oentbuf, sizeof(oentbuf), i, "rtrltime"); MAYHAVE(val64, oentbuf, -1); if (val64 != -1) fprintf(stderr, "%s was obsoleted. Use %s.\n", oentbuf, entbuf); else { fprintf(stderr, "%s should be specified " "for interface %s.\n", entbuf, ifi->ifi_ifname); val64 = rai->rai_lifetime; } } if (val64 < 0 || val64 > 0xffffffff) { syslog(LOG_ERR, "<%s> route lifetime (%" PRIu64 ") for " "%s/%d on %s out of range", __func__, val64, addr, rti->rti_prefixlen, ifi->ifi_ifname); goto getconfig_free_rti; } rti->rti_ltime = (uint32_t)val64; /* link into chain */ TAILQ_INSERT_TAIL(&rai->rai_route, rti, rti_next); rai->rai_routes++; continue; getconfig_free_rti: free(rti); } /* DNS server and DNS search list information */ for (i = -1; i < MAXRDNSSENT ; i++) { struct rdnss *rdn; struct rdnss_addr *rdna; char *ap; int c; makeentry(entbuf, sizeof(entbuf), i, "rdnss"); addr = (char *)agetstr(entbuf, &bp); if (addr == NULL) continue; ELM_MALLOC(rdn, exit(1)); TAILQ_INIT(&rdn->rd_list); for (ap = addr; ap - addr < (ssize_t)strlen(addr); ap += c+1) { c = strcspn(ap, ","); strncpy(abuf, ap, c); abuf[c] = '\0'; ELM_MALLOC(rdna, goto getconfig_free_rdn); if (inet_pton(AF_INET6, abuf, &rdna->ra_dns) != 1) { syslog(LOG_ERR, "<%s> inet_pton failed for %s", __func__, abuf); free(rdna); goto getconfig_free_rdn; } TAILQ_INSERT_TAIL(&rdn->rd_list, rdna, ra_next); } makeentry(entbuf, sizeof(entbuf), i, "rdnssltime"); MAYHAVE(val, entbuf, (rai->rai_maxinterval * 3 / 2)); if ((uint16_t)val < rai->rai_maxinterval || (uint16_t)val > rai->rai_maxinterval * 2) { syslog(LOG_ERR, "%s (%" PRIu16 ") on %s is invalid " "(must be between %d and %d)", entbuf, val, ifi->ifi_ifname, rai->rai_maxinterval, rai->rai_maxinterval * 2); goto getconfig_free_rdn; } rdn->rd_ltime = val; /* link into chain */ TAILQ_INSERT_TAIL(&rai->rai_rdnss, rdn, rd_next); continue; getconfig_free_rdn: while ((rdna = TAILQ_FIRST(&rdn->rd_list)) != NULL) { TAILQ_REMOVE(&rdn->rd_list, rdna, ra_next); free(rdna); } free(rdn); } for (i = -1; i < MAXDNSSLENT ; i++) { struct dnssl *dns; struct dnssl_addr *dnsa; char *ap; int c; makeentry(entbuf, sizeof(entbuf), i, "dnssl"); addr = (char *)agetstr(entbuf, &bp); if (addr == NULL) continue; ELM_MALLOC(dns, exit(1)); TAILQ_INIT(&dns->dn_list); for (ap = addr; ap - addr < (ssize_t)strlen(addr); ap += c+1) { c = strcspn(ap, ","); strncpy(abuf, ap, c); abuf[c] = '\0'; ELM_MALLOC(dnsa, goto getconfig_free_dns); dnsa->da_len = dname_labelenc(dnsa->da_dom, abuf); if (dnsa->da_len < 0) { syslog(LOG_ERR, "Invalid dnssl entry: %s", abuf); goto getconfig_free_dns; } syslog(LOG_DEBUG, "<%s>: dnsa->da_len = %d", __func__, dnsa->da_len); TAILQ_INSERT_TAIL(&dns->dn_list, dnsa, da_next); } makeentry(entbuf, sizeof(entbuf), i, "dnsslltime"); MAYHAVE(val, entbuf, (rai->rai_maxinterval * 3 / 2)); if ((uint16_t)val < rai->rai_maxinterval || (uint16_t)val > rai->rai_maxinterval * 2) { syslog(LOG_ERR, "%s (%" PRIu16 ") on %s is invalid " "(must be between %d and %d)", entbuf, val, ifi->ifi_ifname, rai->rai_maxinterval, rai->rai_maxinterval * 2); goto getconfig_free_dns; } dns->dn_ltime = val; /* link into chain */ TAILQ_INSERT_TAIL(&rai->rai_dnssl, dns, dn_next); continue; getconfig_free_dns: while ((dnsa = TAILQ_FIRST(&dns->dn_list)) != NULL) { TAILQ_REMOVE(&dns->dn_list, dnsa, da_next); free(dnsa); } free(dns); } /* * handle pref64 */ for (i = -1; i < MAXPREF64 ; i++) { struct pref64 *prf64; makeentry(entbuf, sizeof(entbuf), i, "pref64"); addr = (char *)agetstr(entbuf, &bp); if (addr == NULL) continue; ELM_MALLOC(prf64, exit(1)); if (inet_pton(AF_INET6, addr, &prf64->p64_prefix) != 1) { syslog(LOG_ERR, "<%s> inet_pton failed for %s", __func__, addr); goto getconfig_free_prf64; } makeentry(entbuf, sizeof(entbuf), i, "pref64len"); MAYHAVE(val64, entbuf, 96); switch (val64) { case 96: prf64->p64_plc = 0; break; case 64: prf64->p64_plc = 1; break; case 56: prf64->p64_plc = 2; break; case 48: prf64->p64_plc = 3; break; case 40: prf64->p64_plc = 4; break; case 32: prf64->p64_plc = 5; break; default: syslog(LOG_ERR, "PREF64 prefix length %" PRIi64 "on %s is invalid; skipping", val64, ifi->ifi_ifname); goto getconfig_free_prf64; } makeentry(entbuf, sizeof(entbuf), i, "pref64lifetime"); MAYHAVE(val64, entbuf, (rai->rai_lifetime * 3)); /* This logic is from RFC 8781 section 4.1. */ if (val64 > 65528) val64 = 65528; val64 = (val64 + 7) / 8; prf64->p64_sl = (uint16_t)val64; /* link into chain */ TAILQ_INSERT_TAIL(&rai->rai_pref64, prf64, p64_next); continue; getconfig_free_prf64: free(prf64); } /* construct the sending packet */ make_packet(rai); /* * If an entry with the same ifindex exists, remove it first. * Before the removal, RDNSS and DNSSL options with * zero-lifetime will be sent. */ switch (ifi->ifi_state) { case IFI_STATE_UNCONFIGURED: /* UNCONFIGURED -> TRANSITIVE */ error = sock_mc_join(&sock, ifi->ifi_ifindex); if (error) exit(1); ifi->ifi_state = IFI_STATE_TRANSITIVE; ifi->ifi_burstcount = MAX_INITIAL_RTR_ADVERTISEMENTS; ifi->ifi_burstinterval = MAX_INITIAL_RTR_ADVERT_INTERVAL; /* The same two rai mean initial burst */ ifi->ifi_rainfo = rai; ifi->ifi_rainfo_trans = rai; TAILQ_INSERT_TAIL(&railist, rai, rai_next); if (ifi->ifi_ra_timer == NULL) ifi->ifi_ra_timer = rtadvd_add_timer(ra_timeout, ra_timer_update, ifi, ifi); ra_timer_update(ifi, &ifi->ifi_ra_timer->rat_tm); rtadvd_set_timer(&ifi->ifi_ra_timer->rat_tm, ifi->ifi_ra_timer); syslog(LOG_DEBUG, "<%s> ifname=%s marked as TRANSITIVE (initial burst).", __func__, ifi->ifi_ifname); break; case IFI_STATE_CONFIGURED: /* CONFIGURED -> TRANSITIVE */ rai_old = ifi->ifi_rainfo; if (rai_old == NULL) { syslog(LOG_ERR, "<%s> ifi_rainfo is NULL" " in IFI_STATE_CONFIGURED.", __func__); ifi = NULL; break; } else { struct rdnss *rdn; struct dnssl *dns; rai_old->rai_lifetime = 0; TAILQ_FOREACH(rdn, &rai_old->rai_rdnss, rd_next) rdn->rd_ltime = 0; TAILQ_FOREACH(dns, &rai_old->rai_dnssl, dn_next) dns->dn_ltime = 0; ifi->ifi_rainfo_trans = rai_old; ifi->ifi_state = IFI_STATE_TRANSITIVE; ifi->ifi_burstcount = MAX_FINAL_RTR_ADVERTISEMENTS; ifi->ifi_burstinterval = MIN_DELAY_BETWEEN_RAS; ra_timer_update(ifi, &ifi->ifi_ra_timer->rat_tm); rtadvd_set_timer(&ifi->ifi_ra_timer->rat_tm, ifi->ifi_ra_timer); syslog(LOG_DEBUG, "<%s> ifname=%s marked as TRANSITIVE" " (transitional burst)", __func__, ifi->ifi_ifname); } ifi->ifi_rainfo = rai; TAILQ_INSERT_TAIL(&railist, rai, rai_next); break; case IFI_STATE_TRANSITIVE: if (ifi->ifi_rainfo != NULL) { if (ifi->ifi_rainfo == ifi->ifi_rainfo_trans) { /* Reinitialize initial burst */ rm_rainfo(ifi->ifi_rainfo); ifi->ifi_rainfo = rai; ifi->ifi_rainfo_trans = rai; ifi->ifi_burstcount = MAX_INITIAL_RTR_ADVERTISEMENTS; ifi->ifi_burstinterval = MAX_INITIAL_RTR_ADVERT_INTERVAL; } else { /* Replace ifi_rainfo with the new one */ rm_rainfo(ifi->ifi_rainfo); ifi->ifi_rainfo = rai; } TAILQ_INSERT_TAIL(&railist, rai, rai_next); ra_timer_update(ifi, &ifi->ifi_ra_timer->rat_tm); rtadvd_set_timer(&ifi->ifi_ra_timer->rat_tm, ifi->ifi_ra_timer); } else { /* XXX: NOTREACHED. Being shut down. */ syslog(LOG_ERR, "<%s> %s is shutting down. Skipped.", __func__, ifi->ifi_ifname); rm_rainfo(rai); return (NULL); } break; } return (ifi); getconfig_free_rai: free(rai); return (NULL); } void get_prefix(struct rainfo *rai) { struct ifaddrs *ifap, *ifa; struct prefix *pfx; struct in6_addr *a; struct ifinfo *ifi; char *p, *ep, *m, *lim; char ntopbuf[INET6_ADDRSTRLEN]; if (getifaddrs(&ifap) < 0) { syslog(LOG_ERR, "<%s> can't get interface addresses", __func__); exit(1); } ifi = rai->rai_ifinfo; for (ifa = ifap; ifa; ifa = ifa->ifa_next) { int plen; if (strcmp(ifa->ifa_name, ifi->ifi_ifname) != 0) continue; if (ifa->ifa_addr->sa_family != AF_INET6) continue; a = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; if (IN6_IS_ADDR_LINKLOCAL(a)) continue; /* get prefix length */ m = (char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr; lim = (char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len; plen = prefixlen(m, lim); if (plen <= 0 || plen > 128) { syslog(LOG_ERR, "<%s> failed to get prefixlen " "or prefix is invalid", __func__); exit(1); } if (plen == 128) /* XXX */ continue; if (find_prefix(rai, a, plen)) { /* ignore a duplicated prefix. */ continue; } /* allocate memory to store prefix info. */ ELM_MALLOC(pfx, exit(1)); /* set prefix, sweep bits outside of prefixlen */ pfx->pfx_prefixlen = plen; memcpy(&pfx->pfx_prefix, a, sizeof(*a)); p = (char *)&pfx->pfx_prefix; ep = (char *)(&pfx->pfx_prefix + 1); while (m < lim && p < ep) *p++ &= *m++; while (p < ep) *p++ = 0x00; if (!inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf, sizeof(ntopbuf))) { syslog(LOG_ERR, "<%s> inet_ntop failed", __func__); exit(1); } syslog(LOG_DEBUG, "<%s> add %s/%d to prefix list on %s", __func__, ntopbuf, pfx->pfx_prefixlen, ifi->ifi_ifname); /* set other fields with protocol defaults */ pfx->pfx_validlifetime = DEF_ADVVALIDLIFETIME; pfx->pfx_preflifetime = DEF_ADVPREFERREDLIFETIME; pfx->pfx_onlinkflg = 1; pfx->pfx_autoconfflg = 1; pfx->pfx_origin = PREFIX_FROM_KERNEL; pfx->pfx_rainfo = rai; /* link into chain */ TAILQ_INSERT_TAIL(&rai->rai_prefix, pfx, pfx_next); /* counter increment */ rai->rai_pfxs++; } freeifaddrs(ifap); } static void makeentry(char *buf, size_t len, int id, const char *string) { if (id < 0) strlcpy(buf, string, len); else snprintf(buf, len, "%s%d", string, id); } /* * Add a prefix to the list of specified interface and reconstruct * the outgoing packet. * The prefix must not be in the list. * XXX: other parameters of the prefix (e.g. lifetime) should be * able to be specified. */ static void add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr) { struct prefix *pfx; struct ifinfo *ifi; char ntopbuf[INET6_ADDRSTRLEN]; ifi = rai->rai_ifinfo; ELM_MALLOC(pfx, return); pfx->pfx_prefix = ipr->ipr_prefix.sin6_addr; pfx->pfx_prefixlen = ipr->ipr_plen; pfx->pfx_validlifetime = ipr->ipr_vltime; pfx->pfx_preflifetime = ipr->ipr_pltime; pfx->pfx_onlinkflg = ipr->ipr_raf_onlink; pfx->pfx_autoconfflg = ipr->ipr_raf_auto; pfx->pfx_origin = PREFIX_FROM_DYNAMIC; pfx->pfx_rainfo = rai; TAILQ_INSERT_TAIL(&rai->rai_prefix, pfx, pfx_next); syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s", __func__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf, sizeof(ntopbuf)), ipr->ipr_plen, ifi->ifi_ifname); rai->rai_pfxs++; } /* * Delete a prefix to the list of specified interface and reconstruct * the outgoing packet. * The prefix must be in the list. */ void delete_prefix(struct prefix *pfx) { struct rainfo *rai; struct ifinfo *ifi; char ntopbuf[INET6_ADDRSTRLEN]; rai = pfx->pfx_rainfo; ifi = rai->rai_ifinfo; TAILQ_REMOVE(&rai->rai_prefix, pfx, pfx_next); syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s", __func__, inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf, sizeof(ntopbuf)), pfx->pfx_prefixlen, ifi->ifi_ifname); if (pfx->pfx_timer) rtadvd_remove_timer(pfx->pfx_timer); free(pfx); rai->rai_pfxs--; } void invalidate_prefix(struct prefix *pfx) { struct timespec timo; struct rainfo *rai; struct ifinfo *ifi; char ntopbuf[INET6_ADDRSTRLEN]; rai = pfx->pfx_rainfo; ifi = rai->rai_ifinfo; if (pfx->pfx_timer) { /* sanity check */ syslog(LOG_ERR, "<%s> assumption failure: timer already exists", __func__); exit(1); } syslog(LOG_DEBUG, "<%s> prefix %s/%d was invalidated on %s, " "will expire in %ld seconds", __func__, inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf, sizeof(ntopbuf)), pfx->pfx_prefixlen, ifi->ifi_ifname, (long)prefix_timo); /* set the expiration timer */ pfx->pfx_timer = rtadvd_add_timer(prefix_timeout, NULL, pfx, NULL); if (pfx->pfx_timer == NULL) { syslog(LOG_ERR, "<%s> failed to add a timer for a prefix. " "remove the prefix", __func__); delete_prefix(pfx); } timo.tv_sec = prefix_timo; timo.tv_nsec = 0; rtadvd_set_timer(&timo, pfx->pfx_timer); } static struct rtadvd_timer * prefix_timeout(void *arg) { delete_prefix((struct prefix *)arg); return (NULL); } void update_prefix(struct prefix *pfx) { struct rainfo *rai; struct ifinfo *ifi; char ntopbuf[INET6_ADDRSTRLEN]; rai = pfx->pfx_rainfo; ifi = rai->rai_ifinfo; if (pfx->pfx_timer == NULL) { /* sanity check */ syslog(LOG_ERR, "<%s> assumption failure: timer does not exist", __func__); exit(1); } syslog(LOG_DEBUG, "<%s> prefix %s/%d was re-enabled on %s", __func__, inet_ntop(AF_INET6, &pfx->pfx_prefix, ntopbuf, sizeof(ntopbuf)), pfx->pfx_prefixlen, ifi->ifi_ifname); /* stop the expiration timer */ rtadvd_remove_timer(pfx->pfx_timer); pfx->pfx_timer = NULL; } /* * Try to get an in6_prefixreq contents for a prefix which matches * ipr->ipr_prefix and ipr->ipr_plen and belongs to * the interface whose name is ipr->ipr_name[]. */ static int init_prefix(struct in6_prefixreq *ipr) { #if 0 int s; if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "<%s> socket: %s", __func__, strerror(errno)); exit(1); } if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) { syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __func__, strerror(errno)); ipr->ipr_vltime = DEF_ADVVALIDLIFETIME; ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME; ipr->ipr_raf_onlink = 1; ipr->ipr_raf_auto = 1; /* omit other field initialization */ } else if (ipr->ipr_origin < PR_ORIG_RR) { char ntopbuf[INET6_ADDRSTRLEN]; syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is" "lower than PR_ORIG_RR(router renumbering)." "This should not happen if I am router", __func__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf, sizeof(ntopbuf)), ipr->ipr_origin); close(s); return (1); } close(s); return (0); #else ipr->ipr_vltime = DEF_ADVVALIDLIFETIME; ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME; ipr->ipr_raf_onlink = 1; ipr->ipr_raf_auto = 1; return (0); #endif } void make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen) { struct in6_prefixreq ipr; memset(&ipr, 0, sizeof(ipr)); if (if_indextoname(ifindex, ipr.ipr_name) == NULL) { syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't " "exist. This should not happen! %s", __func__, ifindex, strerror(errno)); exit(1); } ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix); ipr.ipr_prefix.sin6_family = AF_INET6; ipr.ipr_prefix.sin6_addr = *addr; ipr.ipr_plen = plen; if (init_prefix(&ipr)) return; /* init failed by some error */ add_prefix(rai, &ipr); } void make_packet(struct rainfo *rai) { size_t packlen, lladdroptlen = 0; char *buf; struct nd_router_advert *ra; struct nd_opt_prefix_info *ndopt_pi; struct nd_opt_mtu *ndopt_mtu; struct nd_opt_route_info *ndopt_rti; struct rtinfo *rti; struct nd_opt_rdnss *ndopt_rdnss; struct rdnss *rdn; struct nd_opt_dnssl *ndopt_dnssl; struct dnssl *dns; struct pref64 *prf64; struct nd_opt_pref64 *ndopt_pref64; size_t len; struct prefix *pfx; struct ifinfo *ifi; ifi = rai->rai_ifinfo; /* calculate total length */ packlen = sizeof(struct nd_router_advert); if (rai->rai_advlinkopt) { if ((lladdroptlen = lladdropt_length(&ifi->ifi_sdl)) == 0) { syslog(LOG_INFO, "<%s> link-layer address option has" " null length on %s. Treat as not included.", __func__, ifi->ifi_ifname); rai->rai_advlinkopt = 0; } packlen += lladdroptlen; } if (rai->rai_pfxs) packlen += sizeof(struct nd_opt_prefix_info) * rai->rai_pfxs; if (rai->rai_linkmtu) packlen += sizeof(struct nd_opt_mtu); TAILQ_FOREACH(rti, &rai->rai_route, rti_next) packlen += sizeof(struct nd_opt_route_info) + ((rti->rti_prefixlen + 0x3f) >> 6) * 8; TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) { struct rdnss_addr *rdna; packlen += sizeof(struct nd_opt_rdnss); TAILQ_FOREACH(rdna, &rdn->rd_list, ra_next) packlen += sizeof(rdna->ra_dns); } TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) { struct dnssl_addr *dnsa; packlen += sizeof(struct nd_opt_dnssl); len = 0; TAILQ_FOREACH(dnsa, &dns->dn_list, da_next) len += dnsa->da_len; /* A zero octet and 8 octet boundary */ len++; len += (len % 8) ? 8 - len % 8 : 0; packlen += len; } TAILQ_FOREACH(prf64, &rai->rai_pref64, p64_next) packlen += sizeof(struct nd_opt_pref64); /* allocate memory for the packet */ if ((buf = malloc(packlen)) == NULL) { syslog(LOG_ERR, "<%s> can't get enough memory for an RA packet", __func__); exit(1); } memset(buf, 0, packlen); if (rai->rai_ra_data) /* Free old data if any. */ free(rai->rai_ra_data); rai->rai_ra_data = buf; /* XXX: what if packlen > 576? */ rai->rai_ra_datalen = packlen; /* * construct the packet */ ra = (struct nd_router_advert *)buf; ra->nd_ra_type = ND_ROUTER_ADVERT; ra->nd_ra_code = 0; ra->nd_ra_cksum = 0; ra->nd_ra_curhoplimit = (uint8_t)(0xff & rai->rai_hoplimit); /* * XXX: the router preference field, which is a 2-bit field, should be * initialized before other fields. */ ra->nd_ra_flags_reserved = 0xff & rai->rai_rtpref; ra->nd_ra_flags_reserved |= rai->rai_managedflg ? ND_RA_FLAG_MANAGED : 0; ra->nd_ra_flags_reserved |= rai->rai_otherflg ? ND_RA_FLAG_OTHER : 0; -#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG - ra->nd_ra_flags_reserved |= - rai->rai_ipv6onlyflg ? ND_RA_FLAG_IPV6_ONLY : 0; -#endif ra->nd_ra_router_lifetime = htons(rai->rai_lifetime); ra->nd_ra_reachable = htonl(rai->rai_reachabletime); ra->nd_ra_retransmit = htonl(rai->rai_retranstimer); buf += sizeof(*ra); if (rai->rai_advlinkopt) { lladdropt_fill(&ifi->ifi_sdl, (struct nd_opt_hdr *)buf); buf += lladdroptlen; } if (rai->rai_linkmtu) { ndopt_mtu = (struct nd_opt_mtu *)buf; ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU; ndopt_mtu->nd_opt_mtu_len = 1; ndopt_mtu->nd_opt_mtu_reserved = 0; ndopt_mtu->nd_opt_mtu_mtu = htonl(rai->rai_linkmtu); buf += sizeof(struct nd_opt_mtu); } TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) { uint32_t vltime, pltime; struct timespec now; ndopt_pi = (struct nd_opt_prefix_info *)buf; ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION; ndopt_pi->nd_opt_pi_len = 4; ndopt_pi->nd_opt_pi_prefix_len = pfx->pfx_prefixlen; ndopt_pi->nd_opt_pi_flags_reserved = 0; if (pfx->pfx_onlinkflg) ndopt_pi->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_ONLINK; if (pfx->pfx_autoconfflg) ndopt_pi->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_AUTO; if (pfx->pfx_timer) vltime = 0; else { if (pfx->pfx_vltimeexpire || pfx->pfx_pltimeexpire) clock_gettime(CLOCK_MONOTONIC_FAST, &now); if (pfx->pfx_vltimeexpire == 0) vltime = pfx->pfx_validlifetime; else vltime = ((time_t)pfx->pfx_vltimeexpire > now.tv_sec) ? pfx->pfx_vltimeexpire - now.tv_sec : 0; } if (pfx->pfx_timer) pltime = 0; else { if (pfx->pfx_pltimeexpire == 0) pltime = pfx->pfx_preflifetime; else pltime = ((time_t)pfx->pfx_pltimeexpire > now.tv_sec) ? pfx->pfx_pltimeexpire - now.tv_sec : 0; } if (vltime < pltime) { /* * this can happen if vltime is decrement but pltime * is not. */ pltime = vltime; } ndopt_pi->nd_opt_pi_valid_time = htonl(vltime); ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime); ndopt_pi->nd_opt_pi_reserved2 = 0; ndopt_pi->nd_opt_pi_prefix = pfx->pfx_prefix; buf += sizeof(struct nd_opt_prefix_info); } TAILQ_FOREACH(rti, &rai->rai_route, rti_next) { uint8_t psize = (rti->rti_prefixlen + 0x3f) >> 6; ndopt_rti = (struct nd_opt_route_info *)buf; ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO; ndopt_rti->nd_opt_rti_len = 1 + psize; ndopt_rti->nd_opt_rti_prefixlen = rti->rti_prefixlen; ndopt_rti->nd_opt_rti_flags = 0xff & rti->rti_rtpref; ndopt_rti->nd_opt_rti_lifetime = htonl(rti->rti_ltime); memcpy(ndopt_rti + 1, &rti->rti_prefix, psize * 8); buf += sizeof(struct nd_opt_route_info) + psize * 8; } TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) { struct rdnss_addr *rdna; ndopt_rdnss = (struct nd_opt_rdnss *)buf; ndopt_rdnss->nd_opt_rdnss_type = ND_OPT_RDNSS; ndopt_rdnss->nd_opt_rdnss_len = 0; ndopt_rdnss->nd_opt_rdnss_reserved = 0; ndopt_rdnss->nd_opt_rdnss_lifetime = htonl(rdn->rd_ltime); buf += sizeof(struct nd_opt_rdnss); TAILQ_FOREACH(rdna, &rdn->rd_list, ra_next) { memcpy(buf, &rdna->ra_dns, sizeof(rdna->ra_dns)); buf += sizeof(rdna->ra_dns); } /* Length field should be in 8 octets */ ndopt_rdnss->nd_opt_rdnss_len = (buf - (char *)ndopt_rdnss) / 8; syslog(LOG_DEBUG, "<%s>: nd_opt_dnss_len = %d", __func__, ndopt_rdnss->nd_opt_rdnss_len); } TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) { struct dnssl_addr *dnsa; ndopt_dnssl = (struct nd_opt_dnssl *)buf; ndopt_dnssl->nd_opt_dnssl_type = ND_OPT_DNSSL; ndopt_dnssl->nd_opt_dnssl_len = 0; ndopt_dnssl->nd_opt_dnssl_reserved = 0; ndopt_dnssl->nd_opt_dnssl_lifetime = htonl(dns->dn_ltime); buf += sizeof(*ndopt_dnssl); TAILQ_FOREACH(dnsa, &dns->dn_list, da_next) { memcpy(buf, dnsa->da_dom, dnsa->da_len); buf += dnsa->da_len; } /* A zero octet after encoded DNS server list. */ *buf++ = '\0'; /* Padding to next 8 octets boundary */ len = buf - (char *)ndopt_dnssl; len += (len % 8) ? 8 - len % 8 : 0; buf = (char *)ndopt_dnssl + len; /* Length field must be in 8 octets */ ndopt_dnssl->nd_opt_dnssl_len = len / 8; syslog(LOG_DEBUG, "<%s>: nd_opt_dnssl_len = %d", __func__, ndopt_dnssl->nd_opt_dnssl_len); } TAILQ_FOREACH(prf64, &rai->rai_pref64, p64_next) { ndopt_pref64 = (struct nd_opt_pref64 *)buf; ndopt_pref64->nd_opt_pref64_type = ND_OPT_PREF64; ndopt_pref64->nd_opt_pref64_len = 2; ndopt_pref64->nd_opt_pref64_sl_plc = (htons(prf64->p64_sl << 3)) | htons((prf64->p64_plc & 0x7)); memcpy(&ndopt_pref64->nd_opt_prefix[0], &prf64->p64_prefix, sizeof(ndopt_pref64->nd_opt_prefix)); buf += sizeof(struct nd_opt_pref64); } } diff --git a/usr.sbin/rtadvd/rtadvd.c b/usr.sbin/rtadvd/rtadvd.c index 1eb8f12a7338..2e9bced7c00e 100644 --- a/usr.sbin/rtadvd/rtadvd.c +++ b/usr.sbin/rtadvd/rtadvd.c @@ -1,1914 +1,1901 @@ /* $KAME: rtadvd.c,v 1.82 2003/08/05 12:34:23 itojun Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * Copyright (C) 2011 Hiroki Sato * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" #include "rtadvd.h" #include "if.h" #include "rrenum.h" #include "advcap.h" #include "timer_subr.h" #include "timer.h" #include "config.h" #include "control.h" #include "control_server.h" #define RTADV_TYPE2BITMASK(type) (0x1 << type) struct msghdr rcvmhdr; static char *rcvcmsgbuf; static size_t rcvcmsgbuflen; static char *sndcmsgbuf = NULL; static size_t sndcmsgbuflen; struct msghdr sndmhdr; struct iovec rcviov[2]; struct iovec sndiov[2]; struct sockaddr_in6 rcvfrom; static const char *pidfilename = _PATH_RTADVDPID; const char *conffile = _PATH_RTADVDCONF; static struct pidfh *pfh; static int dflag, sflag; static int wait_shutdown; #define PFD_RAWSOCK 0 #define PFD_RTSOCK 1 #define PFD_CSOCK 2 #define PFD_MAX 3 struct railist_head_t railist = TAILQ_HEAD_INITIALIZER(railist); struct ifilist_head_t ifilist = TAILQ_HEAD_INITIALIZER(ifilist); struct nd_optlist { TAILQ_ENTRY(nd_optlist) nol_next; struct nd_opt_hdr *nol_opt; }; union nd_opt { struct nd_opt_hdr *opt_array[9]; struct { struct nd_opt_hdr *zero; struct nd_opt_hdr *src_lladdr; struct nd_opt_hdr *tgt_lladdr; struct nd_opt_prefix_info *pi; struct nd_opt_rd_hdr *rh; struct nd_opt_mtu *mtu; TAILQ_HEAD(, nd_optlist) opt_list; } nd_opt_each; }; #define opt_src_lladdr nd_opt_each.src_lladdr #define opt_tgt_lladdr nd_opt_each.tgt_lladdr #define opt_pi nd_opt_each.pi #define opt_rh nd_opt_each.rh #define opt_mtu nd_opt_each.mtu #define opt_list nd_opt_each.opt_list #define NDOPT_FLAG_SRCLINKADDR (1 << 0) #define NDOPT_FLAG_TGTLINKADDR (1 << 1) #define NDOPT_FLAG_PREFIXINFO (1 << 2) #define NDOPT_FLAG_RDHDR (1 << 3) #define NDOPT_FLAG_MTU (1 << 4) #define NDOPT_FLAG_RDNSS (1 << 5) #define NDOPT_FLAG_DNSSL (1 << 6) #define NDOPT_FLAG_PREF64 (1 << 7) static uint32_t ndopt_flags[] = { [ND_OPT_SOURCE_LINKADDR] = NDOPT_FLAG_SRCLINKADDR, [ND_OPT_TARGET_LINKADDR] = NDOPT_FLAG_TGTLINKADDR, [ND_OPT_PREFIX_INFORMATION] = NDOPT_FLAG_PREFIXINFO, [ND_OPT_REDIRECTED_HEADER] = NDOPT_FLAG_RDHDR, [ND_OPT_MTU] = NDOPT_FLAG_MTU, [ND_OPT_RDNSS] = NDOPT_FLAG_RDNSS, [ND_OPT_DNSSL] = NDOPT_FLAG_DNSSL, [ND_OPT_PREF64] = NDOPT_FLAG_PREF64, }; static void rtadvd_shutdown(void); static void sock_open(struct sockinfo *); static void rtsock_open(struct sockinfo *); static void rtadvd_input(struct sockinfo *); static void rs_input(int, struct nd_router_solicit *, struct in6_pktinfo *, struct sockaddr_in6 *); static void ra_input(int, struct nd_router_advert *, struct in6_pktinfo *, struct sockaddr_in6 *); static int prefix_check(struct nd_opt_prefix_info *, struct rainfo *, struct sockaddr_in6 *); static int nd6_options(struct nd_opt_hdr *, int, union nd_opt *, uint32_t); static void free_ndopts(union nd_opt *); static void rtmsg_input(struct sockinfo *); static void set_short_delay(struct ifinfo *); static int check_accept_rtadv(int); static void usage(void) { fprintf(stderr, "usage: rtadvd [-dDfRs] " "[-c configfile] [-C ctlsock] [-M ifname] [-p pidfile]\n"); exit(1); } int main(int argc, char *argv[]) { struct pollfd set[PFD_MAX]; struct timespec *timeout; int i, ch; int fflag = 0, logopt; int error; pid_t pid, otherpid; /* get command line options and arguments */ while ((ch = getopt(argc, argv, "c:C:dDfhM:p:Rs")) != -1) { switch (ch) { case 'c': conffile = optarg; break; case 'C': ctrlsock.si_name = optarg; break; case 'd': dflag++; break; case 'D': dflag += 3; break; case 'f': fflag = 1; break; case 'M': mcastif = optarg; break; case 'R': fprintf(stderr, "rtadvd: " "the -R option is currently ignored.\n"); /* accept_rr = 1; */ /* run anyway... */ break; case 's': sflag = 1; break; case 'p': pidfilename = optarg; break; default: usage(); } } argc -= optind; argv += optind; logopt = LOG_NDELAY | LOG_PID; if (fflag) logopt |= LOG_PERROR; openlog("rtadvd", logopt, LOG_DAEMON); /* set log level */ if (dflag > 2) (void)setlogmask(LOG_UPTO(LOG_DEBUG)); else if (dflag > 1) (void)setlogmask(LOG_UPTO(LOG_INFO)); else if (dflag > 0) (void)setlogmask(LOG_UPTO(LOG_NOTICE)); else (void)setlogmask(LOG_UPTO(LOG_ERR)); /* timer initialization */ rtadvd_timer_init(); pfh = pidfile_open(pidfilename, 0600, &otherpid); if (pfh == NULL) { if (errno == EEXIST) errx(1, "%s already running, pid: %d", getprogname(), otherpid); syslog(LOG_ERR, "failed to open the pid file %s, run anyway.", pidfilename); } if (!fflag) daemon(1, 0); sock_open(&sock); update_ifinfo(&ifilist, UPDATE_IFINFO_ALL); for (i = 0; i < argc; i++) update_persist_ifinfo(&ifilist, argv[i]); csock_open(&ctrlsock, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); if (ctrlsock.si_fd == -1) { syslog(LOG_ERR, "cannot open control socket: %s", strerror(errno)); exit(1); } /* record the current PID */ pid = getpid(); pidfile_write(pfh); set[PFD_RAWSOCK].fd = sock.si_fd; set[PFD_RAWSOCK].events = POLLIN; if (sflag == 0) { rtsock_open(&rtsock); set[PFD_RTSOCK].fd = rtsock.si_fd; set[PFD_RTSOCK].events = POLLIN; } else set[PFD_RTSOCK].fd = -1; set[PFD_CSOCK].fd = ctrlsock.si_fd; set[PFD_CSOCK].events = POLLIN; signal(SIGTERM, set_do_shutdown); signal(SIGINT, set_do_shutdown); signal(SIGHUP, set_do_reload); error = csock_listen(&ctrlsock); if (error) { syslog(LOG_ERR, "cannot listen control socket: %s", strerror(errno)); exit(1); } /* load configuration file */ set_do_reload(0); while (1) { if (is_do_shutdown()) rtadvd_shutdown(); if (is_do_reload()) { loadconfig_ifname(reload_ifname()); if (reload_ifname() == NULL) syslog(LOG_INFO, "configuration file reloaded."); else syslog(LOG_INFO, "configuration file for %s reloaded.", reload_ifname()); reset_do_reload(); } /* timeout handler update for active interfaces */ rtadvd_update_timeout_handler(); /* timer expiration check and reset the timer */ timeout = rtadvd_check_timer(); if (timeout != NULL) { syslog(LOG_DEBUG, "<%s> set timer to %ld:%ld. waiting for " "inputs or timeout", __func__, (long int)timeout->tv_sec, (long int)timeout->tv_nsec / 1000); } else { syslog(LOG_DEBUG, "<%s> there's no timer. waiting for inputs", __func__); } if ((i = poll(set, nitems(set), timeout ? (timeout->tv_sec * 1000 + timeout->tv_nsec / 1000 / 1000) : INFTIM)) < 0) { /* EINTR would occur if a signal was delivered */ if (errno != EINTR) syslog(LOG_ERR, "poll() failed: %s", strerror(errno)); continue; } if (i == 0) /* timeout */ continue; if (rtsock.si_fd != -1 && set[PFD_RTSOCK].revents & POLLIN) rtmsg_input(&rtsock); if (set[PFD_RAWSOCK].revents & POLLIN) rtadvd_input(&sock); if (set[PFD_CSOCK].revents & POLLIN) { int fd; fd = csock_accept(&ctrlsock); if (fd == -1) syslog(LOG_ERR, "cannot accept() control socket: %s", strerror(errno)); else { cm_handler_server(fd); close(fd); } } } exit(0); /* NOTREACHED */ } static void rtadvd_shutdown(void) { struct ifinfo *ifi; struct rainfo *rai; struct rdnss *rdn; struct dnssl *dns; if (wait_shutdown) { syslog(LOG_INFO, "waiting expiration of the all RA timers."); TAILQ_FOREACH(ifi, &ifilist, ifi_next) { /* * Ignore !IFF_UP interfaces in waiting for shutdown. */ if (!(ifi->ifi_flags & IFF_UP) && ifi->ifi_ra_timer != NULL) { ifi->ifi_state = IFI_STATE_UNCONFIGURED; rtadvd_remove_timer(ifi->ifi_ra_timer); ifi->ifi_ra_timer = NULL; syslog(LOG_DEBUG, "<%s> %s(idx=%d) is down. " "Timer removed and marked as UNCONFIGURED.", __func__, ifi->ifi_ifname, ifi->ifi_ifindex); } } TAILQ_FOREACH(ifi, &ifilist, ifi_next) { if (ifi->ifi_ra_timer != NULL) break; } if (ifi == NULL) { syslog(LOG_NOTICE, "gracefully terminated."); exit(0); } sleep(1); return; } syslog(LOG_DEBUG, "<%s> cease to be an advertising router", __func__); wait_shutdown = 1; TAILQ_FOREACH(rai, &railist, rai_next) { rai->rai_lifetime = 0; TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) rdn->rd_ltime = 0; TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) dns->dn_ltime = 0; } TAILQ_FOREACH(ifi, &ifilist, ifi_next) { if (!ifi->ifi_persist) continue; if (ifi->ifi_state == IFI_STATE_UNCONFIGURED) continue; if (ifi->ifi_ra_timer == NULL) continue; if (ifi->ifi_ra_lastsent.tv_sec == 0 && ifi->ifi_ra_lastsent.tv_nsec == 0 && ifi->ifi_ra_timer != NULL) { /* * When RA configured but never sent, * ignore the IF immediately. */ rtadvd_remove_timer(ifi->ifi_ra_timer); ifi->ifi_ra_timer = NULL; ifi->ifi_state = IFI_STATE_UNCONFIGURED; continue; } ifi->ifi_state = IFI_STATE_TRANSITIVE; /* Mark as the shut-down state. */ ifi->ifi_rainfo_trans = ifi->ifi_rainfo; ifi->ifi_rainfo = NULL; ifi->ifi_burstcount = MAX_FINAL_RTR_ADVERTISEMENTS; ifi->ifi_burstinterval = MIN_DELAY_BETWEEN_RAS; ra_timer_update(ifi, &ifi->ifi_ra_timer->rat_tm); rtadvd_set_timer(&ifi->ifi_ra_timer->rat_tm, ifi->ifi_ra_timer); } syslog(LOG_NOTICE, "final RA transmission started."); pidfile_remove(pfh); csock_close(&ctrlsock); } static void rtmsg_input(struct sockinfo *s) { int n, type, ifindex = 0, plen; size_t len; char msg[2048], *next, *lim; char ifname[IFNAMSIZ]; struct if_announcemsghdr *ifan; struct rt_msghdr *rtm; struct prefix *pfx; struct rainfo *rai; struct in6_addr *addr; struct ifinfo *ifi; char addrbuf[INET6_ADDRSTRLEN]; int prefixchange = 0; if (s == NULL) { syslog(LOG_ERR, "<%s> internal error", __func__); exit(1); } n = read(s->si_fd, msg, sizeof(msg)); rtm = (struct rt_msghdr *)msg; syslog(LOG_DEBUG, "<%s> received a routing message " "(type = %d, len = %d)", __func__, rtm->rtm_type, n); if (n > rtm->rtm_msglen) { /* * This usually won't happen for messages received on * a routing socket. */ syslog(LOG_DEBUG, "<%s> received data length is larger than " "1st routing message len. multiple messages? " "read %d bytes, but 1st msg len = %d", __func__, n, rtm->rtm_msglen); #if 0 /* adjust length */ n = rtm->rtm_msglen; #endif } lim = msg + n; for (next = msg; next < lim; next += len) { int oldifflags; next = get_next_msg(next, lim, 0, &len, RTADV_TYPE2BITMASK(RTM_ADD) | RTADV_TYPE2BITMASK(RTM_DELETE) | RTADV_TYPE2BITMASK(RTM_NEWADDR) | RTADV_TYPE2BITMASK(RTM_DELADDR) | RTADV_TYPE2BITMASK(RTM_IFINFO) | RTADV_TYPE2BITMASK(RTM_IFANNOUNCE)); if (len == 0) break; type = ((struct rt_msghdr *)next)->rtm_type; switch (type) { case RTM_ADD: case RTM_DELETE: ifindex = get_rtm_ifindex(next); break; case RTM_NEWADDR: case RTM_DELADDR: ifindex = (int)((struct ifa_msghdr *)next)->ifam_index; break; case RTM_IFINFO: ifindex = (int)((struct if_msghdr *)next)->ifm_index; break; case RTM_IFANNOUNCE: ifan = (struct if_announcemsghdr *)next; switch (ifan->ifan_what) { case IFAN_ARRIVAL: case IFAN_DEPARTURE: break; default: syslog(LOG_DEBUG, "<%s:%d> unknown ifan msg (ifan_what=%d)", __func__, __LINE__, ifan->ifan_what); continue; } syslog(LOG_DEBUG, "<%s>: if_announcemsg (idx=%d:%d)", __func__, ifan->ifan_index, ifan->ifan_what); switch (ifan->ifan_what) { case IFAN_ARRIVAL: syslog(LOG_NOTICE, "interface added (idx=%d)", ifan->ifan_index); update_ifinfo(&ifilist, ifan->ifan_index); loadconfig_index(ifan->ifan_index); break; case IFAN_DEPARTURE: syslog(LOG_NOTICE, "interface removed (idx=%d)", ifan->ifan_index); rm_ifinfo_index(ifan->ifan_index); /* Clear ifi_ifindex */ TAILQ_FOREACH(ifi, &ifilist, ifi_next) { if (ifi->ifi_ifindex == ifan->ifan_index) { ifi->ifi_ifindex = 0; break; } } update_ifinfo(&ifilist, ifan->ifan_index); break; } continue; default: /* should not reach here */ syslog(LOG_DEBUG, "<%s:%d> unknown rtmsg %d on %s", __func__, __LINE__, type, if_indextoname(ifindex, ifname)); continue; } ifi = if_indextoifinfo(ifindex); if (ifi == NULL) { syslog(LOG_DEBUG, "<%s> ifinfo not found for idx=%d. Why?", __func__, ifindex); continue; } rai = ifi->ifi_rainfo; if (rai == NULL) { syslog(LOG_DEBUG, "<%s> route changed on " "non advertising interface(%s)", __func__, ifi->ifi_ifname); continue; } oldifflags = ifi->ifi_flags; /* init ifflags because it may have changed */ update_ifinfo(&ifilist, ifindex); switch (type) { case RTM_ADD: if (sflag) break; /* we aren't interested in prefixes */ addr = get_addr(msg); plen = get_prefixlen(msg); /* sanity check for plen */ /* as RFC2373, prefixlen is at least 4 */ if (plen < 4 || plen > 127) { syslog(LOG_INFO, "<%s> new interface route's" "plen %d is invalid for a prefix", __func__, plen); break; } pfx = find_prefix(rai, addr, plen); if (pfx) { if (pfx->pfx_timer) { /* * If the prefix has been invalidated, * make it available again. */ update_prefix(pfx); prefixchange = 1; } else syslog(LOG_DEBUG, "<%s> new prefix(%s/%d) " "added on %s, " "but it was already in list", __func__, inet_ntop(AF_INET6, addr, (char *)addrbuf, sizeof(addrbuf)), plen, ifi->ifi_ifname); break; } make_prefix(rai, ifindex, addr, plen); prefixchange = 1; break; case RTM_DELETE: if (sflag) break; addr = get_addr(msg); plen = get_prefixlen(msg); /* sanity check for plen */ /* as RFC2373, prefixlen is at least 4 */ if (plen < 4 || plen > 127) { syslog(LOG_INFO, "<%s> deleted interface route's " "plen %d is invalid for a prefix", __func__, plen); break; } pfx = find_prefix(rai, addr, plen); if (pfx == NULL) { syslog(LOG_DEBUG, "<%s> prefix(%s/%d) was deleted on %s, " "but it was not in list", __func__, inet_ntop(AF_INET6, addr, (char *)addrbuf, sizeof(addrbuf)), plen, ifi->ifi_ifname); break; } invalidate_prefix(pfx); prefixchange = 1; break; case RTM_NEWADDR: case RTM_DELADDR: case RTM_IFINFO: break; default: /* should not reach here */ syslog(LOG_DEBUG, "<%s:%d> unknown rtmsg %d on %s", __func__, __LINE__, type, if_indextoname(ifindex, ifname)); return; } /* check if an interface flag is changed */ if ((oldifflags & IFF_UP) && /* UP to DOWN */ !(ifi->ifi_flags & IFF_UP)) { syslog(LOG_NOTICE, "ifi_ifname); rtadvd_remove_timer(ifi->ifi_ra_timer); ifi->ifi_ra_timer = NULL; } else if (!(oldifflags & IFF_UP) && /* DOWN to UP */ (ifi->ifi_flags & IFF_UP)) { syslog(LOG_NOTICE, "interface %s becomes up. restart timer.", ifi->ifi_ifname); ifi->ifi_state = IFI_STATE_TRANSITIVE; ifi->ifi_burstcount = MAX_INITIAL_RTR_ADVERTISEMENTS; ifi->ifi_burstinterval = MAX_INITIAL_RTR_ADVERT_INTERVAL; ifi->ifi_ra_timer = rtadvd_add_timer(ra_timeout, ra_timer_update, ifi, ifi); ra_timer_update(ifi, &ifi->ifi_ra_timer->rat_tm); rtadvd_set_timer(&ifi->ifi_ra_timer->rat_tm, ifi->ifi_ra_timer); } else if (prefixchange && (ifi->ifi_flags & IFF_UP)) { /* * An advertised prefix has been added or invalidated. * Will notice the change in a short delay. */ set_short_delay(ifi); } } } void rtadvd_input(struct sockinfo *s) { ssize_t i; int *hlimp = NULL; #ifdef OLDRAWSOCKET struct ip6_hdr *ip; #endif struct icmp6_hdr *icp; int ifindex = 0; struct cmsghdr *cm; struct in6_pktinfo *pi = NULL; char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; struct in6_addr dst = in6addr_any; struct ifinfo *ifi; syslog(LOG_DEBUG, "<%s> enter", __func__); if (s == NULL) { syslog(LOG_ERR, "<%s> internal error", __func__); exit(1); } /* * Get message. We reset msg_controllen since the field could * be modified if we had received a message before setting * receive options. */ rcvmhdr.msg_controllen = rcvcmsgbuflen; if ((i = recvmsg(s->si_fd, &rcvmhdr, 0)) < 0) return; /* extract optional information via Advanced API */ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr); cm; cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) { if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_PKTINFO && cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); ifindex = pi->ipi6_ifindex; dst = pi->ipi6_addr; } if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_HOPLIMIT && cm->cmsg_len == CMSG_LEN(sizeof(int))) hlimp = (int *)CMSG_DATA(cm); } if (ifindex == 0) { syslog(LOG_ERR, "failed to get receiving interface"); return; } if (hlimp == NULL) { syslog(LOG_ERR, "failed to get receiving hop limit"); return; } /* * If we happen to receive data on an interface which is now gone * or down, just discard the data. */ ifi = if_indextoifinfo(pi->ipi6_ifindex); if (ifi == NULL || !(ifi->ifi_flags & IFF_UP)) { syslog(LOG_INFO, "<%s> received data on a disabled interface (%s)", __func__, (ifi == NULL) ? "[gone]" : ifi->ifi_ifname); return; } #ifdef OLDRAWSOCKET if ((size_t)i < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) { syslog(LOG_ERR, "packet size(%d) is too short", i); return; } ip = (struct ip6_hdr *)rcvmhdr.msg_iov[0].iov_base; icp = (struct icmp6_hdr *)(ip + 1); /* XXX: ext. hdr? */ #else if ((size_t)i < sizeof(struct icmp6_hdr)) { syslog(LOG_ERR, "packet size(%zd) is too short", i); return; } icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base; #endif switch (icp->icmp6_type) { case ND_ROUTER_SOLICIT: /* * Message verification - RFC 4861 6.1.1 * XXX: these checks must be done in the kernel as well, * but we can't completely rely on them. */ if (*hlimp != 255) { syslog(LOG_NOTICE, "RS with invalid hop limit(%d) " "received from %s on %s", *hlimp, inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } if (icp->icmp6_code) { syslog(LOG_NOTICE, "RS with invalid ICMP6 code(%d) " "received from %s on %s", icp->icmp6_code, inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } if ((size_t)i < sizeof(struct nd_router_solicit)) { syslog(LOG_NOTICE, "RS from %s on %s does not have enough " "length (len = %zd)", inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf), i); return; } rs_input(i, (struct nd_router_solicit *)icp, pi, &rcvfrom); break; case ND_ROUTER_ADVERT: /* * Message verification - RFC 4861 6.1.2 * XXX: there's the same dilemma as above... */ if (!IN6_IS_ADDR_LINKLOCAL(&rcvfrom.sin6_addr)) { syslog(LOG_NOTICE, "RA with non-linklocal source address " "received from %s on %s", inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } if (*hlimp != 255) { syslog(LOG_NOTICE, "RA with invalid hop limit(%d) " "received from %s on %s", *hlimp, inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } if (icp->icmp6_code) { syslog(LOG_NOTICE, "RA with invalid ICMP6 code(%d) " "received from %s on %s", icp->icmp6_code, inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } if ((size_t)i < sizeof(struct nd_router_advert)) { syslog(LOG_NOTICE, "RA from %s on %s does not have enough " "length (len = %zd)", inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf), i); return; } ra_input(i, (struct nd_router_advert *)icp, pi, &rcvfrom); break; case ICMP6_ROUTER_RENUMBERING: if (mcastif == NULL) { syslog(LOG_ERR, "received a router renumbering " "message, but not allowed to be accepted"); break; } rr_input(i, (struct icmp6_router_renum *)icp, pi, &rcvfrom, &dst); break; default: /* * Note that this case is POSSIBLE, especially just * after invocation of the daemon. This is because we * could receive message after opening the socket and * before setting ICMP6 type filter(see sock_open()). */ syslog(LOG_ERR, "invalid icmp type(%d)", icp->icmp6_type); return; } } static void rs_input(int len, struct nd_router_solicit *rs, struct in6_pktinfo *pi, struct sockaddr_in6 *from) { char ntopbuf[INET6_ADDRSTRLEN]; char ifnamebuf[IFNAMSIZ]; union nd_opt ndopts; struct rainfo *rai; struct ifinfo *ifi; struct soliciter *sol; syslog(LOG_DEBUG, "<%s> RS received from %s on %s", __func__, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); /* ND option check */ memset(&ndopts, 0, sizeof(ndopts)); TAILQ_INIT(&ndopts.opt_list); if (nd6_options((struct nd_opt_hdr *)(rs + 1), len - sizeof(struct nd_router_solicit), &ndopts, NDOPT_FLAG_SRCLINKADDR)) { syslog(LOG_INFO, "<%s> ND option check failed for an RS from %s on %s", __func__, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } /* * If the IP source address is the unspecified address, there * must be no source link-layer address option in the message. * (RFC 4861 6.1.1) */ if (IN6_IS_ADDR_UNSPECIFIED(&from->sin6_addr) && ndopts.opt_src_lladdr) { syslog(LOG_INFO, "<%s> RS from unspecified src on %s has a link-layer" " address option", __func__, if_indextoname(pi->ipi6_ifindex, ifnamebuf)); goto done; } ifi = if_indextoifinfo(pi->ipi6_ifindex); if (ifi == NULL) { syslog(LOG_INFO, "<%s> if (idx=%d) not found. Why?", __func__, pi->ipi6_ifindex); goto done; } rai = ifi->ifi_rainfo; if (rai == NULL) { syslog(LOG_INFO, "<%s> RS received on non advertising interface(%s)", __func__, if_indextoname(pi->ipi6_ifindex, ifnamebuf)); goto done; } rai->rai_ifinfo->ifi_rsinput++; /* * Decide whether to send RA according to the rate-limit * consideration. */ /* record sockaddr waiting for RA, if possible */ sol = (struct soliciter *)malloc(sizeof(*sol)); if (sol) { sol->sol_addr = *from; /* XXX RFC 2553 need clarification on flowinfo */ sol->sol_addr.sin6_flowinfo = 0; TAILQ_INSERT_TAIL(&rai->rai_soliciter, sol, sol_next); } /* * If there is already a waiting RS packet, don't * update the timer. */ if (ifi->ifi_rs_waitcount++) goto done; set_short_delay(ifi); done: free_ndopts(&ndopts); } static void set_short_delay(struct ifinfo *ifi) { long delay; /* must not be greater than 1000000 */ struct timespec interval, now, min_delay, tm_tmp, *rest; if (ifi->ifi_ra_timer == NULL) return; /* * Compute a random delay. If the computed value * corresponds to a time later than the time the next * multicast RA is scheduled to be sent, ignore the random * delay and send the advertisement at the * already-scheduled time. RFC 4861 6.2.6 */ delay = arc4random_uniform(MAX_RA_DELAY_TIME); interval.tv_sec = 0; interval.tv_nsec = delay * 1000; rest = rtadvd_timer_rest(ifi->ifi_ra_timer); if (TS_CMP(rest, &interval, <)) { syslog(LOG_DEBUG, "<%s> random delay is larger than " "the rest of the current timer", __func__); interval = *rest; } /* * If we sent a multicast Router Advertisement within * the last MIN_DELAY_BETWEEN_RAS seconds, schedule * the advertisement to be sent at a time corresponding to * MIN_DELAY_BETWEEN_RAS plus the random value after the * previous advertisement was sent. */ clock_gettime(CLOCK_MONOTONIC_FAST, &now); TS_SUB(&now, &ifi->ifi_ra_lastsent, &tm_tmp); min_delay.tv_sec = MIN_DELAY_BETWEEN_RAS; min_delay.tv_nsec = 0; if (TS_CMP(&tm_tmp, &min_delay, <)) { TS_SUB(&min_delay, &tm_tmp, &min_delay); TS_ADD(&min_delay, &interval, &interval); } rtadvd_set_timer(&interval, ifi->ifi_ra_timer); } static int check_accept_rtadv(int idx) { struct ifinfo *ifi; TAILQ_FOREACH(ifi, &ifilist, ifi_next) { if (ifi->ifi_ifindex == idx) break; } if (ifi == NULL) { syslog(LOG_DEBUG, "<%s> if (idx=%d) not found. Why?", __func__, idx); return (0); } /* * RA_RECV: ND6_IFF_ACCEPT_RTADV * RA_SEND: ip6.forwarding */ if (update_ifinfo_nd_flags(ifi) != 0) { syslog(LOG_ERR, "cannot get nd6 flags (idx=%d)", idx); return (0); } return (ifi->ifi_nd_flags & ND6_IFF_ACCEPT_RTADV); } static void ra_input(int len, struct nd_router_advert *nra, struct in6_pktinfo *pi, struct sockaddr_in6 *from) { struct rainfo *rai; struct ifinfo *ifi; char ntopbuf[INET6_ADDRSTRLEN]; char ifnamebuf[IFNAMSIZ]; union nd_opt ndopts; const char *on_off[] = {"OFF", "ON"}; uint32_t reachabletime, retranstimer, mtu; int inconsistent = 0; int error; syslog(LOG_DEBUG, "<%s> RA received from %s on %s", __func__, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); /* ND option check */ memset(&ndopts, 0, sizeof(ndopts)); TAILQ_INIT(&ndopts.opt_list); error = nd6_options((struct nd_opt_hdr *)(nra + 1), len - sizeof(struct nd_router_advert), &ndopts, NDOPT_FLAG_SRCLINKADDR | NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU | NDOPT_FLAG_RDNSS | NDOPT_FLAG_DNSSL | NDOPT_FLAG_PREF64); if (error) { syslog(LOG_INFO, "<%s> ND option check failed for an RA from %s on %s", __func__, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } /* * RA consistency check according to RFC 4861 6.2.7 */ ifi = if_indextoifinfo(pi->ipi6_ifindex); if (ifi->ifi_rainfo == NULL) { syslog(LOG_INFO, "<%s> received RA from %s on non-advertising" " interface(%s)", __func__, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); goto done; } rai = ifi->ifi_rainfo; ifi->ifi_rainput++; syslog(LOG_DEBUG, "<%s> ifi->ifi_rainput = %" PRIu64, __func__, ifi->ifi_rainput); /* Cur Hop Limit value */ if (nra->nd_ra_curhoplimit && rai->rai_hoplimit && nra->nd_ra_curhoplimit != rai->rai_hoplimit) { syslog(LOG_NOTICE, "CurHopLimit inconsistent on %s:" " %d from %s, %d from us", ifi->ifi_ifname, nra->nd_ra_curhoplimit, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), rai->rai_hoplimit); inconsistent++; } /* M flag */ if ((nra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) != rai->rai_managedflg) { syslog(LOG_NOTICE, "M flag inconsistent on %s:" " %s from %s, %s from us", ifi->ifi_ifname, on_off[!rai->rai_managedflg], inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), on_off[rai->rai_managedflg]); inconsistent++; } /* O flag */ if ((nra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) != rai->rai_otherflg) { syslog(LOG_NOTICE, "O flag inconsistent on %s:" " %s from %s, %s from us", ifi->ifi_ifname, on_off[!rai->rai_otherflg], inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), on_off[rai->rai_otherflg]); inconsistent++; } -#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG - /* S "IPv6-Only" (Six, Silence-IPv4) flag */ - if ((nra->nd_ra_flags_reserved & ND_RA_FLAG_IPV6_ONLY) != - rai->rai_ipv6onlyflg) { - syslog(LOG_NOTICE, - "S flag inconsistent on %s:" - " %s from %s, %s from us", - ifi->ifi_ifname, on_off[!rai->rai_ipv6onlyflg], - inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, - sizeof(ntopbuf)), on_off[rai->rai_ipv6onlyflg]); - inconsistent++; - } -#endif /* Reachable Time */ reachabletime = ntohl(nra->nd_ra_reachable); if (reachabletime && rai->rai_reachabletime && reachabletime != rai->rai_reachabletime) { syslog(LOG_NOTICE, "ReachableTime inconsistent on %s:" " %d from %s, %d from us", ifi->ifi_ifname, reachabletime, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), rai->rai_reachabletime); inconsistent++; } /* Retrans Timer */ retranstimer = ntohl(nra->nd_ra_retransmit); if (retranstimer && rai->rai_retranstimer && retranstimer != rai->rai_retranstimer) { syslog(LOG_NOTICE, "RetranceTimer inconsistent on %s:" " %d from %s, %d from us", ifi->ifi_ifname, retranstimer, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), rai->rai_retranstimer); inconsistent++; } /* Values in the MTU options */ if (ndopts.opt_mtu) { mtu = ntohl(ndopts.opt_mtu->nd_opt_mtu_mtu); if (mtu && rai->rai_linkmtu && mtu != rai->rai_linkmtu) { syslog(LOG_NOTICE, "MTU option value inconsistent on %s:" " %d from %s, %d from us", ifi->ifi_ifname, mtu, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), rai->rai_linkmtu); inconsistent++; } } /* Preferred and Valid Lifetimes for prefixes */ { struct nd_optlist *nol; if (ndopts.opt_pi) if (prefix_check(ndopts.opt_pi, rai, from)) inconsistent++; TAILQ_FOREACH(nol, &ndopts.opt_list, nol_next) if (prefix_check((struct nd_opt_prefix_info *)nol->nol_opt, rai, from)) inconsistent++; } if (inconsistent) ifi->ifi_rainconsistent++; done: free_ndopts(&ndopts); } static uint32_t udiff(uint32_t u, uint32_t v) { return (u >= v ? u - v : v - u); } /* return a non-zero value if the received prefix is inconsistent with ours */ static int prefix_check(struct nd_opt_prefix_info *pinfo, struct rainfo *rai, struct sockaddr_in6 *from) { struct ifinfo *ifi; uint32_t preferred_time, valid_time; struct prefix *pfx; int inconsistent = 0; char ntopbuf[INET6_ADDRSTRLEN]; char prefixbuf[INET6_ADDRSTRLEN]; struct timespec now; #if 0 /* impossible */ if (pinfo->nd_opt_pi_type != ND_OPT_PREFIX_INFORMATION) return (0); #endif ifi = rai->rai_ifinfo; /* * log if the adveritsed prefix has link-local scope(sanity check?) */ if (IN6_IS_ADDR_LINKLOCAL(&pinfo->nd_opt_pi_prefix)) syslog(LOG_INFO, "<%s> link-local prefix %s/%d is advertised " "from %s on %s", __func__, inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf, sizeof(prefixbuf)), pinfo->nd_opt_pi_prefix_len, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), ifi->ifi_ifname); if ((pfx = find_prefix(rai, &pinfo->nd_opt_pi_prefix, pinfo->nd_opt_pi_prefix_len)) == NULL) { syslog(LOG_INFO, "<%s> prefix %s/%d from %s on %s is not in our list", __func__, inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf, sizeof(prefixbuf)), pinfo->nd_opt_pi_prefix_len, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), ifi->ifi_ifname); return (0); } preferred_time = ntohl(pinfo->nd_opt_pi_preferred_time); if (pfx->pfx_pltimeexpire) { /* * The lifetime is decremented in real time, so we should * compare the expiration time. * (RFC 2461 Section 6.2.7.) * XXX: can we really expect that all routers on the link * have synchronized clocks? */ clock_gettime(CLOCK_MONOTONIC_FAST, &now); preferred_time += now.tv_sec; if (!pfx->pfx_timer && rai->rai_clockskew && udiff(preferred_time, pfx->pfx_pltimeexpire) > rai->rai_clockskew) { syslog(LOG_INFO, "<%s> preferred lifetime for %s/%d" " (decr. in real time) inconsistent on %s:" " %" PRIu32 " from %s, %" PRIu32 " from us", __func__, inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf, sizeof(prefixbuf)), pinfo->nd_opt_pi_prefix_len, ifi->ifi_ifname, preferred_time, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), pfx->pfx_pltimeexpire); inconsistent++; } } else if (!pfx->pfx_timer && preferred_time != pfx->pfx_preflifetime) syslog(LOG_INFO, "<%s> preferred lifetime for %s/%d" " inconsistent on %s:" " %d from %s, %d from us", __func__, inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf, sizeof(prefixbuf)), pinfo->nd_opt_pi_prefix_len, ifi->ifi_ifname, preferred_time, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), pfx->pfx_preflifetime); valid_time = ntohl(pinfo->nd_opt_pi_valid_time); if (pfx->pfx_vltimeexpire) { clock_gettime(CLOCK_MONOTONIC_FAST, &now); valid_time += now.tv_sec; if (!pfx->pfx_timer && rai->rai_clockskew && udiff(valid_time, pfx->pfx_vltimeexpire) > rai->rai_clockskew) { syslog(LOG_INFO, "<%s> valid lifetime for %s/%d" " (decr. in real time) inconsistent on %s:" " %d from %s, %" PRIu32 " from us", __func__, inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf, sizeof(prefixbuf)), pinfo->nd_opt_pi_prefix_len, ifi->ifi_ifname, preferred_time, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), pfx->pfx_vltimeexpire); inconsistent++; } } else if (!pfx->pfx_timer && valid_time != pfx->pfx_validlifetime) { syslog(LOG_INFO, "<%s> valid lifetime for %s/%d" " inconsistent on %s:" " %d from %s, %d from us", __func__, inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, prefixbuf, sizeof(prefixbuf)), pinfo->nd_opt_pi_prefix_len, ifi->ifi_ifname, valid_time, inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf, sizeof(ntopbuf)), pfx->pfx_validlifetime); inconsistent++; } return (inconsistent); } struct prefix * find_prefix(struct rainfo *rai, struct in6_addr *prefix, int plen) { struct prefix *pfx; int bytelen, bitlen; char bitmask; TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) { if (plen != pfx->pfx_prefixlen) continue; bytelen = plen / 8; bitlen = plen % 8; bitmask = 0xff << (8 - bitlen); if (memcmp((void *)prefix, (void *)&pfx->pfx_prefix, bytelen)) continue; if (bitlen == 0 || ((prefix->s6_addr[bytelen] & bitmask) == (pfx->pfx_prefix.s6_addr[bytelen] & bitmask))) { return (pfx); } } return (NULL); } /* check if p0/plen0 matches p1/plen1; return 1 if matches, otherwise 0. */ int prefix_match(struct in6_addr *p0, int plen0, struct in6_addr *p1, int plen1) { int bytelen, bitlen; char bitmask; if (plen0 < plen1) return (0); bytelen = plen1 / 8; bitlen = plen1 % 8; bitmask = 0xff << (8 - bitlen); if (memcmp((void *)p0, (void *)p1, bytelen)) return (0); if (bitlen == 0 || ((p0->s6_addr[bytelen] & bitmask) == (p1->s6_addr[bytelen] & bitmask))) { return (1); } return (0); } static int nd6_options(struct nd_opt_hdr *hdr, int limit, union nd_opt *ndopts, uint32_t optflags) { int optlen = 0; for (; limit > 0; limit -= optlen) { if ((size_t)limit < sizeof(struct nd_opt_hdr)) { syslog(LOG_INFO, "<%s> short option header", __func__); goto bad; } hdr = (struct nd_opt_hdr *)((caddr_t)hdr + optlen); if (hdr->nd_opt_len == 0) { syslog(LOG_INFO, "<%s> bad ND option length(0) (type = %d)", __func__, hdr->nd_opt_type); goto bad; } optlen = hdr->nd_opt_len << 3; if (optlen > limit) { syslog(LOG_INFO, "<%s> short option", __func__); goto bad; } if (hdr->nd_opt_type > ND_OPT_MTU && hdr->nd_opt_type != ND_OPT_RDNSS && hdr->nd_opt_type != ND_OPT_DNSSL && hdr->nd_opt_type != ND_OPT_PREF64) { syslog(LOG_INFO, "<%s> unknown ND option(type %d)", __func__, hdr->nd_opt_type); continue; } if ((ndopt_flags[hdr->nd_opt_type] & optflags) == 0) { syslog(LOG_INFO, "<%s> unexpected ND option(type %d)", __func__, hdr->nd_opt_type); continue; } /* * Option length check. Do it here for all fixed-length * options. */ switch (hdr->nd_opt_type) { case ND_OPT_MTU: if (optlen == sizeof(struct nd_opt_mtu)) break; goto skip; case ND_OPT_RDNSS: if (optlen >= 24 && (optlen - sizeof(struct nd_opt_rdnss)) % 16 == 0) break; goto skip; case ND_OPT_DNSSL: if (optlen >= 16 && (optlen - sizeof(struct nd_opt_dnssl)) % 8 == 0) break; goto skip; case ND_OPT_PREFIX_INFORMATION: if (optlen == sizeof(struct nd_opt_prefix_info)) break; skip: syslog(LOG_INFO, "<%s> invalid option length", __func__); continue; } switch (hdr->nd_opt_type) { case ND_OPT_TARGET_LINKADDR: case ND_OPT_REDIRECTED_HEADER: case ND_OPT_RDNSS: case ND_OPT_DNSSL: case ND_OPT_PREF64: break; /* we don't care about these options */ case ND_OPT_SOURCE_LINKADDR: case ND_OPT_MTU: if (ndopts->opt_array[hdr->nd_opt_type]) { syslog(LOG_INFO, "<%s> duplicated ND option (type = %d)", __func__, hdr->nd_opt_type); } ndopts->opt_array[hdr->nd_opt_type] = hdr; break; case ND_OPT_PREFIX_INFORMATION: { struct nd_optlist *nol; if (ndopts->opt_pi == 0) { ndopts->opt_pi = (struct nd_opt_prefix_info *)hdr; continue; } nol = malloc(sizeof(*nol)); if (nol == NULL) { syslog(LOG_ERR, "<%s> can't allocate memory", __func__); goto bad; } nol->nol_opt = hdr; TAILQ_INSERT_TAIL(&(ndopts->opt_list), nol, nol_next); break; } default: /* impossible */ break; } } return (0); bad: free_ndopts(ndopts); return (-1); } static void free_ndopts(union nd_opt *ndopts) { struct nd_optlist *nol; while ((nol = TAILQ_FIRST(&ndopts->opt_list)) != NULL) { TAILQ_REMOVE(&ndopts->opt_list, nol, nol_next); free(nol); } } void sock_open(struct sockinfo *s) { struct icmp6_filter filt; int on; /* XXX: should be max MTU attached to the node */ static char answer[1500]; syslog(LOG_DEBUG, "<%s> enter", __func__); if (s == NULL) { syslog(LOG_ERR, "<%s> internal error", __func__); exit(1); } rcvcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)); rcvcmsgbuf = (char *)malloc(rcvcmsgbuflen); if (rcvcmsgbuf == NULL) { syslog(LOG_ERR, "<%s> not enough core", __func__); exit(1); } sndcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)); sndcmsgbuf = (char *)malloc(sndcmsgbuflen); if (sndcmsgbuf == NULL) { syslog(LOG_ERR, "<%s> not enough core", __func__); exit(1); } if ((s->si_fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { syslog(LOG_ERR, "<%s> socket: %s", __func__, strerror(errno)); exit(1); } /* specify to tell receiving interface */ on = 1; if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) < 0) { syslog(LOG_ERR, "<%s> IPV6_RECVPKTINFO: %s", __func__, strerror(errno)); exit(1); } on = 1; /* specify to tell value of hoplimit field of received IP6 hdr */ if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)) < 0) { syslog(LOG_ERR, "<%s> IPV6_RECVHOPLIMIT: %s", __func__, strerror(errno)); exit(1); } ICMP6_FILTER_SETBLOCKALL(&filt); ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filt); ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); if (mcastif != NULL) ICMP6_FILTER_SETPASS(ICMP6_ROUTER_RENUMBERING, &filt); if (setsockopt(s->si_fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, sizeof(filt)) < 0) { syslog(LOG_ERR, "<%s> IICMP6_FILTER: %s", __func__, strerror(errno)); exit(1); } /* initialize msghdr for receiving packets */ rcviov[0].iov_base = (caddr_t)answer; rcviov[0].iov_len = sizeof(answer); rcvmhdr.msg_name = (caddr_t)&rcvfrom; rcvmhdr.msg_namelen = sizeof(rcvfrom); rcvmhdr.msg_iov = rcviov; rcvmhdr.msg_iovlen = 1; rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf; rcvmhdr.msg_controllen = rcvcmsgbuflen; /* initialize msghdr for sending packets */ sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); sndmhdr.msg_iov = sndiov; sndmhdr.msg_iovlen = 1; sndmhdr.msg_control = (caddr_t)sndcmsgbuf; sndmhdr.msg_controllen = sndcmsgbuflen; } /* open a routing socket to watch the routing table */ static void rtsock_open(struct sockinfo *s) { if (s == NULL) { syslog(LOG_ERR, "<%s> internal error", __func__); exit(1); } if ((s->si_fd = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { syslog(LOG_ERR, "<%s> socket: %s", __func__, strerror(errno)); exit(1); } } struct ifinfo * if_indextoifinfo(int idx) { struct ifinfo *ifi; char *name, name0[IFNAMSIZ]; /* Check if the interface has a valid name or not. */ if (if_indextoname(idx, name0) == NULL) return (NULL); TAILQ_FOREACH(ifi, &ifilist, ifi_next) { if (ifi->ifi_ifindex == idx) return (ifi); } if (ifi != NULL) syslog(LOG_DEBUG, "<%s> ifi found (idx=%d)", __func__, idx); else syslog(LOG_DEBUG, "<%s> ifi not found (idx=%d)", __func__, idx); return (NULL); /* search failed */ } void ra_output(struct ifinfo *ifi) { int i; struct cmsghdr *cm; struct in6_pktinfo *pi; struct soliciter *sol; struct rainfo *rai; switch (ifi->ifi_state) { case IFI_STATE_CONFIGURED: rai = ifi->ifi_rainfo; break; case IFI_STATE_TRANSITIVE: rai = ifi->ifi_rainfo_trans; break; case IFI_STATE_UNCONFIGURED: syslog(LOG_DEBUG, "<%s> %s is unconfigured. " "Skip sending RAs.", __func__, ifi->ifi_ifname); return; default: rai = NULL; } if (rai == NULL) { syslog(LOG_DEBUG, "<%s> rainfo is NULL on %s." "Skip sending RAs.", __func__, ifi->ifi_ifname); return; } if (!(ifi->ifi_flags & IFF_UP)) { syslog(LOG_DEBUG, "<%s> %s is not up. " "Skip sending RAs.", __func__, ifi->ifi_ifname); return; } /* * Check lifetime, ACCEPT_RTADV flag, and ip6.forwarding. * * (lifetime == 0) = output * (lifetime != 0 && (check_accept_rtadv()) = no output * * Basically, hosts MUST NOT send Router Advertisement * messages at any time (RFC 4861, Section 6.2.3). However, it * would sometimes be useful to allow hosts to advertise some * parameters such as prefix information and link MTU. Thus, * we allow hosts to invoke rtadvd only when router lifetime * (on every advertising interface) is explicitly set * zero. (see also the above section) */ syslog(LOG_DEBUG, "<%s> check lifetime=%d, ACCEPT_RTADV=%d, ip6.forwarding=%d " "on %s", __func__, rai->rai_lifetime, check_accept_rtadv(ifi->ifi_ifindex), getinet6sysctl(IPV6CTL_FORWARDING), ifi->ifi_ifname); if (rai->rai_lifetime != 0) { if (getinet6sysctl(IPV6CTL_FORWARDING) == 0) { syslog(LOG_ERR, "non-zero lifetime RA " "but net.inet6.ip6.forwarding=0. " "Ignored."); return; } if (check_accept_rtadv(ifi->ifi_ifindex)) { syslog(LOG_ERR, "non-zero lifetime RA " "on RA receiving interface %s." " Ignored.", ifi->ifi_ifname); return; } } make_packet(rai); /* XXX: inefficient */ sndmhdr.msg_name = (caddr_t)&sin6_linklocal_allnodes; sndmhdr.msg_iov[0].iov_base = (caddr_t)rai->rai_ra_data; sndmhdr.msg_iov[0].iov_len = rai->rai_ra_datalen; cm = CMSG_FIRSTHDR(&sndmhdr); /* specify the outgoing interface */ cm->cmsg_level = IPPROTO_IPV6; cm->cmsg_type = IPV6_PKTINFO; cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); pi = (struct in6_pktinfo *)CMSG_DATA(cm); memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ pi->ipi6_ifindex = ifi->ifi_ifindex; /* specify the hop limit of the packet */ { int hoplimit = 255; cm = CMSG_NXTHDR(&sndmhdr, cm); cm->cmsg_level = IPPROTO_IPV6; cm->cmsg_type = IPV6_HOPLIMIT; cm->cmsg_len = CMSG_LEN(sizeof(int)); memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); } syslog(LOG_DEBUG, "<%s> send RA on %s, # of RS waitings = %d", __func__, ifi->ifi_ifname, ifi->ifi_rs_waitcount); i = sendmsg(sock.si_fd, &sndmhdr, 0); if (i < 0 || (size_t)i != rai->rai_ra_datalen) { if (i < 0) { syslog(LOG_ERR, "<%s> sendmsg on %s: %s", __func__, ifi->ifi_ifname, strerror(errno)); } } /* * unicast advertisements * XXX commented out. reason: though spec does not forbit it, unicast * advert does not really help */ while ((sol = TAILQ_FIRST(&rai->rai_soliciter)) != NULL) { TAILQ_REMOVE(&rai->rai_soliciter, sol, sol_next); free(sol); } /* update timestamp */ clock_gettime(CLOCK_MONOTONIC_FAST, &ifi->ifi_ra_lastsent); /* update counter */ ifi->ifi_rs_waitcount = 0; ifi->ifi_raoutput++; switch (ifi->ifi_state) { case IFI_STATE_CONFIGURED: if (ifi->ifi_burstcount > 0) ifi->ifi_burstcount--; break; case IFI_STATE_TRANSITIVE: ifi->ifi_burstcount--; if (ifi->ifi_burstcount == 0) { if (ifi->ifi_rainfo == ifi->ifi_rainfo_trans) { /* Initial burst finished. */ if (ifi->ifi_rainfo_trans != NULL) ifi->ifi_rainfo_trans = NULL; } /* Remove burst RA information */ if (ifi->ifi_rainfo_trans != NULL) { rm_rainfo(ifi->ifi_rainfo_trans); ifi->ifi_rainfo_trans = NULL; } if (ifi->ifi_rainfo != NULL) { /* * TRANSITIVE -> CONFIGURED * * After initial burst or transition from * one configuration to another, * ifi_rainfo always points to the next RA * information. */ ifi->ifi_state = IFI_STATE_CONFIGURED; syslog(LOG_DEBUG, "<%s> ifname=%s marked as " "CONFIGURED.", __func__, ifi->ifi_ifname); } else { /* * TRANSITIVE -> UNCONFIGURED * * If ifi_rainfo points to NULL, this * interface is shutting down. * */ int error; ifi->ifi_state = IFI_STATE_UNCONFIGURED; syslog(LOG_DEBUG, "<%s> ifname=%s marked as " "UNCONFIGURED.", __func__, ifi->ifi_ifname); error = sock_mc_leave(&sock, ifi->ifi_ifindex); if (error) exit(1); } } break; } } /* process RA timer */ struct rtadvd_timer * ra_timeout(void *arg) { struct ifinfo *ifi; ifi = (struct ifinfo *)arg; syslog(LOG_DEBUG, "<%s> RA timer on %s is expired", __func__, ifi->ifi_ifname); ra_output(ifi); return (ifi->ifi_ra_timer); } /* update RA timer */ void ra_timer_update(void *arg, struct timespec *tm) { uint16_t interval; struct rainfo *rai; struct ifinfo *ifi; ifi = (struct ifinfo *)arg; rai = ifi->ifi_rainfo; interval = 0; switch (ifi->ifi_state) { case IFI_STATE_UNCONFIGURED: return; break; case IFI_STATE_CONFIGURED: /* * Whenever a multicast advertisement is sent from * an interface, the timer is reset to a * uniformly-distributed random value between the * interface's configured MinRtrAdvInterval and * MaxRtrAdvInterval (RFC4861 6.2.4). */ interval = rai->rai_mininterval; interval += arc4random_uniform(rai->rai_maxinterval - rai->rai_mininterval); break; case IFI_STATE_TRANSITIVE: /* * For the first few advertisements (up to * MAX_INITIAL_RTR_ADVERTISEMENTS), if the randomly chosen * interval is greater than * MAX_INITIAL_RTR_ADVERT_INTERVAL, the timer SHOULD be * set to MAX_INITIAL_RTR_ADVERT_INTERVAL instead. (RFC * 4861 6.2.4) * * In such cases, the router SHOULD transmit one or more * (but not more than MAX_FINAL_RTR_ADVERTISEMENTS) final * multicast Router Advertisements on the interface with a * Router Lifetime field of zero. (RFC 4861 6.2.5) */ interval = ifi->ifi_burstinterval; break; } tm->tv_sec = interval; tm->tv_nsec = 0; syslog(LOG_DEBUG, "<%s> RA timer on %s is set to %ld:%ld", __func__, ifi->ifi_ifname, (long int)tm->tv_sec, (long int)tm->tv_nsec / 1000); } diff --git a/usr.sbin/rtadvd/rtadvd.h b/usr.sbin/rtadvd/rtadvd.h index 5ecfd1b56423..5b9037b2dcf3 100644 --- a/usr.sbin/rtadvd/rtadvd.h +++ b/usr.sbin/rtadvd/rtadvd.h @@ -1,312 +1,309 @@ /* $KAME: rtadvd.h,v 1.26 2003/08/05 12:34:23 itojun Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 1998 WIDE Project. * Copyright (C) 2011 Hiroki Sato * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #define ELM_MALLOC(p,error_action) \ do { \ p = malloc(sizeof(*p)); \ if (p == NULL) { \ syslog(LOG_ERR, "<%s> malloc failed: %s", \ __func__, strerror(errno)); \ error_action; \ } \ memset(p, 0, sizeof(*p)); \ } while(0) #define IN6ADDR_LINKLOCAL_ALLNODES_INIT \ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}} #define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}} #define IN6ADDR_SITELOCAL_ALLROUTERS_INIT \ {{{ 0xff, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}} extern struct sockaddr_in6 sin6_linklocal_allnodes; extern struct sockaddr_in6 sin6_linklocal_allrouters; extern struct sockaddr_in6 sin6_sitelocal_allrouters; /* * RFC 3542 API deprecates IPV6_PKTINFO in favor of * IPV6_RECVPKTINFO */ #ifndef IPV6_RECVPKTINFO #ifdef IPV6_PKTINFO #define IPV6_RECVPKTINFO IPV6_PKTINFO #endif #endif /* * RFC 3542 API deprecates IPV6_HOPLIMIT in favor of * IPV6_RECVHOPLIMIT */ #ifndef IPV6_RECVHOPLIMIT #ifdef IPV6_HOPLIMIT #define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT #endif #endif /* protocol constants and default values */ #define DEF_MAXRTRADVINTERVAL 600 #define DEF_ADVLINKMTU 0 #define DEF_ADVREACHABLETIME 0 #define DEF_ADVRETRANSTIMER 0 #define DEF_ADVCURHOPLIMIT 64 #define DEF_ADVVALIDLIFETIME 2592000 #define DEF_ADVPREFERREDLIFETIME 604800 #define MAXROUTERLIFETIME 9000 #define MIN_MAXINTERVAL 4 #define MAX_MAXINTERVAL 1800 #define MIN_MININTERVAL 3 #define MAXREACHABLETIME 3600000 #define MAX_INITIAL_RTR_ADVERT_INTERVAL 16 #define MAX_INITIAL_RTR_ADVERTISEMENTS 3 #define MAX_FINAL_RTR_ADVERTISEMENTS 3 #define MIN_DELAY_BETWEEN_RAS 3 #define MAX_RA_DELAY_TIME 500000 /* usec */ #define PREFIX_FROM_KERNEL 1 #define PREFIX_FROM_CONFIG 2 #define PREFIX_FROM_DYNAMIC 3 struct prefix { TAILQ_ENTRY(prefix) pfx_next; struct rainfo *pfx_rainfo; /* back pointer to the interface */ /* * Expiration timer. This is used when a prefix derived from * the kernel is deleted. */ struct rtadvd_timer *pfx_timer; uint32_t pfx_validlifetime; /* AdvValidLifetime */ uint32_t pfx_vltimeexpire; /* Expiration of vltime */ uint32_t pfx_preflifetime; /* AdvPreferredLifetime */ uint32_t pfx_pltimeexpire; /* Expiration of pltime */ int pfx_onlinkflg; /* bool: AdvOnLinkFlag */ int pfx_autoconfflg; /* bool: AdvAutonomousFlag */ int pfx_prefixlen; int pfx_origin; /* From kernel or config */ struct in6_addr pfx_prefix; }; struct rtinfo { TAILQ_ENTRY(rtinfo) rti_next; uint32_t rti_ltime; /* route lifetime */ int rti_rtpref; /* route preference */ int rti_prefixlen; struct in6_addr rti_prefix; }; struct rdnss_addr { TAILQ_ENTRY(rdnss_addr) ra_next; struct in6_addr ra_dns; /* DNS server entry */ }; struct rdnss { TAILQ_ENTRY(rdnss) rd_next; TAILQ_HEAD(, rdnss_addr) rd_list; /* list of DNS servers */ uint32_t rd_ltime; /* number of seconds valid */ }; struct pref64 { TAILQ_ENTRY(pref64) p64_next; uint16_t p64_plc; /* prefix length code */ uint16_t p64_sl; /* scaled lifetime */ struct in6_addr p64_prefix; }; /* * The maximum length of a domain name in a DNS search list is calculated * by a domain name + length fields per 63 octets + a zero octet at * the tail and adding 8 octet boundary padding. */ #define _DNAME_LABELENC_MAXLEN \ (NI_MAXHOST + (NI_MAXHOST / 64 + 1) + 1) #define DNAME_LABELENC_MAXLEN \ (_DNAME_LABELENC_MAXLEN + 8 - _DNAME_LABELENC_MAXLEN % 8) struct dnssl_addr { TAILQ_ENTRY(dnssl_addr) da_next; int da_len; /* length of entry */ char da_dom[DNAME_LABELENC_MAXLEN]; /* search domain name entry */ }; struct dnssl { TAILQ_ENTRY(dnssl) dn_next; TAILQ_HEAD(, dnssl_addr) dn_list; /* list of search domains */ uint32_t dn_ltime; /* number of seconds valid */ }; struct soliciter { TAILQ_ENTRY(soliciter) sol_next; struct sockaddr_in6 sol_addr; }; struct rainfo { /* pointer for list */ TAILQ_ENTRY(rainfo) rai_next; /* interface information */ struct ifinfo *rai_ifinfo; int rai_advlinkopt; /* bool: whether include link-layer addr opt */ int rai_advifprefix; /* bool: gather IF prefixes? */ /* Router configuration variables */ uint16_t rai_lifetime; /* AdvDefaultLifetime */ uint16_t rai_maxinterval; /* MaxRtrAdvInterval */ uint16_t rai_mininterval; /* MinRtrAdvInterval */ int rai_managedflg; /* AdvManagedFlag */ int rai_otherflg; /* AdvOtherConfigFlag */ -#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG - int rai_ipv6onlyflg; /* AdvIPv6OnlyFlag */ -#endif int rai_rtpref; /* router preference */ uint32_t rai_linkmtu; /* AdvLinkMTU */ uint32_t rai_reachabletime; /* AdvReachableTime */ uint32_t rai_retranstimer; /* AdvRetransTimer */ uint8_t rai_hoplimit; /* AdvCurHopLimit */ TAILQ_HEAD(, prefix) rai_prefix;/* AdvPrefixList(link head) */ int rai_pfxs; /* number of prefixes */ uint16_t rai_clockskew; /* used for consisitency check of lifetimes */ TAILQ_HEAD(, rdnss) rai_rdnss; /* DNS server list */ TAILQ_HEAD(, dnssl) rai_dnssl; /* search domain list */ TAILQ_HEAD(, rtinfo) rai_route; /* route information option (link head) */ int rai_routes; /* number of route information options */ /* actual RA packet data and its length */ size_t rai_ra_datalen; char *rai_ra_data; TAILQ_HEAD(, pref64) rai_pref64; /* PREF64 option */ /* info about soliciter */ TAILQ_HEAD(, soliciter) rai_soliciter; /* recent solication source */ }; /* RA information list */ extern TAILQ_HEAD(railist_head_t, rainfo) railist; /* * ifi_state: * * (INIT) * | * | update_ifinfo() * | update_persist_ifinfo() * v * UNCONFIGURED * | ^ * loadconfig()| |rm_ifinfo(), ra_output() * (MC join)| |(MC leave) * | | * | | * v | * TRANSITIVE * | ^ * ra_output()| |getconfig() * | | * | | * | | * v | * CONFIGURED * * */ #define IFI_STATE_UNCONFIGURED 0 #define IFI_STATE_CONFIGURED 1 #define IFI_STATE_TRANSITIVE 2 struct ifinfo { TAILQ_ENTRY(ifinfo) ifi_next; uint16_t ifi_state; uint16_t ifi_persist; uint16_t ifi_ifindex; char ifi_ifname[IFNAMSIZ]; uint8_t ifi_type; uint16_t ifi_flags; uint32_t ifi_nd_flags; uint32_t ifi_phymtu; struct sockaddr_dl ifi_sdl; struct rainfo *ifi_rainfo; struct rainfo *ifi_rainfo_trans; uint16_t ifi_burstcount; uint32_t ifi_burstinterval; struct rtadvd_timer *ifi_ra_timer; /* timestamp when the latest RA was sent */ struct timespec ifi_ra_lastsent; uint16_t ifi_rs_waitcount; /* statistics */ uint64_t ifi_raoutput; /* # of RAs sent */ uint64_t ifi_rainput; /* # of RAs received */ uint64_t ifi_rainconsistent; /* # of inconsistent recv'd RAs */ uint64_t ifi_rsinput; /* # of RSs received */ }; /* Interface list */ extern TAILQ_HEAD(ifilist_head_t, ifinfo) ifilist; extern char *mcastif; struct rtadvd_timer *ra_timeout(void *); void ra_timer_update(void *, struct timespec *); void ra_output(struct ifinfo *); int prefix_match(struct in6_addr *, int, struct in6_addr *, int); struct ifinfo *if_indextoifinfo(int); struct prefix *find_prefix(struct rainfo *, struct in6_addr *, int); void rtadvd_set_reload(int); void rtadvd_set_shutdown(int);