1 | /* |
---|
2 | * dhcpcd - DHCP client daemon |
---|
3 | * Copyright (c) 2006-2013 Roy Marples <roy@marples.name> |
---|
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 | |
---|
28 | #include <sys/param.h> |
---|
29 | #include <sys/types.h> |
---|
30 | #include <sys/socket.h> |
---|
31 | |
---|
32 | #include <net/route.h> |
---|
33 | #include <netinet/in.h> |
---|
34 | |
---|
35 | #ifdef __linux__ |
---|
36 | # include <asm/types.h> /* for systems with broken headers */ |
---|
37 | # include <linux/rtnetlink.h> |
---|
38 | /* Match Linux defines to BSD */ |
---|
39 | # define IN6_IFF_TENTATIVE (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC) |
---|
40 | # define IN6_IFF_DUPLICATED IFA_F_DADFAILED |
---|
41 | #else |
---|
42 | #ifdef __FreeBSD__ /* Needed so that including netinet6/in6_var.h works */ |
---|
43 | # include <net/if.h> |
---|
44 | # include <net/if_var.h> |
---|
45 | #endif |
---|
46 | # include <netinet6/in6_var.h> |
---|
47 | #endif |
---|
48 | |
---|
49 | #include <errno.h> |
---|
50 | #include <ifaddrs.h> |
---|
51 | #include <inttypes.h> |
---|
52 | #include <stdlib.h> |
---|
53 | #include <string.h> |
---|
54 | #include <syslog.h> |
---|
55 | #include <unistd.h> |
---|
56 | |
---|
57 | #include "common.h" |
---|
58 | #include "dhcpcd.h" |
---|
59 | #include "dhcp6.h" |
---|
60 | #include "eloop.h" |
---|
61 | #include "ipv6.h" |
---|
62 | #include "ipv6nd.h" |
---|
63 | |
---|
64 | /* Hackery at it's finest. */ |
---|
65 | #ifndef s6_addr32 |
---|
66 | # define s6_addr32 __u6_addr.__u6_addr32 |
---|
67 | #endif |
---|
68 | |
---|
69 | #define EUI64_GBIT 0x01 |
---|
70 | #define EUI64_UBIT 0x02 |
---|
71 | #define EUI64_TO_IFID(in6) do {(in6)->s6_addr[8] ^= EUI64_UBIT; } \ |
---|
72 | while (/*CONSTCOND*/ 0) |
---|
73 | #define EUI64_GROUP(in6) ((in6)->s6_addr[8] & EUI64_GBIT) |
---|
74 | |
---|
75 | static struct rt6head *routes; |
---|
76 | |
---|
77 | #ifdef DEBUG_MEMORY |
---|
78 | static void |
---|
79 | ipv6_cleanup() |
---|
80 | { |
---|
81 | struct rt6 *rt; |
---|
82 | |
---|
83 | while ((rt = TAILQ_FIRST(routes))) { |
---|
84 | TAILQ_REMOVE(routes, rt, next); |
---|
85 | free(rt); |
---|
86 | } |
---|
87 | free(routes); |
---|
88 | } |
---|
89 | #endif |
---|
90 | |
---|
91 | int |
---|
92 | ipv6_init(void) |
---|
93 | { |
---|
94 | |
---|
95 | if (routes == NULL) { |
---|
96 | routes = malloc(sizeof(*routes)); |
---|
97 | if (routes == NULL) |
---|
98 | return -1; |
---|
99 | TAILQ_INIT(routes); |
---|
100 | #ifdef DEBUG_MEMORY |
---|
101 | atexit(ipv6_cleanup); |
---|
102 | #endif |
---|
103 | } |
---|
104 | return 0; |
---|
105 | } |
---|
106 | |
---|
107 | ssize_t |
---|
108 | ipv6_printaddr(char *s, ssize_t sl, const uint8_t *d, const char *ifname) |
---|
109 | { |
---|
110 | char buf[INET6_ADDRSTRLEN]; |
---|
111 | const char *p; |
---|
112 | ssize_t l; |
---|
113 | |
---|
114 | p = inet_ntop(AF_INET6, d, buf, sizeof(buf)); |
---|
115 | if (p == NULL) |
---|
116 | return -1; |
---|
117 | |
---|
118 | l = strlen(p); |
---|
119 | if (d[0] == 0xfe && (d[1] & 0xc0) == 0x80) |
---|
120 | l += 1 + strlen(ifname); |
---|
121 | |
---|
122 | if (s == NULL) |
---|
123 | return l; |
---|
124 | |
---|
125 | if (sl < l) { |
---|
126 | errno = ENOMEM; |
---|
127 | return -1; |
---|
128 | } |
---|
129 | |
---|
130 | s += strlcpy(s, p, sl); |
---|
131 | if (d[0] == 0xfe && (d[1] & 0xc0) == 0x80) { |
---|
132 | *s++ = '%'; |
---|
133 | s += strlcpy(s, ifname, sl); |
---|
134 | } |
---|
135 | *s = '\0'; |
---|
136 | return l; |
---|
137 | } |
---|
138 | |
---|
139 | int |
---|
140 | ipv6_makeaddr(struct in6_addr *addr, const struct interface *ifp, |
---|
141 | const struct in6_addr *prefix, int prefix_len) |
---|
142 | { |
---|
143 | const struct ipv6_addr_l *ap; |
---|
144 | #if 0 |
---|
145 | static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; |
---|
146 | static u_int8_t allone[8] = |
---|
147 | { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; |
---|
148 | #endif |
---|
149 | |
---|
150 | if (prefix_len < 0 || prefix_len > 64) { |
---|
151 | errno = EINVAL; |
---|
152 | return -1; |
---|
153 | } |
---|
154 | |
---|
155 | memcpy(addr, prefix, sizeof(*prefix)); |
---|
156 | |
---|
157 | /* Try and make the address from the first local-link address */ |
---|
158 | ap = ipv6_linklocal(ifp); |
---|
159 | if (ap) { |
---|
160 | addr->s6_addr32[2] = ap->addr.s6_addr32[2]; |
---|
161 | addr->s6_addr32[3] = ap->addr.s6_addr32[3]; |
---|
162 | return 0; |
---|
163 | } |
---|
164 | |
---|
165 | /* Because we delay a few functions until we get a local-link address |
---|
166 | * there is little point in the below code. |
---|
167 | * It exists in-case we need to create local-link addresses |
---|
168 | * ourselves, but then we would need to be able to send RFC |
---|
169 | * conformant DAD requests. |
---|
170 | * See ipv6ns.c for why we need the kernel to do this. */ |
---|
171 | errno = ENOENT; |
---|
172 | return -1; |
---|
173 | |
---|
174 | #if 0 |
---|
175 | /* Make an EUI64 based off our hardware address */ |
---|
176 | switch (ifp->family) { |
---|
177 | case ARPHRD_ETHER: |
---|
178 | /* Check for a valid hardware address */ |
---|
179 | if (ifp->hwlen != 8 && ifp->hwlen != 6) { |
---|
180 | errno = ENOTSUP; |
---|
181 | return -1; |
---|
182 | } |
---|
183 | if (memcmp(ifp->hwaddr, allzero, ifp->hwlen) == 0 || |
---|
184 | memcmp(ifp->hwaddr, allone, ifp->hwlen) == 0) |
---|
185 | { |
---|
186 | errno = EINVAL; |
---|
187 | return -1; |
---|
188 | } |
---|
189 | |
---|
190 | /* make a EUI64 address */ |
---|
191 | if (ifp->hwlen == 8) |
---|
192 | memcpy(&addr->s6_addr[8], ifp->hwaddr, 8); |
---|
193 | else if (ifp->hwlen == 6) { |
---|
194 | addr->s6_addr[8] = ifp->hwaddr[0]; |
---|
195 | addr->s6_addr[9] = ifp->hwaddr[1]; |
---|
196 | addr->s6_addr[10] = ifp->hwaddr[2]; |
---|
197 | addr->s6_addr[11] = 0xff; |
---|
198 | addr->s6_addr[12] = 0xfe; |
---|
199 | addr->s6_addr[13] = ifp->hwaddr[3]; |
---|
200 | addr->s6_addr[14] = ifp->hwaddr[4]; |
---|
201 | addr->s6_addr[15] = ifp->hwaddr[5]; |
---|
202 | } |
---|
203 | break; |
---|
204 | default: |
---|
205 | errno = ENOTSUP; |
---|
206 | return -1; |
---|
207 | } |
---|
208 | |
---|
209 | /* sanity check: g bit must not indicate "group" */ |
---|
210 | if (EUI64_GROUP(addr)) { |
---|
211 | errno = EINVAL; |
---|
212 | return -1; |
---|
213 | } |
---|
214 | |
---|
215 | EUI64_TO_IFID(addr); |
---|
216 | |
---|
217 | /* sanity check: ifid must not be all zero, avoid conflict with |
---|
218 | * subnet router anycast */ |
---|
219 | if ((addr->s6_addr[8] & ~(EUI64_GBIT | EUI64_UBIT)) == 0x00 && |
---|
220 | memcmp(&addr->s6_addr[9], allzero, 7) == 0) |
---|
221 | { |
---|
222 | errno = EINVAL; |
---|
223 | return -1; |
---|
224 | } |
---|
225 | |
---|
226 | return 0; |
---|
227 | #endif |
---|
228 | } |
---|
229 | |
---|
230 | int |
---|
231 | ipv6_makeprefix(struct in6_addr *prefix, const struct in6_addr *addr, int len) |
---|
232 | { |
---|
233 | int bytelen, bitlen; |
---|
234 | |
---|
235 | if (len < 0 || len > 128) { |
---|
236 | errno = EINVAL; |
---|
237 | return -1; |
---|
238 | } |
---|
239 | |
---|
240 | bytelen = len / NBBY; |
---|
241 | bitlen = len % NBBY; |
---|
242 | memcpy(&prefix->s6_addr, &addr->s6_addr, bytelen); |
---|
243 | if (bitlen != 0) |
---|
244 | prefix->s6_addr[bytelen] >>= NBBY - bitlen; |
---|
245 | memset((char *)prefix->s6_addr + bytelen, 0, |
---|
246 | sizeof(prefix->s6_addr) - bytelen); |
---|
247 | return 0; |
---|
248 | } |
---|
249 | |
---|
250 | int |
---|
251 | ipv6_mask(struct in6_addr *mask, int len) |
---|
252 | { |
---|
253 | static const unsigned char masks[NBBY] = |
---|
254 | { 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; |
---|
255 | int bytes, bits, i; |
---|
256 | |
---|
257 | if (len < 0 || len > 128) { |
---|
258 | errno = EINVAL; |
---|
259 | return -1; |
---|
260 | } |
---|
261 | |
---|
262 | memset(mask, 0, sizeof(*mask)); |
---|
263 | bytes = len / NBBY; |
---|
264 | bits = len % NBBY; |
---|
265 | for (i = 0; i < bytes; i++) |
---|
266 | mask->s6_addr[i] = 0xff; |
---|
267 | if (bits) |
---|
268 | mask->s6_addr[bytes] = masks[bits - 1]; |
---|
269 | return 0; |
---|
270 | } |
---|
271 | |
---|
272 | int |
---|
273 | ipv6_prefixlen(const struct in6_addr *mask) |
---|
274 | { |
---|
275 | int x = 0, y; |
---|
276 | const unsigned char *lim, *p; |
---|
277 | |
---|
278 | lim = (const unsigned char *)mask + sizeof(*mask); |
---|
279 | for (p = (const unsigned char *)mask; p < lim; x++, p++) { |
---|
280 | if (*p != 0xff) |
---|
281 | break; |
---|
282 | } |
---|
283 | y = 0; |
---|
284 | if (p < lim) { |
---|
285 | for (y = 0; y < NBBY; y++) { |
---|
286 | if ((*p & (0x80 >> y)) == 0) |
---|
287 | break; |
---|
288 | } |
---|
289 | } |
---|
290 | |
---|
291 | /* |
---|
292 | * when the limit pointer is given, do a stricter check on the |
---|
293 | * remaining bits. |
---|
294 | */ |
---|
295 | if (p < lim) { |
---|
296 | if (y != 0 && (*p & (0x00ff >> y)) != 0) |
---|
297 | return -1; |
---|
298 | for (p = p + 1; p < lim; p++) |
---|
299 | if (*p != 0) |
---|
300 | return -1; |
---|
301 | } |
---|
302 | |
---|
303 | return x * NBBY + y; |
---|
304 | } |
---|
305 | |
---|
306 | static void |
---|
307 | in6_to_h64(const struct in6_addr *add, uint64_t *vhigh, uint64_t *vlow) |
---|
308 | { |
---|
309 | uint64_t l, h; |
---|
310 | const uint8_t *p = (const uint8_t *)&add->s6_addr; |
---|
311 | |
---|
312 | h = ((uint64_t)p[0] << 56) | |
---|
313 | ((uint64_t)p[1] << 48) | |
---|
314 | ((uint64_t)p[2] << 40) | |
---|
315 | ((uint64_t)p[3] << 32) | |
---|
316 | ((uint64_t)p[4] << 24) | |
---|
317 | ((uint64_t)p[5] << 16) | |
---|
318 | ((uint64_t)p[6] << 8) | |
---|
319 | (uint64_t)p[7]; |
---|
320 | p += 8; |
---|
321 | l = ((uint64_t)p[0] << 56) | |
---|
322 | ((uint64_t)p[1] << 48) | |
---|
323 | ((uint64_t)p[2] << 40) | |
---|
324 | ((uint64_t)p[3] << 32) | |
---|
325 | ((uint64_t)p[4] << 24) | |
---|
326 | ((uint64_t)p[5] << 16) | |
---|
327 | ((uint64_t)p[6] << 8) | |
---|
328 | (uint64_t)p[7]; |
---|
329 | |
---|
330 | *vhigh = h; |
---|
331 | *vlow = l; |
---|
332 | } |
---|
333 | |
---|
334 | static void |
---|
335 | h64_to_in6(uint64_t vhigh, uint64_t vlow, struct in6_addr *add) |
---|
336 | { |
---|
337 | uint8_t *p = (uint8_t *)&add->s6_addr; |
---|
338 | |
---|
339 | p[0] = vhigh >> 56; |
---|
340 | p[1] = vhigh >> 48; |
---|
341 | p[2] = vhigh >> 40; |
---|
342 | p[3] = vhigh >> 32; |
---|
343 | p[4] = vhigh >> 24; |
---|
344 | p[5] = vhigh >> 16; |
---|
345 | p[6] = vhigh >> 8; |
---|
346 | p[7] = vhigh; |
---|
347 | p += 8; |
---|
348 | p[0] = vlow >> 56; |
---|
349 | p[1] = vlow >> 48; |
---|
350 | p[2] = vlow >> 40; |
---|
351 | p[3] = vlow >> 32; |
---|
352 | p[4] = vlow >> 24; |
---|
353 | p[5] = vlow >> 16; |
---|
354 | p[6] = vlow >> 8; |
---|
355 | p[7] = vlow; |
---|
356 | } |
---|
357 | |
---|
358 | int |
---|
359 | ipv6_userprefix( |
---|
360 | const struct in6_addr *prefix, // prefix from router |
---|
361 | short prefix_len, // length of prefix received |
---|
362 | uint64_t user_number, // "random" number from user |
---|
363 | struct in6_addr *result, // resultant prefix |
---|
364 | short result_len) // desired prefix length |
---|
365 | { |
---|
366 | uint64_t vh, vl, user_low, user_high; |
---|
367 | |
---|
368 | if (prefix_len < 0 || prefix_len > 64 || |
---|
369 | result_len < 0 || result_len > 64) |
---|
370 | { |
---|
371 | errno = EINVAL; |
---|
372 | return -1; |
---|
373 | } |
---|
374 | |
---|
375 | /* Check that the user_number fits inside result_len less prefix_len */ |
---|
376 | if (result_len < prefix_len || user_number > INT_MAX || |
---|
377 | ffs((int)user_number) > result_len - prefix_len) |
---|
378 | { |
---|
379 | errno = ERANGE; |
---|
380 | return -1; |
---|
381 | } |
---|
382 | |
---|
383 | /* virtually shift user number by dest_len, then split at 64 */ |
---|
384 | if (result_len >= 64) { |
---|
385 | user_high = user_number << (result_len - 64); |
---|
386 | user_low = 0; |
---|
387 | } else { |
---|
388 | user_high = user_number >> (64 - result_len); |
---|
389 | user_low = user_number << result_len; |
---|
390 | } |
---|
391 | |
---|
392 | /* convert to two 64bit host order values */ |
---|
393 | in6_to_h64(prefix, &vh, &vl); |
---|
394 | |
---|
395 | vh |= user_high; |
---|
396 | vl |= user_low; |
---|
397 | |
---|
398 | /* copy back result */ |
---|
399 | h64_to_in6(vh, vl, result); |
---|
400 | |
---|
401 | return 0; |
---|
402 | } |
---|
403 | |
---|
404 | int |
---|
405 | ipv6_addaddr(struct ipv6_addr *ap) |
---|
406 | { |
---|
407 | |
---|
408 | syslog(ap->flags & IPV6_AF_NEW ? LOG_INFO : LOG_DEBUG, |
---|
409 | "%s: adding address %s", ap->iface->name, ap->saddr); |
---|
410 | if (!(ap->flags & IPV6_AF_DADCOMPLETED) && |
---|
411 | ipv6_findaddr(ap->iface, &ap->addr)) |
---|
412 | ap->flags |= IPV6_AF_DADCOMPLETED; |
---|
413 | if (add_address6(ap) == -1) { |
---|
414 | syslog(LOG_ERR, "add_address6 %m"); |
---|
415 | return -1; |
---|
416 | } |
---|
417 | ap->flags &= ~IPV6_AF_NEW; |
---|
418 | ap->flags |= IPV6_AF_ADDED; |
---|
419 | if (ap->delegating_iface) |
---|
420 | ap->flags |= IPV6_AF_DELEGATED; |
---|
421 | if (ap->iface->options->options & DHCPCD_IPV6RA_OWN && |
---|
422 | ipv6_removesubnet(ap->iface, ap) == -1) |
---|
423 | syslog(LOG_ERR,"ipv6_removesubnet %m"); |
---|
424 | if (ap->prefix_pltime == ND6_INFINITE_LIFETIME && |
---|
425 | ap->prefix_vltime == ND6_INFINITE_LIFETIME) |
---|
426 | syslog(LOG_DEBUG, |
---|
427 | "%s: vltime infinity, pltime infinity", |
---|
428 | ap->iface->name); |
---|
429 | else if (ap->prefix_pltime == ND6_INFINITE_LIFETIME) |
---|
430 | syslog(LOG_DEBUG, |
---|
431 | "%s: vltime %"PRIu32" seconds, pltime infinity", |
---|
432 | ap->iface->name, ap->prefix_vltime); |
---|
433 | else if (ap->prefix_vltime == ND6_INFINITE_LIFETIME) |
---|
434 | syslog(LOG_DEBUG, |
---|
435 | "%s: vltime infinity, pltime %"PRIu32"seconds", |
---|
436 | ap->iface->name, ap->prefix_pltime); |
---|
437 | else |
---|
438 | syslog(LOG_DEBUG, |
---|
439 | "%s: vltime %"PRIu32" seconds, pltime %"PRIu32" seconds", |
---|
440 | ap->iface->name, ap->prefix_vltime, ap->prefix_pltime); |
---|
441 | return 0; |
---|
442 | } |
---|
443 | |
---|
444 | void |
---|
445 | ipv6_freedrop_addrs(struct ipv6_addrhead *addrs, int drop, |
---|
446 | const struct interface *ifd) |
---|
447 | { |
---|
448 | struct ipv6_addr *ap, *apn; |
---|
449 | |
---|
450 | TAILQ_FOREACH_SAFE(ap, addrs, next, apn) { |
---|
451 | if (ifd && ap->delegating_iface != ifd) |
---|
452 | continue; |
---|
453 | TAILQ_REMOVE(addrs, ap, next); |
---|
454 | if (ap->dadcallback) |
---|
455 | eloop_q_timeout_delete(0, NULL, ap->dadcallback); |
---|
456 | /* Only drop the address if no other RAs have assigned it. |
---|
457 | * This is safe because the RA is removed from the list |
---|
458 | * before we are called. */ |
---|
459 | if (drop && ap->flags & IPV6_AF_ADDED && |
---|
460 | !ipv6nd_addrexists(ap) && !dhcp6_addrexists(ap) && |
---|
461 | (ap->iface->options->options & |
---|
462 | (DHCPCD_EXITING | DHCPCD_PERSISTENT)) != |
---|
463 | (DHCPCD_EXITING | DHCPCD_PERSISTENT)) |
---|
464 | { |
---|
465 | syslog(LOG_INFO, "%s: deleting address %s", |
---|
466 | ap->iface->name, ap->saddr); |
---|
467 | if (del_address6(ap) == -1 && |
---|
468 | errno != EADDRNOTAVAIL && errno != ENXIO) |
---|
469 | syslog(LOG_ERR, "del_address6 %m"); |
---|
470 | } |
---|
471 | free(ap); |
---|
472 | } |
---|
473 | } |
---|
474 | |
---|
475 | static struct ipv6_state * |
---|
476 | ipv6_getstate(struct interface *ifp) |
---|
477 | { |
---|
478 | struct ipv6_state *state; |
---|
479 | |
---|
480 | state = IPV6_STATE(ifp); |
---|
481 | if (state == NULL) { |
---|
482 | ifp->if_data[IF_DATA_IPV6] = malloc(sizeof(*state)); |
---|
483 | state = IPV6_STATE(ifp); |
---|
484 | if (state == NULL) { |
---|
485 | syslog(LOG_ERR, "%s: %m", __func__); |
---|
486 | return NULL; |
---|
487 | } |
---|
488 | TAILQ_INIT(&state->addrs); |
---|
489 | TAILQ_INIT(&state->ll_callbacks); |
---|
490 | } |
---|
491 | return state; |
---|
492 | } |
---|
493 | |
---|
494 | void |
---|
495 | ipv6_handleifa(int cmd, struct if_head *ifs, const char *ifname, |
---|
496 | const struct in6_addr *addr, int flags) |
---|
497 | { |
---|
498 | struct interface *ifp; |
---|
499 | struct ipv6_state *state; |
---|
500 | struct ipv6_addr_l *ap; |
---|
501 | struct ll_callback *cb; |
---|
502 | |
---|
503 | #if 0 |
---|
504 | char buf[INET6_ADDRSTRLEN]; |
---|
505 | inet_ntop(AF_INET6, &addr->s6_addr, |
---|
506 | buf, INET6_ADDRSTRLEN); |
---|
507 | syslog(LOG_DEBUG, "%s: cmd %d addr %s flags %d", |
---|
508 | ifname, cmd, buf, flags); |
---|
509 | #endif |
---|
510 | |
---|
511 | /* Safety, remove tentative addresses */ |
---|
512 | if (cmd == RTM_NEWADDR) { |
---|
513 | if (flags & (IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED)) |
---|
514 | cmd = RTM_DELADDR; |
---|
515 | #ifdef IN6_IFF_DETACHED |
---|
516 | if (flags & IN6_IFF_DETACHED) |
---|
517 | cmd = RTM_DELADDR; |
---|
518 | #endif |
---|
519 | } |
---|
520 | |
---|
521 | if (ifs == NULL) |
---|
522 | ifs = ifaces; |
---|
523 | if (ifs == NULL) { |
---|
524 | errno = ESRCH; |
---|
525 | return; |
---|
526 | } |
---|
527 | TAILQ_FOREACH(ifp, ifs, next) { |
---|
528 | if (strcmp(ifp->name, ifname) == 0) |
---|
529 | break; |
---|
530 | } |
---|
531 | if (ifp == NULL) { |
---|
532 | errno = ESRCH; |
---|
533 | return; |
---|
534 | } |
---|
535 | |
---|
536 | state = ipv6_getstate(ifp); |
---|
537 | if (state == NULL) |
---|
538 | return; |
---|
539 | |
---|
540 | if (!IN6_IS_ADDR_LINKLOCAL(addr)) { |
---|
541 | ipv6nd_handleifa(cmd, ifname, addr, flags); |
---|
542 | dhcp6_handleifa(cmd, ifname, addr, flags); |
---|
543 | } |
---|
544 | |
---|
545 | /* We don't care about duplicated addresses, so remove them */ |
---|
546 | if (flags & IN6_IFF_DUPLICATED) |
---|
547 | cmd = RTM_DELADDR; |
---|
548 | |
---|
549 | TAILQ_FOREACH(ap, &state->addrs, next) { |
---|
550 | if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr)) |
---|
551 | break; |
---|
552 | } |
---|
553 | |
---|
554 | switch (cmd) { |
---|
555 | case RTM_DELADDR: |
---|
556 | if (ap) { |
---|
557 | TAILQ_REMOVE(&state->addrs, ap, next); |
---|
558 | free(ap); |
---|
559 | } |
---|
560 | break; |
---|
561 | case RTM_NEWADDR: |
---|
562 | if (ap == NULL) { |
---|
563 | ap = calloc(1, sizeof(*ap)); |
---|
564 | memcpy(ap->addr.s6_addr, addr->s6_addr, |
---|
565 | sizeof(ap->addr.s6_addr)); |
---|
566 | TAILQ_INSERT_TAIL(&state->addrs, |
---|
567 | ap, next); |
---|
568 | |
---|
569 | if (IN6_IS_ADDR_LINKLOCAL(&ap->addr)) { |
---|
570 | /* Now run any callbacks. |
---|
571 | * Typically IPv6RS or DHCPv6 */ |
---|
572 | while ((cb = |
---|
573 | TAILQ_FIRST(&state->ll_callbacks))) |
---|
574 | { |
---|
575 | TAILQ_REMOVE(&state->ll_callbacks, |
---|
576 | cb, next); |
---|
577 | cb->callback(cb->arg); |
---|
578 | free(cb); |
---|
579 | } |
---|
580 | } |
---|
581 | } |
---|
582 | break; |
---|
583 | } |
---|
584 | } |
---|
585 | |
---|
586 | const struct ipv6_addr_l * |
---|
587 | ipv6_linklocal(const struct interface *ifp) |
---|
588 | { |
---|
589 | const struct ipv6_state *state; |
---|
590 | const struct ipv6_addr_l *ap; |
---|
591 | |
---|
592 | state = IPV6_CSTATE(ifp); |
---|
593 | if (state) { |
---|
594 | TAILQ_FOREACH(ap, &state->addrs, next) { |
---|
595 | if (IN6_IS_ADDR_LINKLOCAL(&ap->addr)) |
---|
596 | return ap; |
---|
597 | } |
---|
598 | } |
---|
599 | return NULL; |
---|
600 | } |
---|
601 | |
---|
602 | const struct ipv6_addr_l * |
---|
603 | ipv6_findaddr(const struct interface *ifp, const struct in6_addr *addr) |
---|
604 | { |
---|
605 | const struct ipv6_state *state; |
---|
606 | const struct ipv6_addr_l *ap; |
---|
607 | |
---|
608 | state = IPV6_CSTATE(ifp); |
---|
609 | if (state) { |
---|
610 | TAILQ_FOREACH(ap, &state->addrs, next) { |
---|
611 | if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr)) |
---|
612 | return ap; |
---|
613 | } |
---|
614 | } |
---|
615 | return NULL; |
---|
616 | } |
---|
617 | |
---|
618 | int ipv6_addlinklocalcallback(struct interface *ifp, |
---|
619 | void (*callback)(void *), void *arg) |
---|
620 | { |
---|
621 | struct ipv6_state *state; |
---|
622 | struct ll_callback *cb; |
---|
623 | |
---|
624 | state = ipv6_getstate(ifp); |
---|
625 | TAILQ_FOREACH(cb, &state->ll_callbacks, next) { |
---|
626 | if (cb->callback == callback && cb->arg == arg) |
---|
627 | break; |
---|
628 | } |
---|
629 | if (cb == NULL) { |
---|
630 | cb = malloc(sizeof(*cb)); |
---|
631 | if (cb == NULL) { |
---|
632 | syslog(LOG_ERR, "%s: %m", __func__); |
---|
633 | return -1; |
---|
634 | } |
---|
635 | cb->callback = callback; |
---|
636 | cb->arg = arg; |
---|
637 | TAILQ_INSERT_TAIL(&state->ll_callbacks, cb, next); |
---|
638 | } |
---|
639 | return 0; |
---|
640 | } |
---|
641 | |
---|
642 | void |
---|
643 | ipv6_free_ll_callbacks(struct interface *ifp) |
---|
644 | { |
---|
645 | struct ipv6_state *state; |
---|
646 | struct ll_callback *cb; |
---|
647 | |
---|
648 | state = IPV6_STATE(ifp); |
---|
649 | if (state) { |
---|
650 | while ((cb = TAILQ_FIRST(&state->ll_callbacks))) { |
---|
651 | TAILQ_REMOVE(&state->ll_callbacks, cb, next); |
---|
652 | free(cb); |
---|
653 | } |
---|
654 | } |
---|
655 | } |
---|
656 | |
---|
657 | void |
---|
658 | ipv6_free(struct interface *ifp) |
---|
659 | { |
---|
660 | struct ipv6_state *state; |
---|
661 | struct ipv6_addr_l *ap; |
---|
662 | |
---|
663 | ipv6_free_ll_callbacks(ifp); |
---|
664 | state = IPV6_STATE(ifp); |
---|
665 | if (state) { |
---|
666 | while ((ap = TAILQ_FIRST(&state->addrs))) { |
---|
667 | TAILQ_REMOVE(&state->addrs, ap, next); |
---|
668 | free(ap); |
---|
669 | } |
---|
670 | free(state); |
---|
671 | ifp->if_data[IF_DATA_IPV6] = NULL; |
---|
672 | } |
---|
673 | } |
---|
674 | |
---|
675 | int |
---|
676 | ipv6_handleifa_addrs(int cmd, |
---|
677 | struct ipv6_addrhead *addrs, const struct in6_addr *addr, int flags) |
---|
678 | { |
---|
679 | struct ipv6_addr *ap, *apn; |
---|
680 | uint8_t found, alldadcompleted; |
---|
681 | |
---|
682 | alldadcompleted = 1; |
---|
683 | found = 0; |
---|
684 | TAILQ_FOREACH_SAFE(ap, addrs, next, apn) { |
---|
685 | if (!IN6_ARE_ADDR_EQUAL(addr, &ap->addr)) { |
---|
686 | if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) |
---|
687 | alldadcompleted = 0; |
---|
688 | continue; |
---|
689 | } |
---|
690 | switch (cmd) { |
---|
691 | case RTM_DELADDR: |
---|
692 | syslog(LOG_INFO, "%s: deleted address %s", |
---|
693 | ap->iface->name, ap->saddr); |
---|
694 | TAILQ_REMOVE(addrs, ap, next); |
---|
695 | free(ap); |
---|
696 | break; |
---|
697 | case RTM_NEWADDR: |
---|
698 | /* Safety - ignore tentative announcements */ |
---|
699 | if (flags & IN6_IFF_TENTATIVE) |
---|
700 | break; |
---|
701 | if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) { |
---|
702 | found++; |
---|
703 | if (flags & IN6_IFF_DUPLICATED) |
---|
704 | ap->flags |= IPV6_AF_DUPLICATED; |
---|
705 | else |
---|
706 | ap->flags &= ~IPV6_AF_DUPLICATED; |
---|
707 | if (ap->dadcallback) |
---|
708 | ap->dadcallback(ap); |
---|
709 | /* We need to set this here in-case the |
---|
710 | * dadcallback function checks it */ |
---|
711 | ap->flags |= IPV6_AF_DADCOMPLETED; |
---|
712 | } |
---|
713 | break; |
---|
714 | } |
---|
715 | } |
---|
716 | |
---|
717 | return alldadcompleted ? found : 0; |
---|
718 | } |
---|
719 | |
---|
720 | static struct rt6 * |
---|
721 | find_route6(struct rt6head *rts, const struct rt6 *r) |
---|
722 | { |
---|
723 | struct rt6 *rt; |
---|
724 | |
---|
725 | TAILQ_FOREACH(rt, rts, next) { |
---|
726 | if (IN6_ARE_ADDR_EQUAL(&rt->dest, &r->dest) && |
---|
727 | #if HAVE_ROUTE_METRIC |
---|
728 | rt->iface->metric == r->iface->metric && |
---|
729 | #endif |
---|
730 | IN6_ARE_ADDR_EQUAL(&rt->net, &r->net)) |
---|
731 | return rt; |
---|
732 | } |
---|
733 | return NULL; |
---|
734 | } |
---|
735 | |
---|
736 | static void |
---|
737 | desc_route(const char *cmd, const struct rt6 *rt) |
---|
738 | { |
---|
739 | char destbuf[INET6_ADDRSTRLEN]; |
---|
740 | char gatebuf[INET6_ADDRSTRLEN]; |
---|
741 | const char *ifname = rt->iface->name, *dest, *gate; |
---|
742 | |
---|
743 | dest = inet_ntop(AF_INET6, &rt->dest.s6_addr, |
---|
744 | destbuf, INET6_ADDRSTRLEN); |
---|
745 | gate = inet_ntop(AF_INET6, &rt->gate.s6_addr, |
---|
746 | gatebuf, INET6_ADDRSTRLEN); |
---|
747 | if (IN6_ARE_ADDR_EQUAL(&rt->gate, &in6addr_any)) |
---|
748 | syslog(LOG_INFO, "%s: %s route to %s/%d", ifname, cmd, |
---|
749 | dest, ipv6_prefixlen(&rt->net)); |
---|
750 | else if (IN6_ARE_ADDR_EQUAL(&rt->dest, &in6addr_any) && |
---|
751 | IN6_ARE_ADDR_EQUAL(&rt->net, &in6addr_any)) |
---|
752 | syslog(LOG_INFO, "%s: %s default route via %s", ifname, cmd, |
---|
753 | gate); |
---|
754 | else |
---|
755 | syslog(LOG_INFO, "%s: %s route to %s/%d via %s", ifname, cmd, |
---|
756 | dest, ipv6_prefixlen(&rt->net), gate); |
---|
757 | } |
---|
758 | |
---|
759 | #define n_route(a) nc_route(1, a, a) |
---|
760 | #define c_route(a, b) nc_route(0, a, b) |
---|
761 | static int |
---|
762 | nc_route(int add, struct rt6 *ort, struct rt6 *nrt) |
---|
763 | { |
---|
764 | |
---|
765 | /* Don't set default routes if not asked to */ |
---|
766 | if (IN6_IS_ADDR_UNSPECIFIED(&nrt->dest) && |
---|
767 | IN6_IS_ADDR_UNSPECIFIED(&nrt->net) && |
---|
768 | !(nrt->iface->options->options & DHCPCD_GATEWAY)) |
---|
769 | return -1; |
---|
770 | |
---|
771 | desc_route(add ? "adding" : "changing", nrt); |
---|
772 | /* We delete and add the route so that we can change metric and |
---|
773 | * prefer the interface. */ |
---|
774 | del_route6(ort); |
---|
775 | if (add_route6(nrt) == 0) |
---|
776 | return 0; |
---|
777 | syslog(LOG_ERR, "%s: add_route6: %m", nrt->iface->name); |
---|
778 | return -1; |
---|
779 | } |
---|
780 | |
---|
781 | static int |
---|
782 | d_route(struct rt6 *rt) |
---|
783 | { |
---|
784 | int retval; |
---|
785 | |
---|
786 | desc_route("deleting", rt); |
---|
787 | retval = del_route6(rt); |
---|
788 | if (retval != 0 && errno != ENOENT && errno != ESRCH) |
---|
789 | syslog(LOG_ERR,"%s: del_route6: %m", rt->iface->name); |
---|
790 | return retval; |
---|
791 | } |
---|
792 | |
---|
793 | static struct rt6 * |
---|
794 | make_route(const struct interface *ifp, const struct ra *rap) |
---|
795 | { |
---|
796 | struct rt6 *r; |
---|
797 | |
---|
798 | r = calloc(1, sizeof(*r)); |
---|
799 | if (r == NULL) { |
---|
800 | syslog(LOG_ERR, "%s: %m", __func__); |
---|
801 | return NULL; |
---|
802 | } |
---|
803 | r->iface = ifp; |
---|
804 | r->metric = ifp->metric; |
---|
805 | if (rap) |
---|
806 | r->mtu = rap->mtu; |
---|
807 | else |
---|
808 | r->mtu = 0; |
---|
809 | return r; |
---|
810 | } |
---|
811 | |
---|
812 | static struct rt6 * |
---|
813 | make_prefix(const struct interface * ifp, const struct ra *rap, |
---|
814 | const struct ipv6_addr *addr) |
---|
815 | { |
---|
816 | struct rt6 *r; |
---|
817 | |
---|
818 | if (addr == NULL || addr->prefix_len > 128) { |
---|
819 | errno = EINVAL; |
---|
820 | return NULL; |
---|
821 | } |
---|
822 | |
---|
823 | /* There is no point in trying to manage a /128 prefix. */ |
---|
824 | if (addr->prefix_len == 128) |
---|
825 | return NULL; |
---|
826 | |
---|
827 | r = make_route(ifp, rap); |
---|
828 | if (r == NULL) |
---|
829 | return r; |
---|
830 | r->dest = addr->prefix; |
---|
831 | ipv6_mask(&r->net, addr->prefix_len); |
---|
832 | r->gate = in6addr_any; |
---|
833 | return r; |
---|
834 | } |
---|
835 | |
---|
836 | |
---|
837 | static struct rt6 * |
---|
838 | make_router(const struct ra *rap) |
---|
839 | { |
---|
840 | struct rt6 *r; |
---|
841 | |
---|
842 | r = make_route(rap->iface, rap); |
---|
843 | if (r == NULL) |
---|
844 | return NULL; |
---|
845 | r->dest = in6addr_any; |
---|
846 | r->net = in6addr_any; |
---|
847 | r->gate = rap->from; |
---|
848 | return r; |
---|
849 | } |
---|
850 | |
---|
851 | int |
---|
852 | ipv6_removesubnet(const struct interface *ifp, struct ipv6_addr *addr) |
---|
853 | { |
---|
854 | struct rt6 *rt; |
---|
855 | #if HAVE_ROUTE_METRIC |
---|
856 | struct rt6 *ort; |
---|
857 | #endif |
---|
858 | int r; |
---|
859 | |
---|
860 | /* We need to delete the subnet route to have our metric or |
---|
861 | * prefer the interface. */ |
---|
862 | r = 0; |
---|
863 | rt = make_prefix(ifp, NULL, addr); |
---|
864 | if (rt) { |
---|
865 | rt->iface = ifp; |
---|
866 | #ifdef __linux__ |
---|
867 | rt->metric = 256; |
---|
868 | #else |
---|
869 | rt->metric = 0; |
---|
870 | #endif |
---|
871 | #if HAVE_ROUTE_METRIC |
---|
872 | /* For some reason, Linux likes to re-add the subnet |
---|
873 | route under the original metric. |
---|
874 | I would love to find a way of stopping this! */ |
---|
875 | if ((ort = find_route6(routes, rt)) == NULL || |
---|
876 | ort->metric != rt->metric) |
---|
877 | #else |
---|
878 | if (!find_route6(routes, rt)) |
---|
879 | #endif |
---|
880 | { |
---|
881 | r = del_route6(rt); |
---|
882 | if (r == -1 && errno == ESRCH) |
---|
883 | r = 0; |
---|
884 | } |
---|
885 | free(rt); |
---|
886 | } |
---|
887 | return r; |
---|
888 | } |
---|
889 | |
---|
890 | #define RT_IS_DEFAULT(rtp) \ |
---|
891 | (IN6_ARE_ADDR_EQUAL(&((rtp)->dest), &in6addr_any) && \ |
---|
892 | IN6_ARE_ADDR_EQUAL(&((rtp)->net), &in6addr_any)) |
---|
893 | |
---|
894 | static void |
---|
895 | ipv6_build_ra_routes(struct rt6head *dnr, int expired) |
---|
896 | { |
---|
897 | struct rt6 *rt; |
---|
898 | const struct ra *rap; |
---|
899 | const struct ipv6_addr *addr; |
---|
900 | |
---|
901 | TAILQ_FOREACH(rap, &ipv6_routers, next) { |
---|
902 | if (rap->expired != expired) |
---|
903 | continue; |
---|
904 | if (rap->iface->options->options & DHCPCD_IPV6RA_OWN) { |
---|
905 | TAILQ_FOREACH(addr, &rap->addrs, next) { |
---|
906 | if ((addr->flags & IPV6_AF_ONLINK) == 0) |
---|
907 | continue; |
---|
908 | rt = make_prefix(rap->iface, rap, addr); |
---|
909 | if (rt) |
---|
910 | TAILQ_INSERT_TAIL(dnr, rt, next); |
---|
911 | } |
---|
912 | } |
---|
913 | if (rap->iface->options->options & |
---|
914 | (DHCPCD_IPV6RA_OWN | DHCPCD_IPV6RA_OWN_DEFAULT)) |
---|
915 | { |
---|
916 | rt = make_router(rap); |
---|
917 | if (rt) |
---|
918 | TAILQ_INSERT_TAIL(dnr, rt, next); |
---|
919 | } |
---|
920 | } |
---|
921 | } |
---|
922 | |
---|
923 | static void |
---|
924 | ipv6_build_dhcp_routes(struct rt6head *dnr, enum DH6S dstate) |
---|
925 | { |
---|
926 | const struct interface *ifp; |
---|
927 | const struct dhcp6_state *d6_state; |
---|
928 | const struct ipv6_addr *addr; |
---|
929 | struct rt6 *rt; |
---|
930 | |
---|
931 | TAILQ_FOREACH(ifp, ifaces, next) { |
---|
932 | if (!(ifp->options->options & DHCPCD_IPV6RA_OWN)) |
---|
933 | continue; |
---|
934 | d6_state = D6_CSTATE(ifp); |
---|
935 | if (d6_state && d6_state->state == dstate) { |
---|
936 | TAILQ_FOREACH(addr, &d6_state->addrs, next) { |
---|
937 | if ((addr->flags & IPV6_AF_ONLINK) == 0 || |
---|
938 | IN6_IS_ADDR_UNSPECIFIED(&addr->addr)) |
---|
939 | continue; |
---|
940 | rt = make_prefix(ifp, NULL, addr); |
---|
941 | if (rt) |
---|
942 | TAILQ_INSERT_TAIL(dnr, rt, next); |
---|
943 | } |
---|
944 | } |
---|
945 | } |
---|
946 | } |
---|
947 | |
---|
948 | void |
---|
949 | ipv6_buildroutes(void) |
---|
950 | { |
---|
951 | struct rt6head dnr, *nrs; |
---|
952 | struct rt6 *rt, *rtn, *or; |
---|
953 | uint8_t have_default; |
---|
954 | unsigned long long o; |
---|
955 | |
---|
956 | TAILQ_INIT(&dnr); |
---|
957 | |
---|
958 | /* First add reachable routers and their prefixes */ |
---|
959 | ipv6_build_ra_routes(&dnr, 0); |
---|
960 | #if HAVE_ROUTE_METRIC |
---|
961 | have_default = (TAILQ_FIRST(&dnr) != NULL); |
---|
962 | #endif |
---|
963 | |
---|
964 | /* We have no way of knowing if prefixes added by DHCP are reachable |
---|
965 | * or not, so we have to assume they are. |
---|
966 | * Add bound before delegated so we can prefer interfaces better */ |
---|
967 | ipv6_build_dhcp_routes(&dnr, DH6S_BOUND); |
---|
968 | ipv6_build_dhcp_routes(&dnr, DH6S_DELEGATED); |
---|
969 | |
---|
970 | #if HAVE_ROUTE_METRIC |
---|
971 | /* If we have an unreachable router, we really do need to remove the |
---|
972 | * route to it beause it could be a lower metric than a reachable |
---|
973 | * router. Of course, we should at least have some routers if all |
---|
974 | * are unreachable. */ |
---|
975 | if (!have_default) |
---|
976 | #endif |
---|
977 | /* Add our non-reachable routers and prefixes |
---|
978 | * Unsure if this is needed, but it's a close match to kernel |
---|
979 | * behaviour */ |
---|
980 | ipv6_build_ra_routes(&dnr, 1); |
---|
981 | |
---|
982 | nrs = malloc(sizeof(*nrs)); |
---|
983 | if (nrs == NULL) { |
---|
984 | syslog(LOG_ERR, "%s: %m", __func__); |
---|
985 | return; |
---|
986 | } |
---|
987 | TAILQ_INIT(nrs); |
---|
988 | have_default = 0; |
---|
989 | TAILQ_FOREACH_SAFE(rt, &dnr, next, rtn) { |
---|
990 | /* Is this route already in our table? */ |
---|
991 | if (find_route6(nrs, rt) != NULL) |
---|
992 | continue; |
---|
993 | //rt->src.s_addr = ifp->addr.s_addr; |
---|
994 | /* Do we already manage it? */ |
---|
995 | if ((or = find_route6(routes, rt))) { |
---|
996 | if (or->iface != rt->iface || |
---|
997 | // or->src.s_addr != ifp->addr.s_addr || |
---|
998 | !IN6_ARE_ADDR_EQUAL(&rt->gate, &or->gate) || |
---|
999 | rt->metric != or->metric) |
---|
1000 | { |
---|
1001 | if (c_route(or, rt) != 0) |
---|
1002 | continue; |
---|
1003 | } |
---|
1004 | TAILQ_REMOVE(routes, or, next); |
---|
1005 | free(or); |
---|
1006 | } else { |
---|
1007 | if (n_route(rt) != 0) |
---|
1008 | continue; |
---|
1009 | } |
---|
1010 | if (RT_IS_DEFAULT(rt)) |
---|
1011 | have_default = 1; |
---|
1012 | TAILQ_REMOVE(&dnr, rt, next); |
---|
1013 | TAILQ_INSERT_TAIL(nrs, rt, next); |
---|
1014 | } |
---|
1015 | |
---|
1016 | /* Free any routes we failed to add/change */ |
---|
1017 | while ((rt = TAILQ_FIRST(&dnr))) { |
---|
1018 | TAILQ_REMOVE(&dnr, rt, next); |
---|
1019 | free(rt); |
---|
1020 | } |
---|
1021 | |
---|
1022 | /* Remove old routes we used to manage |
---|
1023 | * If we own the default route, but not RA management itself |
---|
1024 | * then we need to preserve the last best default route we had */ |
---|
1025 | while ((rt = TAILQ_LAST(routes, rt6head))) { |
---|
1026 | TAILQ_REMOVE(routes, rt, next); |
---|
1027 | if (find_route6(nrs, rt) == NULL) { |
---|
1028 | o = rt->iface->options->options; |
---|
1029 | if (!have_default && |
---|
1030 | (o & DHCPCD_IPV6RA_OWN_DEFAULT) && |
---|
1031 | !(o & DHCPCD_IPV6RA_OWN) && |
---|
1032 | RT_IS_DEFAULT(rt)) |
---|
1033 | have_default = 1; |
---|
1034 | /* no need to add it back to our routing table |
---|
1035 | * as we delete an exiting route when we add |
---|
1036 | * a new one */ |
---|
1037 | else if ((rt->iface->options->options & |
---|
1038 | (DHCPCD_EXITING | DHCPCD_PERSISTENT)) != |
---|
1039 | (DHCPCD_EXITING | DHCPCD_PERSISTENT)) |
---|
1040 | d_route(rt); |
---|
1041 | } |
---|
1042 | free(rt); |
---|
1043 | } |
---|
1044 | |
---|
1045 | free(routes); |
---|
1046 | routes = nrs; |
---|
1047 | } |
---|