]>
Commit | Line | Data |
---|---|---|
8cc47ba2 | 1 | /* |
91cd7324 | 2 | * dhcpcd - DHCP client daemon |
fad320b9 | 3 | * Copyright (c) 2006-2014 Roy Marples <roy@marples.name> |
91cd7324 RM |
4 | * All rights reserved |
5 | ||
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions | |
8 | * are met: | |
9 | * 1. Redistributions of source code must retain the above copyright | |
10 | * notice, this list of conditions and the following disclaimer. | |
11 | * 2. Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * | |
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
25 | * SUCH DAMAGE. | |
26 | */ | |
27 | ||
eebe9a18 | 28 | #include <sys/ioctl.h> |
91cd7324 RM |
29 | #include <sys/param.h> |
30 | #include <sys/socket.h> | |
31 | #include <net/if.h> | |
32 | #include <netinet/in.h> | |
33 | #include <netinet/ip6.h> | |
34 | #include <netinet/icmp6.h> | |
35 | ||
d7555c12 RM |
36 | #ifdef __linux__ |
37 | # define _LINUX_IN6_H | |
38 | # include <linux/ipv6.h> | |
39 | #endif | |
40 | ||
91cd7324 | 41 | #include <errno.h> |
e8c8e9b9 | 42 | #include <fcntl.h> |
91cd7324 RM |
43 | #include <stddef.h> |
44 | #include <stdlib.h> | |
45 | #include <string.h> | |
46 | #include <syslog.h> | |
fbbb0875 | 47 | #include <unistd.h> |
91cd7324 | 48 | |
4eb7b489 RM |
49 | /* Currently, no known kernel allows us to send from the unspecified address |
50 | * which is required for DAD to work. This isn't that much of a problem as | |
51 | * the kernel will do DAD for us correctly, however we don't know the exact | |
52 | * randomness the kernel applies to the timeouts. So we just follow the same | |
53 | * logic and have a little faith. | |
54 | * This define is purely for completeness */ | |
55 | // #define IPV6_SEND_DAD | |
56 | ||
e6ca60ee | 57 | #define ELOOP_QUEUE 2 |
91cd7324 | 58 | #include "common.h" |
91cd7324 | 59 | #include "dhcpcd.h" |
d7555c12 | 60 | #include "dhcp6.h" |
91cd7324 | 61 | #include "eloop.h" |
eebe9a18 | 62 | #include "ipv6.h" |
e82129a4 | 63 | #include "ipv6nd.h" |
294eff4d | 64 | #include "script.h" |
91cd7324 | 65 | |
0acfc5e3 RM |
66 | #if defined(LISTEN_DAD) && defined(INET6) |
67 | # warning kernel does not report DAD results to userland | |
68 | # warning listening to duplicated addresses on the wire | |
69 | #endif | |
70 | ||
d7555c12 RM |
71 | /* Debugging Router Solicitations is a lot of spam, so disable it */ |
72 | //#define DEBUG_RS | |
73 | ||
91cd7324 RM |
74 | #define RTR_SOLICITATION_INTERVAL 4 /* seconds */ |
75 | #define MAX_RTR_SOLICITATIONS 3 /* times */ | |
76 | ||
77 | #ifndef ND_OPT_RDNSS | |
78 | #define ND_OPT_RDNSS 25 | |
79 | struct nd_opt_rdnss { /* RDNSS option RFC 6106 */ | |
80 | uint8_t nd_opt_rdnss_type; | |
81 | uint8_t nd_opt_rdnss_len; | |
82 | uint16_t nd_opt_rdnss_reserved; | |
83 | uint32_t nd_opt_rdnss_lifetime; | |
84 | /* followed by list of IP prefixes */ | |
3491ea4d | 85 | } __packed; |
91cd7324 RM |
86 | #endif |
87 | ||
88 | #ifndef ND_OPT_DNSSL | |
89 | #define ND_OPT_DNSSL 31 | |
90 | struct nd_opt_dnssl { /* DNSSL option RFC 6106 */ | |
91 | uint8_t nd_opt_dnssl_type; | |
92 | uint8_t nd_opt_dnssl_len; | |
93 | uint16_t nd_opt_dnssl_reserved; | |
94 | uint32_t nd_opt_dnssl_lifetime; | |
95 | /* followed by list of DNS servers */ | |
3491ea4d | 96 | } __packed; |
91cd7324 RM |
97 | #endif |
98 | ||
eebe9a18 RM |
99 | /* Minimal IPv6 MTU */ |
100 | #ifndef IPV6_MMTU | |
101 | #define IPV6_MMTU 1280 | |
102 | #endif | |
103 | ||
104 | #ifndef ND_RA_FLAG_RTPREF_HIGH | |
105 | #define ND_RA_FLAG_RTPREF_MASK 0x18 | |
106 | #define ND_RA_FLAG_RTPREF_HIGH 0x08 | |
107 | #define ND_RA_FLAG_RTPREF_MEDIUM 0x00 | |
108 | #define ND_RA_FLAG_RTPREF_LOW 0x18 | |
109 | #define ND_RA_FLAG_RTPREF_RSV 0x10 | |
110 | #endif | |
111 | ||
112 | /* RTPREF_MEDIUM has to be 0! */ | |
113 | #define RTPREF_HIGH 1 | |
114 | #define RTPREF_MEDIUM 0 | |
115 | #define RTPREF_LOW (-1) | |
116 | #define RTPREF_RESERVED (-2) | |
117 | #define RTPREF_INVALID (-3) /* internal */ | |
118 | ||
e82129a4 RM |
119 | #define MIN_RANDOM_FACTOR 500 /* millisecs */ |
120 | #define MAX_RANDOM_FACTOR 1500 /* millisecs */ | |
121 | #define MIN_RANDOM_FACTOR_U MIN_RANDOM_FACTOR * 1000 /* usecs */ | |
122 | #define MAX_RANDOM_FACTOR_U MAX_RANDOM_FACTOR * 1000 /* usecs */ | |
123 | ||
124 | #if BYTE_ORDER == BIG_ENDIAN | |
125 | #define IPV6_ADDR_INT32_ONE 1 | |
126 | #define IPV6_ADDR_INT16_MLL 0xff02 | |
127 | #elif BYTE_ORDER == LITTLE_ENDIAN | |
128 | #define IPV6_ADDR_INT32_ONE 0x01000000 | |
129 | #define IPV6_ADDR_INT16_MLL 0x02ff | |
130 | #endif | |
131 | ||
132 | /* Debugging Neighbor Solicitations is a lot of spam, so disable it */ | |
133 | //#define DEBUG_NS | |
134 | // | |
135 | ||
7cece083 RM |
136 | static void ipv6nd_handledata(void *arg); |
137 | ||
65e5b9f9 RM |
138 | /* |
139 | * Android ships buggy ICMP6 filter headers. | |
140 | * Supply our own until they fix their shit. | |
141 | * References: | |
142 | * https://android-review.googlesource.com/#/c/58438/ | |
143 | * http://code.google.com/p/android/issues/original?id=32621&seq=24 | |
144 | */ | |
145 | #ifdef __ANDROID__ | |
146 | #undef ICMP6_FILTER_WILLPASS | |
147 | #undef ICMP6_FILTER_WILLBLOCK | |
148 | #undef ICMP6_FILTER_SETPASS | |
149 | #undef ICMP6_FILTER_SETBLOCK | |
150 | #undef ICMP6_FILTER_SETPASSALL | |
151 | #undef ICMP6_FILTER_SETBLOCKALL | |
152 | #define ICMP6_FILTER_WILLPASS(type, filterp) \ | |
153 | ((((filterp)->icmp6_filt[(type) >> 5]) & (1 << ((type) & 31))) == 0) | |
154 | #define ICMP6_FILTER_WILLBLOCK(type, filterp) \ | |
155 | ((((filterp)->icmp6_filt[(type) >> 5]) & (1 << ((type) & 31))) != 0) | |
156 | #define ICMP6_FILTER_SETPASS(type, filterp) \ | |
157 | ((((filterp)->icmp6_filt[(type) >> 5]) &= ~(1 << ((type) & 31)))) | |
158 | #define ICMP6_FILTER_SETBLOCK(type, filterp) \ | |
159 | ((((filterp)->icmp6_filt[(type) >> 5]) |= (1 << ((type) & 31)))) | |
160 | #define ICMP6_FILTER_SETPASSALL(filterp) \ | |
161 | memset(filterp, 0, sizeof(struct icmp6_filter)); | |
162 | #define ICMP6_FILTER_SETBLOCKALL(filterp) \ | |
163 | memset(filterp, 0xff, sizeof(struct icmp6_filter)); | |
164 | #endif | |
165 | ||
aae24feb | 166 | static int |
4eb7b489 | 167 | ipv6nd_open(struct dhcpcd_ctx *dctx) |
91cd7324 | 168 | { |
4eb7b489 | 169 | struct ipv6_ctx *ctx; |
91cd7324 | 170 | int on; |
4eb7b489 | 171 | struct icmp6_filter filt; |
e82129a4 RM |
172 | #ifdef IPV6_SEND_DAD |
173 | union { | |
174 | struct sockaddr sa; | |
175 | struct sockaddr_in6 sin; | |
176 | } su; | |
177 | #endif | |
91cd7324 | 178 | |
4eb7b489 RM |
179 | ctx = dctx->ipv6; |
180 | if (ctx->nd_fd != -1) | |
181 | goto unspec; | |
e8c8e9b9 | 182 | #ifdef SOCK_CLOEXEC |
4eb7b489 | 183 | ctx->nd_fd = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, |
cc050202 | 184 | IPPROTO_ICMPV6); |
4eb7b489 | 185 | if (ctx->nd_fd == -1) |
fbbb0875 | 186 | return -1; |
e8c8e9b9 RM |
187 | #else |
188 | if ((ctx->nd_fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1) | |
189 | return -1; | |
190 | if ((on = fcntl(ctx->nd_fd, F_GETFD, 0)) == -1 || | |
191 | fcntl(ctx->nd_fd, F_SETFD, on | FD_CLOEXEC) == -1) | |
192 | { | |
193 | close(ctx->nd_fd); | |
194 | ctx->nd_fd = -1; | |
195 | return -1; | |
196 | } | |
197 | if ((on = fcntl(ctx->nd_fd, F_GETFL, 0)) == -1 || | |
198 | fcntl(ctx->nd_fd, F_SETFL, on | O_NONBLOCK) == -1) | |
199 | { | |
200 | close(ctx->nd_fd); | |
201 | ctx->nd_fd = -1; | |
202 | return -1; | |
203 | } | |
204 | #endif | |
fbbb0875 | 205 | |
91cd7324 | 206 | on = 1; |
4eb7b489 RM |
207 | if (setsockopt(ctx->nd_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, |
208 | &on, sizeof(on)) == -1) | |
fbbb0875 | 209 | goto eexit; |
91cd7324 RM |
210 | |
211 | on = 1; | |
4eb7b489 RM |
212 | if (setsockopt(ctx->nd_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, |
213 | &on, sizeof(on)) == -1) | |
fbbb0875 | 214 | goto eexit; |
91cd7324 RM |
215 | |
216 | ICMP6_FILTER_SETBLOCKALL(&filt); | |
4eb7b489 | 217 | ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filt); |
91cd7324 | 218 | ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); |
4eb7b489 RM |
219 | if (setsockopt(ctx->nd_fd, IPPROTO_ICMPV6, ICMP6_FILTER, |
220 | &filt, sizeof(filt)) == -1) | |
fbbb0875 | 221 | goto eexit; |
fbbb0875 | 222 | |
4eb7b489 | 223 | eloop_event_add(dctx->eloop, ctx->nd_fd, ipv6nd_handledata, dctx); |
91cd7324 | 224 | |
4eb7b489 | 225 | unspec: |
e82129a4 | 226 | #ifdef IPV6_SEND_DAD |
4eb7b489 RM |
227 | if (ctx->unspec_fd != -1) |
228 | return ctx->nd_fd; | |
e82129a4 | 229 | |
4eb7b489 | 230 | ICMP6_FILTER_SETBLOCKALL(&filt); |
e82129a4 | 231 | |
e82129a4 | 232 | /* We send DAD requests from the unspecified address. */ |
e8c8e9b9 RM |
233 | #ifdef SOCK_CLOEXEC |
234 | ctx->unspec_fd = socket(AF_INET6, | |
235 | SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, | |
236 | IPPROTO_ICMPV6); | |
4eb7b489 | 237 | if (ctx->unspec_fd == -1) |
e8c8e9b9 RM |
238 | return -1; |
239 | #else | |
240 | if ((ctx->unspec_fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1) | |
241 | return -1; | |
242 | if ((on = fcntl(ctx->unspec_fd, F_GETFD, 0)) == -1 || | |
243 | fcntl(ctx->unspec_fd, F_SETFD, on | FD_CLOEXEC) == -1) | |
244 | { | |
245 | close(ctx->unspec_fd); | |
246 | ctx->unspec_fd = -1; | |
247 | return -1; | |
248 | } | |
249 | if ((on = fcntl(ctx->unspec_fd, F_GETFL, 0)) == -1 || | |
250 | fcntl(ctx->unspec_fd, F_SETFL, on | O_NONBLOCK) == -1) | |
251 | { | |
252 | close(ctx->unspec_fd); | |
253 | ctx->unspec_fd = -1; | |
254 | return -1; | |
255 | } | |
256 | #endif | |
257 | ||
4eb7b489 RM |
258 | if (setsockopt(ctx->unspec_fd, IPPROTO_ICMPV6, ICMP6_FILTER, |
259 | &filt, sizeof(filt)) == -1) | |
e82129a4 RM |
260 | goto eexit; |
261 | memset(&su, 0, sizeof(su)); | |
262 | su.sin.sin6_family = AF_INET6; | |
263 | #ifdef SIN6_LEN | |
264 | su.sin.sin6_len = sizeof(su.sin); | |
265 | #endif | |
4eb7b489 | 266 | if (bind(ctx->unspec_fd, &su.sa, sizeof(su.sin)) == -1) |
e82129a4 RM |
267 | goto eexit; |
268 | #endif | |
269 | ||
e82129a4 | 270 | #ifdef LISTEN_DAD |
0d033d17 RM |
271 | if (!ctx->dad_warned) { |
272 | syslog(LOG_WARNING, | |
273 | "kernel does not report DAD results to userland"); | |
274 | syslog(LOG_WARNING, | |
275 | "warning listening to duplicated addresses on the wire"); | |
276 | ctx->dad_warned = 1; | |
277 | } | |
e82129a4 RM |
278 | #endif |
279 | ||
4eb7b489 | 280 | return ctx->nd_fd; |
e82129a4 RM |
281 | |
282 | eexit: | |
4eb7b489 | 283 | if (ctx->nd_fd != -1) { |
4eb7b489 | 284 | eloop_event_delete(dctx->eloop, ctx->nd_fd); |
e9882fb0 | 285 | close(ctx->nd_fd); |
4eb7b489 RM |
286 | ctx->nd_fd = -1; |
287 | } | |
5b0fe196 | 288 | #ifdef IPV6_SEND_DAD |
4eb7b489 RM |
289 | if (ctx->unpsec_fd != -1) { |
290 | close(ctx->unspec_fd); | |
291 | ctx->unspec_fd = -1; | |
292 | } | |
e82129a4 RM |
293 | #endif |
294 | return -1; | |
295 | } | |
296 | ||
297 | static int | |
298 | ipv6nd_makersprobe(struct interface *ifp) | |
91cd7324 | 299 | { |
ca15a0aa | 300 | struct rs_state *state; |
91cd7324 RM |
301 | struct nd_router_solicit *rs; |
302 | struct nd_opt_hdr *nd; | |
303 | ||
ca15a0aa RM |
304 | state = RS_STATE(ifp); |
305 | free(state->rs); | |
306 | state->rslen = sizeof(*rs) + ROUNDUP8(ifp->hwlen + 2); | |
10e17e3f | 307 | state->rs = calloc(1, state->rslen); |
ca15a0aa | 308 | if (state->rs == NULL) |
91cd7324 | 309 | return -1; |
ca15a0aa | 310 | rs = (struct nd_router_solicit *)(void *)state->rs; |
91cd7324 RM |
311 | rs->nd_rs_type = ND_ROUTER_SOLICIT; |
312 | rs->nd_rs_code = 0; | |
313 | rs->nd_rs_cksum = 0; | |
314 | rs->nd_rs_reserved = 0; | |
ca15a0aa | 315 | nd = (struct nd_opt_hdr *)(state->rs + sizeof(*rs)); |
91cd7324 RM |
316 | nd->nd_opt_type = ND_OPT_SOURCE_LINKADDR; |
317 | nd->nd_opt_len = (ROUNDUP8(ifp->hwlen + 2)) >> 3; | |
318 | memcpy(nd + 1, ifp->hwaddr, ifp->hwlen); | |
319 | return 0; | |
320 | } | |
673e81e5 | 321 | |
91cd7324 | 322 | static void |
e82129a4 | 323 | ipv6nd_sendrsprobe(void *arg) |
91cd7324 RM |
324 | { |
325 | struct interface *ifp = arg; | |
4eb7b489 | 326 | struct ipv6_ctx *ctx; |
ca15a0aa | 327 | struct rs_state *state; |
91cd7324 RM |
328 | struct sockaddr_in6 dst; |
329 | struct cmsghdr *cm; | |
330 | struct in6_pktinfo pi; | |
331 | int hoplimit = HOPLIMIT; | |
332 | ||
0e906716 | 333 | if (ipv6_linklocal(ifp) == NULL) { |
5331b839 | 334 | syslog(LOG_DEBUG, |
5301406a | 335 | "%s: delaying Router Solicitation for LL address", |
5331b839 | 336 | ifp->name); |
e82129a4 | 337 | ipv6_addlinklocalcallback(ifp, ipv6nd_sendrsprobe, ifp); |
5331b839 RM |
338 | return; |
339 | } | |
340 | ||
4eb7b489 RM |
341 | memset(&dst, 0, sizeof(dst)); |
342 | dst.sin6_family = AF_INET6; | |
343 | #ifdef SIN6_LEN | |
344 | dst.sin6_len = sizeof(dst); | |
345 | #endif | |
22f64b55 | 346 | dst.sin6_scope_id = ifp->index; |
4eb7b489 RM |
347 | if (inet_pton(AF_INET6, ALLROUTERS, &dst.sin6_addr.s6_addr) != 1) { |
348 | syslog(LOG_ERR, "%s: %m", __func__); | |
349 | return; | |
350 | } | |
91cd7324 | 351 | |
ca15a0aa | 352 | state = RS_STATE(ifp); |
4eb7b489 RM |
353 | ctx = ifp->ctx->ipv6; |
354 | ctx->sndhdr.msg_name = (caddr_t)&dst; | |
355 | ctx->sndhdr.msg_iov[0].iov_base = state->rs; | |
356 | ctx->sndhdr.msg_iov[0].iov_len = state->rslen; | |
91cd7324 RM |
357 | |
358 | /* Set the outbound interface */ | |
4eb7b489 | 359 | cm = CMSG_FIRSTHDR(&ctx->sndhdr); |
91cd7324 RM |
360 | cm->cmsg_level = IPPROTO_IPV6; |
361 | cm->cmsg_type = IPV6_PKTINFO; | |
362 | cm->cmsg_len = CMSG_LEN(sizeof(pi)); | |
363 | memset(&pi, 0, sizeof(pi)); | |
2c77247b | 364 | pi.ipi6_ifindex = ifp->index; |
91cd7324 RM |
365 | memcpy(CMSG_DATA(cm), &pi, sizeof(pi)); |
366 | ||
367 | /* Hop limit */ | |
4eb7b489 | 368 | cm = CMSG_NXTHDR(&ctx->sndhdr, cm); |
91cd7324 RM |
369 | cm->cmsg_level = IPPROTO_IPV6; |
370 | cm->cmsg_type = IPV6_HOPLIMIT; | |
371 | cm->cmsg_len = CMSG_LEN(sizeof(hoplimit)); | |
372 | memcpy(CMSG_DATA(cm), &hoplimit, sizeof(hoplimit)); | |
373 | ||
ad574a91 | 374 | syslog(LOG_DEBUG, "%s: sending Router Solicitation", ifp->name); |
4eb7b489 | 375 | if (sendmsg(ctx->nd_fd, &ctx->sndhdr, 0) == -1) { |
c5d2e393 | 376 | syslog(LOG_ERR, "%s: %s: sendmsg: %m", ifp->name, __func__); |
e82129a4 | 377 | ipv6nd_drop(ifp); |
7b9cd6f0 | 378 | ifp->options->options &= ~(DHCPCD_IPV6 | DHCPCD_IPV6RS); |
83e82504 RM |
379 | return; |
380 | } | |
91cd7324 | 381 | |
ca15a0aa | 382 | if (state->rsprobes++ < MAX_RTR_SOLICITATIONS) |
4eb7b489 RM |
383 | eloop_timeout_add_sec(ifp->ctx->eloop, |
384 | RTR_SOLICITATION_INTERVAL, ipv6nd_sendrsprobe, ifp); | |
91cd7324 | 385 | else |
ad574a91 | 386 | syslog(LOG_WARNING, "%s: no IPv6 Routers available", ifp->name); |
91cd7324 RM |
387 | } |
388 | ||
389 | static void | |
e82129a4 | 390 | ipv6nd_free_opts(struct ra *rap) |
91cd7324 | 391 | { |
eebe9a18 | 392 | struct ra_opt *rao; |
91cd7324 | 393 | |
eebe9a18 RM |
394 | while ((rao = TAILQ_FIRST(&rap->options))) { |
395 | TAILQ_REMOVE(&rap->options, rao, next); | |
396 | free(rao->option); | |
397 | free(rao); | |
398 | } | |
399 | } | |
91cd7324 | 400 | |
e54dee19 | 401 | int |
4eb7b489 | 402 | ipv6nd_addrexists(struct dhcpcd_ctx *ctx, const struct ipv6_addr *addr) |
376e8b80 RM |
403 | { |
404 | struct ra *rap; | |
405 | struct ipv6_addr *ap; | |
406 | ||
4eb7b489 | 407 | TAILQ_FOREACH(rap, ctx->ipv6->ra_routers, next) { |
376e8b80 | 408 | TAILQ_FOREACH(ap, &rap->addrs, next) { |
7013b073 RM |
409 | if (addr == NULL) { |
410 | if ((ap->flags & | |
411 | (IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED)) == | |
412 | (IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED)) | |
413 | return 1; | |
73e77470 | 414 | } else if (IN6_ARE_ADDR_EQUAL(&ap->addr, &addr->addr)) |
376e8b80 RM |
415 | return 1; |
416 | } | |
417 | } | |
418 | return 0; | |
419 | } | |
420 | ||
e82129a4 | 421 | void ipv6nd_freedrop_ra(struct ra *rap, int drop) |
eebe9a18 RM |
422 | { |
423 | ||
4eb7b489 RM |
424 | eloop_timeout_delete(rap->iface->ctx->eloop, NULL, rap->iface); |
425 | eloop_timeout_delete(rap->iface->ctx->eloop, NULL, rap); | |
5b428df2 | 426 | if (!drop) |
4eb7b489 | 427 | TAILQ_REMOVE(rap->iface->ctx->ipv6->ra_routers, rap, next); |
8fdedf59 | 428 | ipv6_freedrop_addrs(&rap->addrs, drop, NULL); |
e82129a4 | 429 | ipv6nd_free_opts(rap); |
eebe9a18 RM |
430 | free(rap->data); |
431 | free(rap->ns); | |
432 | free(rap); | |
433 | } | |
434 | ||
435 | ssize_t | |
e82129a4 | 436 | ipv6nd_free(struct interface *ifp) |
eebe9a18 | 437 | { |
ca15a0aa | 438 | struct rs_state *state; |
eebe9a18 | 439 | struct ra *rap, *ran; |
4eb7b489 | 440 | struct dhcpcd_ctx *ctx; |
eebe9a18 RM |
441 | ssize_t n; |
442 | ||
ca15a0aa | 443 | state = RS_STATE(ifp); |
a9d78def RM |
444 | if (state == NULL) |
445 | return 0; | |
446 | ||
447 | free(state->rs); | |
448 | free(state); | |
449 | ifp->if_data[IF_DATA_IPV6ND] = NULL; | |
eebe9a18 | 450 | n = 0; |
4eb7b489 | 451 | TAILQ_FOREACH_SAFE(rap, ifp->ctx->ipv6->ra_routers, next, ran) { |
eebe9a18 | 452 | if (rap->iface == ifp) { |
e82129a4 | 453 | ipv6nd_free_ra(rap); |
eebe9a18 | 454 | n++; |
91cd7324 | 455 | } |
eebe9a18 | 456 | } |
a9d78def RM |
457 | |
458 | /* If we don't have any more IPv6 enabled interfaces, | |
459 | * close the global socket and release resources */ | |
4eb7b489 RM |
460 | ctx = ifp->ctx; |
461 | TAILQ_FOREACH(ifp, ctx->ifaces, next) { | |
a9d78def RM |
462 | if (RS_STATE(ifp)) |
463 | break; | |
464 | } | |
465 | if (ifp == NULL) { | |
4eb7b489 | 466 | if (ctx->ipv6->nd_fd != -1) { |
4eb7b489 | 467 | eloop_event_delete(ctx->eloop, ctx->ipv6->nd_fd); |
e9882fb0 | 468 | close(ctx->ipv6->nd_fd); |
4eb7b489 | 469 | ctx->ipv6->nd_fd = -1; |
a9d78def | 470 | } |
a9d78def RM |
471 | } |
472 | ||
eebe9a18 RM |
473 | return n; |
474 | } | |
475 | ||
476 | static int | |
477 | rtpref(struct ra *rap) | |
478 | { | |
ca15a0aa | 479 | |
eebe9a18 RM |
480 | switch (rap->flags & ND_RA_FLAG_RTPREF_MASK) { |
481 | case ND_RA_FLAG_RTPREF_HIGH: | |
482 | return (RTPREF_HIGH); | |
483 | case ND_RA_FLAG_RTPREF_MEDIUM: | |
484 | case ND_RA_FLAG_RTPREF_RSV: | |
485 | return (RTPREF_MEDIUM); | |
486 | case ND_RA_FLAG_RTPREF_LOW: | |
487 | return (RTPREF_LOW); | |
488 | default: | |
489 | syslog(LOG_ERR, "rtpref: impossible RA flag %x", rap->flags); | |
490 | return (RTPREF_INVALID); | |
491 | } | |
492 | /* NOTREACHED */ | |
493 | } | |
494 | ||
495 | static void | |
4eb7b489 | 496 | add_router(struct ipv6_ctx *ctx, struct ra *router) |
eebe9a18 RM |
497 | { |
498 | struct ra *rap; | |
499 | ||
4eb7b489 | 500 | TAILQ_FOREACH(rap, ctx->ra_routers, next) { |
eebe9a18 RM |
501 | if (router->iface->metric < rap->iface->metric || |
502 | (router->iface->metric == rap->iface->metric && | |
503 | rtpref(router) > rtpref(rap))) | |
504 | { | |
505 | TAILQ_INSERT_BEFORE(rap, router, next); | |
506 | return; | |
91cd7324 RM |
507 | } |
508 | } | |
4eb7b489 | 509 | TAILQ_INSERT_TAIL(ctx->ra_routers, router, next); |
91cd7324 RM |
510 | } |
511 | ||
b5b066a5 | 512 | static int |
e82129a4 | 513 | ipv6nd_scriptrun(struct ra *rap) |
a8df1b28 | 514 | { |
e3e77f72 | 515 | int hasdns; |
d5690e93 | 516 | struct ipv6_addr *ap; |
a8df1b28 RM |
517 | const struct ra_opt *rao; |
518 | ||
519 | /* If all addresses have completed DAD run the script */ | |
a8df1b28 | 520 | TAILQ_FOREACH(ap, &rap->addrs, next) { |
a824f281 RM |
521 | if ((ap->flags & (IPV6_AF_ONLINK | IPV6_AF_AUTOCONF)) == |
522 | (IPV6_AF_ONLINK | IPV6_AF_AUTOCONF)) | |
523 | { | |
d5690e93 RM |
524 | if (!(ap->flags & IPV6_AF_DADCOMPLETED) && |
525 | ipv6_findaddr(ap->iface, &ap->addr)) | |
526 | ap->flags |= IPV6_AF_DADCOMPLETED; | |
527 | if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) { | |
528 | syslog(LOG_DEBUG, | |
529 | "%s: waiting for Router Advertisement" | |
530 | " DAD to complete", | |
531 | rap->iface->name); | |
b5b066a5 | 532 | return 0; |
d5690e93 | 533 | } |
d8194bcd | 534 | } |
a8df1b28 RM |
535 | } |
536 | ||
537 | /* If we don't require RDNSS then set hasdns = 1 so we fork */ | |
538 | if (!(rap->iface->options->options & DHCPCD_IPV6RA_REQRDNSS)) | |
539 | hasdns = 1; | |
540 | else { | |
541 | hasdns = 0; | |
542 | TAILQ_FOREACH(rao, &rap->options, next) { | |
543 | if (rao->type == ND_OPT_RDNSS && | |
544 | rao->option && | |
545 | timerisset(&rao->expire)) | |
546 | { | |
547 | hasdns = 1; | |
548 | break; | |
549 | } | |
550 | } | |
551 | } | |
552 | ||
553 | script_runreason(rap->iface, "ROUTERADVERT"); | |
554 | if (hasdns) | |
4eb7b489 | 555 | hasdns = daemonise(rap->iface->ctx); |
a8df1b28 RM |
556 | #if 0 |
557 | else if (options & DHCPCD_DAEMONISE && | |
558 | !(options & DHCPCD_DAEMONISED) && new_data) | |
559 | syslog(LOG_WARNING, | |
560 | "%s: did not fork due to an absent" | |
561 | " RDNSS option in the RA", | |
562 | ifp->name); | |
563 | } | |
564 | #endif | |
b5b066a5 | 565 | return hasdns; |
a8df1b28 RM |
566 | } |
567 | ||
d8194bcd | 568 | static void |
e82129a4 | 569 | ipv6nd_dadcallback(void *arg) |
d8194bcd RM |
570 | { |
571 | struct ipv6_addr *ap = arg, *rapap; | |
572 | struct interface *ifp; | |
573 | struct ra *rap; | |
574 | int wascompleted, found; | |
575 | ||
46b8a6b7 | 576 | wascompleted = (ap->flags & IPV6_AF_DADCOMPLETED); |
e82129a4 | 577 | ipv6nd_cancelprobeaddr(ap); |
46b8a6b7 RM |
578 | ap->flags |= IPV6_AF_DADCOMPLETED; |
579 | if (ap->flags & IPV6_AF_DUPLICATED) | |
d8194bcd RM |
580 | /* No idea what how to try and make another address :( */ |
581 | syslog(LOG_WARNING, "%s: DAD detected %s", | |
582 | ap->iface->name, ap->saddr); | |
583 | #ifdef IPV6_SEND_DAD | |
584 | else | |
585 | ipv6_addaddr(ap); | |
586 | #endif | |
587 | ||
588 | if (!wascompleted) { | |
589 | ifp = ap->iface; | |
590 | ||
4eb7b489 | 591 | TAILQ_FOREACH(rap, ifp->ctx->ipv6->ra_routers, next) { |
d8194bcd RM |
592 | if (rap->iface != ifp) |
593 | continue; | |
594 | wascompleted = 1; | |
e92ca600 | 595 | found = 0; |
d8194bcd | 596 | TAILQ_FOREACH(rapap, &rap->addrs, next) { |
a824f281 RM |
597 | if (rapap->flags & IPV6_AF_AUTOCONF && |
598 | (rapap->flags & IPV6_AF_DADCOMPLETED) == 0) | |
599 | { | |
d8194bcd RM |
600 | wascompleted = 0; |
601 | break; | |
602 | } | |
603 | if (rapap == ap) | |
604 | found = 1; | |
605 | } | |
606 | ||
607 | if (wascompleted && found && rap->lifetime) { | |
ad574a91 | 608 | syslog(LOG_DEBUG, |
d8194bcd RM |
609 | "%s: Router Advertisement DAD completed", |
610 | rap->iface->name); | |
b5b066a5 RM |
611 | if (ipv6nd_scriptrun(rap)) |
612 | return; | |
d8194bcd RM |
613 | } |
614 | } | |
615 | } | |
616 | } | |
617 | ||
aae24feb | 618 | static void |
4eb7b489 RM |
619 | ipv6nd_handlera(struct ipv6_ctx *ctx, struct interface *ifp, |
620 | struct icmp6_hdr *icp, ssize_t len) | |
91cd7324 | 621 | { |
e82129a4 | 622 | ssize_t l, m, n, olen; |
7be4b9b3 | 623 | struct nd_router_advert *nd_ra; |
91cd7324 RM |
624 | struct nd_opt_prefix_info *pi; |
625 | struct nd_opt_mtu *mtu; | |
626 | struct nd_opt_rdnss *rdnss; | |
627 | struct nd_opt_dnssl *dnssl; | |
eebe9a18 | 628 | uint32_t lifetime, mtuv; |
91cd7324 RM |
629 | uint8_t *p, *op; |
630 | struct in6_addr addr; | |
631 | char buf[INET6_ADDRSTRLEN]; | |
632 | const char *cbp; | |
633 | struct ra *rap; | |
634 | struct nd_opt_hdr *ndo; | |
eebe9a18 RM |
635 | struct ra_opt *rao; |
636 | struct ipv6_addr *ap; | |
d7555c12 | 637 | char *opt, *tmp; |
91cd7324 | 638 | struct timeval expire; |
a8df1b28 | 639 | uint8_t new_rap, new_data; |
91cd7324 | 640 | |
91cd7324 | 641 | if ((size_t)len < sizeof(struct nd_router_advert)) { |
4eb7b489 | 642 | syslog(LOG_ERR, "IPv6 RA packet too short from %s", ctx->sfrom); |
91cd7324 RM |
643 | return; |
644 | } | |
645 | ||
4eb7b489 RM |
646 | if (!IN6_IS_ADDR_LINKLOCAL(&ctx->from.sin6_addr)) { |
647 | syslog(LOG_ERR, "RA from non local address %s", ctx->sfrom); | |
91cd7324 RM |
648 | return; |
649 | } | |
650 | ||
91cd7324 | 651 | if (ifp == NULL) { |
d7555c12 | 652 | #ifdef DEBUG_RS |
4eb7b489 RM |
653 | syslog(LOG_DEBUG, "RA for unexpected interface from %s", |
654 | ctx->sfrom); | |
4c6a8bec RM |
655 | #endif |
656 | return; | |
657 | } | |
658 | if (!(ifp->options->options & DHCPCD_IPV6RS)) { | |
659 | #ifdef DEBUG_RS | |
660 | syslog(LOG_DEBUG, "%s: unexpected RA from %s", | |
4eb7b489 | 661 | ifp->name, ctx->sfrom); |
d7555c12 | 662 | #endif |
91cd7324 RM |
663 | return; |
664 | } | |
0e906716 | 665 | |
e7a30a46 | 666 | /* We could receive a RA before we sent a RS*/ |
0e906716 RM |
667 | if (ipv6_linklocal(ifp) == NULL) { |
668 | #ifdef DEBUG_RS | |
669 | syslog(LOG_DEBUG, "%s: received RA from %s (no link-local)", | |
4eb7b489 | 670 | ifp->name, ctx->sfrom); |
0e906716 RM |
671 | #endif |
672 | return; | |
673 | } | |
674 | ||
4eb7b489 | 675 | TAILQ_FOREACH(rap, ctx->ra_routers, next) { |
fe292175 | 676 | if (ifp == rap->iface && |
4eb7b489 | 677 | memcmp(rap->from.s6_addr, ctx->from.sin6_addr.s6_addr, |
91cd7324 RM |
678 | sizeof(rap->from.s6_addr)) == 0) |
679 | break; | |
680 | } | |
46caaa5e | 681 | |
e42bbc9b RM |
682 | nd_ra = (struct nd_router_advert *)icp; |
683 | /* Don't bother doing anything if we don't know about a router | |
684 | * expiring */ | |
685 | if ((rap == NULL || rap->lifetime == 0) | |
686 | && nd_ra->nd_ra_router_lifetime == 0) | |
687 | return; | |
688 | ||
46caaa5e RM |
689 | /* We don't want to spam the log with the fact we got an RA every |
690 | * 30 seconds or so, so only spam the log if it's different. */ | |
ee70f4ab | 691 | if (rap == NULL || (rap->data_len != len || |
46caaa5e RM |
692 | memcmp(rap->data, (unsigned char *)icp, rap->data_len) != 0)) |
693 | { | |
694 | if (rap) { | |
695 | free(rap->data); | |
696 | rap->data_len = 0; | |
ea112ab2 RM |
697 | free(rap->ns); |
698 | rap->ns = NULL; | |
699 | rap->nslen = 0; | |
46caaa5e | 700 | } |
d7555c12 | 701 | new_data = 1; |
d7555c12 RM |
702 | } else |
703 | new_data = 0; | |
ee70f4ab RM |
704 | if (new_data || ifp->options->options & DHCPCD_DEBUG) |
705 | syslog(LOG_INFO, "%s: Router Advertisement from %s", | |
4eb7b489 | 706 | ifp->name, ctx->sfrom); |
46caaa5e | 707 | |
91cd7324 | 708 | if (rap == NULL) { |
10e17e3f RM |
709 | rap = calloc(1, sizeof(*rap)); |
710 | if (rap == NULL) { | |
711 | syslog(LOG_ERR, "%s: %m", __func__); | |
712 | return; | |
713 | } | |
eebe9a18 | 714 | rap->iface = ifp; |
4eb7b489 | 715 | memcpy(rap->from.s6_addr, ctx->from.sin6_addr.s6_addr, |
91cd7324 | 716 | sizeof(rap->from.s6_addr)); |
4eb7b489 | 717 | strlcpy(rap->sfrom, ctx->sfrom, sizeof(rap->sfrom)); |
eebe9a18 RM |
718 | TAILQ_INIT(&rap->addrs); |
719 | TAILQ_INIT(&rap->options); | |
720 | new_rap = 1; | |
721 | } else | |
722 | new_rap = 0; | |
46caaa5e | 723 | if (rap->data_len == 0) { |
28382337 RM |
724 | rap->data = malloc(len); |
725 | if (rap->data == NULL) { | |
726 | syslog(LOG_ERR, "%s: %m", __func__); | |
727 | if (new_rap) | |
728 | free(rap); | |
729 | return; | |
730 | } | |
46caaa5e RM |
731 | memcpy(rap->data, icp, len); |
732 | rap->data_len = len; | |
91cd7324 RM |
733 | } |
734 | ||
735 | get_monotonic(&rap->received); | |
eebe9a18 | 736 | rap->flags = nd_ra->nd_ra_flags_reserved; |
e42bbc9b RM |
737 | if (new_rap == 0 && rap->lifetime == 0) |
738 | syslog(LOG_WARNING, "%s: %s router available", | |
739 | ifp->name, rap->sfrom); | |
7be4b9b3 | 740 | rap->lifetime = ntohs(nd_ra->nd_ra_router_lifetime); |
ea112ab2 RM |
741 | if (nd_ra->nd_ra_reachable) { |
742 | rap->reachable = ntohl(nd_ra->nd_ra_reachable); | |
743 | if (rap->reachable > MAX_REACHABLE_TIME) | |
744 | rap->reachable = 0; | |
745 | } | |
746 | if (nd_ra->nd_ra_retransmit) | |
747 | rap->retrans = ntohl(nd_ra->nd_ra_retransmit); | |
b88df421 RM |
748 | if (rap->lifetime) |
749 | rap->expired = 0; | |
91cd7324 RM |
750 | |
751 | len -= sizeof(struct nd_router_advert); | |
752 | p = ((uint8_t *)icp) + sizeof(struct nd_router_advert); | |
753 | olen = 0; | |
754 | lifetime = ~0U; | |
755 | for (olen = 0; len > 0; p += olen, len -= olen) { | |
756 | if ((size_t)len < sizeof(struct nd_opt_hdr)) { | |
4eb7b489 | 757 | syslog(LOG_ERR, "%s: short option", ifp->name); |
91cd7324 RM |
758 | break; |
759 | } | |
760 | ndo = (struct nd_opt_hdr *)p; | |
761 | olen = ndo->nd_opt_len * 8 ; | |
762 | if (olen == 0) { | |
763 | syslog(LOG_ERR, "%s: zero length option", ifp->name); | |
764 | break; | |
765 | } | |
766 | if (olen > len) { | |
767 | syslog(LOG_ERR, | |
768 | "%s: Option length exceeds message", ifp->name); | |
769 | break; | |
770 | } | |
771 | ||
772 | opt = NULL; | |
773 | switch (ndo->nd_opt_type) { | |
774 | case ND_OPT_PREFIX_INFORMATION: | |
eebe9a18 | 775 | pi = (struct nd_opt_prefix_info *)(void *)ndo; |
91cd7324 RM |
776 | if (pi->nd_opt_pi_len != 4) { |
777 | syslog(LOG_ERR, | |
778 | "%s: invalid option len for prefix", | |
779 | ifp->name); | |
780 | break; | |
781 | } | |
782 | if (pi->nd_opt_pi_prefix_len > 128) { | |
783 | syslog(LOG_ERR, "%s: invalid prefix len", | |
784 | ifp->name); | |
785 | break; | |
786 | } | |
787 | if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) || | |
788 | IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) | |
789 | { | |
790 | syslog(LOG_ERR, | |
791 | "%s: invalid prefix in RA", ifp->name); | |
792 | break; | |
793 | } | |
e54dee19 RM |
794 | if (ntohl(pi->nd_opt_pi_preferred_time) > |
795 | ntohl(pi->nd_opt_pi_valid_time)) | |
796 | { | |
797 | syslog(LOG_ERR, | |
798 | "%s: pltime > vltime", ifp->name); | |
799 | break; | |
800 | } | |
eebe9a18 RM |
801 | TAILQ_FOREACH(ap, &rap->addrs, next) |
802 | if (ap->prefix_len ==pi->nd_opt_pi_prefix_len && | |
803 | memcmp(ap->prefix.s6_addr, | |
804 | pi->nd_opt_pi_prefix.s6_addr, | |
805 | sizeof(ap->prefix.s6_addr)) == 0) | |
806 | break; | |
807 | if (ap == NULL) { | |
cd3612e5 RM |
808 | if (!(pi->nd_opt_pi_flags_reserved & |
809 | ND_OPT_PI_FLAG_AUTO) && | |
810 | !(pi->nd_opt_pi_flags_reserved & | |
811 | ND_OPT_PI_FLAG_ONLINK)) | |
4242c5f2 | 812 | continue; |
66fd5d67 | 813 | ap = calloc(1, sizeof(*ap)); |
28382337 RM |
814 | if (ap == NULL) { |
815 | syslog(LOG_ERR, "%s: %m", __func__); | |
816 | break; | |
817 | } | |
66fd5d67 | 818 | ap->iface = rap->iface; |
a824f281 | 819 | ap->flags = IPV6_AF_NEW; |
eebe9a18 RM |
820 | ap->prefix_len = pi->nd_opt_pi_prefix_len; |
821 | memcpy(ap->prefix.s6_addr, | |
822 | pi->nd_opt_pi_prefix.s6_addr, | |
823 | sizeof(ap->prefix.s6_addr)); | |
e54dee19 RM |
824 | if (pi->nd_opt_pi_flags_reserved & |
825 | ND_OPT_PI_FLAG_AUTO) | |
826 | { | |
a824f281 | 827 | ap->flags |= IPV6_AF_AUTOCONF; |
5331b839 | 828 | ipv6_makeaddr(&ap->addr, ifp, |
e54dee19 RM |
829 | &ap->prefix, |
830 | pi->nd_opt_pi_prefix_len); | |
831 | cbp = inet_ntop(AF_INET6, | |
832 | ap->addr.s6_addr, | |
4eb7b489 | 833 | buf, sizeof(buf)); |
e54dee19 RM |
834 | if (cbp) |
835 | snprintf(ap->saddr, | |
836 | sizeof(ap->saddr), | |
837 | "%s/%d", | |
838 | cbp, ap->prefix_len); | |
839 | else | |
840 | ap->saddr[0] = '\0'; | |
841 | } else { | |
842 | memset(&ap->addr, 0, sizeof(ap->addr)); | |
eebe9a18 | 843 | ap->saddr[0] = '\0'; |
e54dee19 | 844 | } |
e82129a4 | 845 | ap->dadcallback = ipv6nd_dadcallback; |
eebe9a18 | 846 | TAILQ_INSERT_TAIL(&rap->addrs, ap, next); |
66fd5d67 | 847 | } |
cd3612e5 RM |
848 | if (pi->nd_opt_pi_flags_reserved & |
849 | ND_OPT_PI_FLAG_ONLINK) | |
46b8a6b7 | 850 | ap->flags |= IPV6_AF_ONLINK; |
eebe9a18 RM |
851 | ap->prefix_vltime = |
852 | ntohl(pi->nd_opt_pi_valid_time); | |
853 | ap->prefix_pltime = | |
854 | ntohl(pi->nd_opt_pi_preferred_time); | |
66fd5d67 | 855 | ap->nsprobes = 0; |
50083515 RM |
856 | if (opt) { |
857 | l = strlen(opt); | |
fa245a4d | 858 | tmp = realloc(opt, |
50083515 | 859 | l + strlen(ap->saddr) + 2); |
fa245a4d RM |
860 | if (tmp) { |
861 | opt = tmp; | |
862 | opt[l] = ' '; | |
863 | strcpy(opt + l + 1, ap->saddr); | |
864 | } | |
50083515 | 865 | } else |
78369646 | 866 | opt = strdup(ap->saddr); |
50083515 | 867 | lifetime = ap->prefix_vltime; |
91cd7324 RM |
868 | break; |
869 | ||
870 | case ND_OPT_MTU: | |
eebe9a18 RM |
871 | mtu = (struct nd_opt_mtu *)(void *)p; |
872 | mtuv = ntohl(mtu->nd_opt_mtu_mtu); | |
873 | if (mtuv < IPV6_MMTU) { | |
874 | syslog(LOG_ERR, "%s: invalid MTU %d", | |
875 | ifp->name, mtuv); | |
876 | break; | |
877 | } | |
f98846d4 | 878 | rap->mtu = mtuv; |
eebe9a18 | 879 | snprintf(buf, sizeof(buf), "%d", mtuv); |
78369646 | 880 | opt = strdup(buf); |
91cd7324 RM |
881 | break; |
882 | ||
883 | case ND_OPT_RDNSS: | |
884 | rdnss = (struct nd_opt_rdnss *)p; | |
885 | lifetime = ntohl(rdnss->nd_opt_rdnss_lifetime); | |
886 | op = (uint8_t *)ndo; | |
887 | op += offsetof(struct nd_opt_rdnss, | |
888 | nd_opt_rdnss_lifetime); | |
889 | op += sizeof(rdnss->nd_opt_rdnss_lifetime); | |
890 | l = 0; | |
c2e168a8 RM |
891 | for (n = ndo->nd_opt_len - 1; n > 1; n -= 2, |
892 | op += sizeof(addr.s6_addr)) | |
893 | { | |
b2e8d8da RM |
894 | m = ipv6_printaddr(NULL, 0, op, ifp->name); |
895 | if (m != -1) | |
896 | l += m + 1; | |
c2e168a8 RM |
897 | } |
898 | op = (uint8_t *)ndo; | |
899 | op += offsetof(struct nd_opt_rdnss, | |
900 | nd_opt_rdnss_lifetime); | |
901 | op += sizeof(rdnss->nd_opt_rdnss_lifetime); | |
902 | tmp = opt = malloc(l); | |
b2e8d8da RM |
903 | if (opt) { |
904 | for (n = ndo->nd_opt_len - 1; n > 1; n -= 2, | |
905 | op += sizeof(addr.s6_addr)) | |
906 | { | |
907 | m = ipv6_printaddr(tmp, l, op, | |
908 | ifp->name); | |
909 | if (m != -1) { | |
910 | l -= (m + 1); | |
911 | tmp += m; | |
912 | *tmp++ = ' '; | |
b2e8d8da RM |
913 | } |
914 | } | |
915 | if (tmp != opt) | |
916 | (*--tmp) = '\0'; | |
917 | else | |
918 | *opt = '\0'; | |
91cd7324 RM |
919 | } |
920 | break; | |
673e81e5 | 921 | |
91cd7324 RM |
922 | case ND_OPT_DNSSL: |
923 | dnssl = (struct nd_opt_dnssl *)p; | |
924 | lifetime = ntohl(dnssl->nd_opt_dnssl_lifetime); | |
925 | op = p + offsetof(struct nd_opt_dnssl, | |
926 | nd_opt_dnssl_lifetime); | |
927 | op += sizeof(dnssl->nd_opt_dnssl_lifetime); | |
928 | n = (dnssl->nd_opt_dnssl_len - 1) * 8; | |
929 | l = decode_rfc3397(NULL, 0, n, op); | |
930 | if (l < 1) { | |
931 | syslog(LOG_ERR, "%s: invalid DNSSL option", | |
932 | ifp->name); | |
933 | } else { | |
78369646 RM |
934 | tmp = malloc(l); |
935 | if (tmp) { | |
936 | decode_rfc3397(tmp, l, n, op); | |
937 | n = print_string(NULL, 0, | |
938 | l - 1, (const uint8_t *)tmp); | |
939 | opt = malloc(n); | |
940 | if (opt) | |
941 | print_string(opt, n, | |
942 | l - 1, | |
943 | (const uint8_t *)tmp); | |
944 | free(tmp); | |
945 | } | |
91cd7324 RM |
946 | } |
947 | break; | |
17b0dbad RM |
948 | |
949 | default: | |
950 | continue; | |
91cd7324 RM |
951 | } |
952 | ||
78369646 RM |
953 | if (opt == NULL) { |
954 | syslog(LOG_ERR, "%s: %m", __func__); | |
91cd7324 | 955 | continue; |
78369646 | 956 | } |
eebe9a18 | 957 | TAILQ_FOREACH(rao, &rap->options, next) { |
91cd7324 RM |
958 | if (rao->type == ndo->nd_opt_type && |
959 | strcmp(rao->option, opt) == 0) | |
960 | break; | |
961 | } | |
962 | if (lifetime == 0) { | |
963 | if (rao) { | |
eebe9a18 | 964 | TAILQ_REMOVE(&rap->options, rao, next); |
91cd7324 RM |
965 | free(rao->option); |
966 | free(rao); | |
967 | } | |
f08afbd8 | 968 | free(opt); |
91cd7324 RM |
969 | continue; |
970 | } | |
971 | ||
972 | if (rao == NULL) { | |
28382337 RM |
973 | rao = malloc(sizeof(*rao)); |
974 | if (rao == NULL) { | |
975 | syslog(LOG_ERR, "%s: %m", __func__); | |
976 | continue; | |
977 | } | |
91cd7324 RM |
978 | rao->type = ndo->nd_opt_type; |
979 | rao->option = opt; | |
eebe9a18 | 980 | TAILQ_INSERT_TAIL(&rap->options, rao, next); |
bb02dff1 RM |
981 | } else |
982 | free(opt); | |
449df9c8 RM |
983 | if (lifetime == ~0U) |
984 | timerclear(&rao->expire); | |
985 | else { | |
91cd7324 RM |
986 | expire.tv_sec = lifetime; |
987 | expire.tv_usec = 0; | |
988 | timeradd(&rap->received, &expire, &rao->expire); | |
989 | } | |
990 | } | |
991 | ||
eebe9a18 | 992 | if (new_rap) |
4eb7b489 RM |
993 | add_router(ifp->ctx->ipv6, rap); |
994 | if (ifp->ctx->options & DHCPCD_TEST) { | |
294eff4d | 995 | script_runreason(ifp, "TEST"); |
d7555c12 | 996 | goto handle_flag; |
b88df421 | 997 | } |
ef0f1a1c | 998 | ipv6nd_probeaddrs(&rap->addrs); |
4eb7b489 | 999 | ipv6_buildroutes(ifp->ctx); |
a8df1b28 | 1000 | |
b88df421 | 1001 | /* We will get run by the expire function */ |
b5b066a5 RM |
1002 | if (rap->lifetime) { |
1003 | if (ipv6nd_scriptrun(rap)) | |
1004 | return; | |
1005 | } | |
61dd6cf9 | 1006 | |
4eb7b489 RM |
1007 | eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); |
1008 | eloop_timeout_delete(ifp->ctx->eloop, NULL, rap); /* reachable timer */ | |
eebe9a18 RM |
1009 | |
1010 | /* If we're owning the RA then we need to try and ensure the | |
1011 | * router is actually reachable */ | |
fbbb0875 RM |
1012 | if (ifp->options->options & DHCPCD_IPV6RA_OWN || |
1013 | ifp->options->options & DHCPCD_IPV6RA_OWN_DEFAULT) | |
eebe9a18 RM |
1014 | { |
1015 | rap->nsprobes = 0; | |
e42bbc9b | 1016 | if (rap->lifetime) |
e82129a4 | 1017 | ipv6nd_proberouter(rap); |
eebe9a18 | 1018 | } |
d7555c12 RM |
1019 | |
1020 | handle_flag: | |
1021 | if (rap->flags & ND_RA_FLAG_MANAGED) { | |
ee70f4ab RM |
1022 | if (rap->lifetime && new_data && |
1023 | dhcp6_start(ifp, DH6S_INIT) == -1) | |
e54dee19 | 1024 | syslog(LOG_ERR, "dhcp6_start: %s: %m", ifp->name); |
d7555c12 | 1025 | } else if (rap->flags & ND_RA_FLAG_OTHER) { |
ee70f4ab RM |
1026 | if (rap->lifetime && new_data && |
1027 | dhcp6_start(ifp, DH6S_INFORM) == -1) | |
d7555c12 RM |
1028 | syslog(LOG_ERR, "dhcp6_start: %s: %m", ifp->name); |
1029 | } else { | |
8fdedf59 | 1030 | if (rap->lifetime && new_data) |
d7555c12 RM |
1031 | syslog(LOG_DEBUG, "%s: No DHCPv6 instruction in RA", |
1032 | ifp->name); | |
4eb7b489 RM |
1033 | if (ifp->ctx->options & DHCPCD_TEST) { |
1034 | eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS); | |
a9d78def RM |
1035 | return; |
1036 | } | |
d7555c12 | 1037 | } |
35308011 RM |
1038 | |
1039 | /* Expire should be called last as the rap object could be destroyed */ | |
e82129a4 | 1040 | ipv6nd_expirera(ifp); |
eebe9a18 RM |
1041 | } |
1042 | ||
1043 | int | |
e82129a4 | 1044 | ipv6nd_has_ra(const struct interface *ifp) |
eebe9a18 RM |
1045 | { |
1046 | const struct ra *rap; | |
1047 | ||
2433e54d RM |
1048 | if (ifp->ctx->ipv6) { |
1049 | TAILQ_FOREACH(rap, ifp->ctx->ipv6->ra_routers, next) | |
1050 | if (rap->iface == ifp) | |
1051 | return 1; | |
1052 | } | |
eebe9a18 | 1053 | return 0; |
91cd7324 RM |
1054 | } |
1055 | ||
1056 | ssize_t | |
e82129a4 | 1057 | ipv6nd_env(char **env, const char *prefix, const struct interface *ifp) |
91cd7324 RM |
1058 | { |
1059 | ssize_t l; | |
f98846d4 | 1060 | size_t len; |
91cd7324 RM |
1061 | const struct ra *rap; |
1062 | const struct ra_opt *rao; | |
1063 | int i; | |
eebe9a18 | 1064 | char buffer[32]; |
91cd7324 | 1065 | const char *optn; |
d4e41f4b | 1066 | char **pref, **mtu, **rdnss, **dnssl, ***var, *new; |
eebe9a18 | 1067 | |
50083515 | 1068 | i = 0; |
91cd7324 | 1069 | l = 0; |
4eb7b489 | 1070 | TAILQ_FOREACH(rap, ifp->ctx->ipv6->ra_routers, next) { |
eebe9a18 RM |
1071 | i++; |
1072 | if (rap->iface != ifp) | |
1073 | continue; | |
91cd7324 RM |
1074 | if (env) { |
1075 | snprintf(buffer, sizeof(buffer), | |
1076 | "ra%d_from", i); | |
28382337 RM |
1077 | if (setvar(&env, prefix, buffer, rap->sfrom) == -1) |
1078 | return -1; | |
91cd7324 RM |
1079 | } |
1080 | l++; | |
449df9c8 | 1081 | |
f98846d4 | 1082 | pref = mtu = rdnss = dnssl = NULL; |
eebe9a18 | 1083 | TAILQ_FOREACH(rao, &rap->options, next) { |
91cd7324 RM |
1084 | if (rao->option == NULL) |
1085 | continue; | |
f98846d4 RM |
1086 | var = NULL; |
1087 | switch(rao->type) { | |
91cd7324 RM |
1088 | case ND_OPT_PREFIX_INFORMATION: |
1089 | optn = "prefix"; | |
d4e41f4b | 1090 | var = &pref; |
91cd7324 RM |
1091 | break; |
1092 | case ND_OPT_MTU: | |
1093 | optn = "mtu"; | |
d4e41f4b | 1094 | var = &mtu; |
91cd7324 RM |
1095 | break; |
1096 | case ND_OPT_RDNSS: | |
1097 | optn = "rdnss"; | |
673e81e5 | 1098 | var = &rdnss; |
91cd7324 RM |
1099 | break; |
1100 | case ND_OPT_DNSSL: | |
1101 | optn = "dnssl"; | |
d4e41f4b | 1102 | var = &dnssl; |
91cd7324 RM |
1103 | break; |
1104 | default: | |
1105 | continue; | |
1106 | } | |
d4e41f4b RM |
1107 | if (*var == NULL) { |
1108 | *var = env ? env : &new; | |
1109 | l++; | |
1110 | } else if (env) { | |
f98846d4 RM |
1111 | /* With single only options, last one takes |
1112 | * precedence */ | |
1113 | if (rao->type == ND_OPT_MTU) { | |
1114 | new = strchr(**var, '='); | |
1115 | if (new == NULL) { | |
1116 | syslog(LOG_ERR, "new is null"); | |
1117 | continue; | |
1118 | } else | |
1119 | new++; | |
e54dee19 RM |
1120 | len = (new - **var) + |
1121 | strlen(rao->option) + 1; | |
f98846d4 RM |
1122 | if (len > strlen(**var)) |
1123 | new = realloc(**var, len); | |
1124 | else | |
1125 | new = **var; | |
1126 | if (new) { | |
1127 | **var = new; | |
1128 | new = strchr(**var, '='); | |
1129 | if (new) | |
e54dee19 RM |
1130 | strcpy(new + 1, |
1131 | rao->option); | |
f98846d4 | 1132 | else |
e54dee19 RM |
1133 | syslog(LOG_ERR, |
1134 | "new is null"); | |
f98846d4 RM |
1135 | } |
1136 | continue; | |
1137 | } | |
d4e41f4b RM |
1138 | new = realloc(**var, |
1139 | strlen(**var) + 1 + | |
1140 | strlen(rao->option) + 1); | |
28382337 RM |
1141 | if (new == NULL) |
1142 | return -1; | |
1143 | **var = new; | |
1144 | new += strlen(new); | |
1145 | *new++ = ' '; | |
1146 | strcpy(new, rao->option); | |
1147 | continue; | |
d4e41f4b RM |
1148 | } |
1149 | if (env) { | |
1150 | snprintf(buffer, sizeof(buffer), | |
1151 | "ra%d_%s", i, optn); | |
28382337 RM |
1152 | if (setvar(&env, prefix, buffer, rao->option) |
1153 | == -1) | |
1154 | return -1; | |
d4e41f4b | 1155 | } |
91cd7324 RM |
1156 | } |
1157 | } | |
1158 | ||
28382337 RM |
1159 | if (env) { |
1160 | if (setvard(&env, prefix, "ra_count", i) == -1) | |
1161 | return -1; | |
1162 | } | |
91cd7324 RM |
1163 | l++; |
1164 | return l; | |
1165 | } | |
1166 | ||
a8df1b28 | 1167 | void |
4eb7b489 | 1168 | ipv6nd_handleifa(struct dhcpcd_ctx *ctx, int cmd, const char *ifname, |
d8194bcd | 1169 | const struct in6_addr *addr, int flags) |
a8df1b28 RM |
1170 | { |
1171 | struct ra *rap; | |
a8df1b28 | 1172 | |
4eb7b489 RM |
1173 | if (ctx->ipv6 == NULL) |
1174 | return; | |
1175 | TAILQ_FOREACH(rap, ctx->ipv6->ra_routers, next) { | |
a8df1b28 RM |
1176 | if (strcmp(rap->iface->name, ifname)) |
1177 | continue; | |
d8194bcd | 1178 | ipv6_handleifa_addrs(cmd, &rap->addrs, addr, flags); |
a8df1b28 RM |
1179 | } |
1180 | } | |
1181 | ||
91cd7324 | 1182 | void |
e82129a4 | 1183 | ipv6nd_expirera(void *arg) |
91cd7324 RM |
1184 | { |
1185 | struct interface *ifp; | |
eebe9a18 RM |
1186 | struct ra *rap, *ran; |
1187 | struct ra_opt *rao, *raon; | |
91cd7324 | 1188 | struct timeval now, lt, expire, next; |
d4e41f4b | 1189 | int expired, valid; |
91cd7324 RM |
1190 | |
1191 | ifp = arg; | |
1192 | get_monotonic(&now); | |
1193 | expired = 0; | |
91cd7324 RM |
1194 | timerclear(&next); |
1195 | ||
4eb7b489 | 1196 | TAILQ_FOREACH_SAFE(rap, ifp->ctx->ipv6->ra_routers, next, ran) { |
eebe9a18 RM |
1197 | if (rap->iface != ifp) |
1198 | continue; | |
91cd7324 RM |
1199 | lt.tv_sec = rap->lifetime; |
1200 | lt.tv_usec = 0; | |
1201 | timeradd(&rap->received, <, &expire); | |
e42bbc9b | 1202 | if (rap->lifetime == 0 || timercmp(&now, &expire, >)) { |
d4e41f4b | 1203 | valid = 0; |
35308011 | 1204 | if (!rap->expired) { |
ad574a91 | 1205 | syslog(LOG_WARNING, |
e42bbc9b | 1206 | "%s: %s: router expired", |
35308011 RM |
1207 | ifp->name, rap->sfrom); |
1208 | rap->expired = expired = 1; | |
e82129a4 | 1209 | ipv6nd_cancelproberouter(rap); |
35308011 RM |
1210 | } |
1211 | } else { | |
d4e41f4b | 1212 | valid = 1; |
35308011 RM |
1213 | timersub(&expire, &now, <); |
1214 | if (!timerisset(&next) || timercmp(&next, <, >)) | |
1215 | next = lt; | |
1216 | } | |
1217 | ||
78ae7296 RM |
1218 | /* Addresses are expired in ipv6ns_probeaddrs |
1219 | * so that DHCPv6 addresses can be removed also. */ | |
eebe9a18 | 1220 | TAILQ_FOREACH_SAFE(rao, &rap->options, next, raon) { |
47102c83 RM |
1221 | if (rap->expired) { |
1222 | switch(rao->type) { | |
1223 | case ND_OPT_RDNSS: /* FALLTHROUGH */ | |
1224 | case ND_OPT_DNSSL: | |
1225 | /* RFC6018 end of section 5.2 states | |
1226 | * that if tha RA has a lifetime of 0 | |
1227 | * then we should expire these | |
1228 | * options */ | |
1229 | TAILQ_REMOVE(&rap->options, rao, next); | |
1230 | expired = 1; | |
1231 | free(rao->option); | |
1232 | free(rao); | |
1233 | continue; | |
1234 | } | |
1235 | } | |
449df9c8 RM |
1236 | if (!timerisset(&rao->expire)) |
1237 | continue; | |
1238 | if (timercmp(&now, &rao->expire, >)) { | |
35308011 RM |
1239 | /* Expired prefixes are logged above */ |
1240 | if (rao->type != ND_OPT_PREFIX_INFORMATION) | |
ad574a91 | 1241 | syslog(LOG_WARNING, |
35308011 RM |
1242 | "%s: %s: expired option %d", |
1243 | ifp->name, rap->sfrom, rao->type); | |
eebe9a18 RM |
1244 | TAILQ_REMOVE(&rap->options, rao, next); |
1245 | expired = 1; | |
1246 | free(rao->option); | |
1247 | free(rao); | |
449df9c8 RM |
1248 | continue; |
1249 | } | |
d4e41f4b | 1250 | valid = 1; |
449df9c8 | 1251 | timersub(&rao->expire, &now, <); |
91cd7324 RM |
1252 | if (!timerisset(&next) || timercmp(&next, <, >)) |
1253 | next = lt; | |
1254 | } | |
d4e41f4b RM |
1255 | |
1256 | /* No valid lifetimes are left on the RA, so we might | |
1257 | * as well punt it. */ | |
e42bbc9b | 1258 | if (!valid && TAILQ_FIRST(&rap->addrs) == NULL) |
e82129a4 | 1259 | ipv6nd_free_ra(rap); |
91cd7324 RM |
1260 | } |
1261 | ||
1262 | if (timerisset(&next)) | |
4eb7b489 RM |
1263 | eloop_timeout_add_tv(ifp->ctx->eloop, |
1264 | &next, ipv6nd_expirera, ifp); | |
e82129a4 | 1265 | if (expired) { |
4eb7b489 | 1266 | ipv6_buildroutes(ifp->ctx); |
e82129a4 RM |
1267 | script_runreason(ifp, "ROUTERADVERT"); |
1268 | } | |
1269 | } | |
1270 | ||
1271 | void | |
1272 | ipv6nd_drop(struct interface *ifp) | |
1273 | { | |
1274 | struct ra *rap; | |
1275 | int expired = 0; | |
1276 | TAILQ_HEAD(rahead, ra) rtrs; | |
1277 | ||
2433e54d RM |
1278 | if (ifp->ctx->ipv6 == NULL) |
1279 | return; | |
1280 | ||
4eb7b489 | 1281 | eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); |
e82129a4 | 1282 | TAILQ_INIT(&rtrs); |
4eb7b489 | 1283 | TAILQ_FOREACH(rap, ifp->ctx->ipv6->ra_routers, next) { |
e82129a4 RM |
1284 | if (rap->iface == ifp) { |
1285 | rap->expired = expired = 1; | |
4eb7b489 | 1286 | TAILQ_REMOVE(ifp->ctx->ipv6->ra_routers, rap, next); |
e82129a4 RM |
1287 | TAILQ_INSERT_TAIL(&rtrs, rap, next); |
1288 | } | |
1289 | } | |
eebe9a18 | 1290 | if (expired) { |
e82129a4 RM |
1291 | while ((rap = TAILQ_FIRST(&rtrs))) { |
1292 | TAILQ_REMOVE(&rtrs, rap, next); | |
1293 | ipv6nd_drop_ra(rap); | |
1294 | } | |
4eb7b489 | 1295 | ipv6_buildroutes(ifp->ctx); |
15fc1181 RM |
1296 | if ((ifp->options->options & |
1297 | (DHCPCD_EXITING | DHCPCD_PERSISTENT)) != | |
1298 | (DHCPCD_EXITING | DHCPCD_PERSISTENT)) | |
1299 | script_runreason(ifp, "ROUTERADVERT"); | |
e82129a4 RM |
1300 | } |
1301 | } | |
a9d78def | 1302 | |
e82129a4 RM |
1303 | static void |
1304 | ipv6nd_unreachable(void *arg) | |
1305 | { | |
1306 | struct ra *rap = arg; | |
1307 | struct timeval tv; | |
1308 | ||
1309 | /* We could add an unreachable flag and persist the information, | |
1310 | * but that is more effort than it's probably worth. */ | |
1311 | syslog(LOG_WARNING, "%s: %s is unreachable, expiring it", | |
1312 | rap->iface->name, rap->sfrom); | |
1313 | rap->expired = 1; | |
4eb7b489 | 1314 | ipv6_buildroutes(rap->iface->ctx); |
e82129a4 RM |
1315 | script_runreason(rap->iface, "ROUTERADVERT"); /* XXX not RA */ |
1316 | ||
1317 | /* We should still test if it's reachable or not so | |
1318 | * incase it comes back to life and it's preferable. */ | |
1319 | if (rap->reachable) { | |
1320 | ms_to_tv(&tv, rap->reachable); | |
1321 | } else { | |
1322 | tv.tv_sec = REACHABLE_TIME; | |
1323 | tv.tv_usec = 0; | |
1324 | } | |
4eb7b489 RM |
1325 | eloop_timeout_add_tv(rap->iface->ctx->eloop, |
1326 | &tv, ipv6nd_proberouter, rap); | |
e82129a4 RM |
1327 | } |
1328 | ||
1329 | #ifdef LISTEN_DAD | |
1330 | void | |
1331 | ipv6nd_cancelprobeaddr(struct ipv6_addr *ap) | |
1332 | { | |
1333 | ||
4eb7b489 | 1334 | eloop_timeout_delete(ap->iface->ctx->eloop, ipv6nd_probeaddr, ap); |
e82129a4 | 1335 | if (ap->dadcallback) |
4eb7b489 | 1336 | eloop_timeout_delete(ap->iface->ctx->eloop, ap->dadcallback,ap); |
e82129a4 RM |
1337 | } |
1338 | #endif | |
1339 | ||
1340 | void | |
1341 | ipv6nd_probeaddr(void *arg) | |
1342 | { | |
1343 | struct ipv6_addr *ap = arg; | |
1344 | #ifdef IPV6_SEND_DAD | |
1345 | struct nd_neighbor_solicit *ns; | |
1346 | struct nd_opt_hdr *nd; | |
1347 | struct sockaddr_in6 dst; | |
1348 | struct cmsghdr *cm; | |
1349 | struct in6_pktinfo pi; | |
1350 | int hoplimit = HOPLIMIT; | |
1351 | #else | |
1352 | #ifdef LISTEN_DAD | |
1353 | struct timeval tv, rtv; | |
1354 | struct timeval mtv; | |
1355 | int i; | |
1356 | #endif | |
1357 | #endif | |
1358 | ||
1359 | if (ap->dadcallback && | |
1360 | ((ap->flags & IPV6_AF_NEW) == 0 || | |
1361 | ap->nsprobes >= ap->iface->options->dadtransmits)) | |
1362 | { | |
1363 | #ifdef IPV6_SEND_DAD | |
1364 | ap->dadcallback(ap); | |
1365 | #else | |
ef0f1a1c RM |
1366 | if (!(ap->flags & IPV6_AF_AUTOCONF) || |
1367 | ap->iface->options->options & DHCPCD_IPV6RA_OWN) | |
1368 | ipv6_addaddr(ap); | |
e82129a4 RM |
1369 | #endif |
1370 | return; | |
1371 | } | |
1372 | ||
4eb7b489 RM |
1373 | if (ipv6nd_open(ap->iface->ctx) == -1) { |
1374 | syslog(LOG_ERR, "%s: ipv6nd_open: %m", __func__); | |
e82129a4 | 1375 | return; |
4eb7b489 | 1376 | } |
e82129a4 RM |
1377 | |
1378 | ap->flags &= ~IPV6_AF_DADCOMPLETED; | |
1379 | ||
1380 | #ifdef IPV6_SEND_DAD | |
1381 | if (!ap->ns) { | |
1382 | ap->nslen = sizeof(*ns) + ROUNDUP8(ap->iface->hwlen + 2); | |
1383 | ap->ns = calloc(1, ap->nslen); | |
1384 | if (ap->ns == NULL) { | |
1385 | syslog(LOG_ERR, "%s: %m", __func__); | |
1386 | return; | |
1387 | } | |
1388 | ns = (struct nd_neighbor_solicit *)(void *)ap->ns; | |
1389 | ns->nd_ns_type = ND_NEIGHBOR_SOLICIT; | |
1390 | //ns->nd_ns_cksum = 0; | |
1391 | //ns->nd_ns_code = 0; | |
1392 | //ns->nd_ns_reserved = 0; | |
1393 | ns->nd_ns_target = ap->addr; | |
1394 | nd = (struct nd_opt_hdr *)(ap->ns + sizeof(*ns)); | |
1395 | nd->nd_opt_type = ND_OPT_SOURCE_LINKADDR; | |
1396 | nd->nd_opt_len = (ROUNDUP8(ap->iface->hwlen + 2)) >> 3; | |
1397 | memcpy(nd + 1, ap->iface->hwaddr, ap->iface->hwlen); | |
1398 | } | |
1399 | ||
1400 | memset(&dst, 0, sizeof(dst)); | |
1401 | dst.sin6_family = AF_INET6; | |
1402 | #ifdef SIN6_LEN | |
1403 | dst.sin6_len = sizeof(dst); | |
1404 | #endif | |
1405 | dst.sin6_addr.s6_addr16[0] = IPV6_ADDR_INT16_MLL; | |
1406 | dst.sin6_addr.s6_addr16[1] = 0; | |
1407 | dst.sin6_addr.s6_addr32[1] = 0; | |
1408 | dst.sin6_addr.s6_addr32[2] = IPV6_ADDR_INT32_ONE; | |
1409 | dst.sin6_addr.s6_addr32[3] = ap->addr.s6_addr32[3]; | |
1410 | dst.sin6_addr.s6_addr[12] = 0xff; | |
1411 | ||
1412 | //memcpy(&dst.sin6_addr, &ap->addr, sizeof(dst.sin6_addr)); | |
1413 | dst.sin6_scope_id = ap->iface->index; | |
1414 | ||
1415 | sndhdr.msg_name = (caddr_t)&dst; | |
1416 | sndhdr.msg_iov[0].iov_base = ap->ns; | |
1417 | sndhdr.msg_iov[0].iov_len = ap->nslen; | |
1418 | ||
1419 | /* Set the outbound interface */ | |
1420 | cm = CMSG_FIRSTHDR(&sndhdr); | |
1421 | cm->cmsg_level = IPPROTO_IPV6; | |
1422 | cm->cmsg_type = IPV6_PKTINFO; | |
1423 | cm->cmsg_len = CMSG_LEN(sizeof(pi)); | |
1424 | memset(&pi, 0, sizeof(pi)); | |
1425 | pi.ipi6_ifindex = ap->iface->index; | |
1426 | memcpy(CMSG_DATA(cm), &pi, sizeof(pi)); | |
1427 | ||
1428 | /* Hop limit */ | |
1429 | cm = CMSG_NXTHDR(&sndhdr, cm); | |
1430 | cm->cmsg_level = IPPROTO_IPV6; | |
1431 | cm->cmsg_type = IPV6_HOPLIMIT; | |
1432 | cm->cmsg_len = CMSG_LEN(sizeof(hoplimit)); | |
1433 | memcpy(CMSG_DATA(cm), &hoplimit, sizeof(hoplimit)); | |
1434 | ||
1435 | #ifdef DEBUG_NS | |
1436 | syslog(LOG_INFO, "%s: sending IPv6 NS for %s", | |
1437 | ap->iface->name, ap->saddr); | |
1438 | if (ap->dadcallback == NULL) | |
1439 | syslog(LOG_WARNING, "%s: no callback!", ap->iface->name); | |
1440 | #endif | |
1441 | if (sendmsg(unspec_sock, &sndhdr, 0) == -1) { | |
1442 | syslog(LOG_ERR, "%s: %s: sendmsg: %m", | |
1443 | ap->iface->name, __func__); | |
1444 | return; | |
1445 | } | |
1446 | ||
1447 | if (ap->dadcallback) { | |
1448 | ms_to_tv(&tv, RETRANS_TIMER); | |
1449 | ms_to_tv(&rtv, MIN_RANDOM_FACTOR); | |
1450 | timeradd(&tv, &rtv, &tv); | |
1451 | rtv.tv_sec = 0; | |
1452 | rtv.tv_usec = arc4random() % | |
1453 | (MAX_RANDOM_FACTOR_U - MIN_RANDOM_FACTOR_U); | |
1454 | timeradd(&tv, &rtv, &tv); | |
1455 | ||
4eb7b489 | 1456 | eloop_timeout_add_tv(ap->iface->ctx->eloop, &tv, |
e82129a4 RM |
1457 | ++(ap->nsprobes) < ap->iface->options->dadtransmits ? |
1458 | ipv6nd_probeaddr : ap->dadcallback, | |
1459 | ap); | |
1460 | } | |
1461 | #else /* IPV6_SEND_DAD */ | |
ef0f1a1c RM |
1462 | |
1463 | if (!(ap->flags & IPV6_AF_AUTOCONF) || | |
1464 | ap->iface->options->options & DHCPCD_IPV6RA_OWN) | |
1465 | ipv6_addaddr(ap); | |
1466 | ||
e82129a4 RM |
1467 | #ifdef LISTEN_DAD |
1468 | /* Let the kernel handle DAD. | |
1469 | * We don't know the timings, so just wait for the max */ | |
1470 | if (ap->dadcallback) { | |
1471 | mtv.tv_sec = 0; | |
1472 | mtv.tv_usec = 0; | |
1473 | for (i = 0; i < ap->iface->options->dadtransmits; i++) { | |
1474 | ms_to_tv(&tv, RETRANS_TIMER); | |
1475 | ms_to_tv(&rtv, MAX_RANDOM_FACTOR); | |
1476 | timeradd(&tv, &rtv, &tv); | |
1477 | timeradd(&mtv, &tv, &mtv); | |
1478 | } | |
4eb7b489 RM |
1479 | eloop_timeout_add_tv(ap->iface->ctx->eloop, |
1480 | &mtv, ap->dadcallback, ap); | |
e82129a4 RM |
1481 | } |
1482 | #endif | |
1483 | #endif /* IPV6_SEND_DAD */ | |
1484 | } | |
1485 | ||
1486 | ssize_t | |
1487 | ipv6nd_probeaddrs(struct ipv6_addrhead *addrs) | |
1488 | { | |
1489 | struct ipv6_addr *ap, *apn; | |
1490 | ssize_t i; | |
1491 | ||
1492 | i = 0; | |
1493 | TAILQ_FOREACH_SAFE(ap, addrs, next, apn) { | |
1494 | if (ap->prefix_vltime == 0) { | |
1495 | TAILQ_REMOVE(addrs, ap, next); | |
1496 | if (ap->flags & IPV6_AF_ADDED) { | |
1497 | syslog(LOG_INFO, "%s: deleting address %s", | |
1498 | ap->iface->name, ap->saddr); | |
1499 | i++; | |
ef0f1a1c RM |
1500 | if (!IN6_IS_ADDR_UNSPECIFIED(&ap->addr) && |
1501 | del_address6(ap) == -1 && | |
1502 | errno != EADDRNOTAVAIL && errno != ENXIO) | |
1503 | syslog(LOG_ERR, "del_address6 %m"); | |
e82129a4 | 1504 | } |
e82129a4 | 1505 | if (ap->dadcallback) |
4eb7b489 RM |
1506 | eloop_q_timeout_delete(ap->iface->ctx->eloop, |
1507 | 0, NULL, ap->dadcallback); | |
e82129a4 | 1508 | free(ap); |
38f20553 | 1509 | } else if (!IN6_IS_ADDR_UNSPECIFIED(&ap->addr)) { |
e82129a4 RM |
1510 | ipv6nd_probeaddr(ap); |
1511 | if (ap->flags & IPV6_AF_NEW) | |
1512 | i++; | |
1513 | } | |
1514 | } | |
1515 | ||
1516 | return i; | |
1517 | } | |
1518 | ||
1519 | void | |
1520 | ipv6nd_proberouter(void *arg) | |
1521 | { | |
1522 | struct ra *rap = arg; | |
1523 | struct nd_neighbor_solicit *ns; | |
1524 | struct nd_opt_hdr *nd; | |
1525 | struct sockaddr_in6 dst; | |
1526 | struct cmsghdr *cm; | |
1527 | struct in6_pktinfo pi; | |
1528 | int hoplimit = HOPLIMIT; | |
1529 | struct timeval tv, rtv; | |
4eb7b489 | 1530 | struct ipv6_ctx *ctx; |
e82129a4 | 1531 | |
4eb7b489 RM |
1532 | if (ipv6nd_open(rap->iface->ctx) == -1) { |
1533 | syslog(LOG_ERR, "%s: ipv6nd_open: %m", __func__); | |
e82129a4 | 1534 | return; |
4eb7b489 | 1535 | } |
e82129a4 RM |
1536 | |
1537 | if (!rap->ns) { | |
1538 | rap->nslen = sizeof(*ns) + ROUNDUP8(rap->iface->hwlen + 2); | |
1539 | rap->ns = calloc(1, rap->nslen); | |
1540 | if (rap->ns == NULL) { | |
1541 | syslog(LOG_ERR, "%s: %m", __func__); | |
1542 | return; | |
1543 | } | |
1544 | ns = (struct nd_neighbor_solicit *)(void *)rap->ns; | |
1545 | ns->nd_ns_type = ND_NEIGHBOR_SOLICIT; | |
1546 | //ns->nd_ns_cksum = 0; | |
1547 | //ns->nd_ns_code = 0; | |
1548 | //ns->nd_ns_reserved = 0; | |
1549 | ns->nd_ns_target = rap->from; | |
1550 | nd = (struct nd_opt_hdr *)(rap->ns + sizeof(*ns)); | |
1551 | nd->nd_opt_type = ND_OPT_SOURCE_LINKADDR; | |
1552 | nd->nd_opt_len = (ROUNDUP8(rap->iface->hwlen + 2)) >> 3; | |
1553 | memcpy(nd + 1, rap->iface->hwaddr, rap->iface->hwlen); | |
1554 | } | |
1555 | ||
1556 | memset(&dst, 0, sizeof(dst)); | |
1557 | dst.sin6_family = AF_INET6; | |
1558 | #ifdef SIN6_LEN | |
1559 | dst.sin6_len = sizeof(dst); | |
1560 | #endif | |
1561 | memcpy(&dst.sin6_addr, &rap->from, sizeof(dst.sin6_addr)); | |
1562 | dst.sin6_scope_id = rap->iface->index; | |
1563 | ||
4eb7b489 RM |
1564 | ctx = rap->iface->ctx->ipv6; |
1565 | ctx->sndhdr.msg_name = (caddr_t)&dst; | |
1566 | ctx->sndhdr.msg_iov[0].iov_base = rap->ns; | |
1567 | ctx->sndhdr.msg_iov[0].iov_len = rap->nslen; | |
e82129a4 RM |
1568 | |
1569 | /* Set the outbound interface */ | |
4eb7b489 | 1570 | cm = CMSG_FIRSTHDR(&ctx->sndhdr); |
e82129a4 RM |
1571 | cm->cmsg_level = IPPROTO_IPV6; |
1572 | cm->cmsg_type = IPV6_PKTINFO; | |
1573 | cm->cmsg_len = CMSG_LEN(sizeof(pi)); | |
1574 | memset(&pi, 0, sizeof(pi)); | |
1575 | pi.ipi6_ifindex = rap->iface->index; | |
1576 | memcpy(CMSG_DATA(cm), &pi, sizeof(pi)); | |
1577 | ||
1578 | /* Hop limit */ | |
4eb7b489 | 1579 | cm = CMSG_NXTHDR(&ctx->sndhdr, cm); |
e82129a4 RM |
1580 | cm->cmsg_level = IPPROTO_IPV6; |
1581 | cm->cmsg_type = IPV6_HOPLIMIT; | |
1582 | cm->cmsg_len = CMSG_LEN(sizeof(hoplimit)); | |
1583 | memcpy(CMSG_DATA(cm), &hoplimit, sizeof(hoplimit)); | |
1584 | ||
1585 | #ifdef DEBUG_NS | |
1586 | syslog(LOG_INFO, "%s: sending IPv6 NS for %s", | |
1587 | rap->iface->name, rap->sfrom); | |
1588 | #endif | |
4eb7b489 | 1589 | if (sendmsg(ctx->nd_fd, &ctx->sndhdr, 0) == -1) { |
e82129a4 RM |
1590 | syslog(LOG_ERR, "%s: %s: sendmsg: %m", |
1591 | rap->iface->name, __func__); | |
1592 | return; | |
1593 | } | |
1594 | ||
1595 | ms_to_tv(&tv, rap->retrans == 0 ? RETRANS_TIMER : rap->retrans); | |
1596 | ms_to_tv(&rtv, MIN_RANDOM_FACTOR); | |
1597 | timeradd(&tv, &rtv, &tv); | |
1598 | rtv.tv_sec = 0; | |
1599 | rtv.tv_usec = arc4random() % (MAX_RANDOM_FACTOR_U -MIN_RANDOM_FACTOR_U); | |
1600 | timeradd(&tv, &rtv, &tv); | |
4eb7b489 RM |
1601 | eloop_timeout_add_tv(rap->iface->ctx->eloop, |
1602 | &tv, ipv6nd_proberouter, rap); | |
e82129a4 RM |
1603 | |
1604 | if (rap->nsprobes++ == 0) | |
4eb7b489 RM |
1605 | eloop_timeout_add_sec(rap->iface->ctx->eloop, |
1606 | DELAY_FIRST_PROBE_TIME, ipv6nd_unreachable, rap); | |
e82129a4 RM |
1607 | } |
1608 | ||
1609 | void | |
1610 | ipv6nd_cancelproberouter(struct ra *rap) | |
1611 | { | |
1612 | ||
4eb7b489 RM |
1613 | eloop_timeout_delete(rap->iface->ctx->eloop, ipv6nd_proberouter, rap); |
1614 | eloop_timeout_delete(rap->iface->ctx->eloop, ipv6nd_unreachable, rap); | |
e82129a4 RM |
1615 | } |
1616 | ||
e82129a4 | 1617 | static void |
4eb7b489 RM |
1618 | ipv6nd_handlena(struct ipv6_ctx *ctx, struct interface *ifp, |
1619 | struct icmp6_hdr *icp, ssize_t len) | |
e82129a4 RM |
1620 | { |
1621 | struct nd_neighbor_advert *nd_na; | |
1622 | struct ra *rap; | |
1623 | int is_router, is_solicited; | |
1624 | #ifdef DEBUG_NS | |
1625 | int found; | |
1626 | #endif | |
1627 | struct timeval tv; | |
1628 | ||
1629 | #ifdef LISTEN_DAD | |
1630 | struct dhcp6_state *d6state; | |
1631 | struct ipv6_addr *ap; | |
1632 | #endif | |
1633 | ||
1634 | if ((size_t)len < sizeof(struct nd_neighbor_advert)) { | |
4eb7b489 | 1635 | syslog(LOG_ERR, "IPv6 NA packet too short from %s", ctx->sfrom); |
e82129a4 RM |
1636 | return; |
1637 | } | |
1638 | ||
1639 | if (ifp == NULL) { | |
1640 | #ifdef DEBUG_NS | |
4eb7b489 RM |
1641 | syslog(LOG_DEBUG, "NA for unexpected interface from %s", |
1642 | ctx->sfrom); | |
e82129a4 RM |
1643 | #endif |
1644 | return; | |
1645 | } | |
1646 | ||
1647 | nd_na = (struct nd_neighbor_advert *)icp; | |
1648 | is_router = nd_na->nd_na_flags_reserved & ND_NA_FLAG_ROUTER; | |
1649 | is_solicited = nd_na->nd_na_flags_reserved & ND_NA_FLAG_SOLICITED; | |
1650 | ||
1651 | if (IN6_IS_ADDR_MULTICAST(&nd_na->nd_na_target)) { | |
1652 | syslog(LOG_ERR, "%s: NA for multicast address from %s", | |
4eb7b489 | 1653 | ifp->name, ctx->sfrom); |
e82129a4 RM |
1654 | return; |
1655 | } | |
1656 | ||
1657 | #ifdef DEBUG_NS | |
1658 | found = 0; | |
1659 | #endif | |
4eb7b489 | 1660 | TAILQ_FOREACH(rap, ctx->ra_routers, next) { |
e82129a4 RM |
1661 | if (rap->iface != ifp) |
1662 | continue; | |
1663 | if (memcmp(rap->from.s6_addr, nd_na->nd_na_target.s6_addr, | |
1664 | sizeof(rap->from.s6_addr)) == 0) | |
1665 | break; | |
1666 | #ifdef LISTEN_DAD | |
1667 | TAILQ_FOREACH(ap, &rap->addrs, next) { | |
1668 | if (memcmp(ap->addr.s6_addr, | |
1669 | nd_na->nd_na_target.s6_addr, | |
1670 | sizeof(ap->addr.s6_addr)) == 0) | |
1671 | { | |
1672 | ap->flags |= IPV6_AF_DUPLICATED; | |
1673 | if (ap->dadcallback) | |
1674 | ap->dadcallback(ap); | |
1675 | #ifdef DEBUG_NS | |
1676 | found++; | |
1677 | #endif | |
1678 | } | |
1679 | } | |
1680 | #endif | |
1681 | } | |
1682 | if (rap == NULL) { | |
1683 | #ifdef LISTEN_DAD | |
1684 | d6state = D6_STATE(ifp); | |
1685 | if (d6state) { | |
1686 | TAILQ_FOREACH(ap, &d6state->addrs, next) { | |
1687 | if (memcmp(ap->addr.s6_addr, | |
1688 | nd_na->nd_na_target.s6_addr, | |
1689 | sizeof(ap->addr.s6_addr)) == 0) | |
1690 | { | |
1691 | ap->flags |= IPV6_AF_DUPLICATED; | |
1692 | if (ap->dadcallback) | |
1693 | ap->dadcallback(ap); | |
1694 | #ifdef DEBUG_NS | |
1695 | found++; | |
1696 | #endif | |
1697 | } | |
1698 | } | |
1699 | } | |
1700 | #endif | |
1701 | ||
1702 | #ifdef DEBUG_NS | |
1703 | if (found == 0) | |
1704 | syslog(LOG_DEBUG, "%s: unexpected NA from %s", | |
4eb7b489 | 1705 | ifp->name, ctx->sfrom); |
e82129a4 RM |
1706 | #endif |
1707 | return; | |
1708 | } | |
1709 | ||
1710 | #ifdef DEBUG_NS | |
1711 | syslog(LOG_DEBUG, "%s: %sNA from %s", | |
4eb7b489 | 1712 | ifp->name, is_solicited ? "solicited " : "", ctx->sfrom); |
e82129a4 RM |
1713 | #endif |
1714 | ||
1715 | /* Node is no longer a router, so remove it from consideration */ | |
1716 | if (!is_router && !rap->expired) { | |
1717 | syslog(LOG_INFO, "%s: %s is no longer a router", | |
4eb7b489 | 1718 | ifp->name, ctx->sfrom); |
e82129a4 RM |
1719 | rap->expired = 1; |
1720 | ipv6nd_cancelproberouter(rap); | |
4eb7b489 | 1721 | ipv6_buildroutes(ifp->ctx); |
294eff4d | 1722 | script_runreason(ifp, "ROUTERADVERT"); |
e82129a4 RM |
1723 | return; |
1724 | } | |
1725 | ||
1726 | if (is_solicited && is_router && rap->lifetime) { | |
1727 | if (rap->expired) { | |
1728 | rap->expired = 0; | |
1729 | syslog(LOG_INFO, "%s: %s is reachable again", | |
4eb7b489 RM |
1730 | ifp->name, ctx->sfrom); |
1731 | ipv6_buildroutes(ifp->ctx); | |
e82129a4 RM |
1732 | script_runreason(rap->iface, "ROUTERADVERT"); /* XXX */ |
1733 | } | |
1734 | rap->nsprobes = 0; | |
1735 | if (rap->reachable) { | |
1736 | ms_to_tv(&tv, rap->reachable); | |
1737 | } else { | |
1738 | tv.tv_sec = REACHABLE_TIME; | |
1739 | tv.tv_usec = 0; | |
1740 | } | |
4eb7b489 RM |
1741 | eloop_timeout_add_tv(rap->iface->ctx->eloop, |
1742 | &tv, ipv6nd_proberouter, rap); | |
1743 | eloop_timeout_delete(rap->iface->ctx->eloop, | |
1744 | ipv6nd_unreachable, rap); | |
eebe9a18 | 1745 | } |
91cd7324 RM |
1746 | } |
1747 | ||
e82129a4 | 1748 | static void |
4eb7b489 | 1749 | ipv6nd_handledata(void *arg) |
e82129a4 | 1750 | { |
4eb7b489 RM |
1751 | struct dhcpcd_ctx *dhcpcd_ctx; |
1752 | struct ipv6_ctx *ctx; | |
e82129a4 RM |
1753 | ssize_t len; |
1754 | struct cmsghdr *cm; | |
1755 | int hoplimit; | |
1756 | struct in6_pktinfo pkt; | |
1757 | struct icmp6_hdr *icp; | |
1758 | struct interface *ifp; | |
1759 | ||
4eb7b489 RM |
1760 | dhcpcd_ctx = arg; |
1761 | ctx = dhcpcd_ctx->ipv6; | |
1762 | len = recvmsg(ctx->nd_fd, &ctx->rcvhdr, 0); | |
e82129a4 RM |
1763 | if (len == -1) { |
1764 | syslog(LOG_ERR, "recvmsg: %m"); | |
1765 | return; | |
1766 | } | |
4eb7b489 RM |
1767 | ctx->sfrom = inet_ntop(AF_INET6, &ctx->from.sin6_addr, |
1768 | ctx->ntopbuf, INET6_ADDRSTRLEN); | |
e82129a4 | 1769 | if ((size_t)len < sizeof(struct icmp6_hdr)) { |
4eb7b489 RM |
1770 | syslog(LOG_ERR, "IPv6 ICMP packet too short from %s", |
1771 | ctx->sfrom); | |
e82129a4 RM |
1772 | return; |
1773 | } | |
1774 | ||
1775 | pkt.ipi6_ifindex = hoplimit = 0; | |
4eb7b489 | 1776 | for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&ctx->rcvhdr); |
e82129a4 | 1777 | cm; |
4eb7b489 | 1778 | cm = (struct cmsghdr *)CMSG_NXTHDR(&ctx->rcvhdr, cm)) |
e82129a4 RM |
1779 | { |
1780 | if (cm->cmsg_level != IPPROTO_IPV6) | |
1781 | continue; | |
1782 | switch(cm->cmsg_type) { | |
1783 | case IPV6_PKTINFO: | |
1784 | if (cm->cmsg_len == CMSG_LEN(sizeof(pkt))) | |
1785 | memcpy(&pkt, CMSG_DATA(cm), sizeof(pkt)); | |
1786 | break; | |
1787 | case IPV6_HOPLIMIT: | |
1788 | if (cm->cmsg_len == CMSG_LEN(sizeof(int))) | |
1789 | memcpy(&hoplimit, CMSG_DATA(cm), sizeof(int)); | |
1790 | break; | |
1791 | } | |
1792 | } | |
1793 | ||
1794 | if (pkt.ipi6_ifindex == 0 || hoplimit == 0) { | |
1795 | syslog(LOG_ERR, | |
4eb7b489 RM |
1796 | "IPv6 RA/NA did not contain index or hop limit from %s", |
1797 | ctx->sfrom); | |
e82129a4 RM |
1798 | return; |
1799 | } | |
1800 | ||
4eb7b489 | 1801 | TAILQ_FOREACH(ifp, dhcpcd_ctx->ifaces, next) { |
e82129a4 RM |
1802 | if (ifp->index == (unsigned int)pkt.ipi6_ifindex) |
1803 | break; | |
1804 | } | |
1805 | ||
4eb7b489 | 1806 | icp = (struct icmp6_hdr *)ctx->rcvhdr.msg_iov[0].iov_base; |
e82129a4 RM |
1807 | if (icp->icmp6_code == 0) { |
1808 | switch(icp->icmp6_type) { | |
1809 | case ND_NEIGHBOR_ADVERT: | |
4eb7b489 | 1810 | ipv6nd_handlena(ctx, ifp, icp, len); |
e82129a4 RM |
1811 | return; |
1812 | case ND_ROUTER_ADVERT: | |
4eb7b489 | 1813 | ipv6nd_handlera(ctx, ifp, icp, len); |
e82129a4 RM |
1814 | return; |
1815 | } | |
1816 | } | |
7cece083 | 1817 | |
e82129a4 | 1818 | syslog(LOG_ERR, "invalid IPv6 type %d or code %d from %s", |
4eb7b489 | 1819 | icp->icmp6_type, icp->icmp6_code, ctx->sfrom); |
e82129a4 RM |
1820 | } |
1821 | ||
91cd7324 | 1822 | int |
e82129a4 | 1823 | ipv6nd_startrs(struct interface *ifp) |
91cd7324 | 1824 | { |
ca15a0aa | 1825 | struct rs_state *state; |
91cd7324 | 1826 | |
e42bbc9b | 1827 | syslog(LOG_INFO, "%s: soliciting an IPv6 router", ifp->name); |
4eb7b489 RM |
1828 | if (ipv6nd_open(ifp->ctx) == -1) { |
1829 | syslog(LOG_ERR, "%s: ipv6nd_open: %m", __func__); | |
1830 | return -1; | |
ca15a0aa RM |
1831 | } |
1832 | ||
4eb7b489 | 1833 | eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); |
673e81e5 RM |
1834 | |
1835 | state = RS_STATE(ifp); | |
1836 | if (state == NULL) { | |
e82129a4 | 1837 | ifp->if_data[IF_DATA_IPV6ND] = calloc(1, sizeof(*state)); |
673e81e5 | 1838 | state = RS_STATE(ifp); |
fbbb0875 RM |
1839 | if (state == NULL) { |
1840 | syslog(LOG_ERR, "%s: %m", __func__); | |
1841 | return -1; | |
1842 | } | |
673e81e5 RM |
1843 | } |
1844 | ||
1845 | /* Always make a new probe as the underlying hardware | |
1846 | * address could have changed. */ | |
e82129a4 | 1847 | ipv6nd_makersprobe(ifp); |
fbbb0875 | 1848 | if (state->rs == NULL) { |
e82129a4 | 1849 | syslog(LOG_ERR, "%s: ipv6ns_makersprobe: %m", __func__); |
673e81e5 | 1850 | return -1; |
fbbb0875 | 1851 | } |
91cd7324 | 1852 | |
ca15a0aa | 1853 | state->rsprobes = 0; |
e82129a4 | 1854 | ipv6nd_sendrsprobe(ifp); |
91cd7324 RM |
1855 | return 0; |
1856 | } |