18d36e1dfSRoy Marples /* SPDX-License-Identifier: BSD-2-Clause */
27827cba2SAaron LI /*
37827cba2SAaron LI * dhcpcd - IPv6 ND handling
480aa9461SRoy Marples * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
57827cba2SAaron LI * All rights reserved
67827cba2SAaron LI
77827cba2SAaron LI * Redistribution and use in source and binary forms, with or without
87827cba2SAaron LI * modification, are permitted provided that the following conditions
97827cba2SAaron LI * are met:
107827cba2SAaron LI * 1. Redistributions of source code must retain the above copyright
117827cba2SAaron LI * notice, this list of conditions and the following disclaimer.
127827cba2SAaron LI * 2. Redistributions in binary form must reproduce the above copyright
137827cba2SAaron LI * notice, this list of conditions and the following disclaimer in the
147827cba2SAaron LI * documentation and/or other materials provided with the distribution.
157827cba2SAaron LI *
167827cba2SAaron LI * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
177827cba2SAaron LI * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
187827cba2SAaron LI * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
197827cba2SAaron LI * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
207827cba2SAaron LI * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
217827cba2SAaron LI * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
227827cba2SAaron LI * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
237827cba2SAaron LI * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
247827cba2SAaron LI * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
257827cba2SAaron LI * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
267827cba2SAaron LI * SUCH DAMAGE.
277827cba2SAaron LI */
287827cba2SAaron LI
297827cba2SAaron LI #include <sys/ioctl.h>
307827cba2SAaron LI #include <sys/param.h>
317827cba2SAaron LI #include <sys/socket.h>
327827cba2SAaron LI #include <net/if.h>
337827cba2SAaron LI #include <net/route.h>
347827cba2SAaron LI #include <netinet/in.h>
357827cba2SAaron LI #include <netinet/ip6.h>
367827cba2SAaron LI #include <netinet/icmp6.h>
377827cba2SAaron LI
388d36e1dfSRoy Marples #include <assert.h>
397827cba2SAaron LI #include <errno.h>
407827cba2SAaron LI #include <fcntl.h>
417827cba2SAaron LI #include <stddef.h>
427827cba2SAaron LI #include <stdlib.h>
437827cba2SAaron LI #include <string.h>
446e63cc1fSRoy Marples #include <syslog.h>
457827cba2SAaron LI #include <unistd.h>
467827cba2SAaron LI
476e63cc1fSRoy Marples #define ELOOP_QUEUE ELOOP_IPV6ND
487827cba2SAaron LI #include "common.h"
497827cba2SAaron LI #include "dhcpcd.h"
508d36e1dfSRoy Marples #include "dhcp-common.h"
517827cba2SAaron LI #include "dhcp6.h"
527827cba2SAaron LI #include "eloop.h"
537827cba2SAaron LI #include "if.h"
547827cba2SAaron LI #include "ipv6.h"
557827cba2SAaron LI #include "ipv6nd.h"
567827cba2SAaron LI #include "logerr.h"
576e63cc1fSRoy Marples #include "privsep.h"
587827cba2SAaron LI #include "route.h"
597827cba2SAaron LI #include "script.h"
607827cba2SAaron LI
617827cba2SAaron LI /* Debugging Router Solicitations is a lot of spam, so disable it */
627827cba2SAaron LI //#define DEBUG_RS
637827cba2SAaron LI
64cc34ba0cSRoy Marples #ifndef ND_RA_FLAG_HOME_AGENT
65cc34ba0cSRoy Marples #define ND_RA_FLAG_HOME_AGENT 0x20 /* Home Agent flag in RA */
66cc34ba0cSRoy Marples #endif
67cc34ba0cSRoy Marples #ifndef ND_RA_FLAG_PROXY
68cc34ba0cSRoy Marples #define ND_RA_FLAG_PROXY 0x04 /* Proxy */
69cc34ba0cSRoy Marples #endif
70cc34ba0cSRoy Marples #ifndef ND_OPT_PI_FLAG_ROUTER
71cc34ba0cSRoy Marples #define ND_OPT_PI_FLAG_ROUTER 0x20 /* Router flag in PI */
72cc34ba0cSRoy Marples #endif
73cc34ba0cSRoy Marples
74*54175cefSRoy Marples #ifndef ND_OPT_RI
75*54175cefSRoy Marples #define ND_OPT_RI 24
76*54175cefSRoy Marples struct nd_opt_ri { /* Route Information option RFC4191 */
77*54175cefSRoy Marples uint8_t nd_opt_ri_type;
78*54175cefSRoy Marples uint8_t nd_opt_ri_len;
79*54175cefSRoy Marples uint8_t nd_opt_ri_prefixlen;
80*54175cefSRoy Marples uint8_t nd_opt_ri_flags_reserved;
81*54175cefSRoy Marples uint32_t nd_opt_ri_lifetime;
82*54175cefSRoy Marples struct in6_addr nd_opt_ri_prefix;
83*54175cefSRoy Marples };
84*54175cefSRoy Marples __CTASSERT(sizeof(struct nd_opt_ri) == 24);
85*54175cefSRoy Marples #define OPT_RI_FLAG_PREFERENCE(flags) ((flags & 0x18) >> 3)
86*54175cefSRoy Marples #endif
87*54175cefSRoy Marples
887827cba2SAaron LI #ifndef ND_OPT_RDNSS
897827cba2SAaron LI #define ND_OPT_RDNSS 25
907827cba2SAaron LI struct nd_opt_rdnss { /* RDNSS option RFC 6106 */
917827cba2SAaron LI uint8_t nd_opt_rdnss_type;
927827cba2SAaron LI uint8_t nd_opt_rdnss_len;
937827cba2SAaron LI uint16_t nd_opt_rdnss_reserved;
947827cba2SAaron LI uint32_t nd_opt_rdnss_lifetime;
957827cba2SAaron LI /* followed by list of IP prefixes */
967827cba2SAaron LI };
977827cba2SAaron LI __CTASSERT(sizeof(struct nd_opt_rdnss) == 8);
987827cba2SAaron LI #endif
997827cba2SAaron LI
1007827cba2SAaron LI #ifndef ND_OPT_DNSSL
1017827cba2SAaron LI #define ND_OPT_DNSSL 31
1027827cba2SAaron LI struct nd_opt_dnssl { /* DNSSL option RFC 6106 */
1037827cba2SAaron LI uint8_t nd_opt_dnssl_type;
1047827cba2SAaron LI uint8_t nd_opt_dnssl_len;
1057827cba2SAaron LI uint16_t nd_opt_dnssl_reserved;
1067827cba2SAaron LI uint32_t nd_opt_dnssl_lifetime;
1077827cba2SAaron LI /* followed by list of DNS servers */
1087827cba2SAaron LI };
10980aa9461SRoy Marples __CTASSERT(sizeof(struct nd_opt_dnssl) == 8);
1107827cba2SAaron LI #endif
1117827cba2SAaron LI
1127827cba2SAaron LI /* Impossible options, so we can easily add extras */
1137827cba2SAaron LI #define _ND_OPT_PREFIX_ADDR 255 + 1
1147827cba2SAaron LI
1157827cba2SAaron LI /* Minimal IPv6 MTU */
1167827cba2SAaron LI #ifndef IPV6_MMTU
1177827cba2SAaron LI #define IPV6_MMTU 1280
1187827cba2SAaron LI #endif
1197827cba2SAaron LI
1207827cba2SAaron LI #ifndef ND_RA_FLAG_RTPREF_HIGH
1217827cba2SAaron LI #define ND_RA_FLAG_RTPREF_MASK 0x18
1227827cba2SAaron LI #define ND_RA_FLAG_RTPREF_HIGH 0x08
1237827cba2SAaron LI #define ND_RA_FLAG_RTPREF_MEDIUM 0x00
1247827cba2SAaron LI #define ND_RA_FLAG_RTPREF_LOW 0x18
1257827cba2SAaron LI #define ND_RA_FLAG_RTPREF_RSV 0x10
1267827cba2SAaron LI #endif
1277827cba2SAaron LI
1288d36e1dfSRoy Marples #define EXPIRED_MAX 5 /* Remember 5 expired routers to avoid
1298d36e1dfSRoy Marples logspam. */
1308d36e1dfSRoy Marples
1317827cba2SAaron LI #define MIN_RANDOM_FACTOR 500 /* millisecs */
1327827cba2SAaron LI #define MAX_RANDOM_FACTOR 1500 /* millisecs */
1337827cba2SAaron LI #define MIN_RANDOM_FACTOR_U MIN_RANDOM_FACTOR * 1000 /* usecs */
1347827cba2SAaron LI #define MAX_RANDOM_FACTOR_U MAX_RANDOM_FACTOR * 1000 /* usecs */
1357827cba2SAaron LI
1367827cba2SAaron LI #if BYTE_ORDER == BIG_ENDIAN
1377827cba2SAaron LI #define IPV6_ADDR_INT32_ONE 1
1387827cba2SAaron LI #define IPV6_ADDR_INT16_MLL 0xff02
1397827cba2SAaron LI #elif BYTE_ORDER == LITTLE_ENDIAN
1407827cba2SAaron LI #define IPV6_ADDR_INT32_ONE 0x01000000
1417827cba2SAaron LI #define IPV6_ADDR_INT16_MLL 0x02ff
1427827cba2SAaron LI #endif
1437827cba2SAaron LI
1447827cba2SAaron LI /* Debugging Neighbor Solicitations is a lot of spam, so disable it */
1457827cba2SAaron LI //#define DEBUG_NS
1467827cba2SAaron LI //
1477827cba2SAaron LI
14880aa9461SRoy Marples static void ipv6nd_handledata(void *, unsigned short);
149*54175cefSRoy Marples static struct routeinfo *routeinfo_findalloc(struct ra *, const struct in6_addr *, uint8_t);
150*54175cefSRoy Marples static void routeinfohead_free(struct routeinfohead *);
1517827cba2SAaron LI
1527827cba2SAaron LI /*
1537827cba2SAaron LI * Android ships buggy ICMP6 filter headers.
1547827cba2SAaron LI * Supply our own until they fix their shit.
1557827cba2SAaron LI * References:
1567827cba2SAaron LI * https://android-review.googlesource.com/#/c/58438/
1577827cba2SAaron LI * http://code.google.com/p/android/issues/original?id=32621&seq=24
1587827cba2SAaron LI */
1597827cba2SAaron LI #ifdef __ANDROID__
1607827cba2SAaron LI #undef ICMP6_FILTER_WILLPASS
1617827cba2SAaron LI #undef ICMP6_FILTER_WILLBLOCK
1627827cba2SAaron LI #undef ICMP6_FILTER_SETPASS
1637827cba2SAaron LI #undef ICMP6_FILTER_SETBLOCK
1647827cba2SAaron LI #undef ICMP6_FILTER_SETPASSALL
1657827cba2SAaron LI #undef ICMP6_FILTER_SETBLOCKALL
1667827cba2SAaron LI #define ICMP6_FILTER_WILLPASS(type, filterp) \
1677827cba2SAaron LI ((((filterp)->icmp6_filt[(type) >> 5]) & (1 << ((type) & 31))) == 0)
1687827cba2SAaron LI #define ICMP6_FILTER_WILLBLOCK(type, filterp) \
1697827cba2SAaron LI ((((filterp)->icmp6_filt[(type) >> 5]) & (1 << ((type) & 31))) != 0)
1707827cba2SAaron LI #define ICMP6_FILTER_SETPASS(type, filterp) \
1717827cba2SAaron LI ((((filterp)->icmp6_filt[(type) >> 5]) &= ~(1 << ((type) & 31))))
1727827cba2SAaron LI #define ICMP6_FILTER_SETBLOCK(type, filterp) \
1737827cba2SAaron LI ((((filterp)->icmp6_filt[(type) >> 5]) |= (1 << ((type) & 31))))
1747827cba2SAaron LI #define ICMP6_FILTER_SETPASSALL(filterp) \
1757827cba2SAaron LI memset(filterp, 0, sizeof(struct icmp6_filter));
1767827cba2SAaron LI #define ICMP6_FILTER_SETBLOCKALL(filterp) \
1777827cba2SAaron LI memset(filterp, 0xff, sizeof(struct icmp6_filter));
1787827cba2SAaron LI #endif
1797827cba2SAaron LI
1807827cba2SAaron LI /* Support older systems with different defines */
1817827cba2SAaron LI #if !defined(IPV6_RECVHOPLIMIT) && defined(IPV6_HOPLIMIT)
1827827cba2SAaron LI #define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT
1837827cba2SAaron LI #endif
1847827cba2SAaron LI #if !defined(IPV6_RECVPKTINFO) && defined(IPV6_PKTINFO)
1857827cba2SAaron LI #define IPV6_RECVPKTINFO IPV6_PKTINFO
1867827cba2SAaron LI #endif
1877827cba2SAaron LI
1887827cba2SAaron LI /* Handy defines */
1897827cba2SAaron LI #define ipv6nd_free_ra(ra) ipv6nd_freedrop_ra((ra), 0)
1907827cba2SAaron LI #define ipv6nd_drop_ra(ra) ipv6nd_freedrop_ra((ra), 1)
1917827cba2SAaron LI
1927827cba2SAaron LI void
ipv6nd_printoptions(const struct dhcpcd_ctx * ctx,const struct dhcp_opt * opts,size_t opts_len)1937827cba2SAaron LI ipv6nd_printoptions(const struct dhcpcd_ctx *ctx,
1947827cba2SAaron LI const struct dhcp_opt *opts, size_t opts_len)
1957827cba2SAaron LI {
1967827cba2SAaron LI size_t i, j;
1977827cba2SAaron LI const struct dhcp_opt *opt, *opt2;
1987827cba2SAaron LI int cols;
1997827cba2SAaron LI
2007827cba2SAaron LI for (i = 0, opt = ctx->nd_opts;
2017827cba2SAaron LI i < ctx->nd_opts_len; i++, opt++)
2027827cba2SAaron LI {
2037827cba2SAaron LI for (j = 0, opt2 = opts; j < opts_len; j++, opt2++)
2047827cba2SAaron LI if (opt2->option == opt->option)
2057827cba2SAaron LI break;
2067827cba2SAaron LI if (j == opts_len) {
2077827cba2SAaron LI cols = printf("%03d %s", opt->option, opt->var);
2087827cba2SAaron LI dhcp_print_option_encoding(opt, cols);
2097827cba2SAaron LI }
2107827cba2SAaron LI }
2117827cba2SAaron LI for (i = 0, opt = opts; i < opts_len; i++, opt++) {
2127827cba2SAaron LI cols = printf("%03d %s", opt->option, opt->var);
2137827cba2SAaron LI dhcp_print_option_encoding(opt, cols);
2147827cba2SAaron LI }
2157827cba2SAaron LI }
2167827cba2SAaron LI
217d4fb1e02SRoy Marples int
ipv6nd_open(bool recv)218d4fb1e02SRoy Marples ipv6nd_open(bool recv)
2197827cba2SAaron LI {
220b9ccd228SRoy Marples int fd, on;
2217827cba2SAaron LI struct icmp6_filter filt;
2227827cba2SAaron LI
223d4fb1e02SRoy Marples fd = xsocket(PF_INET6, SOCK_RAW | SOCK_CXNB, IPPROTO_ICMPV6);
224b9ccd228SRoy Marples if (fd == -1)
2257827cba2SAaron LI return -1;
2267827cba2SAaron LI
227d4fb1e02SRoy Marples ICMP6_FILTER_SETBLOCKALL(&filt);
228d4fb1e02SRoy Marples
2297827cba2SAaron LI /* RFC4861 4.1 */
2307827cba2SAaron LI on = 255;
231b9ccd228SRoy Marples if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
2327827cba2SAaron LI &on, sizeof(on)) == -1)
2337827cba2SAaron LI goto eexit;
2347827cba2SAaron LI
235d4fb1e02SRoy Marples if (recv) {
2367827cba2SAaron LI on = 1;
237b9ccd228SRoy Marples if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
238b9ccd228SRoy Marples &on, sizeof(on)) == -1)
239b9ccd228SRoy Marples goto eexit;
240b9ccd228SRoy Marples
241b9ccd228SRoy Marples on = 1;
242b9ccd228SRoy Marples if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
2437827cba2SAaron LI &on, sizeof(on)) == -1)
2447827cba2SAaron LI goto eexit;
2457827cba2SAaron LI
2467827cba2SAaron LI ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
247d4fb1e02SRoy Marples
248d4fb1e02SRoy Marples #ifdef SO_RERROR
249d4fb1e02SRoy Marples on = 1;
250d4fb1e02SRoy Marples if (setsockopt(fd, SOL_SOCKET, SO_RERROR,
251d4fb1e02SRoy Marples &on, sizeof(on)) == -1)
252d4fb1e02SRoy Marples goto eexit;
253d4fb1e02SRoy Marples #endif
254d4fb1e02SRoy Marples }
255d4fb1e02SRoy Marples
256b9ccd228SRoy Marples if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER,
2577827cba2SAaron LI &filt, sizeof(filt)) == -1)
2587827cba2SAaron LI goto eexit;
2597827cba2SAaron LI
260b9ccd228SRoy Marples return fd;
2617827cba2SAaron LI
2627827cba2SAaron LI eexit:
263b9ccd228SRoy Marples close(fd);
2647827cba2SAaron LI return -1;
2657827cba2SAaron LI }
2667827cba2SAaron LI
2678d36e1dfSRoy Marples #ifdef __sun
2686e63cc1fSRoy Marples int
ipv6nd_openif(struct interface * ifp)269d4fb1e02SRoy Marples ipv6nd_openif(struct interface *ifp)
2708d36e1dfSRoy Marples {
271b9ccd228SRoy Marples int fd;
2728d36e1dfSRoy Marples struct ipv6_mreq mreq = {
2738d36e1dfSRoy Marples .ipv6mr_multiaddr = IN6ADDR_LINKLOCAL_ALLNODES_INIT,
2748d36e1dfSRoy Marples .ipv6mr_interface = ifp->index
2758d36e1dfSRoy Marples };
2768d36e1dfSRoy Marples struct rs_state *state = RS_STATE(ifp);
2778d36e1dfSRoy Marples uint_t ifindex = ifp->index;
2788d36e1dfSRoy Marples
2798d36e1dfSRoy Marples if (state->nd_fd != -1)
2808d36e1dfSRoy Marples return state->nd_fd;
2818d36e1dfSRoy Marples
282d4fb1e02SRoy Marples fd = ipv6nd_open(true);
283b9ccd228SRoy Marples if (fd == -1)
2848d36e1dfSRoy Marples return -1;
2858d36e1dfSRoy Marples
286b9ccd228SRoy Marples if (setsockopt(fd, IPPROTO_IPV6, IPV6_BOUND_IF,
2878d36e1dfSRoy Marples &ifindex, sizeof(ifindex)) == -1)
2888d36e1dfSRoy Marples {
289b9ccd228SRoy Marples close(fd);
2908d36e1dfSRoy Marples return -1;
2918d36e1dfSRoy Marples }
2928d36e1dfSRoy Marples
293b9ccd228SRoy Marples if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
2948d36e1dfSRoy Marples &mreq, sizeof(mreq)) == -1)
2958d36e1dfSRoy Marples {
296b9ccd228SRoy Marples close(fd);
2978d36e1dfSRoy Marples return -1;
2988d36e1dfSRoy Marples }
2998d36e1dfSRoy Marples
30080aa9461SRoy Marples if (eloop_event_add(ifp->ctx->eloop, fd, ELE_READ,
30180aa9461SRoy Marples ipv6nd_handledata, ifp) == -1)
30280aa9461SRoy Marples {
30380aa9461SRoy Marples close(fd);
30480aa9461SRoy Marples return -1;
30580aa9461SRoy Marples }
30680aa9461SRoy Marples
307b9ccd228SRoy Marples state->nd_fd = fd;
308b9ccd228SRoy Marples return fd;
3098d36e1dfSRoy Marples }
3108d36e1dfSRoy Marples #endif
3118d36e1dfSRoy Marples
3127827cba2SAaron LI static int
ipv6nd_makersprobe(struct interface * ifp)3137827cba2SAaron LI ipv6nd_makersprobe(struct interface *ifp)
3147827cba2SAaron LI {
3157827cba2SAaron LI struct rs_state *state;
3167827cba2SAaron LI struct nd_router_solicit *rs;
3177827cba2SAaron LI
3187827cba2SAaron LI state = RS_STATE(ifp);
3197827cba2SAaron LI free(state->rs);
3207827cba2SAaron LI state->rslen = sizeof(*rs);
3217827cba2SAaron LI if (ifp->hwlen != 0)
3227827cba2SAaron LI state->rslen += (size_t)ROUNDUP8(ifp->hwlen + 2);
3237827cba2SAaron LI state->rs = calloc(1, state->rslen);
3247827cba2SAaron LI if (state->rs == NULL)
3257827cba2SAaron LI return -1;
3267827cba2SAaron LI rs = state->rs;
3277827cba2SAaron LI rs->nd_rs_type = ND_ROUTER_SOLICIT;
3287827cba2SAaron LI //rs->nd_rs_code = 0;
3297827cba2SAaron LI //rs->nd_rs_cksum = 0;
3307827cba2SAaron LI //rs->nd_rs_reserved = 0;
3317827cba2SAaron LI
3327827cba2SAaron LI if (ifp->hwlen != 0) {
3337827cba2SAaron LI struct nd_opt_hdr *nd;
3347827cba2SAaron LI
3357827cba2SAaron LI nd = (struct nd_opt_hdr *)(state->rs + 1);
3367827cba2SAaron LI nd->nd_opt_type = ND_OPT_SOURCE_LINKADDR;
3377827cba2SAaron LI nd->nd_opt_len = (uint8_t)((ROUNDUP8(ifp->hwlen + 2)) >> 3);
3387827cba2SAaron LI memcpy(nd + 1, ifp->hwaddr, ifp->hwlen);
3397827cba2SAaron LI }
3407827cba2SAaron LI return 0;
3417827cba2SAaron LI }
3427827cba2SAaron LI
3437827cba2SAaron LI static void
ipv6nd_sendrsprobe(void * arg)3447827cba2SAaron LI ipv6nd_sendrsprobe(void *arg)
3457827cba2SAaron LI {
3467827cba2SAaron LI struct interface *ifp = arg;
3478d36e1dfSRoy Marples struct rs_state *state = RS_STATE(ifp);
3488d36e1dfSRoy Marples struct sockaddr_in6 dst = {
3498d36e1dfSRoy Marples .sin6_family = AF_INET6,
3508d36e1dfSRoy Marples .sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT,
3518d36e1dfSRoy Marples .sin6_scope_id = ifp->index,
3528d36e1dfSRoy Marples };
3538d36e1dfSRoy Marples struct iovec iov = { .iov_base = state->rs, .iov_len = state->rslen };
354d4fb1e02SRoy Marples union {
355d4fb1e02SRoy Marples struct cmsghdr hdr;
356d4fb1e02SRoy Marples uint8_t buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
357d4fb1e02SRoy Marples } cmsgbuf = { .buf = { 0 } };
3588d36e1dfSRoy Marples struct msghdr msg = {
3598d36e1dfSRoy Marples .msg_name = &dst, .msg_namelen = sizeof(dst),
3608d36e1dfSRoy Marples .msg_iov = &iov, .msg_iovlen = 1,
361d4fb1e02SRoy Marples .msg_control = cmsgbuf.buf, .msg_controllen = sizeof(cmsgbuf.buf),
3628d36e1dfSRoy Marples };
3637827cba2SAaron LI struct cmsghdr *cm;
3648d36e1dfSRoy Marples struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index };
3658d36e1dfSRoy Marples int s;
366d4fb1e02SRoy Marples #ifndef __sun
367d4fb1e02SRoy Marples struct dhcpcd_ctx *ctx = ifp->ctx;
368d4fb1e02SRoy Marples #endif
3697827cba2SAaron LI
3707827cba2SAaron LI if (ipv6_linklocal(ifp) == NULL) {
3717827cba2SAaron LI logdebugx("%s: delaying Router Solicitation for LL address",
3727827cba2SAaron LI ifp->name);
3737827cba2SAaron LI ipv6_addlinklocalcallback(ifp, ipv6nd_sendrsprobe, ifp);
3747827cba2SAaron LI return;
3757827cba2SAaron LI }
3767827cba2SAaron LI
3777827cba2SAaron LI #ifdef HAVE_SA_LEN
3787827cba2SAaron LI dst.sin6_len = sizeof(dst);
3797827cba2SAaron LI #endif
3807827cba2SAaron LI
3817827cba2SAaron LI /* Set the outbound interface */
3828d36e1dfSRoy Marples cm = CMSG_FIRSTHDR(&msg);
3837827cba2SAaron LI if (cm == NULL) /* unlikely */
3847827cba2SAaron LI return;
3857827cba2SAaron LI cm->cmsg_level = IPPROTO_IPV6;
3867827cba2SAaron LI cm->cmsg_type = IPV6_PKTINFO;
3877827cba2SAaron LI cm->cmsg_len = CMSG_LEN(sizeof(pi));
3887827cba2SAaron LI memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
3897827cba2SAaron LI
3907827cba2SAaron LI logdebugx("%s: sending Router Solicitation", ifp->name);
3916e63cc1fSRoy Marples #ifdef PRIVSEP
3926e63cc1fSRoy Marples if (IN_PRIVSEP(ifp->ctx)) {
3936e63cc1fSRoy Marples if (ps_inet_sendnd(ifp, &msg) == -1)
3946e63cc1fSRoy Marples logerr(__func__);
3956e63cc1fSRoy Marples goto sent;
3966e63cc1fSRoy Marples }
3976e63cc1fSRoy Marples #endif
3988d36e1dfSRoy Marples #ifdef __sun
399d4fb1e02SRoy Marples if (state->nd_fd == -1) {
400d4fb1e02SRoy Marples if (ipv6nd_openif(ifp) == -1) {
401d4fb1e02SRoy Marples logerr(__func__);
402d4fb1e02SRoy Marples return;
403d4fb1e02SRoy Marples }
404d4fb1e02SRoy Marples }
4058d36e1dfSRoy Marples s = state->nd_fd;
4068d36e1dfSRoy Marples #else
407d4fb1e02SRoy Marples if (ctx->nd_fd == -1) {
408d4fb1e02SRoy Marples ctx->nd_fd = ipv6nd_open(true);
409d4fb1e02SRoy Marples if (ctx->nd_fd == -1) {
410d4fb1e02SRoy Marples logerr(__func__);
411d4fb1e02SRoy Marples return;
412d4fb1e02SRoy Marples }
41380aa9461SRoy Marples if (eloop_event_add(ctx->eloop, ctx->nd_fd, ELE_READ,
41480aa9461SRoy Marples ipv6nd_handledata, ctx) == -1)
41580aa9461SRoy Marples logerr("%s: eloop_event_add", __func__);
416d4fb1e02SRoy Marples }
4178d36e1dfSRoy Marples s = ifp->ctx->nd_fd;
4188d36e1dfSRoy Marples #endif
4198d36e1dfSRoy Marples if (sendmsg(s, &msg, 0) == -1) {
4207827cba2SAaron LI logerr(__func__);
4217827cba2SAaron LI /* Allow IPv6ND to continue .... at most a few errors
4227827cba2SAaron LI * would be logged.
4237827cba2SAaron LI * Generally the error is ENOBUFS when struggling to
4247827cba2SAaron LI * associate with an access point. */
4257827cba2SAaron LI }
4267827cba2SAaron LI
4276e63cc1fSRoy Marples #ifdef PRIVSEP
4286e63cc1fSRoy Marples sent:
4296e63cc1fSRoy Marples #endif
4307827cba2SAaron LI if (state->rsprobes++ < MAX_RTR_SOLICITATIONS)
4317827cba2SAaron LI eloop_timeout_add_sec(ifp->ctx->eloop,
4327827cba2SAaron LI RTR_SOLICITATION_INTERVAL, ipv6nd_sendrsprobe, ifp);
4336e63cc1fSRoy Marples else
4347827cba2SAaron LI logwarnx("%s: no IPv6 Routers available", ifp->name);
4357827cba2SAaron LI }
4367827cba2SAaron LI
4378d36e1dfSRoy Marples #ifdef ND6_ADVERTISE
4388d36e1dfSRoy Marples static void
ipv6nd_sendadvertisement(void * arg)4398d36e1dfSRoy Marples ipv6nd_sendadvertisement(void *arg)
4407827cba2SAaron LI {
4418d36e1dfSRoy Marples struct ipv6_addr *ia = arg;
4428d36e1dfSRoy Marples struct interface *ifp = ia->iface;
4438d36e1dfSRoy Marples struct dhcpcd_ctx *ctx = ifp->ctx;
4448d36e1dfSRoy Marples struct sockaddr_in6 dst = {
4458d36e1dfSRoy Marples .sin6_family = AF_INET6,
4468d36e1dfSRoy Marples .sin6_addr = IN6ADDR_LINKLOCAL_ALLNODES_INIT,
4478d36e1dfSRoy Marples .sin6_scope_id = ifp->index,
4488d36e1dfSRoy Marples };
4498d36e1dfSRoy Marples struct iovec iov = { .iov_base = ia->na, .iov_len = ia->na_len };
450d4fb1e02SRoy Marples union {
451d4fb1e02SRoy Marples struct cmsghdr hdr;
452d4fb1e02SRoy Marples uint8_t buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
453d4fb1e02SRoy Marples } cmsgbuf = { .buf = { 0 } };
4548d36e1dfSRoy Marples struct msghdr msg = {
4558d36e1dfSRoy Marples .msg_name = &dst, .msg_namelen = sizeof(dst),
4568d36e1dfSRoy Marples .msg_iov = &iov, .msg_iovlen = 1,
457d4fb1e02SRoy Marples .msg_control = cmsgbuf.buf, .msg_controllen = sizeof(cmsgbuf.buf),
4588d36e1dfSRoy Marples };
4598d36e1dfSRoy Marples struct cmsghdr *cm;
4608d36e1dfSRoy Marples struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index };
4618d36e1dfSRoy Marples const struct rs_state *state = RS_CSTATE(ifp);
4628d36e1dfSRoy Marples int s;
4638d36e1dfSRoy Marples
46493ddca5eSRoy Marples if (state == NULL || !if_is_link_up(ifp))
4658d36e1dfSRoy Marples goto freeit;
4668d36e1dfSRoy Marples
4678d36e1dfSRoy Marples #ifdef SIN6_LEN
4688d36e1dfSRoy Marples dst.sin6_len = sizeof(dst);
4698d36e1dfSRoy Marples #endif
4708d36e1dfSRoy Marples
4718d36e1dfSRoy Marples /* Set the outbound interface. */
4728d36e1dfSRoy Marples cm = CMSG_FIRSTHDR(&msg);
4738d36e1dfSRoy Marples assert(cm != NULL);
4748d36e1dfSRoy Marples cm->cmsg_level = IPPROTO_IPV6;
4758d36e1dfSRoy Marples cm->cmsg_type = IPV6_PKTINFO;
4768d36e1dfSRoy Marples cm->cmsg_len = CMSG_LEN(sizeof(pi));
4778d36e1dfSRoy Marples memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
4788d36e1dfSRoy Marples logdebugx("%s: sending NA for %s", ifp->name, ia->saddr);
4796e63cc1fSRoy Marples
4806e63cc1fSRoy Marples #ifdef PRIVSEP
4816e63cc1fSRoy Marples if (IN_PRIVSEP(ifp->ctx)) {
4826e63cc1fSRoy Marples if (ps_inet_sendnd(ifp, &msg) == -1)
4836e63cc1fSRoy Marples logerr(__func__);
4846e63cc1fSRoy Marples goto sent;
4856e63cc1fSRoy Marples }
4866e63cc1fSRoy Marples #endif
4878d36e1dfSRoy Marples #ifdef __sun
4888d36e1dfSRoy Marples s = state->nd_fd;
4898d36e1dfSRoy Marples #else
4908d36e1dfSRoy Marples s = ctx->nd_fd;
4918d36e1dfSRoy Marples #endif
4928d36e1dfSRoy Marples if (sendmsg(s, &msg, 0) == -1)
4938d36e1dfSRoy Marples logerr(__func__);
4948d36e1dfSRoy Marples
4956e63cc1fSRoy Marples #ifdef PRIVSEP
4966e63cc1fSRoy Marples sent:
4976e63cc1fSRoy Marples #endif
4988d36e1dfSRoy Marples if (++ia->na_count < MAX_NEIGHBOR_ADVERTISEMENT) {
4998d36e1dfSRoy Marples eloop_timeout_add_sec(ctx->eloop,
5008d36e1dfSRoy Marples state->retrans / 1000, ipv6nd_sendadvertisement, ia);
5018d36e1dfSRoy Marples return;
5028d36e1dfSRoy Marples }
5038d36e1dfSRoy Marples
5048d36e1dfSRoy Marples freeit:
5058d36e1dfSRoy Marples free(ia->na);
5068d36e1dfSRoy Marples ia->na = NULL;
5078d36e1dfSRoy Marples ia->na_count = 0;
5088d36e1dfSRoy Marples }
5098d36e1dfSRoy Marples
5108d36e1dfSRoy Marples void
ipv6nd_advertise(struct ipv6_addr * ia)5118d36e1dfSRoy Marples ipv6nd_advertise(struct ipv6_addr *ia)
5128d36e1dfSRoy Marples {
5138d36e1dfSRoy Marples struct dhcpcd_ctx *ctx;
5148d36e1dfSRoy Marples struct interface *ifp;
5158d36e1dfSRoy Marples struct ipv6_state *state;
5168d36e1dfSRoy Marples struct ipv6_addr *iap, *iaf;
5178d36e1dfSRoy Marples struct nd_neighbor_advert *na;
5188d36e1dfSRoy Marples
5198d36e1dfSRoy Marples if (IN6_IS_ADDR_MULTICAST(&ia->addr))
5208d36e1dfSRoy Marples return;
5218d36e1dfSRoy Marples
5228d36e1dfSRoy Marples #ifdef __sun
5238d36e1dfSRoy Marples if (!(ia->flags & IPV6_AF_AUTOCONF) && ia->flags & IPV6_AF_RAPFX)
5248d36e1dfSRoy Marples return;
5258d36e1dfSRoy Marples #endif
5268d36e1dfSRoy Marples
5278d36e1dfSRoy Marples ctx = ia->iface->ctx;
5288d36e1dfSRoy Marples /* Find the most preferred address to advertise. */
5298d36e1dfSRoy Marples iaf = NULL;
5308d36e1dfSRoy Marples TAILQ_FOREACH(ifp, ctx->ifaces, next) {
5318d36e1dfSRoy Marples state = IPV6_STATE(ifp);
53293ddca5eSRoy Marples if (state == NULL || !if_is_link_up(ifp))
5338d36e1dfSRoy Marples continue;
5348d36e1dfSRoy Marples
5358d36e1dfSRoy Marples TAILQ_FOREACH(iap, &state->addrs, next) {
5368d36e1dfSRoy Marples if (!IN6_ARE_ADDR_EQUAL(&iap->addr, &ia->addr))
5378d36e1dfSRoy Marples continue;
5388d36e1dfSRoy Marples
5398d36e1dfSRoy Marples /* Cancel any current advertisement. */
5408d36e1dfSRoy Marples eloop_timeout_delete(ctx->eloop,
5418d36e1dfSRoy Marples ipv6nd_sendadvertisement, iap);
5428d36e1dfSRoy Marples
5438d36e1dfSRoy Marples /* Don't advertise what we can't use. */
5448d36e1dfSRoy Marples if (iap->prefix_vltime == 0 ||
5458d36e1dfSRoy Marples iap->addr_flags & IN6_IFF_NOTUSEABLE)
5468d36e1dfSRoy Marples continue;
5478d36e1dfSRoy Marples
5488d36e1dfSRoy Marples if (iaf == NULL ||
5498d36e1dfSRoy Marples iaf->iface->metric > iap->iface->metric)
5508d36e1dfSRoy Marples iaf = iap;
5518d36e1dfSRoy Marples }
5528d36e1dfSRoy Marples }
5538d36e1dfSRoy Marples if (iaf == NULL)
5548d36e1dfSRoy Marples return;
5558d36e1dfSRoy Marples
5568d36e1dfSRoy Marples /* Make the packet. */
5578d36e1dfSRoy Marples ifp = iaf->iface;
5588d36e1dfSRoy Marples iaf->na_len = sizeof(*na);
5598d36e1dfSRoy Marples if (ifp->hwlen != 0)
5608d36e1dfSRoy Marples iaf->na_len += (size_t)ROUNDUP8(ifp->hwlen + 2);
5618d36e1dfSRoy Marples na = calloc(1, iaf->na_len);
5628d36e1dfSRoy Marples if (na == NULL) {
5638d36e1dfSRoy Marples logerr(__func__);
5648d36e1dfSRoy Marples return;
5658d36e1dfSRoy Marples }
5668d36e1dfSRoy Marples
5678d36e1dfSRoy Marples na->nd_na_type = ND_NEIGHBOR_ADVERT;
5688d36e1dfSRoy Marples na->nd_na_flags_reserved = ND_NA_FLAG_OVERRIDE;
569d4fb1e02SRoy Marples #if defined(PRIVSEP) && (defined(__linux__) || defined(HAVE_PLEDGE))
570d4fb1e02SRoy Marples if (IN_PRIVSEP(ctx)) {
571b8b69544SRoy Marples if (ps_root_ip6forwarding(ctx, ifp->name) != 0)
572d4fb1e02SRoy Marples na->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER;
573d4fb1e02SRoy Marples } else
574d4fb1e02SRoy Marples #endif
575b8b69544SRoy Marples if (ip6_forwarding(ifp->name) != 0)
5768d36e1dfSRoy Marples na->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER;
5778d36e1dfSRoy Marples na->nd_na_target = ia->addr;
5788d36e1dfSRoy Marples
5798d36e1dfSRoy Marples if (ifp->hwlen != 0) {
5808d36e1dfSRoy Marples struct nd_opt_hdr *opt;
5818d36e1dfSRoy Marples
5828d36e1dfSRoy Marples opt = (struct nd_opt_hdr *)(na + 1);
5838d36e1dfSRoy Marples opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
5848d36e1dfSRoy Marples opt->nd_opt_len = (uint8_t)((ROUNDUP8(ifp->hwlen + 2)) >> 3);
5858d36e1dfSRoy Marples memcpy(opt + 1, ifp->hwaddr, ifp->hwlen);
5868d36e1dfSRoy Marples }
5878d36e1dfSRoy Marples
5888d36e1dfSRoy Marples iaf->na_count = 0;
5898d36e1dfSRoy Marples free(iaf->na);
5908d36e1dfSRoy Marples iaf->na = na;
5918d36e1dfSRoy Marples eloop_timeout_delete(ctx->eloop, ipv6nd_sendadvertisement, iaf);
5928d36e1dfSRoy Marples ipv6nd_sendadvertisement(iaf);
5938d36e1dfSRoy Marples }
5948d36e1dfSRoy Marples #elif !defined(SMALL)
5958d36e1dfSRoy Marples #warning kernel does not support userland sending ND6 advertisements
5968d36e1dfSRoy Marples #endif /* ND6_ADVERTISE */
5978d36e1dfSRoy Marples
5988d36e1dfSRoy Marples static void
ipv6nd_expire(void * arg)5998d36e1dfSRoy Marples ipv6nd_expire(void *arg)
6008d36e1dfSRoy Marples {
6018d36e1dfSRoy Marples struct interface *ifp = arg;
6027827cba2SAaron LI struct ra *rap;
6037827cba2SAaron LI
6047827cba2SAaron LI if (ifp->ctx->ra_routers == NULL)
6057827cba2SAaron LI return;
6067827cba2SAaron LI
6077827cba2SAaron LI TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {
6086e63cc1fSRoy Marples if (rap->iface == ifp && rap->willexpire)
6096e63cc1fSRoy Marples rap->doexpire = true;
6107827cba2SAaron LI }
6117827cba2SAaron LI ipv6nd_expirera(ifp);
6127827cba2SAaron LI }
6137827cba2SAaron LI
6147827cba2SAaron LI void
ipv6nd_startexpire(struct interface * ifp)6158d36e1dfSRoy Marples ipv6nd_startexpire(struct interface *ifp)
6167827cba2SAaron LI {
6176e63cc1fSRoy Marples struct ra *rap;
6187827cba2SAaron LI
6196e63cc1fSRoy Marples if (ifp->ctx->ra_routers == NULL)
6206e63cc1fSRoy Marples return;
6216e63cc1fSRoy Marples
6226e63cc1fSRoy Marples TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {
6236e63cc1fSRoy Marples if (rap->iface == ifp)
6246e63cc1fSRoy Marples rap->willexpire = true;
6256e63cc1fSRoy Marples }
6266e63cc1fSRoy Marples eloop_q_timeout_add_sec(ifp->ctx->eloop, ELOOP_IPV6RA_EXPIRE,
6276e63cc1fSRoy Marples RTR_CARRIER_EXPIRE, ipv6nd_expire, ifp);
6288d36e1dfSRoy Marples }
6298d36e1dfSRoy Marples
630280986e4SRoy Marples int
ipv6nd_rtpref(uint8_t flags)631*54175cefSRoy Marples ipv6nd_rtpref(uint8_t flags)
632b9ccd228SRoy Marples {
633b9ccd228SRoy Marples
634*54175cefSRoy Marples switch (flags & ND_RA_FLAG_RTPREF_MASK) {
635b9ccd228SRoy Marples case ND_RA_FLAG_RTPREF_HIGH:
636b9ccd228SRoy Marples return RTPREF_HIGH;
637b9ccd228SRoy Marples case ND_RA_FLAG_RTPREF_MEDIUM:
638b9ccd228SRoy Marples case ND_RA_FLAG_RTPREF_RSV:
639b9ccd228SRoy Marples return RTPREF_MEDIUM;
640b9ccd228SRoy Marples case ND_RA_FLAG_RTPREF_LOW:
641b9ccd228SRoy Marples return RTPREF_LOW;
642b9ccd228SRoy Marples default:
643*54175cefSRoy Marples logerrx("%s: impossible RA flag %x", __func__, flags);
644b9ccd228SRoy Marples return RTPREF_INVALID;
645b9ccd228SRoy Marples }
646b9ccd228SRoy Marples /* NOTREACHED */
647b9ccd228SRoy Marples }
648b9ccd228SRoy Marples
649b9ccd228SRoy Marples static void
ipv6nd_sortrouters(struct dhcpcd_ctx * ctx)650b9ccd228SRoy Marples ipv6nd_sortrouters(struct dhcpcd_ctx *ctx)
651b9ccd228SRoy Marples {
652b9ccd228SRoy Marples struct ra_head sorted_routers = TAILQ_HEAD_INITIALIZER(sorted_routers);
653b9ccd228SRoy Marples struct ra *ra1, *ra2;
654b9ccd228SRoy Marples
655b9ccd228SRoy Marples while ((ra1 = TAILQ_FIRST(ctx->ra_routers)) != NULL) {
656b9ccd228SRoy Marples TAILQ_REMOVE(ctx->ra_routers, ra1, next);
657b9ccd228SRoy Marples TAILQ_FOREACH(ra2, &sorted_routers, next) {
6586e63cc1fSRoy Marples if (ra1->iface->metric > ra2->iface->metric)
659b9ccd228SRoy Marples continue;
660b9ccd228SRoy Marples if (ra1->expired && !ra2->expired)
661b9ccd228SRoy Marples continue;
6626e63cc1fSRoy Marples if (ra1->willexpire && !ra2->willexpire)
6636e63cc1fSRoy Marples continue;
664b9ccd228SRoy Marples if (ra1->lifetime == 0 && ra2->lifetime != 0)
665b9ccd228SRoy Marples continue;
666b9ccd228SRoy Marples if (!ra1->isreachable && ra2->reachable)
667b9ccd228SRoy Marples continue;
668*54175cefSRoy Marples if (ipv6nd_rtpref(ra1->flags) <= ipv6nd_rtpref(ra2->flags))
669b9ccd228SRoy Marples continue;
670b9ccd228SRoy Marples /* All things being equal, prefer older routers. */
6716e63cc1fSRoy Marples /* We don't need to check time, becase newer
6726e63cc1fSRoy Marples * routers are always added to the tail and then
6736e63cc1fSRoy Marples * sorted. */
674b9ccd228SRoy Marples TAILQ_INSERT_BEFORE(ra2, ra1, next);
675b9ccd228SRoy Marples break;
676b9ccd228SRoy Marples }
677b9ccd228SRoy Marples if (ra2 == NULL)
678b9ccd228SRoy Marples TAILQ_INSERT_TAIL(&sorted_routers, ra1, next);
679b9ccd228SRoy Marples }
680b9ccd228SRoy Marples
681b9ccd228SRoy Marples TAILQ_CONCAT(ctx->ra_routers, &sorted_routers, next);
682b9ccd228SRoy Marples }
683b9ccd228SRoy Marples
684b9ccd228SRoy Marples static void
ipv6nd_applyra(struct interface * ifp)685d4fb1e02SRoy Marples ipv6nd_applyra(struct interface *ifp)
686b9ccd228SRoy Marples {
687b9ccd228SRoy Marples struct ra *rap;
688b9ccd228SRoy Marples struct rs_state *state = RS_STATE(ifp);
689d4fb1e02SRoy Marples struct ra defra = {
690d4fb1e02SRoy Marples .iface = ifp,
691d4fb1e02SRoy Marples .hoplimit = IPV6_DEFHLIM ,
692d4fb1e02SRoy Marples .reachable = REACHABLE_TIME,
693d4fb1e02SRoy Marples .retrans = RETRANS_TIMER,
694d4fb1e02SRoy Marples };
695b9ccd228SRoy Marples
696d4fb1e02SRoy Marples TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {
697b9ccd228SRoy Marples if (rap->iface == ifp)
698b9ccd228SRoy Marples break;
699b9ccd228SRoy Marples }
700b9ccd228SRoy Marples
701d4fb1e02SRoy Marples /* If we have no Router Advertisement, then set default values. */
702d4fb1e02SRoy Marples if (rap == NULL || rap->expired || rap->willexpire)
703d4fb1e02SRoy Marples rap = &defra;
704b9ccd228SRoy Marples
705b9ccd228SRoy Marples state->retrans = rap->retrans;
7066e63cc1fSRoy Marples if (if_applyra(rap) == -1 && errno != ENOENT)
707b9ccd228SRoy Marples logerr(__func__);
708b9ccd228SRoy Marples }
709b9ccd228SRoy Marples
7108d36e1dfSRoy Marples /*
7118d36e1dfSRoy Marples * Neighbour reachability.
7128d36e1dfSRoy Marples *
7138d36e1dfSRoy Marples * RFC 4681 6.2.5 says when a node is no longer a router it MUST
7148d36e1dfSRoy Marples * send a RA with a zero lifetime.
7158d36e1dfSRoy Marples * All OS's I know of set the NA router flag if they are a router
7168d36e1dfSRoy Marples * or not and disregard that they are actively advertising or
7178d36e1dfSRoy Marples * shutting down. If the interface is disabled, it cant't send a NA at all.
7188d36e1dfSRoy Marples *
7198d36e1dfSRoy Marples * As such we CANNOT rely on the NA Router flag and MUST use
7208d36e1dfSRoy Marples * unreachability or receive a RA with a lifetime of zero to remove
7218d36e1dfSRoy Marples * the node as a default router.
7228d36e1dfSRoy Marples */
7238d36e1dfSRoy Marples void
ipv6nd_neighbour(struct dhcpcd_ctx * ctx,struct in6_addr * addr,bool reachable)7248d36e1dfSRoy Marples ipv6nd_neighbour(struct dhcpcd_ctx *ctx, struct in6_addr *addr, bool reachable)
7258d36e1dfSRoy Marples {
7268d36e1dfSRoy Marples struct ra *rap, *rapr;
7278d36e1dfSRoy Marples
7288d36e1dfSRoy Marples if (ctx->ra_routers == NULL)
7298d36e1dfSRoy Marples return;
7308d36e1dfSRoy Marples
7317827cba2SAaron LI TAILQ_FOREACH(rap, ctx->ra_routers, next) {
7328d36e1dfSRoy Marples if (IN6_ARE_ADDR_EQUAL(&rap->from, addr))
7337827cba2SAaron LI break;
7347827cba2SAaron LI }
7358d36e1dfSRoy Marples
736ce6cc02eSRoy Marples if (rap == NULL || rap->expired || rap->isreachable == reachable)
7378d36e1dfSRoy Marples return;
7388d36e1dfSRoy Marples
739b9ccd228SRoy Marples rap->isreachable = reachable;
740b9ccd228SRoy Marples loginfox("%s: %s is %s", rap->iface->name, rap->sfrom,
741b9ccd228SRoy Marples reachable ? "reachable again" : "unreachable");
742b9ccd228SRoy Marples
743b9ccd228SRoy Marples /* See if we can install a reachable default router. */
744b9ccd228SRoy Marples ipv6nd_sortrouters(ctx);
745d4fb1e02SRoy Marples ipv6nd_applyra(rap->iface);
746b9ccd228SRoy Marples rt_build(ctx, AF_INET6);
747b9ccd228SRoy Marples
748b9ccd228SRoy Marples if (reachable)
7498d36e1dfSRoy Marples return;
7508d36e1dfSRoy Marples
7518d36e1dfSRoy Marples /* If we have no reachable default routers, try and solicit one. */
7528d36e1dfSRoy Marples TAILQ_FOREACH(rapr, ctx->ra_routers, next) {
7538d36e1dfSRoy Marples if (rap == rapr || rap->iface != rapr->iface)
7548d36e1dfSRoy Marples continue;
7558d36e1dfSRoy Marples if (rapr->isreachable && !rapr->expired && rapr->lifetime)
7568d36e1dfSRoy Marples break;
7577827cba2SAaron LI }
7588d36e1dfSRoy Marples
7598d36e1dfSRoy Marples if (rapr == NULL)
7608d36e1dfSRoy Marples ipv6nd_startrs(rap->iface);
7617827cba2SAaron LI }
7627827cba2SAaron LI
7637827cba2SAaron LI const struct ipv6_addr *
ipv6nd_iffindaddr(const struct interface * ifp,const struct in6_addr * addr,unsigned int flags)7647827cba2SAaron LI ipv6nd_iffindaddr(const struct interface *ifp, const struct in6_addr *addr,
7657827cba2SAaron LI unsigned int flags)
7667827cba2SAaron LI {
7677827cba2SAaron LI struct ra *rap;
7687827cba2SAaron LI struct ipv6_addr *ap;
7697827cba2SAaron LI
7707827cba2SAaron LI if (ifp->ctx->ra_routers == NULL)
7717827cba2SAaron LI return NULL;
7727827cba2SAaron LI
7737827cba2SAaron LI TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {
7747827cba2SAaron LI if (rap->iface != ifp)
7757827cba2SAaron LI continue;
7767827cba2SAaron LI TAILQ_FOREACH(ap, &rap->addrs, next) {
7777827cba2SAaron LI if (ipv6_findaddrmatch(ap, addr, flags))
7787827cba2SAaron LI return ap;
7797827cba2SAaron LI }
7807827cba2SAaron LI }
7817827cba2SAaron LI return NULL;
7827827cba2SAaron LI }
7837827cba2SAaron LI
7847827cba2SAaron LI struct ipv6_addr *
ipv6nd_findaddr(struct dhcpcd_ctx * ctx,const struct in6_addr * addr,unsigned int flags)7857827cba2SAaron LI ipv6nd_findaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr,
7867827cba2SAaron LI unsigned int flags)
7877827cba2SAaron LI {
7887827cba2SAaron LI struct ra *rap;
7897827cba2SAaron LI struct ipv6_addr *ap;
7907827cba2SAaron LI
7917827cba2SAaron LI if (ctx->ra_routers == NULL)
7927827cba2SAaron LI return NULL;
7937827cba2SAaron LI
7947827cba2SAaron LI TAILQ_FOREACH(rap, ctx->ra_routers, next) {
7957827cba2SAaron LI TAILQ_FOREACH(ap, &rap->addrs, next) {
7967827cba2SAaron LI if (ipv6_findaddrmatch(ap, addr, flags))
7977827cba2SAaron LI return ap;
7987827cba2SAaron LI }
7997827cba2SAaron LI }
8007827cba2SAaron LI return NULL;
8017827cba2SAaron LI }
8027827cba2SAaron LI
8036e63cc1fSRoy Marples static struct ipv6_addr *
ipv6nd_rapfindprefix(struct ra * rap,const struct in6_addr * pfx,uint8_t pfxlen)8046e63cc1fSRoy Marples ipv6nd_rapfindprefix(struct ra *rap,
8056e63cc1fSRoy Marples const struct in6_addr *pfx, uint8_t pfxlen)
8066e63cc1fSRoy Marples {
8076e63cc1fSRoy Marples struct ipv6_addr *ia;
8086e63cc1fSRoy Marples
8096e63cc1fSRoy Marples TAILQ_FOREACH(ia, &rap->addrs, next) {
8106e63cc1fSRoy Marples if (ia->prefix_vltime == 0)
8116e63cc1fSRoy Marples continue;
8126e63cc1fSRoy Marples if (ia->prefix_len == pfxlen &&
8136e63cc1fSRoy Marples IN6_ARE_ADDR_EQUAL(&ia->prefix, pfx))
8146e63cc1fSRoy Marples break;
8156e63cc1fSRoy Marples }
8166e63cc1fSRoy Marples return ia;
8176e63cc1fSRoy Marples }
8186e63cc1fSRoy Marples
8196e63cc1fSRoy Marples struct ipv6_addr *
ipv6nd_iffindprefix(struct interface * ifp,const struct in6_addr * pfx,uint8_t pfxlen)8206e63cc1fSRoy Marples ipv6nd_iffindprefix(struct interface *ifp,
8216e63cc1fSRoy Marples const struct in6_addr *pfx, uint8_t pfxlen)
8226e63cc1fSRoy Marples {
8236e63cc1fSRoy Marples struct ra *rap;
8246e63cc1fSRoy Marples struct ipv6_addr *ia;
8256e63cc1fSRoy Marples
8266e63cc1fSRoy Marples ia = NULL;
8276e63cc1fSRoy Marples TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {
8286e63cc1fSRoy Marples if (rap->iface != ifp)
8296e63cc1fSRoy Marples continue;
8306e63cc1fSRoy Marples ia = ipv6nd_rapfindprefix(rap, pfx, pfxlen);
8316e63cc1fSRoy Marples if (ia != NULL)
8326e63cc1fSRoy Marples break;
8336e63cc1fSRoy Marples }
8346e63cc1fSRoy Marples return ia;
8356e63cc1fSRoy Marples }
8366e63cc1fSRoy Marples
8377827cba2SAaron LI static void
ipv6nd_removefreedrop_ra(struct ra * rap,int remove_ra,int drop_ra)8387827cba2SAaron LI ipv6nd_removefreedrop_ra(struct ra *rap, int remove_ra, int drop_ra)
8397827cba2SAaron LI {
8407827cba2SAaron LI
8417827cba2SAaron LI eloop_timeout_delete(rap->iface->ctx->eloop, NULL, rap->iface);
8427827cba2SAaron LI eloop_timeout_delete(rap->iface->ctx->eloop, NULL, rap);
8437827cba2SAaron LI if (remove_ra)
8447827cba2SAaron LI TAILQ_REMOVE(rap->iface->ctx->ra_routers, rap, next);
8457827cba2SAaron LI ipv6_freedrop_addrs(&rap->addrs, drop_ra, NULL);
846*54175cefSRoy Marples routeinfohead_free(&rap->rinfos);
8477827cba2SAaron LI free(rap->data);
8487827cba2SAaron LI free(rap);
8497827cba2SAaron LI }
8507827cba2SAaron LI
8517827cba2SAaron LI static void
ipv6nd_freedrop_ra(struct ra * rap,int drop)8527827cba2SAaron LI ipv6nd_freedrop_ra(struct ra *rap, int drop)
8537827cba2SAaron LI {
8547827cba2SAaron LI
8557827cba2SAaron LI ipv6nd_removefreedrop_ra(rap, 1, drop);
8567827cba2SAaron LI }
8577827cba2SAaron LI
8587827cba2SAaron LI ssize_t
ipv6nd_free(struct interface * ifp)8597827cba2SAaron LI ipv6nd_free(struct interface *ifp)
8607827cba2SAaron LI {
8617827cba2SAaron LI struct rs_state *state;
8627827cba2SAaron LI struct ra *rap, *ran;
8637827cba2SAaron LI struct dhcpcd_ctx *ctx;
8647827cba2SAaron LI ssize_t n;
8657827cba2SAaron LI
8667827cba2SAaron LI state = RS_STATE(ifp);
8677827cba2SAaron LI if (state == NULL)
8687827cba2SAaron LI return 0;
8697827cba2SAaron LI
8708d36e1dfSRoy Marples ctx = ifp->ctx;
8718d36e1dfSRoy Marples #ifdef __sun
8728d36e1dfSRoy Marples eloop_event_delete(ctx->eloop, state->nd_fd);
8738d36e1dfSRoy Marples close(state->nd_fd);
8748d36e1dfSRoy Marples #endif
8757827cba2SAaron LI free(state->rs);
8767827cba2SAaron LI free(state);
8777827cba2SAaron LI ifp->if_data[IF_DATA_IPV6ND] = NULL;
8787827cba2SAaron LI n = 0;
8797827cba2SAaron LI TAILQ_FOREACH_SAFE(rap, ifp->ctx->ra_routers, next, ran) {
8807827cba2SAaron LI if (rap->iface == ifp) {
8817827cba2SAaron LI ipv6nd_free_ra(rap);
8827827cba2SAaron LI n++;
8837827cba2SAaron LI }
8847827cba2SAaron LI }
8857827cba2SAaron LI
8868d36e1dfSRoy Marples #ifndef __sun
8877827cba2SAaron LI /* If we don't have any more IPv6 enabled interfaces,
8887827cba2SAaron LI * close the global socket and release resources */
8897827cba2SAaron LI TAILQ_FOREACH(ifp, ctx->ifaces, next) {
8907827cba2SAaron LI if (RS_STATE(ifp))
8917827cba2SAaron LI break;
8927827cba2SAaron LI }
8937827cba2SAaron LI if (ifp == NULL) {
8947827cba2SAaron LI if (ctx->nd_fd != -1) {
8957827cba2SAaron LI eloop_event_delete(ctx->eloop, ctx->nd_fd);
8967827cba2SAaron LI close(ctx->nd_fd);
8977827cba2SAaron LI ctx->nd_fd = -1;
8987827cba2SAaron LI }
8997827cba2SAaron LI }
9008d36e1dfSRoy Marples #endif
9017827cba2SAaron LI
9027827cba2SAaron LI return n;
9037827cba2SAaron LI }
9047827cba2SAaron LI
9056e63cc1fSRoy Marples static void
ipv6nd_scriptrun(struct ra * rap)9067827cba2SAaron LI ipv6nd_scriptrun(struct ra *rap)
9077827cba2SAaron LI {
9086e63cc1fSRoy Marples int hasdns, hasaddress;
9097827cba2SAaron LI struct ipv6_addr *ap;
9107827cba2SAaron LI
9117827cba2SAaron LI hasaddress = 0;
9127827cba2SAaron LI /* If all addresses have completed DAD run the script */
9137827cba2SAaron LI TAILQ_FOREACH(ap, &rap->addrs, next) {
9147827cba2SAaron LI if ((ap->flags & (IPV6_AF_AUTOCONF | IPV6_AF_ADDED)) ==
9157827cba2SAaron LI (IPV6_AF_AUTOCONF | IPV6_AF_ADDED))
9167827cba2SAaron LI {
9177827cba2SAaron LI hasaddress = 1;
9187827cba2SAaron LI if (!(ap->flags & IPV6_AF_DADCOMPLETED) &&
9197827cba2SAaron LI ipv6_iffindaddr(ap->iface, &ap->addr,
9207827cba2SAaron LI IN6_IFF_TENTATIVE))
9217827cba2SAaron LI ap->flags |= IPV6_AF_DADCOMPLETED;
9227827cba2SAaron LI if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) {
9237827cba2SAaron LI logdebugx("%s: waiting for Router Advertisement"
9247827cba2SAaron LI " DAD to complete",
9257827cba2SAaron LI rap->iface->name);
9266e63cc1fSRoy Marples return;
9277827cba2SAaron LI }
9287827cba2SAaron LI }
9297827cba2SAaron LI }
9307827cba2SAaron LI
9317827cba2SAaron LI /* If we don't require RDNSS then set hasdns = 1 so we fork */
9327827cba2SAaron LI if (!(rap->iface->options->options & DHCPCD_IPV6RA_REQRDNSS))
9337827cba2SAaron LI hasdns = 1;
9347827cba2SAaron LI else {
9357827cba2SAaron LI hasdns = rap->hasdns;
9367827cba2SAaron LI }
9377827cba2SAaron LI
9387827cba2SAaron LI script_runreason(rap->iface, "ROUTERADVERT");
9397827cba2SAaron LI if (hasdns && (hasaddress ||
9407827cba2SAaron LI !(rap->flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER))))
9416e63cc1fSRoy Marples dhcpcd_daemonise(rap->iface->ctx);
9427827cba2SAaron LI #if 0
9437827cba2SAaron LI else if (options & DHCPCD_DAEMONISE &&
9447827cba2SAaron LI !(options & DHCPCD_DAEMONISED) && new_data)
9457827cba2SAaron LI logwarnx("%s: did not fork due to an absent"
9467827cba2SAaron LI " RDNSS option in the RA",
9477827cba2SAaron LI ifp->name);
9487827cba2SAaron LI #endif
9497827cba2SAaron LI }
9507827cba2SAaron LI
9517827cba2SAaron LI static void
ipv6nd_addaddr(void * arg)9527827cba2SAaron LI ipv6nd_addaddr(void *arg)
9537827cba2SAaron LI {
9547827cba2SAaron LI struct ipv6_addr *ap = arg;
9557827cba2SAaron LI
9567827cba2SAaron LI ipv6_addaddr(ap, NULL);
9577827cba2SAaron LI }
9587827cba2SAaron LI
9597827cba2SAaron LI int
ipv6nd_dadcompleted(const struct interface * ifp)9607827cba2SAaron LI ipv6nd_dadcompleted(const struct interface *ifp)
9617827cba2SAaron LI {
9627827cba2SAaron LI const struct ra *rap;
9637827cba2SAaron LI const struct ipv6_addr *ap;
9647827cba2SAaron LI
9657827cba2SAaron LI TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {
9667827cba2SAaron LI if (rap->iface != ifp)
9677827cba2SAaron LI continue;
9687827cba2SAaron LI TAILQ_FOREACH(ap, &rap->addrs, next) {
9697827cba2SAaron LI if (ap->flags & IPV6_AF_AUTOCONF &&
9707827cba2SAaron LI ap->flags & IPV6_AF_ADDED &&
9717827cba2SAaron LI !(ap->flags & IPV6_AF_DADCOMPLETED))
9727827cba2SAaron LI return 0;
9737827cba2SAaron LI }
9747827cba2SAaron LI }
9757827cba2SAaron LI return 1;
9767827cba2SAaron LI }
9777827cba2SAaron LI
9787827cba2SAaron LI static void
ipv6nd_dadcallback(void * arg)9797827cba2SAaron LI ipv6nd_dadcallback(void *arg)
9807827cba2SAaron LI {
9817827cba2SAaron LI struct ipv6_addr *ia = arg, *rapap;
9827827cba2SAaron LI struct interface *ifp;
9837827cba2SAaron LI struct ra *rap;
9847827cba2SAaron LI int wascompleted, found;
9857827cba2SAaron LI char buf[INET6_ADDRSTRLEN];
9867827cba2SAaron LI const char *p;
9877827cba2SAaron LI int dadcounter;
9887827cba2SAaron LI
9897827cba2SAaron LI ifp = ia->iface;
9907827cba2SAaron LI wascompleted = (ia->flags & IPV6_AF_DADCOMPLETED);
9917827cba2SAaron LI ia->flags |= IPV6_AF_DADCOMPLETED;
992d4fb1e02SRoy Marples if (ia->addr_flags & IN6_IFF_DUPLICATED) {
9937827cba2SAaron LI ia->dadcounter++;
9947827cba2SAaron LI logwarnx("%s: DAD detected %s", ifp->name, ia->saddr);
9957827cba2SAaron LI
9967827cba2SAaron LI /* Try and make another stable private address.
9977827cba2SAaron LI * Because ap->dadcounter is always increamented,
9987827cba2SAaron LI * a different address is generated. */
9997827cba2SAaron LI /* XXX Cache DAD counter per prefix/id/ssid? */
10006e63cc1fSRoy Marples if (ifp->options->options & DHCPCD_SLAACPRIVATE &&
10016e63cc1fSRoy Marples IA6_CANAUTOCONF(ia))
10026e63cc1fSRoy Marples {
10036e63cc1fSRoy Marples unsigned int delay;
10046e63cc1fSRoy Marples
10057827cba2SAaron LI if (ia->dadcounter >= IDGEN_RETRIES) {
10067827cba2SAaron LI logerrx("%s: unable to obtain a"
10077827cba2SAaron LI " stable private address",
10087827cba2SAaron LI ifp->name);
10097827cba2SAaron LI goto try_script;
10107827cba2SAaron LI }
10117827cba2SAaron LI loginfox("%s: deleting address %s",
10127827cba2SAaron LI ifp->name, ia->saddr);
10137827cba2SAaron LI if (if_address6(RTM_DELADDR, ia) == -1 &&
10147827cba2SAaron LI errno != EADDRNOTAVAIL && errno != ENXIO)
10157827cba2SAaron LI logerr(__func__);
10167827cba2SAaron LI dadcounter = ia->dadcounter;
10177827cba2SAaron LI if (ipv6_makestableprivate(&ia->addr,
10187827cba2SAaron LI &ia->prefix, ia->prefix_len,
10197827cba2SAaron LI ifp, &dadcounter) == -1)
10207827cba2SAaron LI {
10217827cba2SAaron LI logerr("ipv6_makestableprivate");
10227827cba2SAaron LI return;
10237827cba2SAaron LI }
10247827cba2SAaron LI ia->dadcounter = dadcounter;
10257827cba2SAaron LI ia->flags &= ~(IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED);
10267827cba2SAaron LI ia->flags |= IPV6_AF_NEW;
10277827cba2SAaron LI p = inet_ntop(AF_INET6, &ia->addr, buf, sizeof(buf));
10287827cba2SAaron LI if (p)
10297827cba2SAaron LI snprintf(ia->saddr,
10307827cba2SAaron LI sizeof(ia->saddr),
10317827cba2SAaron LI "%s/%d",
10327827cba2SAaron LI p, ia->prefix_len);
10337827cba2SAaron LI else
10347827cba2SAaron LI ia->saddr[0] = '\0';
10356e63cc1fSRoy Marples delay = arc4random_uniform(IDGEN_DELAY * MSEC_PER_SEC);
10366e63cc1fSRoy Marples eloop_timeout_add_msec(ifp->ctx->eloop, delay,
10377827cba2SAaron LI ipv6nd_addaddr, ia);
10387827cba2SAaron LI return;
10397827cba2SAaron LI }
10407827cba2SAaron LI }
10417827cba2SAaron LI
10427827cba2SAaron LI try_script:
10437827cba2SAaron LI if (!wascompleted) {
10447827cba2SAaron LI TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {
10457827cba2SAaron LI if (rap->iface != ifp)
10467827cba2SAaron LI continue;
10477827cba2SAaron LI wascompleted = 1;
10487827cba2SAaron LI found = 0;
10497827cba2SAaron LI TAILQ_FOREACH(rapap, &rap->addrs, next) {
10507827cba2SAaron LI if (rapap->flags & IPV6_AF_AUTOCONF &&
10517827cba2SAaron LI rapap->flags & IPV6_AF_ADDED &&
10527827cba2SAaron LI (rapap->flags & IPV6_AF_DADCOMPLETED) == 0)
10537827cba2SAaron LI {
10547827cba2SAaron LI wascompleted = 0;
10557827cba2SAaron LI break;
10567827cba2SAaron LI }
10577827cba2SAaron LI if (rapap == ia)
10587827cba2SAaron LI found = 1;
10597827cba2SAaron LI }
10607827cba2SAaron LI
10617827cba2SAaron LI if (wascompleted && found) {
10627827cba2SAaron LI logdebugx("%s: Router Advertisement DAD "
10637827cba2SAaron LI "completed",
10647827cba2SAaron LI rap->iface->name);
10656e63cc1fSRoy Marples ipv6nd_scriptrun(rap);
10667827cba2SAaron LI }
10677827cba2SAaron LI }
10688d36e1dfSRoy Marples #ifdef ND6_ADVERTISE
10698d36e1dfSRoy Marples ipv6nd_advertise(ia);
10708d36e1dfSRoy Marples #endif
10717827cba2SAaron LI }
10727827cba2SAaron LI }
10737827cba2SAaron LI
10746e63cc1fSRoy Marples static struct ipv6_addr *
ipv6nd_findmarkstale(struct ra * rap,struct ipv6_addr * ia,bool mark)10756e63cc1fSRoy Marples ipv6nd_findmarkstale(struct ra *rap, struct ipv6_addr *ia, bool mark)
10766e63cc1fSRoy Marples {
10776e63cc1fSRoy Marples struct dhcpcd_ctx *ctx = ia->iface->ctx;
10786e63cc1fSRoy Marples struct ra *rap2;
10796e63cc1fSRoy Marples struct ipv6_addr *ia2;
10806e63cc1fSRoy Marples
10816e63cc1fSRoy Marples TAILQ_FOREACH(rap2, ctx->ra_routers, next) {
10826e63cc1fSRoy Marples if (rap2 == rap ||
10836e63cc1fSRoy Marples rap2->iface != rap->iface ||
10846e63cc1fSRoy Marples rap2->expired)
10856e63cc1fSRoy Marples continue;
10866e63cc1fSRoy Marples TAILQ_FOREACH(ia2, &rap2->addrs, next) {
10876e63cc1fSRoy Marples if (!IN6_ARE_ADDR_EQUAL(&ia->prefix, &ia2->prefix))
10886e63cc1fSRoy Marples continue;
10896e63cc1fSRoy Marples if (!(ia2->flags & IPV6_AF_STALE))
10906e63cc1fSRoy Marples return ia2;
10916e63cc1fSRoy Marples if (mark)
10926e63cc1fSRoy Marples ia2->prefix_pltime = 0;
10936e63cc1fSRoy Marples }
10946e63cc1fSRoy Marples }
10956e63cc1fSRoy Marples return NULL;
10966e63cc1fSRoy Marples }
10976e63cc1fSRoy Marples
10987827cba2SAaron LI #ifndef DHCP6
10997827cba2SAaron LI /* If DHCPv6 is compiled out, supply a shim to provide an error message
11007827cba2SAaron LI * if IPv6RA requests DHCPv6. */
11018d36e1dfSRoy Marples enum DH6S {
11028d36e1dfSRoy Marples DH6S_REQUEST,
11038d36e1dfSRoy Marples DH6S_INFORM,
11048d36e1dfSRoy Marples };
11057827cba2SAaron LI static int
dhcp6_start(__unused struct interface * ifp,__unused enum DH6S init_state)11067827cba2SAaron LI dhcp6_start(__unused struct interface *ifp, __unused enum DH6S init_state)
11077827cba2SAaron LI {
11087827cba2SAaron LI
11097827cba2SAaron LI errno = ENOTSUP;
11107827cba2SAaron LI return -1;
11117827cba2SAaron LI }
11127827cba2SAaron LI #endif
11137827cba2SAaron LI
11147827cba2SAaron LI static void
ipv6nd_handlera(struct dhcpcd_ctx * ctx,const struct sockaddr_in6 * from,const char * sfrom,struct interface * ifp,struct icmp6_hdr * icp,size_t len,int hoplimit)11158d36e1dfSRoy Marples ipv6nd_handlera(struct dhcpcd_ctx *ctx,
11168d36e1dfSRoy Marples const struct sockaddr_in6 *from, const char *sfrom,
11178d36e1dfSRoy Marples struct interface *ifp, struct icmp6_hdr *icp, size_t len, int hoplimit)
11187827cba2SAaron LI {
11197827cba2SAaron LI size_t i, olen;
11207827cba2SAaron LI struct nd_router_advert *nd_ra;
11217827cba2SAaron LI struct nd_opt_hdr ndo;
11227827cba2SAaron LI struct nd_opt_prefix_info pi;
11237827cba2SAaron LI struct nd_opt_mtu mtu;
11247827cba2SAaron LI struct nd_opt_rdnss rdnss;
1125*54175cefSRoy Marples struct nd_opt_ri ri;
1126*54175cefSRoy Marples struct routeinfo *rinfo;
11277827cba2SAaron LI uint8_t *p;
11287827cba2SAaron LI struct ra *rap;
11297827cba2SAaron LI struct in6_addr pi_prefix;
11306e63cc1fSRoy Marples struct ipv6_addr *ia;
11317827cba2SAaron LI struct dhcp_opt *dho;
11328d36e1dfSRoy Marples bool new_rap, new_data, has_address;
11337827cba2SAaron LI uint32_t old_lifetime;
1134b9ccd228SRoy Marples int ifmtu;
11356e63cc1fSRoy Marples int loglevel;
1136cc34ba0cSRoy Marples unsigned int flags;
11377827cba2SAaron LI #ifdef IPV6_MANAGETEMPADDR
11386e63cc1fSRoy Marples bool new_ia;
11397827cba2SAaron LI #endif
11407827cba2SAaron LI
11417f8103cdSRoy Marples if (ifp == NULL || RS_STATE(ifp) == NULL) {
11427827cba2SAaron LI #ifdef DEBUG_RS
11438d36e1dfSRoy Marples logdebugx("RA for unexpected interface from %s", sfrom);
11447827cba2SAaron LI #endif
11457827cba2SAaron LI return;
11467827cba2SAaron LI }
11477827cba2SAaron LI
11487827cba2SAaron LI if (len < sizeof(struct nd_router_advert)) {
11498d36e1dfSRoy Marples logerrx("IPv6 RA packet too short from %s", sfrom);
11507827cba2SAaron LI return;
11517827cba2SAaron LI }
11527827cba2SAaron LI
11537827cba2SAaron LI /* RFC 4861 7.1.2 */
11547827cba2SAaron LI if (hoplimit != 255) {
11558d36e1dfSRoy Marples logerrx("invalid hoplimit(%d) in RA from %s", hoplimit, sfrom);
11567827cba2SAaron LI return;
11577827cba2SAaron LI }
11588d36e1dfSRoy Marples if (!IN6_IS_ADDR_LINKLOCAL(&from->sin6_addr)) {
11598d36e1dfSRoy Marples logerrx("RA from non local address %s", sfrom);
11607827cba2SAaron LI return;
11617827cba2SAaron LI }
11627827cba2SAaron LI
11637827cba2SAaron LI if (!(ifp->options->options & DHCPCD_IPV6RS)) {
11647827cba2SAaron LI #ifdef DEBUG_RS
11658d36e1dfSRoy Marples logerrx("%s: unexpected RA from %s", ifp->name, sfrom);
11667827cba2SAaron LI #endif
11677827cba2SAaron LI return;
11687827cba2SAaron LI }
11697827cba2SAaron LI
11707827cba2SAaron LI /* We could receive a RA before we sent a RS*/
11717827cba2SAaron LI if (ipv6_linklocal(ifp) == NULL) {
11727827cba2SAaron LI #ifdef DEBUG_RS
11737827cba2SAaron LI logdebugx("%s: received RA from %s (no link-local)",
11748d36e1dfSRoy Marples ifp->name, sfrom);
11757827cba2SAaron LI #endif
11767827cba2SAaron LI return;
11777827cba2SAaron LI }
11787827cba2SAaron LI
11798d36e1dfSRoy Marples if (ipv6_iffindaddr(ifp, &from->sin6_addr, IN6_IFF_TENTATIVE)) {
11807827cba2SAaron LI logdebugx("%s: ignoring RA from ourself %s",
11818d36e1dfSRoy Marples ifp->name, sfrom);
11827827cba2SAaron LI return;
11837827cba2SAaron LI }
11847827cba2SAaron LI
1185d4fb1e02SRoy Marples /*
1186d4fb1e02SRoy Marples * Because we preserve RA's and expire them quickly after
1187d4fb1e02SRoy Marples * carrier up, it's important to reset the kernels notion of
1188d4fb1e02SRoy Marples * reachable timers back to default values before applying
1189d4fb1e02SRoy Marples * new RA values.
1190d4fb1e02SRoy Marples */
1191d4fb1e02SRoy Marples TAILQ_FOREACH(rap, ctx->ra_routers, next) {
1192d4fb1e02SRoy Marples if (ifp == rap->iface)
1193d4fb1e02SRoy Marples break;
1194d4fb1e02SRoy Marples }
11957f8103cdSRoy Marples if (rap != NULL && rap->willexpire)
1196d4fb1e02SRoy Marples ipv6nd_applyra(ifp);
1197d4fb1e02SRoy Marples
11987827cba2SAaron LI TAILQ_FOREACH(rap, ctx->ra_routers, next) {
11997827cba2SAaron LI if (ifp == rap->iface &&
12008d36e1dfSRoy Marples IN6_ARE_ADDR_EQUAL(&rap->from, &from->sin6_addr))
12017827cba2SAaron LI break;
12027827cba2SAaron LI }
12037827cba2SAaron LI
12047827cba2SAaron LI nd_ra = (struct nd_router_advert *)icp;
12057827cba2SAaron LI
12067827cba2SAaron LI /* We don't want to spam the log with the fact we got an RA every
12077827cba2SAaron LI * 30 seconds or so, so only spam the log if it's different. */
12087827cba2SAaron LI if (rap == NULL || (rap->data_len != len ||
12097827cba2SAaron LI memcmp(rap->data, (unsigned char *)icp, rap->data_len) != 0))
12107827cba2SAaron LI {
12117827cba2SAaron LI if (rap) {
12127827cba2SAaron LI free(rap->data);
12137827cba2SAaron LI rap->data_len = 0;
12147827cba2SAaron LI }
12157827cba2SAaron LI new_data = true;
12167827cba2SAaron LI } else
12177827cba2SAaron LI new_data = false;
12187827cba2SAaron LI if (rap == NULL) {
12197827cba2SAaron LI rap = calloc(1, sizeof(*rap));
12207827cba2SAaron LI if (rap == NULL) {
12217827cba2SAaron LI logerr(__func__);
12227827cba2SAaron LI return;
12237827cba2SAaron LI }
12247827cba2SAaron LI rap->iface = ifp;
12258d36e1dfSRoy Marples rap->from = from->sin6_addr;
12268d36e1dfSRoy Marples strlcpy(rap->sfrom, sfrom, sizeof(rap->sfrom));
12277827cba2SAaron LI TAILQ_INIT(&rap->addrs);
1228*54175cefSRoy Marples TAILQ_INIT(&rap->rinfos);
12297827cba2SAaron LI new_rap = true;
12308d36e1dfSRoy Marples rap->isreachable = true;
12317827cba2SAaron LI } else
12327827cba2SAaron LI new_rap = false;
12337827cba2SAaron LI if (rap->data_len == 0) {
12347827cba2SAaron LI rap->data = malloc(len);
12357827cba2SAaron LI if (rap->data == NULL) {
12367827cba2SAaron LI logerr(__func__);
12377827cba2SAaron LI if (new_rap)
12387827cba2SAaron LI free(rap);
12397827cba2SAaron LI return;
12407827cba2SAaron LI }
12417827cba2SAaron LI memcpy(rap->data, icp, len);
12427827cba2SAaron LI rap->data_len = len;
12437827cba2SAaron LI }
12447827cba2SAaron LI
12457827cba2SAaron LI /* We could change the debug level based on new_data, but some
12467827cba2SAaron LI * routers like to decrease the advertised valid and preferred times
12477827cba2SAaron LI * in accordance with the own prefix times which would result in too
12487827cba2SAaron LI * much needless log spam. */
12496e63cc1fSRoy Marples if (rap->willexpire)
12506e63cc1fSRoy Marples new_data = true;
12517a0236bfSRoy Marples loglevel = new_rap || rap->willexpire || !rap->isreachable ?
1252a0d9933aSRoy Marples LOG_INFO : LOG_DEBUG;
12536e63cc1fSRoy Marples logmessage(loglevel, "%s: Router Advertisement from %s",
12546e63cc1fSRoy Marples ifp->name, rap->sfrom);
12557827cba2SAaron LI
12567827cba2SAaron LI clock_gettime(CLOCK_MONOTONIC, &rap->acquired);
12577827cba2SAaron LI rap->flags = nd_ra->nd_ra_flags_reserved;
12587827cba2SAaron LI old_lifetime = rap->lifetime;
12597827cba2SAaron LI rap->lifetime = ntohs(nd_ra->nd_ra_router_lifetime);
1260b9ccd228SRoy Marples if (nd_ra->nd_ra_curhoplimit != 0)
1261b9ccd228SRoy Marples rap->hoplimit = nd_ra->nd_ra_curhoplimit;
1262b9ccd228SRoy Marples else
1263b9ccd228SRoy Marples rap->hoplimit = IPV6_DEFHLIM;
1264b9ccd228SRoy Marples if (nd_ra->nd_ra_reachable != 0) {
12657827cba2SAaron LI rap->reachable = ntohl(nd_ra->nd_ra_reachable);
12667827cba2SAaron LI if (rap->reachable > MAX_REACHABLE_TIME)
12677827cba2SAaron LI rap->reachable = 0;
1268b9ccd228SRoy Marples } else
1269b9ccd228SRoy Marples rap->reachable = REACHABLE_TIME;
1270b9ccd228SRoy Marples if (nd_ra->nd_ra_retransmit != 0)
1271b9ccd228SRoy Marples rap->retrans = ntohl(nd_ra->nd_ra_retransmit);
1272b9ccd228SRoy Marples else
1273b9ccd228SRoy Marples rap->retrans = RETRANS_TIMER;
12746e63cc1fSRoy Marples rap->expired = rap->willexpire = rap->doexpire = false;
12758d36e1dfSRoy Marples rap->hasdns = false;
12768d36e1dfSRoy Marples rap->isreachable = true;
12778d36e1dfSRoy Marples has_address = false;
1278b9ccd228SRoy Marples rap->mtu = 0;
12797827cba2SAaron LI
12807827cba2SAaron LI #ifdef IPV6_AF_TEMPORARY
12817827cba2SAaron LI ipv6_markaddrsstale(ifp, IPV6_AF_TEMPORARY);
12827827cba2SAaron LI #endif
12836e63cc1fSRoy Marples TAILQ_FOREACH(ia, &rap->addrs, next) {
12846e63cc1fSRoy Marples ia->flags |= IPV6_AF_STALE;
12857827cba2SAaron LI }
12867827cba2SAaron LI
12877827cba2SAaron LI len -= sizeof(struct nd_router_advert);
12887827cba2SAaron LI p = ((uint8_t *)icp) + sizeof(struct nd_router_advert);
12897827cba2SAaron LI for (; len > 0; p += olen, len -= olen) {
12907827cba2SAaron LI if (len < sizeof(ndo)) {
12917827cba2SAaron LI logerrx("%s: short option", ifp->name);
12927827cba2SAaron LI break;
12937827cba2SAaron LI }
12947827cba2SAaron LI memcpy(&ndo, p, sizeof(ndo));
12957827cba2SAaron LI olen = (size_t)ndo.nd_opt_len * 8;
12967827cba2SAaron LI if (olen == 0) {
12977827cba2SAaron LI logerrx("%s: zero length option", ifp->name);
12987827cba2SAaron LI break;
12997827cba2SAaron LI }
13007827cba2SAaron LI if (olen > len) {
13017827cba2SAaron LI logerrx("%s: option length exceeds message",
13027827cba2SAaron LI ifp->name);
13037827cba2SAaron LI break;
13047827cba2SAaron LI }
13057827cba2SAaron LI
13067827cba2SAaron LI if (has_option_mask(ifp->options->rejectmasknd,
13077827cba2SAaron LI ndo.nd_opt_type))
13087827cba2SAaron LI {
13097827cba2SAaron LI for (i = 0, dho = ctx->nd_opts;
13107827cba2SAaron LI i < ctx->nd_opts_len;
13117827cba2SAaron LI i++, dho++)
13127827cba2SAaron LI {
13137827cba2SAaron LI if (dho->option == ndo.nd_opt_type)
13147827cba2SAaron LI break;
13157827cba2SAaron LI }
13167827cba2SAaron LI if (dho != NULL)
13177827cba2SAaron LI logwarnx("%s: reject RA (option %s) from %s",
13188d36e1dfSRoy Marples ifp->name, dho->var, rap->sfrom);
13197827cba2SAaron LI else
13207827cba2SAaron LI logwarnx("%s: reject RA (option %d) from %s",
13218d36e1dfSRoy Marples ifp->name, ndo.nd_opt_type, rap->sfrom);
13227827cba2SAaron LI if (new_rap)
13237827cba2SAaron LI ipv6nd_removefreedrop_ra(rap, 0, 0);
13247827cba2SAaron LI else
13257827cba2SAaron LI ipv6nd_free_ra(rap);
13267827cba2SAaron LI return;
13277827cba2SAaron LI }
13287827cba2SAaron LI
13297827cba2SAaron LI if (has_option_mask(ifp->options->nomasknd, ndo.nd_opt_type))
13307827cba2SAaron LI continue;
13317827cba2SAaron LI
13327827cba2SAaron LI switch (ndo.nd_opt_type) {
13337827cba2SAaron LI case ND_OPT_PREFIX_INFORMATION:
133480aa9461SRoy Marples {
133580aa9461SRoy Marples uint32_t vltime, pltime;
133680aa9461SRoy Marples
13376e63cc1fSRoy Marples loglevel = new_data ? LOG_ERR : LOG_DEBUG;
13387827cba2SAaron LI if (ndo.nd_opt_len != 4) {
1339cc34ba0cSRoy Marples logmessage(loglevel,
1340cc34ba0cSRoy Marples "%s: invalid option len for prefix",
13417827cba2SAaron LI ifp->name);
13427827cba2SAaron LI continue;
13437827cba2SAaron LI }
13447827cba2SAaron LI memcpy(&pi, p, sizeof(pi));
13457827cba2SAaron LI if (pi.nd_opt_pi_prefix_len > 128) {
1346cc34ba0cSRoy Marples logmessage(loglevel, "%s: invalid prefix len",
1347cc34ba0cSRoy Marples ifp->name);
13487827cba2SAaron LI continue;
13497827cba2SAaron LI }
13507827cba2SAaron LI /* nd_opt_pi_prefix is not aligned. */
13517827cba2SAaron LI memcpy(&pi_prefix, &pi.nd_opt_pi_prefix,
13527827cba2SAaron LI sizeof(pi_prefix));
13537827cba2SAaron LI if (IN6_IS_ADDR_MULTICAST(&pi_prefix) ||
13547827cba2SAaron LI IN6_IS_ADDR_LINKLOCAL(&pi_prefix))
13557827cba2SAaron LI {
1356cc34ba0cSRoy Marples logmessage(loglevel, "%s: invalid prefix in RA",
1357cc34ba0cSRoy Marples ifp->name);
13587827cba2SAaron LI continue;
13597827cba2SAaron LI }
136080aa9461SRoy Marples
136180aa9461SRoy Marples vltime = ntohl(pi.nd_opt_pi_valid_time);
136280aa9461SRoy Marples pltime = ntohl(pi.nd_opt_pi_preferred_time);
136380aa9461SRoy Marples if (pltime > vltime) {
1364cc34ba0cSRoy Marples logmessage(loglevel, "%s: pltime > vltime",
1365cc34ba0cSRoy Marples ifp->name);
13667827cba2SAaron LI continue;
13677827cba2SAaron LI }
13687827cba2SAaron LI
13697827cba2SAaron LI flags = IPV6_AF_RAPFX;
1370cc34ba0cSRoy Marples /* If no flags are set, that means the prefix is
1371cc34ba0cSRoy Marples * available via the router. */
1372cc34ba0cSRoy Marples if (pi.nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK)
1373cc34ba0cSRoy Marples flags |= IPV6_AF_ONLINK;
1374cc34ba0cSRoy Marples if (pi.nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO &&
13757827cba2SAaron LI rap->iface->options->options &
13767827cba2SAaron LI DHCPCD_IPV6RA_AUTOCONF)
13777827cba2SAaron LI flags |= IPV6_AF_AUTOCONF;
1378cc34ba0cSRoy Marples if (pi.nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ROUTER)
1379cc34ba0cSRoy Marples flags |= IPV6_AF_ROUTER;
13807827cba2SAaron LI
1381cc34ba0cSRoy Marples ia = ipv6nd_rapfindprefix(rap,
1382cc34ba0cSRoy Marples &pi_prefix, pi.nd_opt_pi_prefix_len);
1383cc34ba0cSRoy Marples if (ia == NULL) {
13846e63cc1fSRoy Marples ia = ipv6_newaddr(rap->iface,
13857827cba2SAaron LI &pi_prefix, pi.nd_opt_pi_prefix_len, flags);
13866e63cc1fSRoy Marples if (ia == NULL)
13877827cba2SAaron LI break;
138880aa9461SRoy Marples
13896e63cc1fSRoy Marples ia->prefix = pi_prefix;
139080aa9461SRoy Marples ia->created = ia->acquired = rap->acquired;
139180aa9461SRoy Marples ia->prefix_vltime = vltime;
139280aa9461SRoy Marples ia->prefix_pltime = pltime;
139380aa9461SRoy Marples
13948d36e1dfSRoy Marples if (flags & IPV6_AF_AUTOCONF)
13956e63cc1fSRoy Marples ia->dadcallback = ipv6nd_dadcallback;
139680aa9461SRoy Marples
13976e63cc1fSRoy Marples TAILQ_INSERT_TAIL(&rap->addrs, ia, next);
13987827cba2SAaron LI
13997827cba2SAaron LI #ifdef IPV6_MANAGETEMPADDR
14007827cba2SAaron LI /* New address to dhcpcd RA handling.
14017827cba2SAaron LI * If the address already exists and a valid
14027827cba2SAaron LI * temporary address also exists then
14037827cba2SAaron LI * extend the existing one rather than
14047827cba2SAaron LI * create a new one */
14058d36e1dfSRoy Marples if (flags & IPV6_AF_AUTOCONF &&
14066e63cc1fSRoy Marples ipv6_iffindaddr(ifp, &ia->addr,
14077827cba2SAaron LI IN6_IFF_NOTUSEABLE) &&
14086e63cc1fSRoy Marples ipv6_settemptime(ia, 0))
14096e63cc1fSRoy Marples new_ia = false;
14107827cba2SAaron LI else
14116e63cc1fSRoy Marples new_ia = true;
14127827cba2SAaron LI #endif
141380aa9461SRoy Marples
14147827cba2SAaron LI } else {
141580aa9461SRoy Marples uint32_t rmtime;
141680aa9461SRoy Marples
141780aa9461SRoy Marples /*
141880aa9461SRoy Marples * RFC 4862 5.5.3.e
141980aa9461SRoy Marples * Don't terminate existing connections.
142080aa9461SRoy Marples * This means that to actually remove the
142180aa9461SRoy Marples * existing prefix, the RA needs to stop
142280aa9461SRoy Marples * broadcasting the prefix and just let it
142380aa9461SRoy Marples * expire in 2 hours.
142480aa9461SRoy Marples * It might want to broadcast it to reduce
142580aa9461SRoy Marples * the vltime if it was greater than 2 hours
142680aa9461SRoy Marples * to start with/
142780aa9461SRoy Marples */
142880aa9461SRoy Marples ia->prefix_pltime = pltime;
142980aa9461SRoy Marples if (ia->prefix_vltime) {
143080aa9461SRoy Marples uint32_t elapsed;
143180aa9461SRoy Marples
143280aa9461SRoy Marples elapsed = (uint32_t)eloop_timespec_diff(
143380aa9461SRoy Marples &rap->acquired, &ia->acquired,
143480aa9461SRoy Marples NULL);
143580aa9461SRoy Marples rmtime = ia->prefix_vltime - elapsed;
143680aa9461SRoy Marples if (rmtime > ia->prefix_vltime)
143780aa9461SRoy Marples rmtime = 0;
143880aa9461SRoy Marples } else
143980aa9461SRoy Marples rmtime = 0;
144080aa9461SRoy Marples if (vltime > MIN_EXTENDED_VLTIME ||
144180aa9461SRoy Marples vltime > rmtime)
144280aa9461SRoy Marples ia->prefix_vltime = vltime;
144380aa9461SRoy Marples else if (rmtime <= MIN_EXTENDED_VLTIME)
144480aa9461SRoy Marples /* No SEND support from RFC 3971 so
144580aa9461SRoy Marples * leave vltime alone */
144680aa9461SRoy Marples ia->prefix_vltime = rmtime;
144780aa9461SRoy Marples else
144880aa9461SRoy Marples ia->prefix_vltime = MIN_EXTENDED_VLTIME;
144980aa9461SRoy Marples
145080aa9461SRoy Marples /* Ensure pltime still fits */
145180aa9461SRoy Marples if (pltime < ia->prefix_vltime)
145280aa9461SRoy Marples ia->prefix_pltime = pltime;
145380aa9461SRoy Marples else
145480aa9461SRoy Marples ia->prefix_pltime = ia->prefix_vltime;
145580aa9461SRoy Marples
1456cc34ba0cSRoy Marples ia->flags |= flags;
14576e63cc1fSRoy Marples ia->flags &= ~IPV6_AF_STALE;
14586e63cc1fSRoy Marples ia->acquired = rap->acquired;
145980aa9461SRoy Marples
146080aa9461SRoy Marples #ifdef IPV6_MANAGETEMPADDR
146180aa9461SRoy Marples new_ia = false;
146280aa9461SRoy Marples #endif
14637827cba2SAaron LI }
146480aa9461SRoy Marples
14656e63cc1fSRoy Marples if (ia->prefix_vltime != 0 &&
14666e63cc1fSRoy Marples ia->flags & IPV6_AF_AUTOCONF)
14678d36e1dfSRoy Marples has_address = true;
14687827cba2SAaron LI
14697827cba2SAaron LI #ifdef IPV6_MANAGETEMPADDR
14707827cba2SAaron LI /* RFC4941 Section 3.3.3 */
14716e63cc1fSRoy Marples if (ia->flags & IPV6_AF_AUTOCONF &&
14727a0236bfSRoy Marples ia->iface->options->options & DHCPCD_SLAACTEMP &&
14736e63cc1fSRoy Marples IA6_CANAUTOCONF(ia))
14747827cba2SAaron LI {
14756e63cc1fSRoy Marples if (!new_ia) {
14766e63cc1fSRoy Marples if (ipv6_settemptime(ia, 1) == NULL)
14776e63cc1fSRoy Marples new_ia = true;
14787827cba2SAaron LI }
14796e63cc1fSRoy Marples if (new_ia && ia->prefix_pltime) {
14806e63cc1fSRoy Marples if (ipv6_createtempaddr(ia,
14816e63cc1fSRoy Marples &ia->acquired) == NULL)
14827827cba2SAaron LI logerr("ipv6_createtempaddr");
14837827cba2SAaron LI }
14847827cba2SAaron LI }
14857827cba2SAaron LI #endif
14867827cba2SAaron LI break;
148780aa9461SRoy Marples }
14887827cba2SAaron LI
14897827cba2SAaron LI case ND_OPT_MTU:
14908d36e1dfSRoy Marples if (len < sizeof(mtu)) {
14916e63cc1fSRoy Marples logmessage(loglevel, "%s: short MTU option", ifp->name);
14928d36e1dfSRoy Marples break;
14938d36e1dfSRoy Marples }
14947827cba2SAaron LI memcpy(&mtu, p, sizeof(mtu));
14957827cba2SAaron LI mtu.nd_opt_mtu_mtu = ntohl(mtu.nd_opt_mtu_mtu);
14967827cba2SAaron LI if (mtu.nd_opt_mtu_mtu < IPV6_MMTU) {
14976e63cc1fSRoy Marples logmessage(loglevel, "%s: invalid MTU %d",
14987827cba2SAaron LI ifp->name, mtu.nd_opt_mtu_mtu);
14997827cba2SAaron LI break;
15007827cba2SAaron LI }
1501b9ccd228SRoy Marples ifmtu = if_getmtu(ifp);
1502b9ccd228SRoy Marples if (ifmtu == -1)
1503b9ccd228SRoy Marples logerr("if_getmtu");
1504b9ccd228SRoy Marples else if (mtu.nd_opt_mtu_mtu > (uint32_t)ifmtu) {
15056e63cc1fSRoy Marples logmessage(loglevel, "%s: advertised MTU %d"
1506b9ccd228SRoy Marples " is greater than link MTU %d",
1507b9ccd228SRoy Marples ifp->name, mtu.nd_opt_mtu_mtu, ifmtu);
1508b9ccd228SRoy Marples rap->mtu = (uint32_t)ifmtu;
1509b9ccd228SRoy Marples } else
15107827cba2SAaron LI rap->mtu = mtu.nd_opt_mtu_mtu;
15117827cba2SAaron LI break;
15127827cba2SAaron LI case ND_OPT_RDNSS:
15138d36e1dfSRoy Marples if (len < sizeof(rdnss)) {
15146e63cc1fSRoy Marples logmessage(loglevel, "%s: short RDNSS option", ifp->name);
15158d36e1dfSRoy Marples break;
15168d36e1dfSRoy Marples }
15177827cba2SAaron LI memcpy(&rdnss, p, sizeof(rdnss));
15187827cba2SAaron LI if (rdnss.nd_opt_rdnss_lifetime &&
15197827cba2SAaron LI rdnss.nd_opt_rdnss_len > 1)
15207827cba2SAaron LI rap->hasdns = 1;
15217827cba2SAaron LI break;
1522*54175cefSRoy Marples case ND_OPT_RI:
1523*54175cefSRoy Marples if (ndo.nd_opt_len > 3) {
1524*54175cefSRoy Marples logmessage(loglevel, "%s: invalid route info option",
1525*54175cefSRoy Marples ifp->name);
1526*54175cefSRoy Marples break;
1527*54175cefSRoy Marples }
1528*54175cefSRoy Marples memset(&ri, 0, sizeof(ri));
1529*54175cefSRoy Marples memcpy(&ri, p, olen); /* may be smaller than sizeof(ri), pad with zero */
1530*54175cefSRoy Marples if(ri.nd_opt_ri_prefixlen > 128) {
1531*54175cefSRoy Marples logmessage(loglevel, "%s: invalid route info prefix length",
1532*54175cefSRoy Marples ifp->name);
1533*54175cefSRoy Marples break;
1534*54175cefSRoy Marples }
1535*54175cefSRoy Marples
1536*54175cefSRoy Marples /* rfc4191 3.1 - RI for ::/0 applies to default route */
1537*54175cefSRoy Marples if(ri.nd_opt_ri_prefixlen == 0) {
1538*54175cefSRoy Marples rap->lifetime = ntohl(ri.nd_opt_ri_lifetime);
1539*54175cefSRoy Marples
1540*54175cefSRoy Marples /* Update preference leaving other flags intact */
1541*54175cefSRoy Marples rap->flags = ((rap->flags & (~ (unsigned int)ND_RA_FLAG_RTPREF_MASK))
1542*54175cefSRoy Marples | ri.nd_opt_ri_flags_reserved) & 0xff;
1543*54175cefSRoy Marples
1544*54175cefSRoy Marples break;
1545*54175cefSRoy Marples }
1546*54175cefSRoy Marples
1547*54175cefSRoy Marples /* Update existing route info instead of rebuilding all routes so that
1548*54175cefSRoy Marples previously announced but now absent routes can stay alive. To kill a
1549*54175cefSRoy Marples route early, an RI with lifetime=0 needs to be received (rfc4191 3.1)*/
1550*54175cefSRoy Marples rinfo = routeinfo_findalloc(rap, &ri.nd_opt_ri_prefix, ri.nd_opt_ri_prefixlen);
1551*54175cefSRoy Marples if(rinfo == NULL) {
1552*54175cefSRoy Marples logerr(__func__);
1553*54175cefSRoy Marples break;
1554*54175cefSRoy Marples }
1555*54175cefSRoy Marples
1556*54175cefSRoy Marples /* Update/initialize other route info params */
1557*54175cefSRoy Marples rinfo->flags = ri.nd_opt_ri_flags_reserved;
1558*54175cefSRoy Marples rinfo->lifetime = ntohl(ri.nd_opt_ri_lifetime);
1559*54175cefSRoy Marples rinfo->acquired = rap->acquired;
1560*54175cefSRoy Marples
1561*54175cefSRoy Marples break;
15627827cba2SAaron LI default:
15637827cba2SAaron LI continue;
15647827cba2SAaron LI }
15657827cba2SAaron LI }
15667827cba2SAaron LI
15677827cba2SAaron LI for (i = 0, dho = ctx->nd_opts;
15687827cba2SAaron LI i < ctx->nd_opts_len;
15697827cba2SAaron LI i++, dho++)
15707827cba2SAaron LI {
15717827cba2SAaron LI if (has_option_mask(ifp->options->requiremasknd,
15727827cba2SAaron LI dho->option))
15737827cba2SAaron LI {
15747827cba2SAaron LI logwarnx("%s: reject RA (no option %s) from %s",
15758d36e1dfSRoy Marples ifp->name, dho->var, rap->sfrom);
15767827cba2SAaron LI if (new_rap)
15777827cba2SAaron LI ipv6nd_removefreedrop_ra(rap, 0, 0);
15787827cba2SAaron LI else
15797827cba2SAaron LI ipv6nd_free_ra(rap);
15807827cba2SAaron LI return;
15817827cba2SAaron LI }
15827827cba2SAaron LI }
15837827cba2SAaron LI
15846e63cc1fSRoy Marples TAILQ_FOREACH(ia, &rap->addrs, next) {
15856e63cc1fSRoy Marples if (!(ia->flags & IPV6_AF_STALE) || ia->prefix_pltime == 0)
15866e63cc1fSRoy Marples continue;
15876e63cc1fSRoy Marples if (ipv6nd_findmarkstale(rap, ia, false) != NULL)
15886e63cc1fSRoy Marples continue;
15896e63cc1fSRoy Marples ipv6nd_findmarkstale(rap, ia, true);
15906e63cc1fSRoy Marples logdebugx("%s: %s: became stale", ifp->name, ia->saddr);
15916e63cc1fSRoy Marples /* Technically this violates RFC 4861 6.3.4,
15926e63cc1fSRoy Marples * but we need a mechanism to tell the kernel to
15936e63cc1fSRoy Marples * try and prefer other addresses. */
15946e63cc1fSRoy Marples ia->prefix_pltime = 0;
15956e63cc1fSRoy Marples }
15966e63cc1fSRoy Marples
1597*54175cefSRoy Marples if (!new_rap && rap->lifetime == 0 && old_lifetime != 0)
1598*54175cefSRoy Marples logwarnx("%s: %s: no longer a default router (lifetime = 0)",
1599*54175cefSRoy Marples ifp->name, rap->sfrom);
1600*54175cefSRoy Marples
160112af092aSRoy Marples if (new_data && !has_address && rap->lifetime && !ipv6_anyglobal(ifp))
16028d36e1dfSRoy Marples logwarnx("%s: no global addresses for default route",
16038d36e1dfSRoy Marples ifp->name);
16048d36e1dfSRoy Marples
16057827cba2SAaron LI if (new_rap)
1606b9ccd228SRoy Marples TAILQ_INSERT_TAIL(ctx->ra_routers, rap, next);
1607b9ccd228SRoy Marples if (new_data)
1608b9ccd228SRoy Marples ipv6nd_sortrouters(ifp->ctx);
16097827cba2SAaron LI
16107827cba2SAaron LI if (ifp->ctx->options & DHCPCD_TEST) {
16117827cba2SAaron LI script_runreason(ifp, "TEST");
16127827cba2SAaron LI goto handle_flag;
16137827cba2SAaron LI }
1614b2927f2bSRoy Marples
1615b2927f2bSRoy Marples if (!(ifp->options->options & DHCPCD_CONFIGURE))
1616b2927f2bSRoy Marples goto run;
1617b2927f2bSRoy Marples
1618d4fb1e02SRoy Marples ipv6nd_applyra(ifp);
16197827cba2SAaron LI ipv6_addaddrs(&rap->addrs);
16207827cba2SAaron LI #ifdef IPV6_MANAGETEMPADDR
16217827cba2SAaron LI ipv6_addtempaddrs(ifp, &rap->acquired);
16227827cba2SAaron LI #endif
16237827cba2SAaron LI rt_build(ifp->ctx, AF_INET6);
1624b2927f2bSRoy Marples
1625b2927f2bSRoy Marples run:
16266e63cc1fSRoy Marples ipv6nd_scriptrun(rap);
16277827cba2SAaron LI
16287827cba2SAaron LI eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
16297827cba2SAaron LI eloop_timeout_delete(ifp->ctx->eloop, NULL, rap); /* reachable timer */
16307827cba2SAaron LI
16317827cba2SAaron LI handle_flag:
16327827cba2SAaron LI if (!(ifp->options->options & DHCPCD_DHCP6))
16337827cba2SAaron LI goto nodhcp6;
16347827cba2SAaron LI /* Only log a DHCPv6 start error if compiled in or debugging is enabled. */
16357827cba2SAaron LI #ifdef DHCP6
16367827cba2SAaron LI #define LOG_DHCP6 logerr
16377827cba2SAaron LI #else
16387827cba2SAaron LI #define LOG_DHCP6 logdebug
16397827cba2SAaron LI #endif
16407827cba2SAaron LI if (rap->flags & ND_RA_FLAG_MANAGED) {
16417827cba2SAaron LI if (new_data && dhcp6_start(ifp, DH6S_REQUEST) == -1)
16427827cba2SAaron LI LOG_DHCP6("dhcp6_start: %s", ifp->name);
16437827cba2SAaron LI } else if (rap->flags & ND_RA_FLAG_OTHER) {
16447827cba2SAaron LI if (new_data && dhcp6_start(ifp, DH6S_INFORM) == -1)
16457827cba2SAaron LI LOG_DHCP6("dhcp6_start: %s", ifp->name);
16467827cba2SAaron LI } else {
16478d36e1dfSRoy Marples #ifdef DHCP6
16487827cba2SAaron LI if (new_data)
16497827cba2SAaron LI logdebugx("%s: No DHCPv6 instruction in RA", ifp->name);
16508d36e1dfSRoy Marples #endif
16517827cba2SAaron LI nodhcp6:
16527827cba2SAaron LI if (ifp->ctx->options & DHCPCD_TEST) {
16537827cba2SAaron LI eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS);
16547827cba2SAaron LI return;
16557827cba2SAaron LI }
16567827cba2SAaron LI }
16577827cba2SAaron LI
16587827cba2SAaron LI /* Expire should be called last as the rap object could be destroyed */
16597827cba2SAaron LI ipv6nd_expirera(ifp);
16607827cba2SAaron LI }
16617827cba2SAaron LI
16628d36e1dfSRoy Marples bool
ipv6nd_hasralifetime(const struct interface * ifp,bool lifetime)16638d36e1dfSRoy Marples ipv6nd_hasralifetime(const struct interface *ifp, bool lifetime)
16647827cba2SAaron LI {
16657827cba2SAaron LI const struct ra *rap;
16667827cba2SAaron LI
16677827cba2SAaron LI if (ifp->ctx->ra_routers) {
16687827cba2SAaron LI TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next)
16696e63cc1fSRoy Marples if (rap->iface == ifp &&
16706e63cc1fSRoy Marples !rap->expired &&
16718d36e1dfSRoy Marples (!lifetime ||rap->lifetime))
16728d36e1dfSRoy Marples return true;
16737827cba2SAaron LI }
16748d36e1dfSRoy Marples return false;
16757827cba2SAaron LI }
16767827cba2SAaron LI
16778d36e1dfSRoy Marples bool
ipv6nd_hasradhcp(const struct interface * ifp,bool managed)16786e63cc1fSRoy Marples ipv6nd_hasradhcp(const struct interface *ifp, bool managed)
16797827cba2SAaron LI {
16807827cba2SAaron LI const struct ra *rap;
16817827cba2SAaron LI
16827827cba2SAaron LI if (ifp->ctx->ra_routers) {
16837827cba2SAaron LI TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {
16847827cba2SAaron LI if (rap->iface == ifp &&
16856e63cc1fSRoy Marples !rap->expired && !rap->willexpire &&
16866e63cc1fSRoy Marples ((managed && rap->flags & ND_RA_FLAG_MANAGED) ||
16876e63cc1fSRoy Marples (!managed && rap->flags & ND_RA_FLAG_OTHER)))
16888d36e1dfSRoy Marples return true;
16897827cba2SAaron LI }
16907827cba2SAaron LI }
16918d36e1dfSRoy Marples return false;
16927827cba2SAaron LI }
16937827cba2SAaron LI
16947827cba2SAaron LI static const uint8_t *
ipv6nd_getoption(struct dhcpcd_ctx * ctx,size_t * os,unsigned int * code,size_t * len,const uint8_t * od,size_t ol,struct dhcp_opt ** oopt)16957827cba2SAaron LI ipv6nd_getoption(struct dhcpcd_ctx *ctx,
16967827cba2SAaron LI size_t *os, unsigned int *code, size_t *len,
16977827cba2SAaron LI const uint8_t *od, size_t ol, struct dhcp_opt **oopt)
16987827cba2SAaron LI {
16997827cba2SAaron LI struct nd_opt_hdr ndo;
17007827cba2SAaron LI size_t i;
17017827cba2SAaron LI struct dhcp_opt *opt;
17027827cba2SAaron LI
17037827cba2SAaron LI if (od) {
17047827cba2SAaron LI *os = sizeof(ndo);
17057827cba2SAaron LI if (ol < *os) {
17067827cba2SAaron LI errno = EINVAL;
17077827cba2SAaron LI return NULL;
17087827cba2SAaron LI }
17097827cba2SAaron LI memcpy(&ndo, od, sizeof(ndo));
17107827cba2SAaron LI i = (size_t)(ndo.nd_opt_len * 8);
17117827cba2SAaron LI if (i > ol) {
17127827cba2SAaron LI errno = EINVAL;
17137827cba2SAaron LI return NULL;
17147827cba2SAaron LI }
17157827cba2SAaron LI *len = i;
17167827cba2SAaron LI *code = ndo.nd_opt_type;
17177827cba2SAaron LI }
17187827cba2SAaron LI
17197827cba2SAaron LI for (i = 0, opt = ctx->nd_opts;
17207827cba2SAaron LI i < ctx->nd_opts_len; i++, opt++)
17217827cba2SAaron LI {
17227827cba2SAaron LI if (opt->option == *code) {
17237827cba2SAaron LI *oopt = opt;
17247827cba2SAaron LI break;
17257827cba2SAaron LI }
17267827cba2SAaron LI }
17277827cba2SAaron LI
17287827cba2SAaron LI if (od)
17297827cba2SAaron LI return od + sizeof(ndo);
17307827cba2SAaron LI return NULL;
17317827cba2SAaron LI }
17327827cba2SAaron LI
17337827cba2SAaron LI ssize_t
ipv6nd_env(FILE * fp,const struct interface * ifp)17348d36e1dfSRoy Marples ipv6nd_env(FILE *fp, const struct interface *ifp)
17357827cba2SAaron LI {
17367827cba2SAaron LI size_t i, j, n, len, olen;
17377827cba2SAaron LI struct ra *rap;
17388d36e1dfSRoy Marples char ndprefix[32];
17397827cba2SAaron LI struct dhcp_opt *opt;
17407827cba2SAaron LI uint8_t *p;
17417827cba2SAaron LI struct nd_opt_hdr ndo;
17427827cba2SAaron LI struct ipv6_addr *ia;
17437827cba2SAaron LI struct timespec now;
1744cc34ba0cSRoy Marples int pref;
17457827cba2SAaron LI
17467827cba2SAaron LI clock_gettime(CLOCK_MONOTONIC, &now);
17477827cba2SAaron LI i = n = 0;
17487827cba2SAaron LI TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {
17498d36e1dfSRoy Marples if (rap->iface != ifp || rap->expired)
17507827cba2SAaron LI continue;
17517827cba2SAaron LI i++;
17528d36e1dfSRoy Marples snprintf(ndprefix, sizeof(ndprefix), "nd%zu", i);
17538d36e1dfSRoy Marples if (efprintf(fp, "%s_from=%s", ndprefix, rap->sfrom) == -1)
17548d36e1dfSRoy Marples return -1;
17558d36e1dfSRoy Marples if (efprintf(fp, "%s_acquired=%lld", ndprefix,
17568d36e1dfSRoy Marples (long long)rap->acquired.tv_sec) == -1)
17578d36e1dfSRoy Marples return -1;
17588d36e1dfSRoy Marples if (efprintf(fp, "%s_now=%lld", ndprefix,
17598d36e1dfSRoy Marples (long long)now.tv_sec) == -1)
17608d36e1dfSRoy Marples return -1;
1761cc34ba0cSRoy Marples if (efprintf(fp, "%s_hoplimit=%u", ndprefix, rap->hoplimit) == -1)
1762cc34ba0cSRoy Marples return -1;
1763*54175cefSRoy Marples pref = ipv6nd_rtpref(rap->flags);
1764cc34ba0cSRoy Marples if (efprintf(fp, "%s_flags=%s%s%s%s%s", ndprefix,
1765cc34ba0cSRoy Marples rap->flags & ND_RA_FLAG_MANAGED ? "M" : "",
1766cc34ba0cSRoy Marples rap->flags & ND_RA_FLAG_OTHER ? "O" : "",
1767cc34ba0cSRoy Marples rap->flags & ND_RA_FLAG_HOME_AGENT ? "H" : "",
1768cc34ba0cSRoy Marples pref == RTPREF_HIGH ? "h" : pref == RTPREF_LOW ? "l" : "",
1769cc34ba0cSRoy Marples rap->flags & ND_RA_FLAG_PROXY ? "P" : "") == -1)
1770cc34ba0cSRoy Marples return -1;
1771cc34ba0cSRoy Marples if (efprintf(fp, "%s_lifetime=%u", ndprefix, rap->lifetime) == -1)
1772cc34ba0cSRoy Marples return -1;
17737827cba2SAaron LI
17747827cba2SAaron LI /* Zero our indexes */
17757827cba2SAaron LI for (j = 0, opt = rap->iface->ctx->nd_opts;
17767827cba2SAaron LI j < rap->iface->ctx->nd_opts_len;
17777827cba2SAaron LI j++, opt++)
17787827cba2SAaron LI dhcp_zero_index(opt);
17797827cba2SAaron LI for (j = 0, opt = rap->iface->options->nd_override;
17807827cba2SAaron LI j < rap->iface->options->nd_override_len;
17817827cba2SAaron LI j++, opt++)
17827827cba2SAaron LI dhcp_zero_index(opt);
17837827cba2SAaron LI
17847827cba2SAaron LI /* Unlike DHCP, ND6 options *may* occur more than once.
17857827cba2SAaron LI * There is also no provision for option concatenation
17867827cba2SAaron LI * unlike DHCP. */
17877827cba2SAaron LI len = rap->data_len - sizeof(struct nd_router_advert);
17887827cba2SAaron LI for (p = rap->data + sizeof(struct nd_router_advert);
17897827cba2SAaron LI len >= sizeof(ndo);
17907827cba2SAaron LI p += olen, len -= olen)
17917827cba2SAaron LI {
17927827cba2SAaron LI memcpy(&ndo, p, sizeof(ndo));
17937827cba2SAaron LI olen = (size_t)(ndo.nd_opt_len * 8);
17947827cba2SAaron LI if (olen > len) {
17957827cba2SAaron LI errno = EINVAL;
17967827cba2SAaron LI break;
17977827cba2SAaron LI }
17987827cba2SAaron LI if (has_option_mask(rap->iface->options->nomasknd,
17997827cba2SAaron LI ndo.nd_opt_type))
18007827cba2SAaron LI continue;
18017827cba2SAaron LI for (j = 0, opt = rap->iface->options->nd_override;
18027827cba2SAaron LI j < rap->iface->options->nd_override_len;
18037827cba2SAaron LI j++, opt++)
18047827cba2SAaron LI if (opt->option == ndo.nd_opt_type)
18057827cba2SAaron LI break;
18067827cba2SAaron LI if (j == rap->iface->options->nd_override_len) {
18077827cba2SAaron LI for (j = 0, opt = rap->iface->ctx->nd_opts;
18087827cba2SAaron LI j < rap->iface->ctx->nd_opts_len;
18097827cba2SAaron LI j++, opt++)
18107827cba2SAaron LI if (opt->option == ndo.nd_opt_type)
18117827cba2SAaron LI break;
18127827cba2SAaron LI if (j == rap->iface->ctx->nd_opts_len)
18137827cba2SAaron LI opt = NULL;
18147827cba2SAaron LI }
18158d36e1dfSRoy Marples if (opt == NULL)
18168d36e1dfSRoy Marples continue;
18178d36e1dfSRoy Marples dhcp_envoption(rap->iface->ctx, fp,
18187827cba2SAaron LI ndprefix, rap->iface->name,
18197827cba2SAaron LI opt, ipv6nd_getoption,
18207827cba2SAaron LI p + sizeof(ndo), olen - sizeof(ndo));
18217827cba2SAaron LI }
18227827cba2SAaron LI
18237827cba2SAaron LI /* We need to output the addresses we actually made
18247827cba2SAaron LI * from the prefix information options as well. */
18257827cba2SAaron LI j = 0;
18267827cba2SAaron LI TAILQ_FOREACH(ia, &rap->addrs, next) {
18278d36e1dfSRoy Marples if (!(ia->flags & IPV6_AF_AUTOCONF) ||
18287827cba2SAaron LI #ifdef IPV6_AF_TEMPORARY
18298d36e1dfSRoy Marples ia->flags & IPV6_AF_TEMPORARY ||
18307827cba2SAaron LI #endif
18318d36e1dfSRoy Marples !(ia->flags & IPV6_AF_ADDED) ||
18328d36e1dfSRoy Marples ia->prefix_vltime == 0)
18337827cba2SAaron LI continue;
18348d36e1dfSRoy Marples if (efprintf(fp, "%s_addr%zu=%s",
18356e63cc1fSRoy Marples ndprefix, ++j, ia->saddr) == -1)
18368d36e1dfSRoy Marples return -1;
18377827cba2SAaron LI }
18387827cba2SAaron LI }
18398d36e1dfSRoy Marples return 1;
18407827cba2SAaron LI }
18417827cba2SAaron LI
18427827cba2SAaron LI void
ipv6nd_handleifa(int cmd,struct ipv6_addr * addr,pid_t pid)18437827cba2SAaron LI ipv6nd_handleifa(int cmd, struct ipv6_addr *addr, pid_t pid)
18447827cba2SAaron LI {
18457827cba2SAaron LI struct ra *rap;
18467827cba2SAaron LI
18477827cba2SAaron LI /* IPv6 init may not have happened yet if we are learning
18487827cba2SAaron LI * existing addresses when dhcpcd starts. */
18497827cba2SAaron LI if (addr->iface->ctx->ra_routers == NULL)
18507827cba2SAaron LI return;
18517827cba2SAaron LI
18527827cba2SAaron LI TAILQ_FOREACH(rap, addr->iface->ctx->ra_routers, next) {
18537827cba2SAaron LI if (rap->iface != addr->iface)
18547827cba2SAaron LI continue;
18557827cba2SAaron LI ipv6_handleifa_addrs(cmd, &rap->addrs, addr, pid);
18567827cba2SAaron LI }
18577827cba2SAaron LI }
18587827cba2SAaron LI
18597827cba2SAaron LI void
ipv6nd_expirera(void * arg)18607827cba2SAaron LI ipv6nd_expirera(void *arg)
18617827cba2SAaron LI {
18627827cba2SAaron LI struct interface *ifp;
18637827cba2SAaron LI struct ra *rap, *ran;
18646e63cc1fSRoy Marples struct timespec now;
18656e63cc1fSRoy Marples uint32_t elapsed;
18668d36e1dfSRoy Marples bool expired, valid;
18677827cba2SAaron LI struct ipv6_addr *ia;
1868*54175cefSRoy Marples struct routeinfo *rinfo, *rinfob;
18698d36e1dfSRoy Marples size_t len, olen;
18708d36e1dfSRoy Marples uint8_t *p;
18718d36e1dfSRoy Marples struct nd_opt_hdr ndo;
18728d36e1dfSRoy Marples #if 0
18738d36e1dfSRoy Marples struct nd_opt_prefix_info pi;
18748d36e1dfSRoy Marples #endif
18758d36e1dfSRoy Marples struct nd_opt_dnssl dnssl;
18768d36e1dfSRoy Marples struct nd_opt_rdnss rdnss;
18776e63cc1fSRoy Marples unsigned int next = 0, ltime;
18788d36e1dfSRoy Marples size_t nexpired = 0;
18797827cba2SAaron LI
18807827cba2SAaron LI ifp = arg;
18817827cba2SAaron LI clock_gettime(CLOCK_MONOTONIC, &now);
18828d36e1dfSRoy Marples expired = false;
18837827cba2SAaron LI
18847827cba2SAaron LI TAILQ_FOREACH_SAFE(rap, ifp->ctx->ra_routers, next, ran) {
18858d36e1dfSRoy Marples if (rap->iface != ifp || rap->expired)
18867827cba2SAaron LI continue;
18878d36e1dfSRoy Marples valid = false;
1888*54175cefSRoy Marples /* lifetime may be set to infinite by rfc4191 route information */
1889*54175cefSRoy Marples if (rap->lifetime && rap->lifetime != ND6_INFINITE_LIFETIME) {
18906e63cc1fSRoy Marples elapsed = (uint32_t)eloop_timespec_diff(&now,
18916e63cc1fSRoy Marples &rap->acquired, NULL);
1892d4fb1e02SRoy Marples if (elapsed >= rap->lifetime || rap->doexpire) {
18937827cba2SAaron LI if (!rap->expired) {
18947827cba2SAaron LI logwarnx("%s: %s: router expired",
18957827cba2SAaron LI ifp->name, rap->sfrom);
18967827cba2SAaron LI rap->lifetime = 0;
18978d36e1dfSRoy Marples expired = true;
18987827cba2SAaron LI }
18997827cba2SAaron LI } else {
19008d36e1dfSRoy Marples valid = true;
19016e63cc1fSRoy Marples ltime = rap->lifetime - elapsed;
19026e63cc1fSRoy Marples if (next == 0 || ltime < next)
19036e63cc1fSRoy Marples next = ltime;
19047827cba2SAaron LI }
19057827cba2SAaron LI }
19067827cba2SAaron LI
19077827cba2SAaron LI /* Not every prefix is tied to an address which
19087827cba2SAaron LI * the kernel can expire, so we need to handle it ourself.
19097827cba2SAaron LI * Also, some OS don't support address lifetimes (Solaris). */
19107827cba2SAaron LI TAILQ_FOREACH(ia, &rap->addrs, next) {
19117827cba2SAaron LI if (ia->prefix_vltime == 0)
19127827cba2SAaron LI continue;
19136e63cc1fSRoy Marples if (ia->prefix_vltime == ND6_INFINITE_LIFETIME &&
19146e63cc1fSRoy Marples !rap->doexpire)
19156e63cc1fSRoy Marples {
19168d36e1dfSRoy Marples valid = true;
19177827cba2SAaron LI continue;
19187827cba2SAaron LI }
19196e63cc1fSRoy Marples elapsed = (uint32_t)eloop_timespec_diff(&now,
19206e63cc1fSRoy Marples &ia->acquired, NULL);
1921d4fb1e02SRoy Marples if (elapsed >= ia->prefix_vltime || rap->doexpire) {
19227827cba2SAaron LI if (ia->flags & IPV6_AF_ADDED) {
19236e63cc1fSRoy Marples logwarnx("%s: expired %s %s",
19246e63cc1fSRoy Marples ia->iface->name,
19256e63cc1fSRoy Marples ia->flags & IPV6_AF_AUTOCONF ?
19266e63cc1fSRoy Marples "address" : "prefix",
19276e63cc1fSRoy Marples ia->saddr);
19287827cba2SAaron LI if (if_address6(RTM_DELADDR, ia)== -1 &&
19297827cba2SAaron LI errno != EADDRNOTAVAIL &&
19307827cba2SAaron LI errno != ENXIO)
19317827cba2SAaron LI logerr(__func__);
19327827cba2SAaron LI }
19337827cba2SAaron LI ia->prefix_vltime = ia->prefix_pltime = 0;
19347827cba2SAaron LI ia->flags &=
19357827cba2SAaron LI ~(IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED);
19368d36e1dfSRoy Marples expired = true;
19377827cba2SAaron LI } else {
19388d36e1dfSRoy Marples valid = true;
19396e63cc1fSRoy Marples ltime = ia->prefix_vltime - elapsed;
19406e63cc1fSRoy Marples if (next == 0 || ltime < next)
19416e63cc1fSRoy Marples next = ltime;
19427827cba2SAaron LI }
19437827cba2SAaron LI }
19447827cba2SAaron LI
1945*54175cefSRoy Marples /* Expire route information */
1946*54175cefSRoy Marples TAILQ_FOREACH_SAFE(rinfo, &rap->rinfos, next, rinfob) {
1947*54175cefSRoy Marples if (rinfo->lifetime == ND6_INFINITE_LIFETIME &&
1948*54175cefSRoy Marples !rap->doexpire)
1949*54175cefSRoy Marples continue;
1950*54175cefSRoy Marples elapsed = (uint32_t)eloop_timespec_diff(&now,
1951*54175cefSRoy Marples &rinfo->acquired, NULL);
1952*54175cefSRoy Marples if (elapsed >= rinfo->lifetime || rap->doexpire) {
1953*54175cefSRoy Marples logwarnx("%s: expired route %s",
1954*54175cefSRoy Marples rap->iface->name, rinfo->sprefix);
1955*54175cefSRoy Marples TAILQ_REMOVE(&rap->rinfos, rinfo, next);
1956*54175cefSRoy Marples }
1957*54175cefSRoy Marples }
1958*54175cefSRoy Marples
19598d36e1dfSRoy Marples /* Work out expiry for ND options */
19606e63cc1fSRoy Marples elapsed = (uint32_t)eloop_timespec_diff(&now,
19616e63cc1fSRoy Marples &rap->acquired, NULL);
19628d36e1dfSRoy Marples len = rap->data_len - sizeof(struct nd_router_advert);
19638d36e1dfSRoy Marples for (p = rap->data + sizeof(struct nd_router_advert);
19648d36e1dfSRoy Marples len >= sizeof(ndo);
19658d36e1dfSRoy Marples p += olen, len -= olen)
19668d36e1dfSRoy Marples {
19678d36e1dfSRoy Marples memcpy(&ndo, p, sizeof(ndo));
19688d36e1dfSRoy Marples olen = (size_t)(ndo.nd_opt_len * 8);
19698d36e1dfSRoy Marples if (olen > len) {
19708d36e1dfSRoy Marples errno = EINVAL;
19718d36e1dfSRoy Marples break;
19728d36e1dfSRoy Marples }
19737827cba2SAaron LI
19748d36e1dfSRoy Marples if (has_option_mask(rap->iface->options->nomasknd,
19758d36e1dfSRoy Marples ndo.nd_opt_type))
19768d36e1dfSRoy Marples continue;
19778d36e1dfSRoy Marples
19788d36e1dfSRoy Marples switch (ndo.nd_opt_type) {
19798d36e1dfSRoy Marples /* Prefix info is already checked in the above loop. */
19808d36e1dfSRoy Marples #if 0
19818d36e1dfSRoy Marples case ND_OPT_PREFIX_INFORMATION:
19828d36e1dfSRoy Marples if (len < sizeof(pi))
19838d36e1dfSRoy Marples break;
19848d36e1dfSRoy Marples memcpy(&pi, p, sizeof(pi));
19858d36e1dfSRoy Marples ltime = pi.nd_opt_pi_valid_time;
19868d36e1dfSRoy Marples break;
19878d36e1dfSRoy Marples #endif
19888d36e1dfSRoy Marples case ND_OPT_DNSSL:
19898d36e1dfSRoy Marples if (len < sizeof(dnssl))
19908d36e1dfSRoy Marples continue;
19918d36e1dfSRoy Marples memcpy(&dnssl, p, sizeof(dnssl));
19928d36e1dfSRoy Marples ltime = dnssl.nd_opt_dnssl_lifetime;
19938d36e1dfSRoy Marples break;
19948d36e1dfSRoy Marples case ND_OPT_RDNSS:
19958d36e1dfSRoy Marples if (len < sizeof(rdnss))
19968d36e1dfSRoy Marples continue;
19978d36e1dfSRoy Marples memcpy(&rdnss, p, sizeof(rdnss));
19988d36e1dfSRoy Marples ltime = rdnss.nd_opt_rdnss_lifetime;
19998d36e1dfSRoy Marples break;
20008d36e1dfSRoy Marples default:
20018d36e1dfSRoy Marples continue;
20028d36e1dfSRoy Marples }
20038d36e1dfSRoy Marples
20048d36e1dfSRoy Marples if (ltime == 0)
20058d36e1dfSRoy Marples continue;
20066e63cc1fSRoy Marples if (rap->doexpire) {
20076e63cc1fSRoy Marples expired = true;
20086e63cc1fSRoy Marples continue;
20096e63cc1fSRoy Marples }
20108d36e1dfSRoy Marples if (ltime == ND6_INFINITE_LIFETIME) {
20118d36e1dfSRoy Marples valid = true;
20128d36e1dfSRoy Marples continue;
20138d36e1dfSRoy Marples }
20148d36e1dfSRoy Marples
20156e63cc1fSRoy Marples ltime = ntohl(ltime);
2016d4fb1e02SRoy Marples if (elapsed >= ltime) {
20178d36e1dfSRoy Marples expired = true;
20188d36e1dfSRoy Marples continue;
20198d36e1dfSRoy Marples }
20208d36e1dfSRoy Marples
20218d36e1dfSRoy Marples valid = true;
20226e63cc1fSRoy Marples ltime -= elapsed;
20236e63cc1fSRoy Marples if (next == 0 || ltime < next)
20246e63cc1fSRoy Marples next = ltime;
20258d36e1dfSRoy Marples }
20268d36e1dfSRoy Marples
20278d36e1dfSRoy Marples if (valid)
20288d36e1dfSRoy Marples continue;
20298d36e1dfSRoy Marples
20308d36e1dfSRoy Marples /* Router has expired. Let's not keep a lot of them. */
20318d36e1dfSRoy Marples rap->expired = true;
20328d36e1dfSRoy Marples if (++nexpired > EXPIRED_MAX)
20337827cba2SAaron LI ipv6nd_free_ra(rap);
20347827cba2SAaron LI }
20357827cba2SAaron LI
20366e63cc1fSRoy Marples if (next != 0)
20376e63cc1fSRoy Marples eloop_timeout_add_sec(ifp->ctx->eloop,
20386e63cc1fSRoy Marples next, ipv6nd_expirera, ifp);
20397827cba2SAaron LI if (expired) {
20406e63cc1fSRoy Marples logwarnx("%s: part of a Router Advertisement expired",
20416e63cc1fSRoy Marples ifp->name);
2042d4fb1e02SRoy Marples ipv6nd_sortrouters(ifp->ctx);
2043d4fb1e02SRoy Marples ipv6nd_applyra(ifp);
20447827cba2SAaron LI rt_build(ifp->ctx, AF_INET6);
20457827cba2SAaron LI script_runreason(ifp, "ROUTERADVERT");
20467827cba2SAaron LI }
20477827cba2SAaron LI }
20487827cba2SAaron LI
20497827cba2SAaron LI void
ipv6nd_drop(struct interface * ifp)20507827cba2SAaron LI ipv6nd_drop(struct interface *ifp)
20517827cba2SAaron LI {
20527827cba2SAaron LI struct ra *rap, *ran;
20538d36e1dfSRoy Marples bool expired = false;
20547827cba2SAaron LI
20557827cba2SAaron LI if (ifp->ctx->ra_routers == NULL)
20567827cba2SAaron LI return;
20577827cba2SAaron LI
20587827cba2SAaron LI eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
20597827cba2SAaron LI TAILQ_FOREACH_SAFE(rap, ifp->ctx->ra_routers, next, ran) {
20607827cba2SAaron LI if (rap->iface == ifp) {
20618d36e1dfSRoy Marples rap->expired = expired = true;
20627827cba2SAaron LI ipv6nd_drop_ra(rap);
20637827cba2SAaron LI }
20647827cba2SAaron LI }
20657827cba2SAaron LI if (expired) {
2066d4fb1e02SRoy Marples ipv6nd_applyra(ifp);
20677827cba2SAaron LI rt_build(ifp->ctx, AF_INET6);
20687827cba2SAaron LI if ((ifp->options->options & DHCPCD_NODROP) != DHCPCD_NODROP)
20697827cba2SAaron LI script_runreason(ifp, "ROUTERADVERT");
20707827cba2SAaron LI }
20717827cba2SAaron LI }
20727827cba2SAaron LI
20736e63cc1fSRoy Marples void
ipv6nd_recvmsg(struct dhcpcd_ctx * ctx,struct msghdr * msg)2074b9ccd228SRoy Marples ipv6nd_recvmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg)
2075b9ccd228SRoy Marples {
2076b9ccd228SRoy Marples struct sockaddr_in6 *from = (struct sockaddr_in6 *)msg->msg_name;
2077b9ccd228SRoy Marples char sfrom[INET6_ADDRSTRLEN];
2078b9ccd228SRoy Marples int hoplimit = 0;
2079b9ccd228SRoy Marples struct icmp6_hdr *icp;
2080b9ccd228SRoy Marples struct interface *ifp;
2081b9ccd228SRoy Marples size_t len = msg->msg_iov[0].iov_len;
2082b9ccd228SRoy Marples
2083b9ccd228SRoy Marples inet_ntop(AF_INET6, &from->sin6_addr, sfrom, sizeof(sfrom));
2084b9ccd228SRoy Marples if ((size_t)len < sizeof(struct icmp6_hdr)) {
2085b9ccd228SRoy Marples logerrx("IPv6 ICMP packet too short from %s", sfrom);
2086b9ccd228SRoy Marples return;
2087b9ccd228SRoy Marples }
2088b9ccd228SRoy Marples
2089b9ccd228SRoy Marples ifp = if_findifpfromcmsg(ctx, msg, &hoplimit);
2090b9ccd228SRoy Marples if (ifp == NULL) {
2091b9ccd228SRoy Marples logerr(__func__);
2092b9ccd228SRoy Marples return;
2093b9ccd228SRoy Marples }
2094b9ccd228SRoy Marples
2095b9ccd228SRoy Marples /* Don't do anything if the user hasn't configured it. */
2096b9ccd228SRoy Marples if (ifp->active != IF_ACTIVE_USER ||
2097b9ccd228SRoy Marples !(ifp->options->options & DHCPCD_IPV6))
2098b9ccd228SRoy Marples return;
2099b9ccd228SRoy Marples
2100b9ccd228SRoy Marples icp = (struct icmp6_hdr *)msg->msg_iov[0].iov_base;
2101b9ccd228SRoy Marples if (icp->icmp6_code == 0) {
2102b9ccd228SRoy Marples switch(icp->icmp6_type) {
2103b9ccd228SRoy Marples case ND_ROUTER_ADVERT:
2104b9ccd228SRoy Marples ipv6nd_handlera(ctx, from, sfrom,
2105b9ccd228SRoy Marples ifp, icp, (size_t)len, hoplimit);
2106b9ccd228SRoy Marples return;
2107b9ccd228SRoy Marples }
2108b9ccd228SRoy Marples }
2109b9ccd228SRoy Marples
2110b9ccd228SRoy Marples logerrx("invalid IPv6 type %d or code %d from %s",
2111b9ccd228SRoy Marples icp->icmp6_type, icp->icmp6_code, sfrom);
2112b9ccd228SRoy Marples }
2113b9ccd228SRoy Marples
2114b9ccd228SRoy Marples static void
ipv6nd_handledata(void * arg,unsigned short events)211580aa9461SRoy Marples ipv6nd_handledata(void *arg, unsigned short events)
21167827cba2SAaron LI {
21177827cba2SAaron LI struct dhcpcd_ctx *ctx;
2118b9ccd228SRoy Marples int fd;
21198d36e1dfSRoy Marples struct sockaddr_in6 from;
2120d4fb1e02SRoy Marples union {
2121d4fb1e02SRoy Marples struct icmp6_hdr hdr;
2122d4fb1e02SRoy Marples uint8_t buf[64 * 1024]; /* Maximum ICMPv6 size */
2123d4fb1e02SRoy Marples } iovbuf;
21248d36e1dfSRoy Marples struct iovec iov = {
2125d4fb1e02SRoy Marples .iov_base = iovbuf.buf, .iov_len = sizeof(iovbuf.buf),
21268d36e1dfSRoy Marples };
21277a0236bfSRoy Marples union {
21287a0236bfSRoy Marples struct cmsghdr hdr;
21297a0236bfSRoy Marples uint8_t buf[CMSG_SPACE(sizeof(struct in6_pktinfo)) +
21307a0236bfSRoy Marples CMSG_SPACE(sizeof(int))];
21317a0236bfSRoy Marples } cmsgbuf = { .buf = { 0 } };
21328d36e1dfSRoy Marples struct msghdr msg = {
21338d36e1dfSRoy Marples .msg_name = &from, .msg_namelen = sizeof(from),
21348d36e1dfSRoy Marples .msg_iov = &iov, .msg_iovlen = 1,
21357a0236bfSRoy Marples .msg_control = cmsgbuf.buf, .msg_controllen = sizeof(cmsgbuf.buf),
21368d36e1dfSRoy Marples };
21377827cba2SAaron LI ssize_t len;
21387827cba2SAaron LI
21398d36e1dfSRoy Marples #ifdef __sun
2140b9ccd228SRoy Marples struct interface *ifp;
21418d36e1dfSRoy Marples struct rs_state *state;
21428d36e1dfSRoy Marples
21438d36e1dfSRoy Marples ifp = arg;
21448d36e1dfSRoy Marples state = RS_STATE(ifp);
21458d36e1dfSRoy Marples ctx = ifp->ctx;
2146b9ccd228SRoy Marples fd = state->nd_fd;
21478d36e1dfSRoy Marples #else
21487827cba2SAaron LI ctx = arg;
2149b9ccd228SRoy Marples fd = ctx->nd_fd;
21508d36e1dfSRoy Marples #endif
215180aa9461SRoy Marples
215280aa9461SRoy Marples if (events != ELE_READ)
215380aa9461SRoy Marples logerrx("%s: unexpected event 0x%04x", __func__, events);
215480aa9461SRoy Marples
2155b9ccd228SRoy Marples len = recvmsg(fd, &msg, 0);
21567827cba2SAaron LI if (len == -1) {
21577827cba2SAaron LI logerr(__func__);
21587827cba2SAaron LI return;
21597827cba2SAaron LI }
21607827cba2SAaron LI
2161b9ccd228SRoy Marples iov.iov_len = (size_t)len;
2162b9ccd228SRoy Marples ipv6nd_recvmsg(ctx, &msg);
21637827cba2SAaron LI }
21647827cba2SAaron LI
21657827cba2SAaron LI static void
ipv6nd_startrs1(void * arg)21667827cba2SAaron LI ipv6nd_startrs1(void *arg)
21677827cba2SAaron LI {
21687827cba2SAaron LI struct interface *ifp = arg;
21697827cba2SAaron LI struct rs_state *state;
21707827cba2SAaron LI
21717827cba2SAaron LI loginfox("%s: soliciting an IPv6 router", ifp->name);
21727827cba2SAaron LI state = RS_STATE(ifp);
21737827cba2SAaron LI if (state == NULL) {
21747827cba2SAaron LI ifp->if_data[IF_DATA_IPV6ND] = calloc(1, sizeof(*state));
21757827cba2SAaron LI state = RS_STATE(ifp);
21767827cba2SAaron LI if (state == NULL) {
21777827cba2SAaron LI logerr(__func__);
21787827cba2SAaron LI return;
21797827cba2SAaron LI }
21808d36e1dfSRoy Marples #ifdef __sun
21818d36e1dfSRoy Marples state->nd_fd = -1;
21828d36e1dfSRoy Marples #endif
21837827cba2SAaron LI }
21847827cba2SAaron LI
21857827cba2SAaron LI /* Always make a new probe as the underlying hardware
21867827cba2SAaron LI * address could have changed. */
21877827cba2SAaron LI ipv6nd_makersprobe(ifp);
21887827cba2SAaron LI if (state->rs == NULL) {
21897827cba2SAaron LI logerr(__func__);
21907827cba2SAaron LI return;
21917827cba2SAaron LI }
21927827cba2SAaron LI
21938d36e1dfSRoy Marples state->retrans = RETRANS_TIMER;
21947827cba2SAaron LI state->rsprobes = 0;
21957827cba2SAaron LI ipv6nd_sendrsprobe(ifp);
21967827cba2SAaron LI }
21977827cba2SAaron LI
21987827cba2SAaron LI void
ipv6nd_startrs(struct interface * ifp)21997827cba2SAaron LI ipv6nd_startrs(struct interface *ifp)
22007827cba2SAaron LI {
22016e63cc1fSRoy Marples unsigned int delay;
22027827cba2SAaron LI
22037827cba2SAaron LI eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
22047827cba2SAaron LI if (!(ifp->options->options & DHCPCD_INITIAL_DELAY)) {
22057827cba2SAaron LI ipv6nd_startrs1(ifp);
22067827cba2SAaron LI return;
22077827cba2SAaron LI }
22087827cba2SAaron LI
22096e63cc1fSRoy Marples delay = arc4random_uniform(MAX_RTR_SOLICITATION_DELAY * MSEC_PER_SEC);
22107827cba2SAaron LI logdebugx("%s: delaying IPv6 router solicitation for %0.1f seconds",
22116e63cc1fSRoy Marples ifp->name, (float)delay / MSEC_PER_SEC);
22126e63cc1fSRoy Marples eloop_timeout_add_msec(ifp->ctx->eloop, delay, ipv6nd_startrs1, ifp);
22137827cba2SAaron LI return;
22147827cba2SAaron LI }
2215*54175cefSRoy Marples
routeinfo_findalloc(struct ra * rap,const struct in6_addr * prefix,uint8_t prefix_len)2216*54175cefSRoy Marples static struct routeinfo *routeinfo_findalloc(struct ra *rap, const struct in6_addr *prefix, uint8_t prefix_len)
2217*54175cefSRoy Marples {
2218*54175cefSRoy Marples struct routeinfo *ri;
2219*54175cefSRoy Marples char buf[INET6_ADDRSTRLEN];
2220*54175cefSRoy Marples const char *p;
2221*54175cefSRoy Marples
2222*54175cefSRoy Marples TAILQ_FOREACH(ri, &rap->rinfos, next) {
2223*54175cefSRoy Marples if (ri->prefix_len == prefix_len &&
2224*54175cefSRoy Marples IN6_ARE_ADDR_EQUAL(&ri->prefix, prefix))
2225*54175cefSRoy Marples return ri;
2226*54175cefSRoy Marples }
2227*54175cefSRoy Marples
2228*54175cefSRoy Marples ri = malloc(sizeof(struct routeinfo));
2229*54175cefSRoy Marples if (ri == NULL)
2230*54175cefSRoy Marples return NULL;
2231*54175cefSRoy Marples
2232*54175cefSRoy Marples memcpy(&ri->prefix, prefix, sizeof(ri->prefix));
2233*54175cefSRoy Marples ri->prefix_len = prefix_len;
2234*54175cefSRoy Marples p = inet_ntop(AF_INET6, prefix, buf, sizeof(buf));
2235*54175cefSRoy Marples if (p)
2236*54175cefSRoy Marples snprintf(ri->sprefix,
2237*54175cefSRoy Marples sizeof(ri->sprefix),
2238*54175cefSRoy Marples "%s/%d",
2239*54175cefSRoy Marples p, prefix_len);
2240*54175cefSRoy Marples else
2241*54175cefSRoy Marples ri->sprefix[0] = '\0';
2242*54175cefSRoy Marples TAILQ_INSERT_TAIL(&rap->rinfos, ri, next);
2243*54175cefSRoy Marples return ri;
2244*54175cefSRoy Marples }
2245*54175cefSRoy Marples
routeinfohead_free(struct routeinfohead * head)2246*54175cefSRoy Marples static void routeinfohead_free(struct routeinfohead *head)
2247*54175cefSRoy Marples {
2248*54175cefSRoy Marples struct routeinfo *ri;
2249*54175cefSRoy Marples
2250*54175cefSRoy Marples while ((ri = TAILQ_FIRST(head))) {
2251*54175cefSRoy Marples TAILQ_REMOVE(head, ri, next);
2252*54175cefSRoy Marples free(ri);
2253*54175cefSRoy Marples }
2254*54175cefSRoy Marples }
2255