/* * dhcpcd - DHCP client daemon * Copyright (c) 2006-2015 Roy Marples * 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 AUTHOR 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 AUTHOR 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 #ifdef __FreeBSD__ /* Needed so that including netinet6/in6_var.h works */ # include #endif #include #include #include #include #include #include #include #ifdef __DragonFly__ # include #elif __APPLE__ /* FIXME: Add apple includes so we can work out SSID */ #else # include # include #endif #include #include #include #include #include #include #include #include #include #if defined(OpenBSD) && OpenBSD >= 201411 /* OpenBSD dropped the global setting from sysctl but left the #define * which causes a EPERM error when trying to use it. * I think both the error and keeping the define are wrong, so we #undef it. */ #undef IPV6CTL_ACCEPT_RTADV #endif #include "config.h" #include "common.h" #include "dhcp.h" #include "if.h" #include "if-options.h" #include "ipv4.h" #include "ipv6.h" #include "ipv6nd.h" #include "bpf-filter.h" #ifndef RT_ROUNDUP #define RT_ROUNDUP(a) \ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) #define RT_ADVANCE(x, n) (x += RT_ROUNDUP((n)->sa_len)) #endif #define COPYOUT(sin, sa) do { \ if ((sa) && ((sa)->sa_family == AF_INET || (sa)->sa_family == 255)) \ (sin) = ((struct sockaddr_in*)(void *)(sa))->sin_addr; \ } while (0) #define COPYOUT6(sin, sa) do { \ if ((sa) && ((sa)->sa_family == AF_INET6 || (sa)->sa_family == 255)) \ (sin) = ((struct sockaddr_in6*)(void *)(sa))->sin6_addr; \ } while (0) #ifndef CLLADDR # define CLLADDR(s) ((const char *)((s)->sdl_data + (s)->sdl_nlen)) #endif int if_init(__unused struct interface *iface) { /* BSD promotes secondary address by default */ return 0; } int if_conf(__unused struct interface *iface) { /* No extra checks needed on BSD */ return 0; } int if_openlinksocket(void) { #ifdef SOCK_CLOEXEC return socket(PF_ROUTE, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); #else int s, flags; if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) == -1) return -1; if ((flags = fcntl(s, F_GETFD, 0)) == -1 || fcntl(s, F_SETFD, flags | FD_CLOEXEC) == -1) { close(s); return -1; } if ((flags = fcntl(s, F_GETFL, 0)) == -1 || fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) { close(s); return -1; } return s; #endif } #if defined(INET) || defined(INET6) static void if_linkaddr(struct sockaddr_dl *sdl, const struct interface *ifp) { memset(sdl, 0, sizeof(*sdl)); sdl->sdl_family = AF_LINK; sdl->sdl_len = sizeof(*sdl); sdl->sdl_nlen = sdl->sdl_alen = sdl->sdl_slen = 0; sdl->sdl_index = (unsigned short)ifp->index; } #endif static int if_getssid1(const char *ifname, uint8_t *ssid) { int s, retval = -1; #if defined(SIOCG80211NWID) struct ifreq ifr; struct ieee80211_nwid nwid; #elif defined(IEEE80211_IOC_SSID) struct ieee80211req ireq; char nwid[IEEE80211_NWID_LEN + 1]; #endif if ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1) return -1; #if defined(SIOCG80211NWID) /* NetBSD */ memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); memset(&nwid, 0, sizeof(nwid)); ifr.ifr_data = (void *)&nwid; if (ioctl(s, SIOCG80211NWID, &ifr) == 0) { if (ssid == NULL) retval = nwid.i_len; else if (nwid.i_len > IF_SSIDSIZE) { errno = ENOBUFS; retval = -1; } else { retval = nwid.i_len; memcpy(ssid, nwid.i_nwid, nwid.i_len); ssid[nwid.i_len] = '\0'; } } #elif defined(IEEE80211_IOC_SSID) /* FreeBSD */ memset(&ireq, 0, sizeof(ireq)); strlcpy(ireq.i_name, ifname, sizeof(ireq.i_name)); ireq.i_type = IEEE80211_IOC_SSID; ireq.i_val = -1; memset(nwid, 0, sizeof(nwid)); ireq.i_data = &nwid; if (ioctl(s, SIOCG80211, &ireq) == 0) { if (ssid == NULL) retval = ireq.i_len; else if (ireq.i_len > IF_SSIDSIZE) { errno = ENOBUFS; retval = -1; } else { retval = ireq.i_len; memcpy(ssid, nwid, ireq.i_len); ssid[ireq.i_len] = '\0'; } } #endif close(s); return retval; } int if_getssid(struct interface *ifp) { int r; r = if_getssid1(ifp->name, ifp->ssid); if (r != -1) ifp->ssid_len = (unsigned int)r; return r; } /* * FreeBSD allows for Virtual Access Points * We need to check if the interface is a Virtual Interface Master * and if so, don't use it. * This check is made by virtue of being a IEEE80211 device but * returning the SSID gives an error. */ int if_vimaster(const char *ifname) { int s, r; struct ifmediareq ifmr; if ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1) return -1; memset(&ifmr, 0, sizeof(ifmr)); strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); r = ioctl(s, SIOCGIFMEDIA, &ifmr); close(s); if (r == -1) return -1; if (ifmr.ifm_status & IFM_AVALID && IFM_TYPE(ifmr.ifm_active) == IFM_IEEE80211) { if (if_getssid1(ifname, NULL) == -1) return 1; } return 0; } static void get_addrs(int type, char *cp, struct sockaddr **sa) { int i; for (i = 0; i < RTAX_MAX; i++) { if (type & (1 << i)) { sa[i] = (struct sockaddr *)cp; RT_ADVANCE(cp, sa[i]); } else sa[i] = NULL; } } #if defined(INET) || defined(INET6) static struct interface * if_findsdl(struct dhcpcd_ctx *ctx, struct sockaddr_dl *sdl) { if (sdl->sdl_nlen) { char ifname[IF_NAMESIZE]; memcpy(ifname, sdl->sdl_data, sdl->sdl_nlen); ifname[sdl->sdl_nlen] = '\0'; return if_find(ctx->ifaces, ifname); } return NULL; } #endif #ifdef INET const char *if_pfname = "Berkley Packet Filter"; int if_openrawsocket(struct interface *ifp, uint16_t protocol) { struct dhcp_state *state; int fd = -1; struct ifreq ifr; int ibuf_len = 0; size_t buf_len; struct bpf_version pv; struct bpf_program pf; #ifdef BIOCIMMEDIATE int flags; #endif #ifdef _PATH_BPF fd = open(_PATH_BPF, O_RDWR | O_CLOEXEC | O_NONBLOCK); #else char device[32]; int n = 0; do { snprintf(device, sizeof(device), "/dev/bpf%d", n++); fd = open(device, O_RDWR | O_CLOEXEC | O_NONBLOCK); } while (fd == -1 && errno == EBUSY); #endif if (fd == -1) return -1; state = D_STATE(ifp); memset(&pv, 0, sizeof(pv)); if (ioctl(fd, BIOCVERSION, &pv) == -1) goto eexit; if (pv.bv_major != BPF_MAJOR_VERSION || pv.bv_minor < BPF_MINOR_VERSION) { logger(ifp->ctx, LOG_ERR, "BPF version mismatch - recompile"); goto eexit; } memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name)); if (ioctl(fd, BIOCSETIF, &ifr) == -1) goto eexit; /* Get the required BPF buffer length from the kernel. */ if (ioctl(fd, BIOCGBLEN, &ibuf_len) == -1) goto eexit; buf_len = (size_t)ibuf_len; if (state->buffer_size != buf_len) { free(state->buffer); state->buffer = malloc(buf_len); if (state->buffer == NULL) goto eexit; state->buffer_size = buf_len; state->buffer_len = state->buffer_pos = 0; } #ifdef BIOCIMMEDIATE flags = 1; if (ioctl(fd, BIOCIMMEDIATE, &flags) == -1) goto eexit; #endif /* Install the DHCP filter */ memset(&pf, 0, sizeof(pf)); if (protocol == ETHERTYPE_ARP) { pf.bf_insns = UNCONST(arp_bpf_filter); pf.bf_len = arp_bpf_filter_len; } else { pf.bf_insns = UNCONST(dhcp_bpf_filter); pf.bf_len = dhcp_bpf_filter_len; } if (ioctl(fd, BIOCSETF, &pf) == -1) goto eexit; return fd; eexit: free(state->buffer); state->buffer = NULL; close(fd); return -1; } ssize_t if_sendrawpacket(const struct interface *ifp, uint16_t protocol, const void *data, size_t len, const uint8_t *dest_hw_addr) { struct iovec iov[2]; struct ether_header hw; int fd; const struct dhcp_state *state; memset(&hw, 0, ETHER_HDR_LEN); if (dest_hw_addr) memcpy(&hw.ether_dhost, dest_hw_addr, ETHER_ADDR_LEN); else memset(&hw.ether_dhost, 0xff, ETHER_ADDR_LEN); hw.ether_type = htons(protocol); iov[0].iov_base = &hw; iov[0].iov_len = ETHER_HDR_LEN; iov[1].iov_base = UNCONST(data); iov[1].iov_len = len; state = D_CSTATE(ifp); if (protocol == ETHERTYPE_ARP) fd = state->arp_fd; else fd = state->raw_fd; return writev(fd, iov, 2); } /* BPF requires that we read the entire buffer. * So we pass the buffer in the API so we can loop on >1 packet. */ ssize_t if_readrawpacket(struct interface *ifp, uint16_t protocol, void *data, size_t len, int *flags) { int fd; struct bpf_hdr packet; ssize_t bytes; const unsigned char *payload; struct dhcp_state *state; state = D_STATE(ifp); if (protocol == ETHERTYPE_ARP) fd = state->arp_fd; else fd = state->raw_fd; *flags = 0; for (;;) { if (state->buffer_len == 0) { bytes = read(fd, state->buffer, state->buffer_size); if (bytes == -1 || bytes == 0) return bytes; state->buffer_len = (size_t)bytes; state->buffer_pos = 0; } bytes = -1; memcpy(&packet, state->buffer + state->buffer_pos, sizeof(packet)); if (packet.bh_caplen != packet.bh_datalen) goto next; /* Incomplete packet, drop. */ if (state->buffer_pos + packet.bh_caplen + packet.bh_hdrlen > state->buffer_len) goto next; /* Packet beyond buffer, drop. */ payload = state->buffer + state->buffer_pos + packet.bh_hdrlen + ETHER_HDR_LEN; bytes = (ssize_t)packet.bh_caplen - ETHER_HDR_LEN; if ((size_t)bytes > len) bytes = (ssize_t)len; memcpy(data, payload, (size_t)bytes); next: state->buffer_pos += BPF_WORDALIGN(packet.bh_hdrlen + packet.bh_caplen); if (state->buffer_pos >= state->buffer_len) { state->buffer_len = state->buffer_pos = 0; *flags |= RAW_EOF; } if (bytes != -1) return bytes; } } int if_address(const struct interface *ifp, const struct in_addr *address, const struct in_addr *netmask, const struct in_addr *broadcast, int action) { int s, r; struct in_aliasreq ifra; if ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1) return -1; memset(&ifra, 0, sizeof(ifra)); strlcpy(ifra.ifra_name, ifp->name, sizeof(ifra.ifra_name)); #define ADDADDR(var, addr) do { \ (var)->sin_family = AF_INET; \ (var)->sin_len = sizeof(*(var)); \ (var)->sin_addr = *(addr); \ } while (/*CONSTCOND*/0) ADDADDR(&ifra.ifra_addr, address); ADDADDR(&ifra.ifra_mask, netmask); if (action >= 0 && broadcast) ADDADDR(&ifra.ifra_broadaddr, broadcast); #undef ADDADDR r = ioctl(s, action < 0 ? SIOCDIFADDR : SIOCAIFADDR, &ifra); close(s); return r; } static int if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, struct rt_msghdr *rtm) { char *cp; struct sockaddr *sa, *rti_info[RTAX_MAX]; cp = (char *)(void *)(rtm + 1); sa = (struct sockaddr *)(void *)cp; if (sa->sa_family != AF_INET) return -1; if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY)) return -1; #ifdef RTF_CLONED if (rtm->rtm_flags & RTF_CLONED) return -1; #endif #ifdef RTF_LOCAL if (rtm->rtm_flags & RTF_LOCAL) return -1; #endif #ifdef RTF_BROADCAST if (rtm->rtm_flags & RTF_BROADCAST) return -1; #endif get_addrs(rtm->rtm_addrs, cp, rti_info); memset(rt, 0, sizeof(*rt)); COPYOUT(rt->dest, rti_info[RTAX_DST]); if (rtm->rtm_addrs & RTA_NETMASK) COPYOUT(rt->net, rti_info[RTAX_NETMASK]); else rt->net.s_addr = INADDR_BROADCAST; COPYOUT(rt->gate, rti_info[RTAX_GATEWAY]); if (rtm->rtm_index) rt->iface = if_findindex(ctx->ifaces, rtm->rtm_index); else if (rtm->rtm_addrs & RTA_IFP) { struct sockaddr_dl *sdl; sdl = (struct sockaddr_dl *)(void *)rti_info[RTAX_IFP]; rt->iface = if_findsdl(ctx, sdl); } /* If we don't have an interface and it's a host route, it maybe * to a local ip via the loopback interface. */ if (rt->iface == NULL && !(~rtm->rtm_flags & (RTF_HOST | RTF_GATEWAY))) { struct ipv4_addr *ia; if ((ia = ipv4_findaddr(ctx, &rt->dest))) rt->iface = ia->iface; } return 0; } int if_route(unsigned char cmd, const struct rt *rt) { const struct dhcp_state *state; union sockunion { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_dl sdl; } su; struct rtm { struct rt_msghdr hdr; char buffer[sizeof(su) * RTAX_MAX]; } rtm; char *bp = rtm.buffer; size_t l; int s, retval; if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) == -1) return -1; #define ADDSU { \ l = RT_ROUNDUP(su.sa.sa_len); \ memcpy(bp, &su, l); \ bp += l; \ } #define ADDADDR(addr) { \ memset(&su, 0, sizeof(su)); \ su.sin.sin_family = AF_INET; \ su.sin.sin_len = sizeof(su.sin); \ (&su.sin)->sin_addr = *addr; \ ADDSU; \ } if (cmd != RTM_DELETE) state = D_CSTATE(rt->iface); else /* appease GCC */ state = NULL; memset(&rtm, 0, sizeof(rtm)); rtm.hdr.rtm_version = RTM_VERSION; rtm.hdr.rtm_seq = 1; rtm.hdr.rtm_type = cmd; rtm.hdr.rtm_addrs = RTA_DST; if (cmd == RTM_ADD || cmd == RTM_CHANGE) rtm.hdr.rtm_addrs |= RTA_GATEWAY; rtm.hdr.rtm_flags = RTF_UP; #ifdef RTF_PINNED if (cmd != RTM_ADD) rtm.hdr.rtm_flags |= RTF_PINNED; #endif if (cmd != RTM_DELETE) { rtm.hdr.rtm_addrs |= RTA_IFA | RTA_IFP; /* None interface subnet routes are static. */ if (rt->gate.s_addr != INADDR_ANY || rt->net.s_addr != state->net.s_addr || rt->dest.s_addr != (state->addr.s_addr & state->net.s_addr)) rtm.hdr.rtm_flags |= RTF_STATIC; else { #ifdef RTF_CLONING rtm.hdr.rtm_flags |= RTF_CLONING; #endif #ifdef RTP_CONNECTED rtm.hdr.rtm_priority = RTP_CONNECTED; #endif } } if (rt->net.s_addr == htonl(INADDR_BROADCAST) && rt->gate.s_addr == htonl(INADDR_ANY)) { #ifdef RTF_CLONING /* We add a cloning network route for a single host. * Traffic to the host will generate a cloned route and the * hardware address will resolve correctly. * It might be more correct to use RTF_HOST instead of * RTF_CLONING, and that does work, but some OS generate * an arp warning diagnostic which we don't want to do. */ rtm.hdr.rtm_flags |= RTF_CLONING; rtm.hdr.rtm_addrs |= RTA_NETMASK; #else rtm.hdr.rtm_flags |= RTF_HOST; #endif } else if (rt->gate.s_addr == htonl(INADDR_LOOPBACK) && rt->net.s_addr == htonl(INADDR_BROADCAST)) { rtm.hdr.rtm_flags |= RTF_HOST | RTF_GATEWAY; /* Going via lo0 so remove the interface flags */ if (cmd == RTM_ADD) rtm.hdr.rtm_addrs &= ~(RTA_IFA | RTA_IFP); } else { rtm.hdr.rtm_addrs |= RTA_NETMASK; if (rtm.hdr.rtm_flags & RTF_STATIC) rtm.hdr.rtm_flags |= RTF_GATEWAY; } if ((cmd == RTM_ADD || cmd == RTM_CHANGE) && !(rtm.hdr.rtm_flags & RTF_GATEWAY)) rtm.hdr.rtm_addrs |= RTA_IFA | RTA_IFP; ADDADDR(&rt->dest); if (rtm.hdr.rtm_addrs & RTA_GATEWAY) { #ifdef RTF_CLONING if ((rtm.hdr.rtm_flags & (RTF_HOST | RTF_CLONING) && #else if ((rtm.hdr.rtm_flags & RTF_HOST && #endif rt->gate.s_addr != htonl(INADDR_LOOPBACK)) || !(rtm.hdr.rtm_flags & RTF_STATIC)) { if_linkaddr(&su.sdl, rt->iface); ADDSU; } else ADDADDR(&rt->gate); } if (rtm.hdr.rtm_addrs & RTA_NETMASK) ADDADDR(&rt->net); if ((cmd == RTM_ADD || cmd == RTM_CHANGE) && (rtm.hdr.rtm_addrs & (RTA_IFP | RTA_IFA))) { rtm.hdr.rtm_index = (unsigned short)rt->iface->index; if (rtm.hdr.rtm_addrs & RTA_IFP) { if_linkaddr(&su.sdl, rt->iface); ADDSU; } if (rtm.hdr.rtm_addrs & RTA_IFA) ADDADDR(&state->addr); } #undef ADDADDR #undef ADDSU rtm.hdr.rtm_msglen = (unsigned short)(bp - (char *)&rtm); retval = write(s, &rtm, rtm.hdr.rtm_msglen) == -1 ? -1 : 0; close(s); return retval; } int if_initrt(struct interface *ifp) { struct rt_msghdr *rtm; int mib[6]; size_t needed; char *buf, *p, *end; struct rt rt; ipv4_freerts(ifp->ctx->ipv4_kroutes); mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET; mib[4] = NET_RT_DUMP; mib[5] = 0; if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1) return -1; if (needed == 0) return 0; if ((buf = malloc(needed)) == NULL) return -1; if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) return -1; end = buf + needed; for (p = buf; p < end; p += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)(void *)p; if (if_copyrt(ifp->ctx, &rt, rtm) == 0) ipv4_handlert(ifp->ctx, RTM_ADD, &rt); } free(buf); return 0; } #ifdef SIOCGIFAFLAG_IN int if_addrflags(const struct in_addr *addr, const struct interface *ifp) { int s, flags; struct ifreq ifr; struct sockaddr_in *sin; s = socket(PF_INET, SOCK_DGRAM, 0); flags = -1; if (s != -1) { memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name)); sin = (struct sockaddr_in *)(void *)&ifr.ifr_addr; sin->sin_family = AF_INET; sin->sin_addr = *addr; if (ioctl(s, SIOCGIFAFLAG_IN, &ifr) != -1) flags = ifr.ifr_addrflags; close(s); } return flags; } #else int if_addrflags(__unused const struct in_addr *addr, __unused const struct interface *ifp) { errno = ENOTSUP; return 0; } #endif #endif /* INET */ #ifdef INET6 static void ifa_scope(struct sockaddr_in6 *sin, unsigned int ifindex) { #ifdef __KAME__ /* KAME based systems want to store the scope inside the sin6_addr * for link local addreses */ if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) { uint16_t scope = htons((uint16_t)ifindex); memcpy(&sin->sin6_addr.s6_addr[2], &scope, sizeof(scope)); } sin->sin6_scope_id = 0; #else if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) sin->sin6_scope_id = ifindex; else sin->sin6_scope_id = 0; #endif } #ifdef __KAME__ #define DESCOPE(ia6) do { \ if (IN6_IS_ADDR_LINKLOCAL((ia6))) \ (ia6)->s6_addr[2] = (ia6)->s6_addr[3] = '\0'; \ } while (/*CONSTCOND */0) #else #define DESCOPE(ia6) #endif int if_address6(const struct ipv6_addr *a, int action) { int s, r; struct in6_aliasreq ifa; struct in6_addr mask; if ((s = socket(PF_INET6, SOCK_DGRAM, 0)) == -1) return -1; memset(&ifa, 0, sizeof(ifa)); strlcpy(ifa.ifra_name, a->iface->name, sizeof(ifa.ifra_name)); /* * We should not set IN6_IFF_TENTATIVE as the kernel should be * able to work out if it's a new address or not. * * We should set IN6_IFF_AUTOCONF, but the kernel won't let us. * This is probably a safety measure, but still it's not entirely right * either. */ #if 0 if (a->autoconf) ifa.ifra_flags |= IN6_IFF_AUTOCONF; #endif #ifdef IPV6_MANGETEMPADDR if (a->flags & IPV6_AF_TEMPORARY) ifa.ifra_flags |= IN6_IFF_TEMPORARY; #endif #define ADDADDR(v, addr) { \ (v)->sin6_family = AF_INET6; \ (v)->sin6_len = sizeof(*v); \ (v)->sin6_addr = *addr; \ } ADDADDR(&ifa.ifra_addr, &a->addr); ifa_scope(&ifa.ifra_addr, a->iface->index); ipv6_mask(&mask, a->prefix_len); ADDADDR(&ifa.ifra_prefixmask, &mask); ifa.ifra_lifetime.ia6t_vltime = a->prefix_vltime; ifa.ifra_lifetime.ia6t_pltime = a->prefix_pltime; #undef ADDADDR r = ioctl(s, action < 0 ? SIOCDIFADDR_IN6 : SIOCAIFADDR_IN6, &ifa); close(s); return r; } static int if_copyrt6(struct dhcpcd_ctx *ctx, struct rt6 *rt, struct rt_msghdr *rtm) { char *cp; struct sockaddr *sa, *rti_info[RTAX_MAX]; cp = (char *)(void *)(rtm + 1); sa = (struct sockaddr *)(void *)cp; if (sa->sa_family != AF_INET6) return -1; if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY)) return -1; #ifdef RTF_CLONED if (rtm->rtm_flags & (RTF_CLONED | RTF_HOST)) return -1; #else if (rtm->rtm_flags & RTF_HOST) return -1; #endif #ifdef RTF_LOCAL if (rtm->rtm_flags & RTF_LOCAL) return -1; #endif get_addrs(rtm->rtm_addrs, cp, rti_info); memset(rt, 0, sizeof(*rt)); rt->flags = (unsigned int)rtm->rtm_flags; COPYOUT6(rt->dest, rti_info[RTAX_DST]); if (rtm->rtm_addrs & RTA_NETMASK) { /* * We need to zero out the struct beyond sin6_len and * ensure it's valid. * I have no idea what the invalid data is for, could be * a kernel bug or actually used for something. * Either way it needs to be zeroed out. */ struct sockaddr_in6 *sin6; size_t e, i, len = 0, final = 0; sin6 = (struct sockaddr_in6 *)(void *)rti_info[RTAX_NETMASK]; rt->net = sin6->sin6_addr; e = sin6->sin6_len - offsetof(struct sockaddr_in6, sin6_addr); if (e > sizeof(struct in6_addr)) e = sizeof(struct in6_addr); for (i = 0; i < e; i++) { switch (rt->net.s6_addr[i] & 0xff) { case 0xff: /* We don't really want the length, * just that it's valid */ len++; break; case 0xfe: case 0xfc: case 0xf8: case 0xf0: case 0xe0: case 0xc0: case 0x80: len++; final = 1; break; default: rt->net.s6_addr[i] = 0x00; final = 1; break; } if (final) break; } if (len == 0) i = 0; while (i < sizeof(rt->net.s6_addr)) rt->net.s6_addr[i++] = 0x00; } else ipv6_mask(&rt->net, 128); COPYOUT6(rt->gate, rti_info[RTAX_GATEWAY]); if (rtm->rtm_index) rt->iface = if_findindex(ctx->ifaces, rtm->rtm_index); else if (rtm->rtm_addrs & RTA_IFP) { struct sockaddr_dl *sdl; sdl = (struct sockaddr_dl *)(void *)rti_info[RTAX_IFP]; rt->iface = if_findsdl(ctx, sdl); } /* If we don't have an interface and it's a host route, it maybe * to a local ip via the loopback interface. */ if (rt->iface == NULL && !(~rtm->rtm_flags & (RTF_HOST | RTF_GATEWAY))) { struct ipv6_addr *ia; if ((ia = ipv6_findaddr(ctx, &rt->dest, 0))) rt->iface = ia->iface; } return 0; } int if_route6(unsigned char cmd, const struct rt6 *rt) { union sockunion { struct sockaddr sa; struct sockaddr_in6 sin; struct sockaddr_dl sdl; } su; struct rtm { struct rt_msghdr hdr; char buffer[sizeof(su) * RTAX_MAX]; } rtm; char *bp = rtm.buffer; size_t l; int s, retval; if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) == -1) return -1; #define ADDSU { \ l = RT_ROUNDUP(su.sa.sa_len); \ memcpy(bp, &su, l); \ bp += l; \ } #define ADDADDRS(addr, scope) { \ memset(&su, 0, sizeof(su)); \ su.sin.sin6_family = AF_INET6; \ su.sin.sin6_len = sizeof(su.sin); \ (&su.sin)->sin6_addr = *addr; \ if (scope) \ ifa_scope(&su.sin, scope); \ ADDSU; \ } #define ADDADDR(addr) ADDADDRS(addr, 0) memset(&rtm, 0, sizeof(rtm)); rtm.hdr.rtm_version = RTM_VERSION; rtm.hdr.rtm_seq = 1; rtm.hdr.rtm_type = cmd; rtm.hdr.rtm_flags = RTF_UP | (int)rt->flags; #ifdef RTF_PINNED if (rtm.hdr.rtm_type != RTM_ADD) rtm.hdr.rtm_flags |= RTF_PINNED; #endif rtm.hdr.rtm_addrs = RTA_DST | RTA_NETMASK; /* None interface subnet routes are static. */ if (IN6_IS_ADDR_UNSPECIFIED(&rt->gate)) { #ifdef RTF_CLONING rtm.hdr.rtm_flags |= RTF_CLONING; #endif #ifdef RTP_CONNECTED rtm.hdr.rtm_priority = RTP_CONNECTED; #endif } else rtm.hdr.rtm_flags |= RTF_GATEWAY | RTF_STATIC; if (cmd == RTM_ADD) rtm.hdr.rtm_addrs |= RTA_GATEWAY; if (cmd == RTM_ADD && !(rtm.hdr.rtm_flags & RTF_REJECT)) rtm.hdr.rtm_addrs |= RTA_IFP | RTA_IFA; ADDADDR(&rt->dest); if (rtm.hdr.rtm_addrs & RTA_GATEWAY) { if (IN6_IS_ADDR_UNSPECIFIED(&rt->gate)) { if_linkaddr(&su.sdl, rt->iface); ADDSU; } else { ADDADDRS(&rt->gate, rt->iface->index); } } if (rtm.hdr.rtm_addrs & RTA_NETMASK) ADDADDR(&rt->net); if ((cmd == RTM_ADD || cmd == RTM_CHANGE) && (rtm.hdr.rtm_addrs & (RTA_IFP | RTA_IFA))) { rtm.hdr.rtm_index = (unsigned short)rt->iface->index; if (rtm.hdr.rtm_addrs & RTA_IFP) { if_linkaddr(&su.sdl, rt->iface); ADDSU; } if (rtm.hdr.rtm_addrs & RTA_IFA) { const struct ipv6_addr *lla; lla = ipv6_linklocal(rt->iface); if (lla == NULL) /* unlikely */ return -1; ADDADDRS(&lla->addr, rt->iface->index); } if (rt->mtu) { rtm.hdr.rtm_inits |= RTV_MTU; rtm.hdr.rtm_rmx.rmx_mtu = rt->mtu; } } #undef ADDADDR #undef ADDSU rtm.hdr.rtm_msglen = (unsigned short)(bp - (char *)&rtm); retval = write(s, &rtm, rtm.hdr.rtm_msglen) == -1 ? -1 : 0; close(s); return retval; } int if_initrt6(struct interface *ifp) { struct rt_msghdr *rtm; int mib[6]; size_t needed; char *buf, *p, *end; struct rt6 rt; ipv6_freerts(&ifp->ctx->ipv6->kroutes); mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET6; mib[4] = NET_RT_DUMP; mib[5] = 0; if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1) return -1; if (needed == 0) return 0; if ((buf = malloc(needed)) == NULL) return -1; if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) return -1; end = buf + needed; for (p = buf; p < end; p += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)(void *)p; if (if_copyrt6(ifp->ctx, &rt, rtm) == 0) ipv6_handlert(ifp->ctx, RTM_ADD, &rt); } free(buf); return 0; } int if_addrflags6(const struct in6_addr *addr, const struct interface *ifp) { int s, flags; struct in6_ifreq ifr6; s = socket(PF_INET6, SOCK_DGRAM, 0); flags = -1; if (s != -1) { memset(&ifr6, 0, sizeof(ifr6)); strlcpy(ifr6.ifr_name, ifp->name, sizeof(ifr6.ifr_name)); ifr6.ifr_addr.sin6_family = AF_INET6; ifr6.ifr_addr.sin6_addr = *addr; ifa_scope(&ifr6.ifr_addr, ifp->index); if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) != -1) flags = ifr6.ifr_ifru.ifru_flags6; close(s); } return flags; } int if_getlifetime6(struct ipv6_addr *ia) { int s, r; struct in6_ifreq ifr6; s = socket(PF_INET6, SOCK_DGRAM, 0); r = -1; if (s != -1) { memset(&ifr6, 0, sizeof(ifr6)); strlcpy(ifr6.ifr_name, ia->iface->name, sizeof(ifr6.ifr_name)); ifr6.ifr_addr.sin6_family = AF_INET6; ifr6.ifr_addr.sin6_addr = ia->addr; ifa_scope(&ifr6.ifr_addr, ia->iface->index); if (ioctl(s, SIOCGIFALIFETIME_IN6, &ifr6) != -1) { time_t t; struct in6_addrlifetime *lifetime; t = time(NULL); lifetime = &ifr6.ifr_ifru.ifru_lifetime; if (lifetime->ia6t_preferred) ia->prefix_pltime = (uint32_t)(lifetime->ia6t_preferred - MIN(t, lifetime->ia6t_preferred)); else ia->prefix_pltime = ND6_INFINITE_LIFETIME; if (lifetime->ia6t_expire) { ia->prefix_vltime = (uint32_t)(lifetime->ia6t_expire - MIN(t, lifetime->ia6t_expire)); /* Calculate the created time */ get_monotonic(&ia->created); ia->created.tv_sec -= lifetime->ia6t_vltime - ia->prefix_vltime; } else ia->prefix_vltime = ND6_INFINITE_LIFETIME; r = 0; } close(s); } return r; } #endif int if_managelink(struct dhcpcd_ctx *ctx) { /* route and ifwatchd like a msg buf size of 2048 */ char msg[2048], *p, *e, *cp; ssize_t bytes; struct rt_msghdr *rtm; struct if_announcemsghdr *ifan; struct if_msghdr *ifm; struct ifa_msghdr *ifam; struct sockaddr *sa, *rti_info[RTAX_MAX]; int len; struct sockaddr_dl sdl; struct interface *ifp; #ifdef INET struct rt rt; #endif #ifdef INET6 struct rt6 rt6; struct in6_addr ia6, net6; struct sockaddr_in6 *sin6; #endif #if (defined(INET) && defined(IN_IFF_TENTATIVE)) || defined(INET6) int ifa_flags; #endif if ((bytes = read(ctx->link_fd, msg, sizeof(msg))) == -1) return -1; e = msg + bytes; for (p = msg; p < e; p += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)(void *)p; // Ignore messages generated by us if (rtm->rtm_pid == getpid()) break; switch(rtm->rtm_type) { #ifdef RTM_IFANNOUNCE case RTM_IFANNOUNCE: ifan = (struct if_announcemsghdr *)(void *)p; switch(ifan->ifan_what) { case IFAN_ARRIVAL: dhcpcd_handleinterface(ctx, 1, ifan->ifan_name); break; case IFAN_DEPARTURE: dhcpcd_handleinterface(ctx, -1, ifan->ifan_name); break; } break; #endif case RTM_IFINFO: ifm = (struct if_msghdr *)(void *)p; ifp = if_findindex(ctx->ifaces, ifm->ifm_index); if (ifp == NULL) break; switch (ifm->ifm_data.ifi_link_state) { case LINK_STATE_DOWN: len = LINK_DOWN; break; case LINK_STATE_UP: len = LINK_UP; break; default: /* handle_carrier will re-load * the interface flags and check for * IFF_RUNNING as some drivers that * don't handle link state also don't * set IFF_RUNNING when this routing * message is generated. * As such, it is a race ...*/ len = LINK_UNKNOWN; break; } dhcpcd_handlecarrier(ctx, len, (unsigned int)ifm->ifm_flags, ifp->name); break; case RTM_ADD: case RTM_CHANGE: case RTM_DELETE: cp = (char *)(void *)(rtm + 1); sa = (struct sockaddr *)(void *)cp; switch (sa->sa_family) { #ifdef INET case AF_INET: if (if_copyrt(ctx, &rt, rtm) == 0) ipv4_handlert(ctx, rtm->rtm_type, &rt); break; #endif #ifdef INET6 case AF_INET6: if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY)) break; /* * BSD caches host routes in the * routing table. * As such, we should be notified of * reachability by its existance * with a hardware address */ if (rtm->rtm_flags & (RTF_HOST)) { get_addrs(rtm->rtm_addrs, cp, rti_info); COPYOUT6(ia6, rti_info[RTAX_DST]); DESCOPE(&ia6); if (rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) memcpy(&sdl, rti_info[RTAX_GATEWAY], sizeof(sdl)); else sdl.sdl_alen = 0; ipv6nd_neighbour(ctx, &ia6, rtm->rtm_type != RTM_DELETE && sdl.sdl_alen ? IPV6ND_REACHABLE : 0); break; } if (if_copyrt6(ctx, &rt6, rtm) == 0) ipv6_handlert(ctx, rtm->rtm_type, &rt6); break; #endif } break; #ifdef RTM_CHGADDR case RTM_CHGADDR: /* FALLTHROUGH */ #endif case RTM_DELADDR: /* FALLTHROUGH */ case RTM_NEWADDR: ifam = (struct ifa_msghdr *)(void *)p; ifp = if_findindex(ctx->ifaces, ifam->ifam_index); if (ifp == NULL) break; cp = (char *)(void *)(ifam + 1); get_addrs(ifam->ifam_addrs, cp, rti_info); if (rti_info[RTAX_IFA] == NULL) break; switch (rti_info[RTAX_IFA]->sa_family) { case AF_LINK: #ifdef RTM_CHGADDR if (rtm->rtm_type != RTM_CHGADDR) break; #else if (rtm->rtm_type != RTM_NEWADDR) break; #endif memcpy(&sdl, rti_info[RTAX_IFA], rti_info[RTAX_IFA]->sa_len); dhcpcd_handlehwaddr(ctx, ifp->name, (const unsigned char*)CLLADDR(&sdl), sdl.sdl_alen); break; #ifdef INET case AF_INET: case 255: /* FIXME: Why 255? */ COPYOUT(rt.dest, rti_info[RTAX_IFA]); COPYOUT(rt.net, rti_info[RTAX_NETMASK]); COPYOUT(rt.gate, rti_info[RTAX_BRD]); if (rtm->rtm_type == RTM_NEWADDR) { ifa_flags = if_addrflags(&rt.dest, ifp); if (ifa_flags == -1) break; } else ifa_flags = 0; ipv4_handleifa(ctx, rtm->rtm_type, NULL, ifp->name, &rt.dest, &rt.net, &rt.gate, ifa_flags); break; #endif #ifdef INET6 case AF_INET6: sin6 = (struct sockaddr_in6*)(void *) rti_info[RTAX_IFA]; ia6 = sin6->sin6_addr; DESCOPE(&ia6); sin6 = (struct sockaddr_in6*)(void *) rti_info[RTAX_NETMASK]; net6 = sin6->sin6_addr; DESCOPE(&net6); if (rtm->rtm_type == RTM_NEWADDR) { ifa_flags = if_addrflags6(&ia6, ifp); if (ifa_flags == -1) break; } else ifa_flags = 0; ipv6_handleifa(ctx, rtm->rtm_type, NULL, ifp->name, &ia6, ipv6_prefixlen(&net6), ifa_flags); break; #endif } break; } } return 0; } #ifndef SYS_NMLN /* OSX */ # define SYS_NMLN 256 #endif #ifndef HW_MACHINE_ARCH # ifdef HW_MODEL /* OpenBSD */ # define HW_MACHINE_ARCH HW_MODEL # endif #endif int if_machinearch(char *str, size_t len) { int mib[2] = { CTL_HW, HW_MACHINE_ARCH }; char march[SYS_NMLN]; size_t marchlen = sizeof(march); if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), march, &marchlen, NULL, 0) != 0) return -1; return snprintf(str, len, ":%s", march); } #ifdef INET6 #ifdef IPV6CTL_ACCEPT_RTADV #define get_inet6_sysctl(code) inet6_sysctl(code, 0, 0) #define set_inet6_sysctl(code, val) inet6_sysctl(code, val, 1) static int inet6_sysctl(int code, int val, int action) { int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 }; size_t size; mib[3] = code; size = sizeof(val); if (action) { if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), NULL, 0, &val, size) == -1) return -1; return 0; } if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &val, &size, NULL, 0) == -1) return -1; return val; } #endif #ifdef IPV6_MANAGETEMPADDR #ifndef IPV6CTL_TEMPVLTIME #define get_inet6_sysctlbyname(code) inet6_sysctlbyname(code, 0, 0) #define set_inet6_sysctlbyname(code, val) inet6_sysctlbyname(code, val, 1) static int inet6_sysctlbyname(const char *name, int val, int action) { size_t size; size = sizeof(val); if (action) { if (sysctlbyname(name, NULL, 0, &val, size) == -1) return -1; return 0; } if (sysctlbyname(name, &val, &size, NULL, 0) == -1) return -1; return val; } #endif int ip6_use_tempaddr(__unused const char *ifname) { int val; #ifdef IPV6CTL_USETEMPADDR val = get_inet6_sysctl(IPV6CTL_USETEMPADDR); #else val = get_inet6_sysctlbyname("net.inet6.ip6.use_tempaddr"); #endif return val == -1 ? 0 : val; } int ip6_temp_preferred_lifetime(__unused const char *ifname) { int val; #ifdef IPV6CTL_TEMPPLTIME val = get_inet6_sysctl(IPV6CTL_TEMPPLTIME); #else val = get_inet6_sysctlbyname("net.inet6.ip6.temppltime"); #endif return val < 0 ? TEMP_PREFERRED_LIFETIME : val; } int ip6_temp_valid_lifetime(__unused const char *ifname) { int val; #ifdef IPV6CTL_TEMPVLTIME val = get_inet6_sysctl(IPV6CTL_TEMPVLTIME); #else val = get_inet6_sysctlbyname("net.inet6.ip6.tempvltime"); #endif return val < 0 ? TEMP_VALID_LIFETIME : val; } #endif #define del_if_nd6_flag(s, ifname, flag) if_nd6_flag((s), (ifp), (flag), -1) #define get_if_nd6_flag(s, ifname, flag) if_nd6_flag((s), (ifp), (flag), 0) #define set_if_nd6_flag(s, ifname, flag) if_nd6_flag((s), (ifp), (flag), 1) static int if_nd6_flag(int s, const struct interface *ifp, unsigned int flag, int set) { struct in6_ndireq nd; unsigned int oflags; memset(&nd, 0, sizeof(nd)); strlcpy(nd.ifname, ifp->name, sizeof(nd.ifname)); if (ioctl(s, SIOCGIFINFO_IN6, &nd) == -1) return -1; if (set == 0) return nd.ndi.flags & flag ? 1 : 0; oflags = nd.ndi.flags; if (set == -1) nd.ndi.flags &= ~flag; else nd.ndi.flags |= flag; if (oflags == nd.ndi.flags) return 0; return ioctl(s, SIOCSIFINFO_FLAGS, &nd); } static int if_raflush(int s) { char dummy[IFNAMSIZ + 8]; strlcpy(dummy, "lo0", sizeof(dummy)); if (ioctl(s, SIOCSRTRFLUSH_IN6, (void *)&dummy) == -1 || ioctl(s, SIOCSPFXFLUSH_IN6, (void *)&dummy) == -1) return -1; return 0; } #ifdef SIOCIFAFATTACH static int af_attach(int s, const struct interface *ifp, int af) { struct if_afreq ifar; strlcpy(ifar.ifar_name, ifp->name, sizeof(ifar.ifar_name)); ifar.ifar_af = af; return ioctl(s, SIOCIFAFATTACH, (void *)&ifar); } #endif #ifdef SIOCGIFXFLAGS static int set_ifxflags(int s, const struct interface *ifp, int own) { struct ifreq ifr; int flags; #ifndef IFXF_NOINET6 /* No point in removing the no inet6 flag if it doesn't * exist and we're not owning inet6. */ if (! own) return 0; #endif strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCGIFXFLAGS, (void *)&ifr) == -1) return -1; flags = ifr.ifr_flags; #ifdef IFXF_NOINET6 flags &= ~IFXF_NOINET6; #endif if (own) flags &= ~IFXF_AUTOCONF6; if (ifr.ifr_flags == flags) return 0; ifr.ifr_flags = flags; return ioctl(s, SIOCSIFXFLAGS, (void *)&ifr); } #endif static int _if_checkipv6(int s, struct dhcpcd_ctx *ctx, const struct interface *ifp, int own) { int ra; if (ifp) { #ifdef ND6_IFF_OVERRIDE_RTADV int override; #endif #ifdef ND6_IFF_IFDISABLED if (del_if_nd6_flag(s, ifp, ND6_IFF_IFDISABLED) == -1) { logger(ifp->ctx, LOG_ERR, "%s: del_if_nd6_flag: ND6_IFF_IFDISABLED: %m", ifp->name); return -1; } #endif #ifdef ND6_IFF_PERFORMNUD if (set_if_nd6_flag(s, ifp, ND6_IFF_PERFORMNUD) == -1) { logger(ifp->ctx, LOG_ERR, "%s: set_if_nd6_flag: ND6_IFF_PERFORMNUD: %m", ifp->name); return -1; } #endif #ifdef ND6_IFF_AUTO_LINKLOCAL if (own) { int all; all = get_if_nd6_flag(s, ifp, ND6_IFF_AUTO_LINKLOCAL); if (all == -1) logger(ifp->ctx, LOG_ERR, "%s: get_if_nd6_flag: " "ND6_IFF_AUTO_LINKLOCAL: %m", ifp->name); else if (all != 0) { logger(ifp->ctx, LOG_DEBUG, "%s: disabling Kernel IPv6 " "auto link-local support", ifp->name); if (del_if_nd6_flag(s, ifp, ND6_IFF_AUTO_LINKLOCAL) == -1) { logger(ifp->ctx, LOG_ERR, "%s: del_if_nd6_flag: " "ND6_IFF_AUTO_LINKLOCAL: %m", ifp->name); return -1; } } } #endif #ifdef SIOCIFAFATTACH if (af_attach(s, ifp, AF_INET6) == -1) { logger(ifp->ctx, LOG_ERR, "%s: af_attach: %m", ifp->name); return 1; } #endif #ifdef SIOCGIFXFLAGS if (set_ifxflags(s, ifp, own) == -1) { logger(ifp->ctx, LOG_ERR, "%s: set_ifxflags: %m", ifp->name); return -1; } #endif #ifdef ND6_IFF_OVERRIDE_RTADV override = get_if_nd6_flag(s, ifp, ND6_IFF_OVERRIDE_RTADV); if (override == -1) logger(ifp->ctx, LOG_ERR, "%s: get_if_nd6_flag: ND6_IFF_OVERRIDE_RTADV: %m", ifp->name); else if (override == 0 && own) { if (set_if_nd6_flag(s, ifp, ND6_IFF_OVERRIDE_RTADV) == -1) logger(ifp->ctx, LOG_ERR, "%s: set_if_nd6_flag: " "ND6_IFF_OVERRIDE_RTADV: %m", ifp->name); else override = 1; } #endif #ifdef ND6_IFF_ACCEPT_RTADV ra = get_if_nd6_flag(s, ifp, ND6_IFF_ACCEPT_RTADV); if (ra == -1) logger(ifp->ctx, LOG_ERR, "%s: get_if_nd6_flag: ND6_IFF_ACCEPT_RTADV: %m", ifp->name); else if (ra != 0 && own) { logger(ifp->ctx, LOG_DEBUG, "%s: disabling Kernel IPv6 RA support", ifp->name); if (del_if_nd6_flag(s, ifp, ND6_IFF_ACCEPT_RTADV) == -1) logger(ifp->ctx, LOG_ERR, "%s: del_if_nd6_flag: " "ND6_IFF_ACCEPT_RTADV: %m", ifp->name); else ra = 0; } else if (ra == 0 && !own) logger(ifp->ctx, LOG_WARNING, "%s: IPv6 kernel autoconf disabled", ifp->name); #ifdef ND6_IFF_OVERRIDE_RTADV if (override == 0 && ra) return ctx->ra_global; #endif return ra; #else return ctx->ra_global; #endif } #ifdef IPV6CTL_ACCEPT_RTADV ra = get_inet6_sysctl(IPV6CTL_ACCEPT_RTADV); if (ra == -1) /* The sysctl probably doesn't exist, but this isn't an * error as such so just log it and continue */ logger(ifp->ctx, errno == ENOENT ? LOG_DEBUG : LOG_WARNING, "IPV6CTL_ACCEPT_RTADV: %m"); else if (ra != 0 && own) { logger(ifp->ctx, LOG_DEBUG, "disabling Kernel IPv6 RA support"); if (set_inet6_sysctl(IPV6CTL_ACCEPT_RTADV, 0) == -1) { logger(ifp->ctx, LOG_ERR, "IPV6CTL_ACCEPT_RTADV: %m"); return ra; } ra = 0; #else ra = 0; if (own) { #endif /* Flush the kernel knowledge of advertised routers * and prefixes so the kernel does not expire prefixes * and default routes we are trying to own. */ if (if_raflush(s) == -1) logger(ctx, LOG_WARNING, "if_raflush: %m"); } ctx->ra_global = ra; return ra; } int if_checkipv6(struct dhcpcd_ctx *ctx, const struct interface *ifp, int own) { int s, r; if ((s = socket(PF_INET6, SOCK_DGRAM, 0)) == -1) return -1; r = _if_checkipv6(s, ctx, ifp, own); close(s); return r; } #endif