source: rtems-libbsd/dhcpcd/ipv6.c @ 338f300

5-freebsd-12
Last change on this file since 338f300 was 338f300, checked in by Christian Mauderer <christian.mauderer@…>, on Apr 25, 2018 at 2:28:00 PM

buildset: Add minimal and everything config.

This adds two new buildset configurations: One that leaves out as much
features as possible and one that enables all features. For the default
configuration WiFi? support is now disabled.

To disable IPv6 for the minimal configuration, all -DINET6 are
eliminated in libbsd.py. They are now replaced by a #ifdef that checks
for RTEMS_BSD_MODULE_NETINET6 instead.

Close #3351.

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