From 8d36e1df80df39bec701fcb39b0b7982e1b94e77 Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Wed, 4 Sep 2019 17:55:23 +0100 Subject: [PATCH] Import dhcpcd-8.0.4 to vendor branch. --- contrib/dhcpcd/BUILDING.md | 171 --- contrib/dhcpcd/LICENSE | 2 +- contrib/dhcpcd/README.DELETED | 30 - contrib/dhcpcd/README.DRAGONFLY | 12 - contrib/dhcpcd/compat/consttime_memequal.h | 28 + contrib/dhcpcd/compat/rb.c | 1346 ++++++++++++++++++++ contrib/dhcpcd/compat/rbtree.h | 211 +++ contrib/dhcpcd/compat/reallocarray.c | 60 - contrib/dhcpcd/compat/reallocarray.h | 37 - contrib/dhcpcd/hooks/10-wpa_supplicant | 10 +- contrib/dhcpcd/hooks/20-resolv.conf | 43 +- contrib/dhcpcd/hooks/29-lookup-hostname | 2 +- contrib/dhcpcd/hooks/30-hostname | 10 +- contrib/dhcpcd/hooks/50-dhcpcd-compat | 41 - contrib/dhcpcd/hooks/50-ntp.conf | 3 +- contrib/dhcpcd/hooks/50-yp.conf | 57 - contrib/dhcpcd/hooks/50-ypbind.in | 2 +- contrib/dhcpcd/hooks/dhcpcd-run-hooks.in | 19 +- contrib/dhcpcd/src/arp.c | 282 ++-- contrib/dhcpcd/src/arp.h | 25 +- contrib/dhcpcd/src/auth.c | 12 +- contrib/dhcpcd/src/auth.h | 3 +- contrib/dhcpcd/src/bpf.c | 136 +- contrib/dhcpcd/src/bpf.h | 5 +- contrib/dhcpcd/src/common.c | 99 +- contrib/dhcpcd/src/common.h | 46 +- contrib/dhcpcd/src/control.c | 160 ++- contrib/dhcpcd/src/control.h | 18 +- contrib/dhcpcd/src/defs.h | 5 +- contrib/dhcpcd/src/dev.c | 190 --- contrib/dhcpcd/src/dev.h | 3 +- contrib/dhcpcd/src/dhcp-common.c | 378 ++---- contrib/dhcpcd/src/dhcp-common.h | 16 +- contrib/dhcpcd/src/dhcp.c | 982 +++++++------- contrib/dhcpcd/src/dhcp.h | 33 +- contrib/dhcpcd/src/dhcp6.c | 538 ++++---- contrib/dhcpcd/src/dhcp6.h | 33 +- contrib/dhcpcd/src/dhcpcd-definitions.conf | 626 --------- contrib/dhcpcd/src/dhcpcd-embedded.c | 407 ++++++ contrib/dhcpcd/src/dhcpcd-embedded.c.in | 36 - .../{dhcpcd-embedded.h.in => dhcpcd-embedded.h} | 8 +- contrib/dhcpcd/src/dhcpcd.8.in | 70 +- contrib/dhcpcd/src/dhcpcd.c | 321 +++-- contrib/dhcpcd/src/dhcpcd.conf.5.in | 136 +- contrib/dhcpcd/src/dhcpcd.h | 57 +- contrib/dhcpcd/src/duid.c | 16 +- contrib/dhcpcd/src/duid.h | 1 + contrib/dhcpcd/src/eloop.c | 3 +- contrib/dhcpcd/src/eloop.h | 3 +- contrib/dhcpcd/src/genembedc | 18 - contrib/dhcpcd/src/genembedh | 17 - contrib/dhcpcd/src/if-bsd.c | 532 +++++--- contrib/dhcpcd/src/if-options.c | 315 +++-- contrib/dhcpcd/src/if-options.h | 9 +- contrib/dhcpcd/src/if.c | 284 +++-- contrib/dhcpcd/src/if.h | 20 +- contrib/dhcpcd/src/ipv4.c | 187 ++- contrib/dhcpcd/src/ipv4.h | 32 +- contrib/dhcpcd/src/ipv4ll.c | 450 ++++--- contrib/dhcpcd/src/ipv4ll.h | 29 +- contrib/dhcpcd/src/ipv6.c | 309 +++-- contrib/dhcpcd/src/ipv6.h | 61 +- contrib/dhcpcd/src/ipv6nd.c | 923 ++++++++------ contrib/dhcpcd/src/ipv6nd.h | 42 +- contrib/dhcpcd/src/logerr.c | 64 +- contrib/dhcpcd/src/logerr.h | 3 +- contrib/dhcpcd/src/route.c | 444 +++++-- contrib/dhcpcd/src/route.h | 40 +- contrib/dhcpcd/src/sa.c | 39 +- contrib/dhcpcd/src/sa.h | 9 +- contrib/dhcpcd/src/script.c | 633 ++++----- contrib/dhcpcd/src/script.h | 4 +- 72 files changed, 6348 insertions(+), 4848 deletions(-) delete mode 100644 contrib/dhcpcd/BUILDING.md delete mode 100644 contrib/dhcpcd/README.DELETED delete mode 100644 contrib/dhcpcd/README.DRAGONFLY create mode 100644 contrib/dhcpcd/compat/consttime_memequal.h create mode 100644 contrib/dhcpcd/compat/rb.c create mode 100644 contrib/dhcpcd/compat/rbtree.h delete mode 100644 contrib/dhcpcd/compat/reallocarray.c delete mode 100644 contrib/dhcpcd/compat/reallocarray.h delete mode 100644 contrib/dhcpcd/hooks/50-dhcpcd-compat delete mode 100644 contrib/dhcpcd/hooks/50-yp.conf delete mode 100644 contrib/dhcpcd/src/dev.c delete mode 100644 contrib/dhcpcd/src/dhcpcd-definitions.conf create mode 100644 contrib/dhcpcd/src/dhcpcd-embedded.c delete mode 100644 contrib/dhcpcd/src/dhcpcd-embedded.c.in rename contrib/dhcpcd/src/{dhcpcd-embedded.h.in => dhcpcd-embedded.h} (89%) delete mode 100755 contrib/dhcpcd/src/genembedc delete mode 100755 contrib/dhcpcd/src/genembedh diff --git a/contrib/dhcpcd/BUILDING.md b/contrib/dhcpcd/BUILDING.md deleted file mode 100644 index f157424143..0000000000 --- a/contrib/dhcpcd/BUILDING.md +++ /dev/null @@ -1,171 +0,0 @@ -# Building dhcpcd - -This attempts to document various ways of building dhcpcd for your -platform. - -Building for distribution (ie making a dhcpcd source tarball) now requires -gmake-4 or any BSD make. - -## Size is an issue -To compile small dhcpcd, maybe to be used for installation media where -size is a concern, you can use the `--small` configure option to enable -a reduced feature set within dhcpcd. -Currently this just removes non important options out of -`dhcpcd-definitions.conf`, the logfile option and -support for DHCPv6 Prefix Delegation. -Other features maybe dropped as and when required. -dhcpcd can also be made smaller by removing the IPv4 or IPv6 stack: - * `--disable-inet` - * `--disable-inet6` - -Or by removing the following features: - * `--disable-auth` - * `--disable-arp` - * `--disable-arping` - * `--disable-ipv4ll` - * `--disable-dhcp6` - -You can also move the embedded extended configuration from the dhcpcd binary -to an external file (LIBEXECDIR/dhcpcd-definitions.conf) - * `--disable-embedded` -If dhcpcd cannot load this file at runtime, dhcpcd will work but will not be -able to decode any DHCP/DHCPv6 options that are not defined by the user -in /etc/dhcpcd.conf. This does not really change the total on disk size. - -## Cross compiling -If you're cross compiling you may need set the platform if OS is different -from the host. -`--target=sparc-sun-netbsd5.0` - -If you're building for an MMU-less system where fork() does not work, you -should `./configure --disable-fork`. -This also puts the `--no-background` flag on and stops the `--background` flag -from working. - -## Default directories -You can change the default dirs with these knobs. -For example, to satisfy FHS compliance you would do this: -`./configure --libexecdir=/lib/dhcpcd dbdir=/var/lib/dhcpcd` - -## Compile Issues -We now default to using `-std=c99`. For 64-bit linux, this always works, but -for 32-bit linux it requires either gnu99 or a patch to `asm/types.h`. -Most distros patch linux headers so this should work fine. -linux-2.6.24 finally ships with a working 32-bit header. -If your linux headers are older, or your distro hasn't patched them you can -set `CSTD=gnu99` to work around this. - -ArchLinux presently sanitises all kernel headers to the latest version -regardless of the version for your CPU. As such, Arch presently ships a -3.12 kernel with 3.17 headers which claim that it supports temporary address -management and no automatic prefix route generation, both of which are -obviously false. You will have to patch support either in the kernel or -out of the headers (or dhcpcd itself) to have correct operation. - -## OS specific issues -Some BSD systems do not allow the manipulation of automatically added subnet -routes. You can find discussion here: - http://mail-index.netbsd.org/tech-net/2008/12/03/msg000896.html -BSD systems where this has been fixed or is known to work are: - NetBSD-5.0 - FreeBSD-10.0 - -Some BSD systems protect against IPv6 NS/NA messages by ensuring that the -source address matches a prefix on the recieved by a RA message. -This is an error as the correct check is for on-link prefixes as the -kernel may not be handling RA itself. -BSD systems where this has been fixed or is known to work are: - NetBSD-7.0 - OpenBSD-5.0 - patch submitted against FreeBSD-10.0 - -Some BSD systems do not announce IPv6 address flag changes, such as -`IN6_IFF_TENTATIVE`, `IN6_IFF_DUPLICATED`, etc. On these systems, -dhcpcd will poll a freshly added address until either `IN6_IFF_TENTATIVE` is -cleared or `IN6_IFF_DUPLICATED` is set and take action accordingly. -BSD systems where this has been fixed or is known to work are: - NetBSD-7.0 - -OpenBSD will always add it's own link-local address if no link-local address -exists, because it doesn't check if the address we are adding is a link-local -address or not. - -Some BSD systems do not announce cached neighbour route changes based -on reachability to userland. For such systems, IPv6 routers will always -be assumed to be reachable until they either stop being a router or expire. -BSD systems where this has been fixed or is known to work are: - NetBSD-7.99.3 - -Linux prior to 3.17 won't allow userland to manage IPv6 temporary addresses. -Either upgrade or don't allow dhcpcd to manage the RA, -so don't set either `ipv6ra_own` or `slaac private` in `dhcpcd.conf` if you -want to have working IPv6 temporary addresses. -SLAAC private addresses are just as private, just stable. - -## Init systems -We try and detect how dhcpcd should interact with system services at runtime. -If we cannot auto-detect how do to this, or it is wrong then -you can change this by passing shell commands to `--serviceexists`, -`--servicecmd` and optionally `--servicestatus` to `./configure` or overriding -the service variables in a hook. - - -## /dev management -Some systems have `/dev` management systems and some of these like to rename -interfaces. As this system would listen in the same way as dhcpcd to new -interface arrivals, dhcpcd needs to listen to the `/dev` management sytem -instead of the kernel. However, if the `/dev` management system breaks, stops -working, or changes to a new one, dhcpcd should still try and continue to work. -To facilitate this, dhcpcd allows a plugin to load to instruct dhcpcd when it -can use an interface. As of the time of writing only udev support is included. -You can disable this with `--without-dev`, or `without-udev`. -NOTE: in Gentoo at least, `sys-fs/udev` as provided by systemd leaks memory -`sys-fs/eudev`, the fork of udev does not and as such is recommended. - -## select -dhcpcd uses eloop.c, which is a portable main event loop with timeouts and -signal handling. Unlike libevent and similar, it can be transplanted directly -within the application - the only caveat outside of POSIX calls is that -you provide queue.h based on a recent BSD (glibc sys/queue.h is not enough). -eloop supports the following polling mechanisms, listed in order of preference: - kqueue, epoll, pollts, ppoll and pselect. -If signal handling is disabled (ie in RTEMS or other single process -OS's) then eloop can use poll. -You can decide which polling mechanism dhcpcd will select in eloop like so -`./configure --with-poll=[kqueue|epoll|pselect|pollts|ppoll]` - - -## Importing into another source control system -To prepare dhcpcd for import into a platform source tree (like NetBSD) -you can use the make import target to create /tmp/dhcpcd-$version and -populate it with all the source files and hooks needed. -In this instance, you may wish to disable some configured tests when -the binary has to run on older versions which lack support, such as getline. -`./configure --without-getline` - - -## Hooks -Not all the hooks in dhcpcd-hooks are installed by default. -By default we install `01-test`, `02-dump`, `10-mtu`, `20-resolv.conf` -and `30-hostname`. -The other hooks, `10-wpa_supplicant`, `15-timezone` and `29-lookup-hostname` -are installed to `$(datadir)/dhcpcd/hooks` by default and need to be -copied to `$(libexecdir)/dhcpcd-hooks` for use. -The configure program attempts to find hooks for systems you have installed. -To add more simply -`./configure -with-hook=ntp.conf` - -Some system services expose the name of the service we are in, -by default dhcpcd will pick `RC_SVCNAME` from the environment. -You can override this in `CPPFLAGS+= -DRC_SVCNAME="YOUR_SVCNAME"`. -This is important because dhcpcd will scrub the environment aside from `$PATH` -before running hooks. -This variable could be used to facilitate service re-entry so this chain could -happen in a custom OS hook: - dhcpcd service marked inactive && dhcpcd service starts - dependant services are not started because dhcpcd is inactive (not stopped) - dhcpcd hook tests if `$if_up = true` and `$af_waiting` is empty or unset. - if true, mark the dhcpcd service as started and then start dependencies - if false and the dhcpcd service was previously started, mark as inactive and - stop any dependant services. - diff --git a/contrib/dhcpcd/LICENSE b/contrib/dhcpcd/LICENSE index b0ca15b5e6..74bcc86259 100644 --- a/contrib/dhcpcd/LICENSE +++ b/contrib/dhcpcd/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2006-2018 Roy Marples +Copyright (c) 2006-2019 Roy Marples All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/contrib/dhcpcd/README.DELETED b/contrib/dhcpcd/README.DELETED deleted file mode 100644 index d7d6ccaa88..0000000000 --- a/contrib/dhcpcd/README.DELETED +++ /dev/null @@ -1,30 +0,0 @@ -.arcconfig -.gitignore -Makefile -Makefile.inc -compat/arc4random.c -compat/arc4random.h -compat/arc4random_uniform.c -compat/arc4random_uniform.h -compat/crypt/md5.c -compat/crypt/md5.h -compat/crypt/sha256.c -compat/crypt/sha256.h -compat/dprintf.c -compat/dprintf.h -compat/endian.h -compat/queue.h -compat/strlcpy.c -compat/strlcpy.h -config-null.mk -configure -hooks/Makefile -iconfig.mk -src/GNUmakefile -src/Makefile -src/dev/ -src/dhcpcd-definitions-small.conf -src/if-linux-wext.c -src/if-linux.c -src/if-sun.c -tests/ diff --git a/contrib/dhcpcd/README.DRAGONFLY b/contrib/dhcpcd/README.DRAGONFLY deleted file mode 100644 index 669803de08..0000000000 --- a/contrib/dhcpcd/README.DRAGONFLY +++ /dev/null @@ -1,12 +0,0 @@ -DHCPCD -====== - -Original source can be downloaded from: -https://github.com/rsmarples/dhcpcd/releases - -file = dhcpcd-dhcpcd-7.0.8.tar.gz -date = 2018 August 20 -size = 282415 -sha1 = 2bc716dae8a9393240757a09c4356458bd584a05 - -The file README.DELETED contains a list of deleted files and directories. diff --git a/contrib/dhcpcd/compat/consttime_memequal.h b/contrib/dhcpcd/compat/consttime_memequal.h new file mode 100644 index 0000000000..9830648410 --- /dev/null +++ b/contrib/dhcpcd/compat/consttime_memequal.h @@ -0,0 +1,28 @@ +/* + * Written by Matthias Drochner . + * Public domain. + */ + +#ifndef CONSTTIME_MEMEQUAL_H +#define CONSTTIME_MEMEQUAL_H +inline static int +consttime_memequal(const void *b1, const void *b2, size_t len) +{ + const unsigned char *c1 = b1, *c2 = b2; + unsigned int res = 0; + + while (len--) + res |= *c1++ ^ *c2++; + + /* + * Map 0 to 1 and [1, 256) to 0 using only constant-time + * arithmetic. + * + * This is not simply `!res' because although many CPUs support + * branchless conditional moves and many compilers will take + * advantage of them, certain compilers generate branches on + * certain CPUs for `!res'. + */ + return (1 & ((res - 1) >> 8)); +} +#endif /* CONSTTIME_MEMEQUAL_H */ diff --git a/contrib/dhcpcd/compat/rb.c b/contrib/dhcpcd/compat/rb.c new file mode 100644 index 0000000000..3c0bed5f70 --- /dev/null +++ b/contrib/dhcpcd/compat/rb.c @@ -0,0 +1,1346 @@ +/* $NetBSD: rb.c,v 1.14 2019/03/08 09:14:54 roy Exp $ */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Matt Thomas . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "common.h" + +#if !defined(_KERNEL) && !defined(_STANDALONE) +#include +#include +#include +#include +#ifdef RBDEBUG +#define KASSERT(s) assert(s) +#define __rbt_unused +#else +#define KASSERT(s) do { } while (/*CONSTCOND*/ 0) +#define __rbt_unused __unused +#endif +__RCSID("$NetBSD: rb.c,v 1.14 2019/03/08 09:14:54 roy Exp $"); +#else +#include +__KERNEL_RCSID(0, "$NetBSD: rb.c,v 1.14 2019/03/08 09:14:54 roy Exp $"); +#ifndef DIAGNOSTIC +#define __rbt_unused __unused +#else +#define __rbt_unused +#endif +#endif + +#ifdef _LIBC +__weak_alias(rb_tree_init, _rb_tree_init) +__weak_alias(rb_tree_find_node, _rb_tree_find_node) +__weak_alias(rb_tree_find_node_geq, _rb_tree_find_node_geq) +__weak_alias(rb_tree_find_node_leq, _rb_tree_find_node_leq) +__weak_alias(rb_tree_insert_node, _rb_tree_insert_node) +__weak_alias(rb_tree_remove_node, _rb_tree_remove_node) +__weak_alias(rb_tree_iterate, _rb_tree_iterate) +#ifdef RBDEBUG +__weak_alias(rb_tree_check, _rb_tree_check) +__weak_alias(rb_tree_depths, _rb_tree_depths) +#endif + +#include "namespace.h" +#endif + +#ifdef RBTEST +#include "rbtree.h" +#else +#include +#endif + +static void rb_tree_insert_rebalance(struct rb_tree *, struct rb_node *); +static void rb_tree_removal_rebalance(struct rb_tree *, struct rb_node *, + unsigned int); +#ifdef RBDEBUG +static const struct rb_node *rb_tree_iterate_const(const struct rb_tree *, + const struct rb_node *, const unsigned int); +static bool rb_tree_check_node(const struct rb_tree *, const struct rb_node *, + const struct rb_node *, bool); +#else +#define rb_tree_check_node(a, b, c, d) true +#endif + +#define RB_NODETOITEM(rbto, rbn) \ + ((void *)((uintptr_t)(rbn) - (rbto)->rbto_node_offset)) +#define RB_ITEMTONODE(rbto, rbn) \ + ((rb_node_t *)((uintptr_t)(rbn) + (rbto)->rbto_node_offset)) + +#define RB_SENTINEL_NODE NULL + +void +rb_tree_init(struct rb_tree *rbt, const rb_tree_ops_t *ops) +{ + + rbt->rbt_ops = ops; + rbt->rbt_root = RB_SENTINEL_NODE; + RB_TAILQ_INIT(&rbt->rbt_nodes); +#ifndef RBSMALL + rbt->rbt_minmax[RB_DIR_LEFT] = rbt->rbt_root; /* minimum node */ + rbt->rbt_minmax[RB_DIR_RIGHT] = rbt->rbt_root; /* maximum node */ +#endif +#ifdef RBSTATS + rbt->rbt_count = 0; + rbt->rbt_insertions = 0; + rbt->rbt_removals = 0; + rbt->rbt_insertion_rebalance_calls = 0; + rbt->rbt_insertion_rebalance_passes = 0; + rbt->rbt_removal_rebalance_calls = 0; + rbt->rbt_removal_rebalance_passes = 0; +#endif +} + +void * +rb_tree_find_node(struct rb_tree *rbt, const void *key) +{ + const rb_tree_ops_t *rbto = rbt->rbt_ops; + rbto_compare_key_fn compare_key = rbto->rbto_compare_key; + struct rb_node *parent = rbt->rbt_root; + + while (!RB_SENTINEL_P(parent)) { + void *pobj = RB_NODETOITEM(rbto, parent); + const signed int diff = (*compare_key)(rbto->rbto_context, + pobj, key); + if (diff == 0) + return pobj; + parent = parent->rb_nodes[diff < 0]; + } + + return NULL; +} + +void * +rb_tree_find_node_geq(struct rb_tree *rbt, const void *key) +{ + const rb_tree_ops_t *rbto = rbt->rbt_ops; + rbto_compare_key_fn compare_key = rbto->rbto_compare_key; + struct rb_node *parent = rbt->rbt_root, *last = NULL; + + while (!RB_SENTINEL_P(parent)) { + void *pobj = RB_NODETOITEM(rbto, parent); + const signed int diff = (*compare_key)(rbto->rbto_context, + pobj, key); + if (diff == 0) + return pobj; + if (diff > 0) + last = parent; + parent = parent->rb_nodes[diff < 0]; + } + + return last == NULL ? NULL : RB_NODETOITEM(rbto, last); +} + +void * +rb_tree_find_node_leq(struct rb_tree *rbt, const void *key) +{ + const rb_tree_ops_t *rbto = rbt->rbt_ops; + rbto_compare_key_fn compare_key = rbto->rbto_compare_key; + struct rb_node *parent = rbt->rbt_root, *last = NULL; + + while (!RB_SENTINEL_P(parent)) { + void *pobj = RB_NODETOITEM(rbto, parent); + const signed int diff = (*compare_key)(rbto->rbto_context, + pobj, key); + if (diff == 0) + return pobj; + if (diff < 0) + last = parent; + parent = parent->rb_nodes[diff < 0]; + } + + return last == NULL ? NULL : RB_NODETOITEM(rbto, last); +} + +void * +rb_tree_insert_node(struct rb_tree *rbt, void *object) +{ + const rb_tree_ops_t *rbto = rbt->rbt_ops; + rbto_compare_nodes_fn compare_nodes = rbto->rbto_compare_nodes; + struct rb_node *parent, *tmp, *self = RB_ITEMTONODE(rbto, object); + unsigned int position; + bool rebalance; + + RBSTAT_INC(rbt->rbt_insertions); + + tmp = rbt->rbt_root; + /* + * This is a hack. Because rbt->rbt_root is just a struct rb_node *, + * just like rb_node->rb_nodes[RB_DIR_LEFT], we can use this fact to + * avoid a lot of tests for root and know that even at root, + * updating RB_FATHER(rb_node)->rb_nodes[RB_POSITION(rb_node)] will + * update rbt->rbt_root. + */ + parent = (struct rb_node *)(void *)&rbt->rbt_root; + position = RB_DIR_LEFT; + + /* + * Find out where to place this new leaf. + */ + while (!RB_SENTINEL_P(tmp)) { + void *tobj = RB_NODETOITEM(rbto, tmp); + const signed int diff = (*compare_nodes)(rbto->rbto_context, + tobj, object); + if (__predict_false(diff == 0)) { + /* + * Node already exists; return it. + */ + return tobj; + } + parent = tmp; + position = (diff < 0); + tmp = parent->rb_nodes[position]; + } + +#ifdef RBDEBUG + { + struct rb_node *prev = NULL, *next = NULL; + + if (position == RB_DIR_RIGHT) + prev = parent; + else if (tmp != rbt->rbt_root) + next = parent; + + /* + * Verify our sequential position + */ + KASSERT(prev == NULL || !RB_SENTINEL_P(prev)); + KASSERT(next == NULL || !RB_SENTINEL_P(next)); + if (prev != NULL && next == NULL) + next = TAILQ_NEXT(prev, rb_link); + if (prev == NULL && next != NULL) + prev = TAILQ_PREV(next, rb_node_qh, rb_link); + KASSERT(prev == NULL || !RB_SENTINEL_P(prev)); + KASSERT(next == NULL || !RB_SENTINEL_P(next)); + KASSERT(prev == NULL || (*compare_nodes)(rbto->rbto_context, + RB_NODETOITEM(rbto, prev), RB_NODETOITEM(rbto, self)) < 0); + KASSERT(next == NULL || (*compare_nodes)(rbto->rbto_context, + RB_NODETOITEM(rbto, self), RB_NODETOITEM(rbto, next)) < 0); + } +#endif + + /* + * Initialize the node and insert as a leaf into the tree. + */ + RB_SET_FATHER(self, parent); + RB_SET_POSITION(self, position); + if (__predict_false(parent == (struct rb_node *)(void *)&rbt->rbt_root)) { + RB_MARK_BLACK(self); /* root is always black */ +#ifndef RBSMALL + rbt->rbt_minmax[RB_DIR_LEFT] = self; + rbt->rbt_minmax[RB_DIR_RIGHT] = self; +#endif + rebalance = false; + } else { + KASSERT(position == RB_DIR_LEFT || position == RB_DIR_RIGHT); +#ifndef RBSMALL + /* + * Keep track of the minimum and maximum nodes. If our + * parent is a minmax node and we on their min/max side, + * we must be the new min/max node. + */ + if (parent == rbt->rbt_minmax[position]) + rbt->rbt_minmax[position] = self; +#endif /* !RBSMALL */ + /* + * All new nodes are colored red. We only need to rebalance + * if our parent is also red. + */ + RB_MARK_RED(self); + rebalance = RB_RED_P(parent); + } + KASSERT(RB_SENTINEL_P(parent->rb_nodes[position])); + self->rb_left = parent->rb_nodes[position]; + self->rb_right = parent->rb_nodes[position]; + parent->rb_nodes[position] = self; + KASSERT(RB_CHILDLESS_P(self)); + + /* + * Insert the new node into a sorted list for easy sequential access + */ + RBSTAT_INC(rbt->rbt_count); +#ifdef RBDEBUG + if (RB_ROOT_P(rbt, self)) { + RB_TAILQ_INSERT_HEAD(&rbt->rbt_nodes, self, rb_link); + } else if (position == RB_DIR_LEFT) { + KASSERT((*compare_nodes)(rbto->rbto_context, + RB_NODETOITEM(rbto, self), + RB_NODETOITEM(rbto, RB_FATHER(self))) < 0); + RB_TAILQ_INSERT_BEFORE(RB_FATHER(self), self, rb_link); + } else { + KASSERT((*compare_nodes)(rbto->rbto_context, + RB_NODETOITEM(rbto, RB_FATHER(self)), + RB_NODETOITEM(rbto, self)) < 0); + RB_TAILQ_INSERT_AFTER(&rbt->rbt_nodes, RB_FATHER(self), + self, rb_link); + } +#endif + KASSERT(rb_tree_check_node(rbt, self, NULL, !rebalance)); + + /* + * Rebalance tree after insertion + */ + if (rebalance) { + rb_tree_insert_rebalance(rbt, self); + KASSERT(rb_tree_check_node(rbt, self, NULL, true)); + } + + /* Succesfully inserted, return our node pointer. */ + return object; +} + +/* + * Swap the location and colors of 'self' and its child @ which. The child + * can not be a sentinel node. This is our rotation function. However, + * since it preserves coloring, it great simplifies both insertion and + * removal since rotation almost always involves the exchanging of colors + * as a separate step. + */ +static void +rb_tree_reparent_nodes(__rbt_unused struct rb_tree *rbt, + struct rb_node *old_father, const unsigned int which) +{ + const unsigned int other = which ^ RB_DIR_OTHER; + struct rb_node * const grandpa = RB_FATHER(old_father); + struct rb_node * const old_child = old_father->rb_nodes[which]; + struct rb_node * const new_father = old_child; + struct rb_node * const new_child = old_father; + + KASSERT(which == RB_DIR_LEFT || which == RB_DIR_RIGHT); + + KASSERT(!RB_SENTINEL_P(old_child)); + KASSERT(RB_FATHER(old_child) == old_father); + + KASSERT(rb_tree_check_node(rbt, old_father, NULL, false)); + KASSERT(rb_tree_check_node(rbt, old_child, NULL, false)); + KASSERT(RB_ROOT_P(rbt, old_father) || + rb_tree_check_node(rbt, grandpa, NULL, false)); + + /* + * Exchange descendant linkages. + */ + grandpa->rb_nodes[RB_POSITION(old_father)] = new_father; + new_child->rb_nodes[which] = old_child->rb_nodes[other]; + new_father->rb_nodes[other] = new_child; + + /* + * Update ancestor linkages + */ + RB_SET_FATHER(new_father, grandpa); + RB_SET_FATHER(new_child, new_father); + + /* + * Exchange properties between new_father and new_child. The only + * change is that new_child's position is now on the other side. + */ +#if 0 + { + struct rb_node tmp; + tmp.rb_info = 0; + RB_COPY_PROPERTIES(&tmp, old_child); + RB_COPY_PROPERTIES(new_father, old_father); + RB_COPY_PROPERTIES(new_child, &tmp); + } +#else + RB_SWAP_PROPERTIES(new_father, new_child); +#endif + RB_SET_POSITION(new_child, other); + + /* + * Make sure to reparent the new child to ourself. + */ + if (!RB_SENTINEL_P(new_child->rb_nodes[which])) { + RB_SET_FATHER(new_child->rb_nodes[which], new_child); + RB_SET_POSITION(new_child->rb_nodes[which], which); + } + + KASSERT(rb_tree_check_node(rbt, new_father, NULL, false)); + KASSERT(rb_tree_check_node(rbt, new_child, NULL, false)); + KASSERT(RB_ROOT_P(rbt, new_father) || + rb_tree_check_node(rbt, grandpa, NULL, false)); +} + +static void +rb_tree_insert_rebalance(struct rb_tree *rbt, struct rb_node *self) +{ + struct rb_node * father = RB_FATHER(self); + struct rb_node * grandpa = RB_FATHER(father); + struct rb_node * uncle; + unsigned int which; + unsigned int other; + + KASSERT(!RB_ROOT_P(rbt, self)); + KASSERT(RB_RED_P(self)); + KASSERT(RB_RED_P(father)); + RBSTAT_INC(rbt->rbt_insertion_rebalance_calls); + + for (;;) { + KASSERT(!RB_SENTINEL_P(self)); + + KASSERT(RB_RED_P(self)); + KASSERT(RB_RED_P(father)); + /* + * We are red and our parent is red, therefore we must have a + * grandfather and he must be black. + */ + grandpa = RB_FATHER(father); + KASSERT(RB_BLACK_P(grandpa)); + KASSERT(RB_DIR_RIGHT == 1 && RB_DIR_LEFT == 0); + which = (father == grandpa->rb_right); + other = which ^ RB_DIR_OTHER; + uncle = grandpa->rb_nodes[other]; + + if (RB_BLACK_P(uncle)) + break; + + RBSTAT_INC(rbt->rbt_insertion_rebalance_passes); + /* + * Case 1: our uncle is red + * Simply invert the colors of our parent and + * uncle and make our grandparent red. And + * then solve the problem up at his level. + */ + RB_MARK_BLACK(uncle); + RB_MARK_BLACK(father); + if (__predict_false(RB_ROOT_P(rbt, grandpa))) { + /* + * If our grandpa is root, don't bother + * setting him to red, just return. + */ + KASSERT(RB_BLACK_P(grandpa)); + return; + } + RB_MARK_RED(grandpa); + self = grandpa; + father = RB_FATHER(self); + KASSERT(RB_RED_P(self)); + if (RB_BLACK_P(father)) { + /* + * If our greatgrandpa is black, we're done. + */ + KASSERT(RB_BLACK_P(rbt->rbt_root)); + return; + } + } + + KASSERT(!RB_ROOT_P(rbt, self)); + KASSERT(RB_RED_P(self)); + KASSERT(RB_RED_P(father)); + KASSERT(RB_BLACK_P(uncle)); + KASSERT(RB_BLACK_P(grandpa)); + /* + * Case 2&3: our uncle is black. + */ + if (self == father->rb_nodes[other]) { + /* + * Case 2: we are on the same side as our uncle + * Swap ourselves with our parent so this case + * becomes case 3. Basically our parent becomes our + * child. + */ + rb_tree_reparent_nodes(rbt, father, other); + KASSERT(RB_FATHER(father) == self); + KASSERT(self->rb_nodes[which] == father); + KASSERT(RB_FATHER(self) == grandpa); + self = father; + father = RB_FATHER(self); + } + KASSERT(RB_RED_P(self) && RB_RED_P(father)); + KASSERT(grandpa->rb_nodes[which] == father); + /* + * Case 3: we are opposite a child of a black uncle. + * Swap our parent and grandparent. Since our grandfather + * is black, our father will become black and our new sibling + * (former grandparent) will become red. + */ + rb_tree_reparent_nodes(rbt, grandpa, which); + KASSERT(RB_FATHER(self) == father); + KASSERT(RB_FATHER(self)->rb_nodes[RB_POSITION(self) ^ RB_DIR_OTHER] == grandpa); + KASSERT(RB_RED_P(self)); + KASSERT(RB_BLACK_P(father)); + KASSERT(RB_RED_P(grandpa)); + + /* + * Final step: Set the root to black. + */ + RB_MARK_BLACK(rbt->rbt_root); +} + +static void +rb_tree_prune_node(struct rb_tree *rbt, struct rb_node *self, bool rebalance) +{ + const unsigned int which = RB_POSITION(self); + struct rb_node *father = RB_FATHER(self); +#ifndef RBSMALL + const bool was_root = RB_ROOT_P(rbt, self); +#endif + + KASSERT(rebalance || (RB_ROOT_P(rbt, self) || RB_RED_P(self))); + KASSERT(!rebalance || RB_BLACK_P(self)); + KASSERT(RB_CHILDLESS_P(self)); + KASSERT(rb_tree_check_node(rbt, self, NULL, false)); + + /* + * Since we are childless, we know that self->rb_left is pointing + * to the sentinel node. + */ + father->rb_nodes[which] = self->rb_left; + + /* + * Remove ourselves from the node list, decrement the count, + * and update min/max. + */ + RB_TAILQ_REMOVE(&rbt->rbt_nodes, self, rb_link); + RBSTAT_DEC(rbt->rbt_count); +#ifndef RBSMALL + if (__predict_false(rbt->rbt_minmax[RB_POSITION(self)] == self)) { + rbt->rbt_minmax[RB_POSITION(self)] = father; + /* + * When removing the root, rbt->rbt_minmax[RB_DIR_LEFT] is + * updated automatically, but we also need to update + * rbt->rbt_minmax[RB_DIR_RIGHT]; + */ + if (__predict_false(was_root)) { + rbt->rbt_minmax[RB_DIR_RIGHT] = father; + } + } + RB_SET_FATHER(self, NULL); +#endif + + /* + * Rebalance if requested. + */ + if (rebalance) + rb_tree_removal_rebalance(rbt, father, which); + KASSERT(was_root || rb_tree_check_node(rbt, father, NULL, true)); +} + +/* + * When deleting an interior node + */ +static void +rb_tree_swap_prune_and_rebalance(struct rb_tree *rbt, struct rb_node *self, + struct rb_node *standin) +{ + const unsigned int standin_which = RB_POSITION(standin); + unsigned int standin_other = standin_which ^ RB_DIR_OTHER; + struct rb_node *standin_son; + struct rb_node *standin_father = RB_FATHER(standin); + bool rebalance = RB_BLACK_P(standin); + + if (standin_father == self) { + /* + * As a child of self, any childen would be opposite of + * our parent. + */ + KASSERT(RB_SENTINEL_P(standin->rb_nodes[standin_other])); + standin_son = standin->rb_nodes[standin_which]; + } else { + /* + * Since we aren't a child of self, any childen would be + * on the same side as our parent. + */ + KASSERT(RB_SENTINEL_P(standin->rb_nodes[standin_which])); + standin_son = standin->rb_nodes[standin_other]; + } + + /* + * the node we are removing must have two children. + */ + KASSERT(RB_TWOCHILDREN_P(self)); + /* + * If standin has a child, it must be red. + */ + KASSERT(RB_SENTINEL_P(standin_son) || RB_RED_P(standin_son)); + + /* + * Verify things are sane. + */ + KASSERT(rb_tree_check_node(rbt, self, NULL, false)); + KASSERT(rb_tree_check_node(rbt, standin, NULL, false)); + + if (__predict_false(RB_RED_P(standin_son))) { + /* + * We know we have a red child so if we flip it to black + * we don't have to rebalance. + */ + KASSERT(rb_tree_check_node(rbt, standin_son, NULL, true)); + RB_MARK_BLACK(standin_son); + rebalance = false; + + if (standin_father == self) { + KASSERT(RB_POSITION(standin_son) == standin_which); + } else { + KASSERT(RB_POSITION(standin_son) == standin_other); + /* + * Change the son's parentage to point to his grandpa. + */ + RB_SET_FATHER(standin_son, standin_father); + RB_SET_POSITION(standin_son, standin_which); + } + } + + if (standin_father == self) { + /* + * If we are about to delete the standin's father, then when + * we call rebalance, we need to use ourselves as our father. + * Otherwise remember our original father. Also, sincef we are + * our standin's father we only need to reparent the standin's + * brother. + * + * | R --> S | + * | Q S --> Q T | + * | t --> | + */ + KASSERT(RB_SENTINEL_P(standin->rb_nodes[standin_other])); + KASSERT(!RB_SENTINEL_P(self->rb_nodes[standin_other])); + KASSERT(self->rb_nodes[standin_which] == standin); + /* + * Have our son/standin adopt his brother as his new son. + */ + standin_father = standin; + } else { + /* + * | R --> S . | + * | / \ | T --> / \ | / | + * | ..... | S --> ..... | T | + * + * Sever standin's connection to his father. + */ + standin_father->rb_nodes[standin_which] = standin_son; + /* + * Adopt the far son. + */ + standin->rb_nodes[standin_other] = self->rb_nodes[standin_other]; + RB_SET_FATHER(standin->rb_nodes[standin_other], standin); + KASSERT(RB_POSITION(self->rb_nodes[standin_other]) == standin_other); + /* + * Use standin_other because we need to preserve standin_which + * for the removal_rebalance. + */ + standin_other = standin_which; + } + + /* + * Move the only remaining son to our standin. If our standin is our + * son, this will be the only son needed to be moved. + */ + KASSERT(standin->rb_nodes[standin_other] != self->rb_nodes[standin_other]); + standin->rb_nodes[standin_other] = self->rb_nodes[standin_other]; + RB_SET_FATHER(standin->rb_nodes[standin_other], standin); + + /* + * Now copy the result of self to standin and then replace + * self with standin in the tree. + */ + RB_COPY_PROPERTIES(standin, self); + RB_SET_FATHER(standin, RB_FATHER(self)); + RB_FATHER(standin)->rb_nodes[RB_POSITION(standin)] = standin; + + /* + * Remove ourselves from the node list, decrement the count, + * and update min/max. + */ + RB_TAILQ_REMOVE(&rbt->rbt_nodes, self, rb_link); + RBSTAT_DEC(rbt->rbt_count); +#ifndef RBSMALL + if (__predict_false(rbt->rbt_minmax[RB_POSITION(self)] == self)) + rbt->rbt_minmax[RB_POSITION(self)] = RB_FATHER(self); + RB_SET_FATHER(self, NULL); +#endif + + KASSERT(rb_tree_check_node(rbt, standin, NULL, false)); + KASSERT(RB_FATHER_SENTINEL_P(standin) + || rb_tree_check_node(rbt, standin_father, NULL, false)); + KASSERT(RB_LEFT_SENTINEL_P(standin) + || rb_tree_check_node(rbt, standin->rb_left, NULL, false)); + KASSERT(RB_RIGHT_SENTINEL_P(standin) + || rb_tree_check_node(rbt, standin->rb_right, NULL, false)); + + if (!rebalance) + return; + + rb_tree_removal_rebalance(rbt, standin_father, standin_which); + KASSERT(rb_tree_check_node(rbt, standin, NULL, true)); +} + +/* + * We could do this by doing + * rb_tree_node_swap(rbt, self, which); + * rb_tree_prune_node(rbt, self, false); + * + * But it's more efficient to just evalate and recolor the child. + */ +static void +rb_tree_prune_blackred_branch(struct rb_tree *rbt, struct rb_node *self, + unsigned int which) +{ + struct rb_node *father = RB_FATHER(self); + struct rb_node *son = self->rb_nodes[which]; +#ifndef RBSMALL + const bool was_root = RB_ROOT_P(rbt, self); +#endif + + KASSERT(which == RB_DIR_LEFT || which == RB_DIR_RIGHT); + KASSERT(RB_BLACK_P(self) && RB_RED_P(son)); + KASSERT(!RB_TWOCHILDREN_P(son)); + KASSERT(RB_CHILDLESS_P(son)); + KASSERT(rb_tree_check_node(rbt, self, NULL, false)); + KASSERT(rb_tree_check_node(rbt, son, NULL, false)); + + /* + * Remove ourselves from the tree and give our former child our + * properties (position, color, root). + */ + RB_COPY_PROPERTIES(son, self); + father->rb_nodes[RB_POSITION(son)] = son; + RB_SET_FATHER(son, father); + + /* + * Remove ourselves from the node list, decrement the count, + * and update minmax. + */ + RB_TAILQ_REMOVE(&rbt->rbt_nodes, self, rb_link); + RBSTAT_DEC(rbt->rbt_count); +#ifndef RBSMALL + if (__predict_false(was_root)) { + KASSERT(rbt->rbt_minmax[which] == son); + rbt->rbt_minmax[which ^ RB_DIR_OTHER] = son; + } else if (rbt->rbt_minmax[RB_POSITION(self)] == self) { + rbt->rbt_minmax[RB_POSITION(self)] = son; + } + RB_SET_FATHER(self, NULL); +#endif + + KASSERT(was_root || rb_tree_check_node(rbt, father, NULL, true)); + KASSERT(rb_tree_check_node(rbt, son, NULL, true)); +} + +void +rb_tree_remove_node(struct rb_tree *rbt, void *object) +{ + const rb_tree_ops_t *rbto = rbt->rbt_ops; + struct rb_node *standin, *self = RB_ITEMTONODE(rbto, object); + unsigned int which; + + KASSERT(!RB_SENTINEL_P(self)); + RBSTAT_INC(rbt->rbt_removals); + + /* + * In the following diagrams, we (the node to be removed) are S. Red + * nodes are lowercase. T could be either red or black. + * + * Remember the major axiom of the red-black tree: the number of + * black nodes from the root to each leaf is constant across all + * leaves, only the number of red nodes varies. + * + * Thus removing a red leaf doesn't require any other changes to a + * red-black tree. So if we must remove a node, attempt to rearrange + * the tree so we can remove a red node. + * + * The simpliest case is a childless red node or a childless root node: + * + * | T --> T | or | R --> * | + * | s --> * | + */ + if (RB_CHILDLESS_P(self)) { + const bool rebalance = RB_BLACK_P(self) && !RB_ROOT_P(rbt, self); + rb_tree_prune_node(rbt, self, rebalance); + return; + } + KASSERT(!RB_CHILDLESS_P(self)); + if (!RB_TWOCHILDREN_P(self)) { + /* + * The next simpliest case is the node we are deleting is + * black and has one red child. + * + * | T --> T --> T | + * | S --> R --> R | + * | r --> s --> * | + */ + which = RB_LEFT_SENTINEL_P(self) ? RB_DIR_RIGHT : RB_DIR_LEFT; + KASSERT(RB_BLACK_P(self)); + KASSERT(RB_RED_P(self->rb_nodes[which])); + KASSERT(RB_CHILDLESS_P(self->rb_nodes[which])); + rb_tree_prune_blackred_branch(rbt, self, which); + return; + } + KASSERT(RB_TWOCHILDREN_P(self)); + + /* + * We invert these because we prefer to remove from the inside of + * the tree. + */ + which = RB_POSITION(self) ^ RB_DIR_OTHER; + + /* + * Let's find the node closes to us opposite of our parent + * Now swap it with ourself, "prune" it, and rebalance, if needed. + */ + standin = RB_ITEMTONODE(rbto, rb_tree_iterate(rbt, object, which)); + rb_tree_swap_prune_and_rebalance(rbt, self, standin); +} + +static void +rb_tree_removal_rebalance(struct rb_tree *rbt, struct rb_node *parent, + unsigned int which) +{ + KASSERT(!RB_SENTINEL_P(parent)); + KASSERT(RB_SENTINEL_P(parent->rb_nodes[which])); + KASSERT(which == RB_DIR_LEFT || which == RB_DIR_RIGHT); + RBSTAT_INC(rbt->rbt_removal_rebalance_calls); + + while (RB_BLACK_P(parent->rb_nodes[which])) { + unsigned int other = which ^ RB_DIR_OTHER; + struct rb_node *brother = parent->rb_nodes[other]; + + RBSTAT_INC(rbt->rbt_removal_rebalance_passes); + + KASSERT(!RB_SENTINEL_P(brother)); + /* + * For cases 1, 2a, and 2b, our brother's children must + * be black and our father must be black + */ + if (RB_BLACK_P(parent) + && RB_BLACK_P(brother->rb_left) + && RB_BLACK_P(brother->rb_right)) { + if (RB_RED_P(brother)) { + /* + * Case 1: Our brother is red, swap its + * position (and colors) with our parent. + * This should now be case 2b (unless C or E + * has a red child which is case 3; thus no + * explicit branch to case 2b). + * + * B -> D + * A d -> b E + * C E -> A C + */ + KASSERT(RB_BLACK_P(parent)); + rb_tree_reparent_nodes(rbt, parent, other); + brother = parent->rb_nodes[other]; + KASSERT(!RB_SENTINEL_P(brother)); + KASSERT(RB_RED_P(parent)); + KASSERT(RB_BLACK_P(brother)); + KASSERT(rb_tree_check_node(rbt, brother, NULL, false)); + KASSERT(rb_tree_check_node(rbt, parent, NULL, false)); + } else { + /* + * Both our parent and brother are black. + * Change our brother to red, advance up rank + * and go through the loop again. + * + * B -> *B + * *A D -> A d + * C E -> C E + */ + RB_MARK_RED(brother); + KASSERT(RB_BLACK_P(brother->rb_left)); + KASSERT(RB_BLACK_P(brother->rb_right)); + if (RB_ROOT_P(rbt, parent)) + return; /* root == parent == black */ + KASSERT(rb_tree_check_node(rbt, brother, NULL, false)); + KASSERT(rb_tree_check_node(rbt, parent, NULL, false)); + which = RB_POSITION(parent); + parent = RB_FATHER(parent); + continue; + } + } + /* + * Avoid an else here so that case 2a above can hit either + * case 2b, 3, or 4. + */ + if (RB_RED_P(parent) + && RB_BLACK_P(brother) + && RB_BLACK_P(brother->rb_left) + && RB_BLACK_P(brother->rb_right)) { + KASSERT(RB_RED_P(parent)); + KASSERT(RB_BLACK_P(brother)); + KASSERT(RB_BLACK_P(brother->rb_left)); + KASSERT(RB_BLACK_P(brother->rb_right)); + /* + * We are black, our father is red, our brother and + * both nephews are black. Simply invert/exchange the + * colors of our father and brother (to black and red + * respectively). + * + * | f --> F | + * | * B --> * b | + * | N N --> N N | + */ + RB_MARK_BLACK(parent); + RB_MARK_RED(brother); + KASSERT(rb_tree_check_node(rbt, brother, NULL, true)); + break; /* We're done! */ + } else { + /* + * Our brother must be black and have at least one + * red child (it may have two). + */ + KASSERT(RB_BLACK_P(brother)); + KASSERT(RB_RED_P(brother->rb_nodes[which]) || + RB_RED_P(brother->rb_nodes[other])); + if (RB_BLACK_P(brother->rb_nodes[other])) { + /* + * Case 3: our brother is black, our near + * nephew is red, and our far nephew is black. + * Swap our brother with our near nephew. + * This result in a tree that matches case 4. + * (Our father could be red or black). + * + * | F --> F | + * | x B --> x B | + * | n --> n | + */ + KASSERT(RB_RED_P(brother->rb_nodes[which])); + rb_tree_reparent_nodes(rbt, brother, which); + KASSERT(RB_FATHER(brother) == parent->rb_nodes[other]); + brother = parent->rb_nodes[other]; + KASSERT(RB_RED_P(brother->rb_nodes[other])); + } + /* + * Case 4: our brother is black and our far nephew + * is red. Swap our father and brother locations and + * change our far nephew to black. (these can be + * done in either order so we change the color first). + * The result is a valid red-black tree and is a + * terminal case. (again we don't care about the + * father's color) + * + * If the father is red, we will get a red-black-black + * tree: + * | f -> f --> b | + * | B -> B --> F N | + * | n -> N --> | + * + * If the father is black, we will get an all black + * tree: + * | F -> F --> B | + * | B -> B --> F N | + * | n -> N --> | + * + * If we had two red nephews, then after the swap, + * our former father would have a red grandson. + */ + KASSERT(RB_BLACK_P(brother)); + KASSERT(RB_RED_P(brother->rb_nodes[other])); + RB_MARK_BLACK(brother->rb_nodes[other]); + rb_tree_reparent_nodes(rbt, parent, other); + break; /* We're done! */ + } + } + KASSERT(rb_tree_check_node(rbt, parent, NULL, true)); +} + +void * +rb_tree_iterate(struct rb_tree *rbt, void *object, const unsigned int direction) +{ + const rb_tree_ops_t *rbto = rbt->rbt_ops; + const unsigned int other = direction ^ RB_DIR_OTHER; + struct rb_node *self; + + KASSERT(direction == RB_DIR_LEFT || direction == RB_DIR_RIGHT); + + if (object == NULL) { +#ifndef RBSMALL + if (RB_SENTINEL_P(rbt->rbt_root)) + return NULL; + return RB_NODETOITEM(rbto, rbt->rbt_minmax[direction]); +#else + self = rbt->rbt_root; + if (RB_SENTINEL_P(self)) + return NULL; + while (!RB_SENTINEL_P(self->rb_nodes[direction])) + self = self->rb_nodes[direction]; + return RB_NODETOITEM(rbto, self); +#endif /* !RBSMALL */ + } + self = RB_ITEMTONODE(rbto, object); + KASSERT(!RB_SENTINEL_P(self)); + /* + * We can't go any further in this direction. We proceed up in the + * opposite direction until our parent is in direction we want to go. + */ + if (RB_SENTINEL_P(self->rb_nodes[direction])) { + while (!RB_ROOT_P(rbt, self)) { + if (other == RB_POSITION(self)) + return RB_NODETOITEM(rbto, RB_FATHER(self)); + self = RB_FATHER(self); + } + return NULL; + } + + /* + * Advance down one in current direction and go down as far as possible + * in the opposite direction. + */ + self = self->rb_nodes[direction]; + KASSERT(!RB_SENTINEL_P(self)); + while (!RB_SENTINEL_P(self->rb_nodes[other])) + self = self->rb_nodes[other]; + return RB_NODETOITEM(rbto, self); +} + +#ifdef RBDEBUG +static const struct rb_node * +rb_tree_iterate_const(const struct rb_tree *rbt, const struct rb_node *self, + const unsigned int direction) +{ + const unsigned int other = direction ^ RB_DIR_OTHER; + KASSERT(direction == RB_DIR_LEFT || direction == RB_DIR_RIGHT); + + if (self == NULL) { +#ifndef RBSMALL + if (RB_SENTINEL_P(rbt->rbt_root)) + return NULL; + return rbt->rbt_minmax[direction]; +#else + self = rbt->rbt_root; + if (RB_SENTINEL_P(self)) + return NULL; + while (!RB_SENTINEL_P(self->rb_nodes[direction])) + self = self->rb_nodes[direction]; + return self; +#endif /* !RBSMALL */ + } + KASSERT(!RB_SENTINEL_P(self)); + /* + * We can't go any further in this direction. We proceed up in the + * opposite direction until our parent is in direction we want to go. + */ + if (RB_SENTINEL_P(self->rb_nodes[direction])) { + while (!RB_ROOT_P(rbt, self)) { + if (other == RB_POSITION(self)) + return RB_FATHER(self); + self = RB_FATHER(self); + } + return NULL; + } + + /* + * Advance down one in current direction and go down as far as possible + * in the opposite direction. + */ + self = self->rb_nodes[direction]; + KASSERT(!RB_SENTINEL_P(self)); + while (!RB_SENTINEL_P(self->rb_nodes[other])) + self = self->rb_nodes[other]; + return self; +} + +static unsigned int +rb_tree_count_black(const struct rb_node *self) +{ + unsigned int left, right; + + if (RB_SENTINEL_P(self)) + return 0; + + left = rb_tree_count_black(self->rb_left); + right = rb_tree_count_black(self->rb_right); + + KASSERT(left == right); + + return left + RB_BLACK_P(self); +} + +static bool +rb_tree_check_node(const struct rb_tree *rbt, const struct rb_node *self, + const struct rb_node *prev, bool red_check) +{ + const rb_tree_ops_t *rbto = rbt->rbt_ops; + rbto_compare_nodes_fn compare_nodes = rbto->rbto_compare_nodes; + + KASSERT(!RB_SENTINEL_P(self)); + KASSERT(prev == NULL || (*compare_nodes)(rbto->rbto_context, + RB_NODETOITEM(rbto, prev), RB_NODETOITEM(rbto, self)) < 0); + + /* + * Verify our relationship to our parent. + */ + if (RB_ROOT_P(rbt, self)) { + KASSERT(self == rbt->rbt_root); + KASSERT(RB_POSITION(self) == RB_DIR_LEFT); + KASSERT(RB_FATHER(self)->rb_nodes[RB_DIR_LEFT] == self); + KASSERT(RB_FATHER(self) == (const struct rb_node *) &rbt->rbt_root); + } else { + int diff = (*compare_nodes)(rbto->rbto_context, + RB_NODETOITEM(rbto, self), + RB_NODETOITEM(rbto, RB_FATHER(self))); + + KASSERT(self != rbt->rbt_root); + KASSERT(!RB_FATHER_SENTINEL_P(self)); + if (RB_POSITION(self) == RB_DIR_LEFT) { + KASSERT(diff < 0); + KASSERT(RB_FATHER(self)->rb_nodes[RB_DIR_LEFT] == self); + } else { + KASSERT(diff > 0); + KASSERT(RB_FATHER(self)->rb_nodes[RB_DIR_RIGHT] == self); + } + } + + /* + * Verify our position in the linked list against the tree itself. + */ + { + const struct rb_node *prev0 = rb_tree_iterate_const(rbt, self, RB_DIR_LEFT); + const struct rb_node *next0 = rb_tree_iterate_const(rbt, self, RB_DIR_RIGHT); + KASSERT(prev0 == TAILQ_PREV(self, rb_node_qh, rb_link)); + KASSERT(next0 == TAILQ_NEXT(self, rb_link)); +#ifndef RBSMALL + KASSERT(prev0 != NULL || self == rbt->rbt_minmax[RB_DIR_LEFT]); + KASSERT(next0 != NULL || self == rbt->rbt_minmax[RB_DIR_RIGHT]); +#endif + } + + /* + * The root must be black. + * There can never be two adjacent red nodes. + */ + if (red_check) { + KASSERT(!RB_ROOT_P(rbt, self) || RB_BLACK_P(self)); + (void) rb_tree_count_black(self); + if (RB_RED_P(self)) { + const struct rb_node *brother; + KASSERT(!RB_ROOT_P(rbt, self)); + brother = RB_FATHER(self)->rb_nodes[RB_POSITION(self) ^ RB_DIR_OTHER]; + KASSERT(RB_BLACK_P(RB_FATHER(self))); + /* + * I'm red and have no children, then I must either + * have no brother or my brother also be red and + * also have no children. (black count == 0) + */ + KASSERT(!RB_CHILDLESS_P(self) + || RB_SENTINEL_P(brother) + || RB_RED_P(brother) + || RB_CHILDLESS_P(brother)); + /* + * If I'm not childless, I must have two children + * and they must be both be black. + */ + KASSERT(RB_CHILDLESS_P(self) + || (RB_TWOCHILDREN_P(self) + && RB_BLACK_P(self->rb_left) + && RB_BLACK_P(self->rb_right))); + /* + * If I'm not childless, thus I have black children, + * then my brother must either be black or have two + * black children. + */ + KASSERT(RB_CHILDLESS_P(self) + || RB_BLACK_P(brother) + || (RB_TWOCHILDREN_P(brother) + && RB_BLACK_P(brother->rb_left) + && RB_BLACK_P(brother->rb_right))); + } else { + /* + * If I'm black and have one child, that child must + * be red and childless. + */ + KASSERT(RB_CHILDLESS_P(self) + || RB_TWOCHILDREN_P(self) + || (!RB_LEFT_SENTINEL_P(self) + && RB_RIGHT_SENTINEL_P(self) + && RB_RED_P(self->rb_left) + && RB_CHILDLESS_P(self->rb_left)) + || (!RB_RIGHT_SENTINEL_P(self) + && RB_LEFT_SENTINEL_P(self) + && RB_RED_P(self->rb_right) + && RB_CHILDLESS_P(self->rb_right))); + + /* + * If I'm a childless black node and my parent is + * black, my 2nd closet relative away from my parent + * is either red or has a red parent or red children. + */ + if (!RB_ROOT_P(rbt, self) + && RB_CHILDLESS_P(self) + && RB_BLACK_P(RB_FATHER(self))) { + const unsigned int which = RB_POSITION(self); + const unsigned int other = which ^ RB_DIR_OTHER; + const struct rb_node *relative0, *relative; + + relative0 = rb_tree_iterate_const(rbt, + self, other); + KASSERT(relative0 != NULL); + relative = rb_tree_iterate_const(rbt, + relative0, other); + KASSERT(relative != NULL); + KASSERT(RB_SENTINEL_P(relative->rb_nodes[which])); +#if 0 + KASSERT(RB_RED_P(relative) + || RB_RED_P(relative->rb_left) + || RB_RED_P(relative->rb_right) + || RB_RED_P(RB_FATHER(relative))); +#endif + } + } + /* + * A grandparent's children must be real nodes and not + * sentinels. First check out grandparent. + */ + KASSERT(RB_ROOT_P(rbt, self) + || RB_ROOT_P(rbt, RB_FATHER(self)) + || RB_TWOCHILDREN_P(RB_FATHER(RB_FATHER(self)))); + /* + * If we are have grandchildren on our left, then + * we must have a child on our right. + */ + KASSERT(RB_LEFT_SENTINEL_P(self) + || RB_CHILDLESS_P(self->rb_left) + || !RB_RIGHT_SENTINEL_P(self)); + /* + * If we are have grandchildren on our right, then + * we must have a child on our left. + */ + KASSERT(RB_RIGHT_SENTINEL_P(self) + || RB_CHILDLESS_P(self->rb_right) + || !RB_LEFT_SENTINEL_P(self)); + + /* + * If we have a child on the left and it doesn't have two + * children make sure we don't have great-great-grandchildren on + * the right. + */ + KASSERT(RB_TWOCHILDREN_P(self->rb_left) + || RB_CHILDLESS_P(self->rb_right) + || RB_CHILDLESS_P(self->rb_right->rb_left) + || RB_CHILDLESS_P(self->rb_right->rb_left->rb_left) + || RB_CHILDLESS_P(self->rb_right->rb_left->rb_right) + || RB_CHILDLESS_P(self->rb_right->rb_right) + || RB_CHILDLESS_P(self->rb_right->rb_right->rb_left) + || RB_CHILDLESS_P(self->rb_right->rb_right->rb_right)); + + /* + * If we have a child on the right and it doesn't have two + * children make sure we don't have great-great-grandchildren on + * the left. + */ + KASSERT(RB_TWOCHILDREN_P(self->rb_right) + || RB_CHILDLESS_P(self->rb_left) + || RB_CHILDLESS_P(self->rb_left->rb_left) + || RB_CHILDLESS_P(self->rb_left->rb_left->rb_left) + || RB_CHILDLESS_P(self->rb_left->rb_left->rb_right) + || RB_CHILDLESS_P(self->rb_left->rb_right) + || RB_CHILDLESS_P(self->rb_left->rb_right->rb_left) + || RB_CHILDLESS_P(self->rb_left->rb_right->rb_right)); + + /* + * If we are fully interior node, then our predecessors and + * successors must have no children in our direction. + */ + if (RB_TWOCHILDREN_P(self)) { + const struct rb_node *prev0; + const struct rb_node *next0; + + prev0 = rb_tree_iterate_const(rbt, self, RB_DIR_LEFT); + KASSERT(prev0 != NULL); + KASSERT(RB_RIGHT_SENTINEL_P(prev0)); + + next0 = rb_tree_iterate_const(rbt, self, RB_DIR_RIGHT); + KASSERT(next0 != NULL); + KASSERT(RB_LEFT_SENTINEL_P(next0)); + } + } + + return true; +} + +void +rb_tree_check(const struct rb_tree *rbt, bool red_check) +{ + const struct rb_node *self; + const struct rb_node *prev; +#ifdef RBSTATS + unsigned int count = 0; +#endif + + KASSERT(rbt->rbt_root != NULL); + KASSERT(RB_LEFT_P(rbt->rbt_root)); + +#if defined(RBSTATS) && !defined(RBSMALL) + KASSERT(rbt->rbt_count > 1 + || rbt->rbt_minmax[RB_DIR_LEFT] == rbt->rbt_minmax[RB_DIR_RIGHT]); +#endif + + prev = NULL; + TAILQ_FOREACH(self, &rbt->rbt_nodes, rb_link) { + rb_tree_check_node(rbt, self, prev, false); +#ifdef RBSTATS + count++; +#endif + } +#ifdef RBSTATS + KASSERT(rbt->rbt_count == count); +#endif + if (red_check) { + KASSERT(RB_BLACK_P(rbt->rbt_root)); + KASSERT(RB_SENTINEL_P(rbt->rbt_root) + || rb_tree_count_black(rbt->rbt_root)); + + /* + * The root must be black. + * There can never be two adjacent red nodes. + */ + TAILQ_FOREACH(self, &rbt->rbt_nodes, rb_link) { + rb_tree_check_node(rbt, self, NULL, true); + } + } +} +#endif /* RBDEBUG */ + +#ifdef RBSTATS +static void +rb_tree_mark_depth(const struct rb_tree *rbt, const struct rb_node *self, + size_t *depths, size_t depth) +{ + if (RB_SENTINEL_P(self)) + return; + + if (RB_TWOCHILDREN_P(self)) { + rb_tree_mark_depth(rbt, self->rb_left, depths, depth + 1); + rb_tree_mark_depth(rbt, self->rb_right, depths, depth + 1); + return; + } + depths[depth]++; + if (!RB_LEFT_SENTINEL_P(self)) { + rb_tree_mark_depth(rbt, self->rb_left, depths, depth + 1); + } + if (!RB_RIGHT_SENTINEL_P(self)) { + rb_tree_mark_depth(rbt, self->rb_right, depths, depth + 1); + } +} + +void +rb_tree_depths(const struct rb_tree *rbt, size_t *depths) +{ + rb_tree_mark_depth(rbt, rbt->rbt_root, depths, 1); +} +#endif /* RBSTATS */ diff --git a/contrib/dhcpcd/compat/rbtree.h b/contrib/dhcpcd/compat/rbtree.h new file mode 100644 index 0000000000..656da22a10 --- /dev/null +++ b/contrib/dhcpcd/compat/rbtree.h @@ -0,0 +1,211 @@ +/* $NetBSD: rbtree.h,v 1.5 2019/03/07 14:39:21 roy Exp $ */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Matt Thomas . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_RBTREE_H_ +#define _SYS_RBTREE_H_ + +#include "config.h" +#include "common.h" + +#if defined(_KERNEL) || defined(_STANDALONE) +#include +#else +#include +#include +#endif +#ifdef HAVE_SYS_QUEUE_H +#include +#else +#include "queue.h" +#endif +#if !defined(__linux__) && !defined(__QNX__) && !defined(__sun) +#include +#else +#include "endian.h" +#endif + +__BEGIN_DECLS + +typedef struct rb_node { + struct rb_node *rb_nodes[2]; +#define RB_DIR_LEFT 0 +#define RB_DIR_RIGHT 1 +#define RB_DIR_OTHER 1 +#define rb_left rb_nodes[RB_DIR_LEFT] +#define rb_right rb_nodes[RB_DIR_RIGHT] + + /* + * rb_info contains the two flags and the parent back pointer. + * We put the two flags in the low two bits since we know that + * rb_node will have an alignment of 4 or 8 bytes. + */ + uintptr_t rb_info; +#define RB_FLAG_POSITION (uintptr_t)0x2 +#define RB_FLAG_RED (uintptr_t)0x1 +#define RB_FLAG_MASK (RB_FLAG_POSITION|RB_FLAG_RED) +#define RB_FATHER(rb) \ + ((struct rb_node *)((rb)->rb_info & ~RB_FLAG_MASK)) +#define RB_SET_FATHER(rb, father) \ + ((void)((rb)->rb_info = (uintptr_t)(father)|((rb)->rb_info & RB_FLAG_MASK))) + +#define RB_SENTINEL_P(rb) ((rb) == NULL) +#define RB_LEFT_SENTINEL_P(rb) RB_SENTINEL_P((rb)->rb_left) +#define RB_RIGHT_SENTINEL_P(rb) RB_SENTINEL_P((rb)->rb_right) +#define RB_FATHER_SENTINEL_P(rb) RB_SENTINEL_P(RB_FATHER((rb))) +#define RB_CHILDLESS_P(rb) \ + (RB_SENTINEL_P(rb) || (RB_LEFT_SENTINEL_P(rb) && RB_RIGHT_SENTINEL_P(rb))) +#define RB_TWOCHILDREN_P(rb) \ + (!RB_SENTINEL_P(rb) && !RB_LEFT_SENTINEL_P(rb) && !RB_RIGHT_SENTINEL_P(rb)) + +#define RB_POSITION(rb) \ + (((rb)->rb_info & RB_FLAG_POSITION) ? RB_DIR_RIGHT : RB_DIR_LEFT) +#define RB_RIGHT_P(rb) (RB_POSITION(rb) == RB_DIR_RIGHT) +#define RB_LEFT_P(rb) (RB_POSITION(rb) == RB_DIR_LEFT) +#define RB_RED_P(rb) (!RB_SENTINEL_P(rb) && ((rb)->rb_info & RB_FLAG_RED) != 0) +#define RB_BLACK_P(rb) (RB_SENTINEL_P(rb) || ((rb)->rb_info & RB_FLAG_RED) == 0) +#define RB_MARK_RED(rb) ((void)((rb)->rb_info |= RB_FLAG_RED)) +#define RB_MARK_BLACK(rb) ((void)((rb)->rb_info &= ~RB_FLAG_RED)) +#define RB_INVERT_COLOR(rb) ((void)((rb)->rb_info ^= RB_FLAG_RED)) +#define RB_ROOT_P(rbt, rb) ((rbt)->rbt_root == (rb)) +#define RB_SET_POSITION(rb, position) \ + ((void)((position) ? ((rb)->rb_info |= RB_FLAG_POSITION) : \ + ((rb)->rb_info &= ~RB_FLAG_POSITION))) +#define RB_ZERO_PROPERTIES(rb) ((void)((rb)->rb_info &= ~RB_FLAG_MASK)) +#define RB_COPY_PROPERTIES(dst, src) \ + ((void)((dst)->rb_info ^= ((dst)->rb_info ^ (src)->rb_info) & RB_FLAG_MASK)) +#define RB_SWAP_PROPERTIES(a, b) do { \ + uintptr_t xorinfo = ((a)->rb_info ^ (b)->rb_info) & RB_FLAG_MASK; \ + (a)->rb_info ^= xorinfo; \ + (b)->rb_info ^= xorinfo; \ + } while (/*CONSTCOND*/ 0) +#ifdef RBDEBUG + TAILQ_ENTRY(rb_node) rb_link; +#endif +} rb_node_t; + +#define RB_TREE_MIN(T) rb_tree_iterate((T), NULL, RB_DIR_LEFT) +#define RB_TREE_MAX(T) rb_tree_iterate((T), NULL, RB_DIR_RIGHT) +#define RB_TREE_NEXT(T, N) rb_tree_iterate((T), (N), RB_DIR_RIGHT) +#define RB_TREE_PREV(T, N) rb_tree_iterate((T), (N), RB_DIR_LEFT) +#define RB_TREE_FOREACH(N, T) \ + for ((N) = RB_TREE_MIN(T); (N); (N) = RB_TREE_NEXT((T), (N))) +#define RB_TREE_FOREACH_REVERSE(N, T) \ + for ((N) = RB_TREE_MAX(T); (N); (N) = RB_TREE_PREV((T), (N))) +#define RB_TREE_FOREACH_SAFE(N, T, S) \ + for ((N) = RB_TREE_MIN(T); \ + (N) && ((S) = RB_TREE_NEXT((T), (N)), 1); \ + (N) = (S)) +#define RB_TREE_FOREACH_REVERSE_SAFE(N, T, S) \ + for ((N) = RB_TREE_MAX(T); \ + (N) && ((S) = RB_TREE_PREV((T), (N)), 1); \ + (N) = (S)) + +#ifdef RBDEBUG +TAILQ_HEAD(rb_node_qh, rb_node); + +#define RB_TAILQ_REMOVE(a, b, c) TAILQ_REMOVE(a, b, c) +#define RB_TAILQ_INIT(a) TAILQ_INIT(a) +#define RB_TAILQ_INSERT_HEAD(a, b, c) TAILQ_INSERT_HEAD(a, b, c) +#define RB_TAILQ_INSERT_BEFORE(a, b, c) TAILQ_INSERT_BEFORE(a, b, c) +#define RB_TAILQ_INSERT_AFTER(a, b, c, d) TAILQ_INSERT_AFTER(a, b, c, d) +#else +#define RB_TAILQ_REMOVE(a, b, c) do { } while (/*CONSTCOND*/0) +#define RB_TAILQ_INIT(a) do { } while (/*CONSTCOND*/0) +#define RB_TAILQ_INSERT_HEAD(a, b, c) do { } while (/*CONSTCOND*/0) +#define RB_TAILQ_INSERT_BEFORE(a, b, c) do { } while (/*CONSTCOND*/0) +#define RB_TAILQ_INSERT_AFTER(a, b, c, d) do { } while (/*CONSTCOND*/0) +#endif /* RBDEBUG */ + +/* + * rbto_compare_nodes_fn: + * return a positive value if the first node > the second node. + * return a negative value if the first node < the second node. + * return 0 if they are considered same. + * + * rbto_compare_key_fn: + * return a positive value if the node > the key. + * return a negative value if the node < the key. + * return 0 if they are considered same. + */ + +typedef signed int (*rbto_compare_nodes_fn)(void *, const void *, const void *); +typedef signed int (*rbto_compare_key_fn)(void *, const void *, const void *); + +typedef struct { + rbto_compare_nodes_fn rbto_compare_nodes; + rbto_compare_key_fn rbto_compare_key; + size_t rbto_node_offset; + void *rbto_context; +} rb_tree_ops_t; + +typedef struct rb_tree { + struct rb_node *rbt_root; + const rb_tree_ops_t *rbt_ops; + struct rb_node *rbt_minmax[2]; +#ifdef RBDEBUG + struct rb_node_qh rbt_nodes; +#endif +#ifdef RBSTATS + unsigned int rbt_count; + unsigned int rbt_insertions; + unsigned int rbt_removals; + unsigned int rbt_insertion_rebalance_calls; + unsigned int rbt_insertion_rebalance_passes; + unsigned int rbt_removal_rebalance_calls; + unsigned int rbt_removal_rebalance_passes; +#endif +} rb_tree_t; + +#ifdef RBSTATS +#define RBSTAT_INC(v) ((void)((v)++)) +#define RBSTAT_DEC(v) ((void)((v)--)) +#else +#define RBSTAT_INC(v) do { } while (/*CONSTCOND*/0) +#define RBSTAT_DEC(v) do { } while (/*CONSTCOND*/0) +#endif + +void rb_tree_init(rb_tree_t *, const rb_tree_ops_t *); +void * rb_tree_insert_node(rb_tree_t *, void *); +void * rb_tree_find_node(rb_tree_t *, const void *); +void * rb_tree_find_node_geq(rb_tree_t *, const void *); +void * rb_tree_find_node_leq(rb_tree_t *, const void *); +void rb_tree_remove_node(rb_tree_t *, void *); +void * rb_tree_iterate(rb_tree_t *, void *, const unsigned int); +#ifdef RBDEBUG +void rb_tree_check(const rb_tree_t *, bool); +#endif +#ifdef RBSTATS +void rb_tree_depths(const rb_tree_t *, size_t *); +#endif + +__END_DECLS + +#endif /* _SYS_RBTREE_H_*/ diff --git a/contrib/dhcpcd/compat/reallocarray.c b/contrib/dhcpcd/compat/reallocarray.c deleted file mode 100644 index 9ff9c20b30..0000000000 --- a/contrib/dhcpcd/compat/reallocarray.c +++ /dev/null @@ -1,60 +0,0 @@ -/* $NetBSD: reallocarr.c,v 1.4 2015/08/20 20:08:04 joerg Exp $ */ - -/*- - * Copyright (c) 2015 Joerg Sonnenberger . - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include - -/* - * To be clear, this is NetBSD's more refined reallocarr(3) function - * made to look like OpenBSD's more useable reallocarray(3) interface. - */ -#include "reallocarray.h" - -#define SQRT_SIZE_MAX (((size_t)1) << (sizeof(size_t) * CHAR_BIT / 2)) -void * -reallocarray(void *ptr, size_t n, size_t size) -{ - - /* - * Try to avoid division here. - * - * It isn't possible to overflow during multiplication if neither - * operand uses any of the most significant half of the bits. - */ - if ((n | size) >= SQRT_SIZE_MAX && n > SIZE_MAX / size) { - errno = EOVERFLOW; - return NULL; - } - return realloc(ptr, n * size); -} diff --git a/contrib/dhcpcd/compat/reallocarray.h b/contrib/dhcpcd/compat/reallocarray.h deleted file mode 100644 index d855e7b259..0000000000 --- a/contrib/dhcpcd/compat/reallocarray.h +++ /dev/null @@ -1,37 +0,0 @@ -/* $NetBSD: reallocarr.c,v 1.4 2015/08/20 20:08:04 joerg Exp $ */ - -/*- - * Copyright (c) 2015 Joerg Sonnenberger . - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef REALLOCARRAY_H -#define REALLOCARRAY_H - -void *reallocarray(void *, size_t, size_t); - -#endif diff --git a/contrib/dhcpcd/hooks/10-wpa_supplicant b/contrib/dhcpcd/hooks/10-wpa_supplicant index 6249385dee..04acce0888 100644 --- a/contrib/dhcpcd/hooks/10-wpa_supplicant +++ b/contrib/dhcpcd/hooks/10-wpa_supplicant @@ -1,7 +1,9 @@ # Start, reconfigure and stop wpa_supplicant per wireless interface. -# This is needed because wpa_supplicant lacks hotplugging of any kind -# and the user should not be expected to have to wire it into their system -# if the base system doesn't do this itself. +# +# This is only needed when using wpa_supplicant-2.5 or older, OR +# when wpa_supplicant has not been built with CONFIG_MATCH_IFACE, OR +# wpa_supplicant was launched without the -M flag to activate +# interface matching. if [ -z "$wpa_supplicant_conf" ]; then for x in \ @@ -93,7 +95,7 @@ wpa_supplicant_stop() err=$(wpa_cli -i"$interface" terminate 2>&1) errn=$? if [ $errn != 0 ]; then - syslog err "failed to start wpa_supplicant" + syslog err "failed to stop wpa_supplicant" syslog err "$err" fi return $errn diff --git a/contrib/dhcpcd/hooks/20-resolv.conf b/contrib/dhcpcd/hooks/20-resolv.conf index b03ab5789f..422d815a44 100644 --- a/contrib/dhcpcd/hooks/20-resolv.conf +++ b/contrib/dhcpcd/hooks/20-resolv.conf @@ -19,6 +19,7 @@ build_resolv_conf() interfaces=$(list_interfaces "$resolv_conf_dir") # Build the resolv.conf + header= if [ -n "$interfaces" ]; then # Build the header for x in ${interfaces}; do @@ -69,30 +70,26 @@ build_resolv_conf() } # Extract any ND DNS options from the RA -# For now, we ignore the lifetime of the DNS options unless they -# are absent or zero. -# In this case they are removed from consideration. -# See draft-gont-6man-slaac-dns-config-issues-01 for issues -# regarding DNS option lifetime in ND messages. +# Obey the lifetimes eval_nd_dns() { - eval ltime=\$nd${i}_rdnss${j}_lifetime - if [ -z "$ltime" -o "$ltime" = 0 ]; then - rdnss= - else + + eval rdnsstime=\$nd${i}_rdnss${j}_lifetime + [ -z "$rdnsstime" ] && return 1 + ltime=$(($rdnsstime - $offset)) + if [ "$ltime" -gt 0 ]; then eval rdnss=\$nd${i}_rdnss${j}_servers + [ -n "$rdnss" ] && new_rdnss="$new_rdnss${new_rdnss:+ }$rdnss" fi - eval ltime=\$nd${i}_dnssl${j}_lifetime - if [ -z "$ltime" -o "$ltime" = 0 ]; then - dnssl= - else + + eval dnssltime=\$nd${i}_dnssl${j}_lifetime + [ -z "$dnssltime" ] && return 1 + ltime=$(($dnssltime - $offset)) + if [ "$ltime" -gt 0 ]; then eval dnssl=\$nd${i}_dnssl${j}_search + [ -n "$dnssl" ] && new_dnssl="$new_dnssl${new_dnssl:+ }$dnssl" fi - [ -z "$rdnss" -a -z "$dnssl" ] && return 1 - - [ -n "$rdnss" ] && new_rdnss="$new_rdnss${new_rdnss:+ }$rdnss" - [ -n "$dnssl" ] && new_dnssl="$new_dnssl${new_dnssl:+ }$dnssl" j=$(($j + 1)) return 0 } @@ -106,12 +103,16 @@ add_resolv_conf() i=1 j=1 while true; do + eval acquired=\$nd${i}_acquired + [ -z "$acquired" ] && break + eval now=\$nd${i}_now + [ -z "$now" ] && break + offset=$(($now - $acquired)) while true; do eval_nd_dns || break done i=$(($i + 1)) j=1 - eval_nd_dns || break done [ -n "$new_rdnss" ] && \ new_domain_name_servers="$new_domain_name_servers${new_domain_name_servers:+ }$new_rdnss" @@ -130,9 +131,9 @@ add_resolv_conf() fi # If we don't have any configuration, remove it - if [ -z "$new_domain_name_servers" -a \ - -z "$new_domain_name" -a \ - -z "$new_domain_search" ]; then + if [ -z "$new_domain_name_servers" ] && + [ -z "$new_domain_name" ] && + [ -z "$new_domain_search" ]; then remove_resolv_conf return $? fi diff --git a/contrib/dhcpcd/hooks/29-lookup-hostname b/contrib/dhcpcd/hooks/29-lookup-hostname index d0b10329cf..811c7a9c89 100644 --- a/contrib/dhcpcd/hooks/29-lookup-hostname +++ b/contrib/dhcpcd/hooks/29-lookup-hostname @@ -29,7 +29,7 @@ lookup_hostname() set_hostname() { - if [ -z "$new_host_name" -a -z "$new_fqdn_name" ]; then + if [ -z "${new_host_name}${new_fqdn_name}" ]; then export new_host_name="$(lookup_hostname)" fi } diff --git a/contrib/dhcpcd/hooks/30-hostname b/contrib/dhcpcd/hooks/30-hostname index 5f7d26da16..53ae6b1147 100644 --- a/contrib/dhcpcd/hooks/30-hostname +++ b/contrib/dhcpcd/hooks/30-hostname @@ -80,15 +80,15 @@ need_hostname() set_hostname_vars if [ -n "$old_fqdn" ]; then - if ${hfqdn} || ! ${hsort}; then + if ${hfqdn} || ! ${hshort}; then [ "$hostname" = "$old_fqdn" ] else [ "$hostname" = "${old_fqdn%%.*}" ] fi elif [ -n "$old_host_name" ]; then if ${hfqdn}; then - if [ -n "$old_domain_name" -a \ - "$old_host_name" = "${old_host_name#*.}" ] + if [ -n "$old_domain_name" ] && + [ "$old_host_name" = "${old_host_name#*.}" ] then [ "$hostname" = \ "$old_host_name.$old_domain_name" ] @@ -131,8 +131,8 @@ set_hostname() fi elif [ -n "$new_host_name" ]; then if ${hfqdn}; then - if [ -n "$new_domain_name" -a \ - "$new_host_name" = "${new_host_name#*.}" ] + if [ -n "$new_domain_name" ] && + [ "$new_host_name" = "${new_host_name#*.}" ] then try_hostname "$new_host_name.$new_domain_name" else diff --git a/contrib/dhcpcd/hooks/50-dhcpcd-compat b/contrib/dhcpcd/hooks/50-dhcpcd-compat deleted file mode 100644 index 0d6256e6ae..0000000000 --- a/contrib/dhcpcd/hooks/50-dhcpcd-compat +++ /dev/null @@ -1,41 +0,0 @@ -# Compat enter hook shim for older dhcpcd versions - -IPADDR=$new_ip_address -INTERFACE=$interface -NETMASK=$new_subnet_mask -BROADCAST=$new_broadcast_address -NETWORK=$new_network_number -DHCPSID=$new_dhcp_server_identifier -GATEWAYS=$new_routers -DNSSERVERS=$new_domain_name_servers -DNSDOMAIN=$new_domain_name -DNSSEARCH=$new_domain_search -NISDOMAIN=$new_nis_domain -NISSERVERS=$new_nis_servers -NTPSERVERS=$new_ntp_servers - -GATEWAY= -for x in $new_routers; do - GATEWAY="$GATEWAY${GATEWAY:+,}$x" -done -DNS= -for x in $new_domain_name_servers; do - DNS="$DNS${DNS:+,}$x" -done - -r="down" -case "$reason" in -RENEW) r="up";; -BOUND|INFORM|REBIND|REBOOT|TEST|TIMEOUT|IPV4LL) r="new";; -esac - -if [ "$r" != "down" ]; then - rm -f /var/lib/dhcpcd-"$INTERFACE".info - for x in IPADDR INTERFACE NETMASK BROADCAST NETWORK DHCPSID GATEWAYS \ - DNSSERVERS DNSDOMAIN DNSSEARCH NISDOMAIN NISSERVERS \ - NTPSERVERS GATEWAY DNS; do - eval echo "$x=\'\$$x\'" >> /var/lib/dhcpcd-"$INTERFACE".info - done -fi - -set -- /var/lib/dhcpcd-"$INTERFACE".info "$r" diff --git a/contrib/dhcpcd/hooks/50-ntp.conf b/contrib/dhcpcd/hooks/50-ntp.conf index 1217868486..4b68f7776d 100644 --- a/contrib/dhcpcd/hooks/50-ntp.conf +++ b/contrib/dhcpcd/hooks/50-ntp.conf @@ -62,6 +62,7 @@ build_ntp_conf() # Build a list of interfaces interfaces=$(list_interfaces "$ntp_conf_dir") + header= servers= if [ -n "$interfaces" ]; then # Build the header @@ -96,7 +97,7 @@ build_ntp_conf() printf %s "$servers" >> "$cf" echo "$signature_base_end${header:+ $from }$header" >> "$cf" else - [ -e "$ntp_conf" -a -e "$cf" ] || return + [ -e "$ntp_conf" ] && [ -e "$cf" ] || return fi # If we changed anything, restart ntpd diff --git a/contrib/dhcpcd/hooks/50-yp.conf b/contrib/dhcpcd/hooks/50-yp.conf deleted file mode 100644 index cc63d13922..0000000000 --- a/contrib/dhcpcd/hooks/50-yp.conf +++ /dev/null @@ -1,57 +0,0 @@ -# Sample dhcpcd hook for ypbind -# This script is only suitable for the Linux version. - -ypbind_pid() -{ - [ -s /var/run/ypbind.pid ] && cat /var/run/ypbind.pid -} - -make_yp_conf() -{ - [ -z "$new_nis_domain" -a -z "$new_nis_servers" ] && return 0 - cf=/etc/yp.conf."$ifname" - rm -f "$cf" - echo "$signature" > "$cf" - prefix= - if [ -n "$new_nis_domain" ]; then - if ! valid_domainname "$new_nis_domain"; then - syslog err "Invalid NIS domain name: $new_nis_domain" - rm -f "$cf" - return 1 - fi - domainname "$new_nis_domain" - if [ -n "$new_nis_servers" ]; then - prefix="domain $new_nis_domain server " - else - echo "domain $new_nis_domain broadcast" >> "$cf" - fi - else - prefix="ypserver " - fi - for x in $new_nis_servers; do - echo "$prefix$x" >> "$cf" - done - save_conf /etc/yp.conf - cat "$cf" > /etc/yp.conf - rm -f "$cf" - pid="$(ypbind_pid)" - if [ -n "$pid" ]; then - kill -HUP "$pid" - fi -} - -restore_yp_conf() -{ - [ -n "$old_nis_domain" ] && domainname "" - restore_conf /etc/yp.conf || return 0 - pid="$(ypbind_pid)" - if [ -n "$pid" ]; then - kill -HUP "$pid" - fi -} - -if $if_up; then - make_yp_conf -elif $if_down; then - restore_yp_conf -fi diff --git a/contrib/dhcpcd/hooks/50-ypbind.in b/contrib/dhcpcd/hooks/50-ypbind.in index eeb3f71845..5bd5eacf9e 100644 --- a/contrib/dhcpcd/hooks/50-ypbind.in +++ b/contrib/dhcpcd/hooks/50-ypbind.in @@ -41,7 +41,7 @@ make_yp_binding() fi nd="$(best_domain)" - if [ $? = 0 -a "$nd" != "$(domainname)" ]; then + if [ $? = 0 ] && [ "$nd" != "$(domainname)" ]; then domainname "$nd" if [ -n "$ypbind_restart_cmd" ]; then eval $ypbind_restart_cmd diff --git a/contrib/dhcpcd/hooks/dhcpcd-run-hooks.in b/contrib/dhcpcd/hooks/dhcpcd-run-hooks.in index 6e506dab99..049bd1b542 100644 --- a/contrib/dhcpcd/hooks/dhcpcd-run-hooks.in +++ b/contrib/dhcpcd/hooks/dhcpcd-run-hooks.in @@ -107,7 +107,7 @@ remove_markers() # Compare two files. comp_file() { - [ -e "$1" -a -e "$2" ] || return 1 + [ -e "$1" ] && [ -e "$2" ] || return 1 if type cmp >/dev/null 2>&1; then cmp -s "$1" "$2" @@ -187,11 +187,11 @@ syslog() valid_domainname() { name="$1" - [ -z "$name" -o ${#name} -gt 255 ] && return 1 + [ -z "$name" ] || [ ${#name} -gt 255 ] && return 1 while [ -n "$name" ]; do label="${name%%.*}" - [ -z "$label" -o ${#label} -gt 63 ] && return 1 + [ -z "$label" ] || [ ${#label} -gt 63 ] && return 1 case "$label" in -*|_*|*-|*_) return 1;; *[![:alnum:]_-]*) return 1;; @@ -231,16 +231,17 @@ detect_init() # systems we try to detect them first. status="@STATUSARG@" : ${status:=status} - if [ -x /bin/systemctl -a -S /run/systemd/private ]; then + if [ -x /bin/systemctl ] && [ -S /run/systemd/private ]; then _service_exists="/bin/systemctl --quiet is-enabled \$1.service" _service_status="/bin/systemctl --quiet is-active \$1.service" _service_cmd="/bin/systemctl \$2 \$1.service" - elif [ -x /usr/bin/systemctl -a -S /run/systemd/private ]; then + elif [ -x /usr/bin/systemctl ] && [ -S /run/systemd/private ]; then _service_exists="/usr/bin/systemctl --quiet is-enabled \$1.service" _service_status="/usr/bin/systemctl --quiet is-active \$1.service" _service_cmd="/usr/bin/systemctl \$2 \$1.service" - elif [ -x /sbin/rc-service -a \ - -s /libexec/rc/init.d/softlevel -o -s /run/openrc/softlevel ] + elif [ -x /sbin/rc-service ] && + { [ -s /libexec/rc/init.d/softlevel ] || + [ -s /run/openrc/softlevel ]; } then _service_exists="/sbin/rc-service -e \$1" _service_cmd="/sbin/rc-service \$1 -- -D \$2" @@ -259,7 +260,7 @@ detect_init() elif [ -x /usr/bin/sv ]; then _service_exists="/usr/bin/sv status \$1 >/dev/null 2>&1" _service_cmd="/usr/bin/sv \$2 \$1" - elif [ -e /etc/slackware-version -a -d /etc/rc.d ]; then + elif [ -e /etc/slackware-version ] && [ -d /etc/rc.d ]; then _service_exists="[ -x /etc/rc.d/rc.\$1 ]" _service_cmd="/etc/rc.d/rc.\$1 \$2" _service_status="/etc/rc.d/rc.\$1 status >/dev/null 2>&1" @@ -274,7 +275,7 @@ detect_init() done if [ -e /etc/arch-release ]; then _service_status="[ -e /var/run/daemons/\$1 ]" - elif [ "$x" = "/etc/rc.d" -a -e /etc/rc.d/rc.subr ]; then + elif [ "$x" = "/etc/rc.d" ] && [ -e /etc/rc.d/rc.subr ]; then _service_status="$x/\$1 check >/dev/null 2>&1" fi fi diff --git a/contrib/dhcpcd/src/arp.c b/contrib/dhcpcd/src/arp.c index f8bff87a29..49393f5a52 100644 --- a/contrib/dhcpcd/src/arp.c +++ b/contrib/dhcpcd/src/arp.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - ARP handler - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -36,6 +37,7 @@ #include #include +#include #include #include @@ -62,8 +64,9 @@ /* Assert the correct structure size for on wire */ __CTASSERT(sizeof(struct arphdr) == 8); -ssize_t -arp_request(const struct interface *ifp, in_addr_t sip, in_addr_t tip) +static ssize_t +arp_request(const struct interface *ifp, + const struct in_addr *sip, const struct in_addr *tip) { uint8_t arp_buffer[ARP_LEN]; struct arphdr ar; @@ -74,7 +77,7 @@ arp_request(const struct interface *ifp, in_addr_t sip, in_addr_t tip) ar.ar_hrd = htons(ifp->family); ar.ar_pro = htons(ETHERTYPE_IP); ar.ar_hln = ifp->hwlen; - ar.ar_pln = sizeof(sip); + ar.ar_pln = sizeof(tip->s_addr); ar.ar_op = htons(ARPOP_REQUEST); p = arp_buffer; @@ -93,9 +96,12 @@ arp_request(const struct interface *ifp, in_addr_t sip, in_addr_t tip) APPEND(&ar, sizeof(ar)); APPEND(ifp->hwaddr, ifp->hwlen); - APPEND(&sip, sizeof(sip)); + if (sip != NULL) + APPEND(&sip->s_addr, sizeof(sip->s_addr)); + else + ZERO(sizeof(tip->s_addr)); ZERO(ifp->hwlen); - APPEND(&tip, sizeof(tip)); + APPEND(&tip->s_addr, sizeof(tip->s_addr)); state = ARP_CSTATE(ifp); return bpf_send(ifp, state->bpf_fd, ETHERTYPE_ARP, arp_buffer, len); @@ -106,6 +112,77 @@ eexit: } static void +arp_report_conflicted(const struct arp_state *astate, + const struct arp_msg *amsg) +{ + char buf[HWADDR_LEN * 3]; + + if (amsg == NULL) { + logerrx("%s: DAD detected %s", + astate->iface->name, inet_ntoa(astate->addr)); + return; + } + + logerrx("%s: hardware address %s claims %s", + astate->iface->name, + hwaddr_ntoa(amsg->sha, astate->iface->hwlen, buf, sizeof(buf)), + inet_ntoa(astate->addr)); +} + + +static void +arp_found(struct arp_state *astate, const struct arp_msg *amsg) +{ + struct interface *ifp; + struct ivp4_addr *ia; +#ifndef KERNEL_RFC5227 + struct timespec now, defend; +#endif + + arp_report_conflicted(astate, amsg); + ifp = astate->iface; + +#pragma GCC diagnostic push /* GCC is clearly wrong about this warning. */ +#pragma GCC diagnostic ignored "-Wincompatible-pointer-types" + /* If we haven't added the address we're doing a probe. */ + ia = ipv4_iffindaddr(ifp, &astate->addr, NULL); +#pragma GCC diagnostic pop + if (ia == NULL) { + if (astate->found_cb != NULL) + astate->found_cb(astate, amsg); + return; + } + +#ifndef KERNEL_RFC5227 + /* RFC 3927 Section 2.5 says a defence should + * broadcast an ARP announcement. + * Because the kernel will also unicast a reply to the + * hardware address which requested the IP address + * the other IPv4LL client will receieve two ARP + * messages. + * If another conflict happens within DEFEND_INTERVAL + * then we must drop our address and negotiate a new one. */ + defend.tv_sec = astate->defend.tv_sec + DEFEND_INTERVAL; + defend.tv_nsec = astate->defend.tv_nsec; + clock_gettime(CLOCK_MONOTONIC, &now); + if (timespeccmp(&defend, &now, >)) + logwarnx("%s: %d second defence failed for %s", + ifp->name, DEFEND_INTERVAL, inet_ntoa(astate->addr)); + else if (arp_request(ifp, &astate->addr, &astate->addr) == -1) + logerr(__func__); + else { + logdebugx("%s: defended address %s", + ifp->name, inet_ntoa(astate->addr)); + astate->defend = now; + return; + } +#endif + + if (astate->defend_failed_cb != NULL) + astate->defend_failed_cb(astate); +} + +static void arp_packet(struct interface *ifp, uint8_t *data, size_t len) { const struct interface *ifn; @@ -164,28 +241,30 @@ arp_packet(struct interface *ifp, uint8_t *data, size_t len) memcpy(&arm.tha, hw_t, ar.ar_hln); memcpy(&arm.tip.s_addr, hw_t + ar.ar_hln, ar.ar_pln); - /* Run the conflicts */ + /* Match the ARP probe to our states. + * Ignore Unicast Poll, RFC1122. */ state = ARP_CSTATE(ifp); TAILQ_FOREACH_SAFE(astate, &state->arp_states, next, astaten) { - if (arm.sip.s_addr != astate->addr.s_addr && - arm.tip.s_addr != astate->addr.s_addr) - continue; - if (astate->conflicted_cb) - astate->conflicted_cb(astate, &arm); + if (IN_ARE_ADDR_EQUAL(&arm.sip, &astate->addr) || + (IN_IS_ADDR_UNSPECIFIED(&arm.sip) && + IN_ARE_ADDR_EQUAL(&arm.tip, &astate->addr) && + state->bpf_flags & BPF_BCAST)) + arp_found(astate, &arm); } } -void +static void arp_close(struct interface *ifp) { struct iarp_state *state; - if ((state = ARP_STATE(ifp)) != NULL && state->bpf_fd != -1) { - eloop_event_delete(ifp->ctx->eloop, state->bpf_fd); - bpf_close(ifp, state->bpf_fd); - state->bpf_fd = -1; - state->bpf_flags |= BPF_EOF; - } + if ((state = ARP_STATE(ifp)) == NULL || state->bpf_fd == -1) + return; + + eloop_event_delete(ifp->ctx->eloop, state->bpf_fd); + bpf_close(ifp, state->bpf_fd); + state->bpf_fd = -1; + state->bpf_flags |= BPF_EOF; } static void @@ -242,7 +321,7 @@ arp_read(void *arg) } } -int +static int arp_open(struct interface *ifp) { struct iarp_state *state; @@ -264,7 +343,8 @@ arp_probed(void *arg) { struct arp_state *astate = arg; - astate->probed_cb(astate); + timespecclear(&astate->defend); + astate->not_found_cb(astate); } static void @@ -289,7 +369,7 @@ arp_probe1(void *arg) ifp->name, inet_ntoa(astate->addr), astate->probes ? astate->probes : PROBE_NUM, PROBE_NUM, timespec_to_double(&tv)); - if (arp_request(ifp, 0, astate->addr.s_addr) == -1) + if (arp_request(ifp, NULL, &astate->addr) == -1) logerr(__func__); } @@ -313,6 +393,23 @@ arp_probe(struct arp_state *astate) } #endif /* ARP */ +static struct arp_state * +arp_find(struct interface *ifp, const struct in_addr *addr) +{ + struct iarp_state *state; + struct arp_state *astate; + + if ((state = ARP_STATE(ifp)) == NULL) + goto out; + TAILQ_FOREACH(astate, &state->arp_states, next) { + if (astate->addr.s_addr == addr->s_addr && astate->iface == ifp) + return astate; + } +out: + errno = ESRCH; + return NULL; +} + static void arp_announced(void *arg) { @@ -341,7 +438,7 @@ arp_announce1(void *arg) logdebugx("%s: ARP announcing %s (%d of %d)", ifp->name, inet_ntoa(astate->addr), astate->claims, ANNOUNCE_NUM); - if (arp_request(ifp, astate->addr.s_addr, astate->addr.s_addr) == -1) + if (arp_request(ifp, &astate->addr, &astate->addr) == -1) logerr(__func__); eloop_timeout_add_sec(ifp->ctx->eloop, ANNOUNCE_WAIT, astate->claims < ANNOUNCE_NUM ? arp_announce1 : arp_announced, @@ -400,72 +497,44 @@ arp_announce(struct arp_state *astate) } void -arp_announceaddr(struct dhcpcd_ctx *ctx, struct in_addr *ia) +arp_ifannounceaddr(struct interface *ifp, const struct in_addr *ia) { - struct interface *ifp; - struct ipv4_addr *iaf; struct arp_state *astate; - TAILQ_FOREACH(ifp, ctx->ifaces, next) { - iaf = ipv4_iffindaddr(ifp, ia, NULL); -#ifdef IN_IFF_NOTUSEABLE - if (iaf && !(iaf->addr_flags & IN_IFF_NOTUSEABLE)) -#else - if (iaf) -#endif - break; - } - if (ifp == NULL) - return; - astate = arp_find(ifp, ia); - if (astate != NULL) - arp_announce(astate); -} - -void -arp_ifannounceaddr(struct interface *ifp, struct in_addr *ia) -{ - struct arp_state *astate; - - astate = arp_new(ifp, ia); - if (astate != NULL) - arp_announce(astate); + if (astate == NULL) { + astate = arp_new(ifp, ia); + if (astate == NULL) + return; + astate->announced_cb = arp_free; + } + arp_announce(astate); } void -arp_report_conflicted(const struct arp_state *astate, - const struct arp_msg *amsg) -{ - - if (amsg != NULL) { - char buf[HWADDR_LEN * 3]; - - logerrx("%s: hardware address %s claims %s", - astate->iface->name, - hwaddr_ntoa(amsg->sha, astate->iface->hwlen, - buf, sizeof(buf)), - inet_ntoa(astate->failed)); - } else - logerrx("%s: DAD detected %s", - astate->iface->name, inet_ntoa(astate->failed)); -} - -struct arp_state * -arp_find(struct interface *ifp, const struct in_addr *addr) +arp_announceaddr(struct dhcpcd_ctx *ctx, const struct in_addr *ia) { - struct iarp_state *state; - struct arp_state *astate; + struct interface *ifp, *iff = NULL; + struct ipv4_addr *iap; - if ((state = ARP_STATE(ifp)) == NULL) - goto out; - TAILQ_FOREACH(astate, &state->arp_states, next) { - if (astate->addr.s_addr == addr->s_addr && astate->iface == ifp) - return astate; + TAILQ_FOREACH(ifp, ctx->ifaces, next) { + if (!ifp->active || ifp->carrier <= LINK_DOWN) + continue; + iap = ipv4_iffindaddr(ifp, ia, NULL); + if (iap == NULL) + continue; +#ifdef IN_IFF_NOTUSEABLE + if (!(iap->addr_flags & IN_IFF_NOTUSEABLE)) + continue; +#endif + if (iff != NULL && iff->metric < ifp->metric) + continue; + iff = ifp; } -out: - errno = ESRCH; - return NULL; + if (iff == NULL) + return; + + arp_ifannounceaddr(iff, ia); } struct arp_state * @@ -531,61 +600,24 @@ arp_free(struct arp_state *astate) arp_tryfree(ifp); } -static void -arp_free_but1(struct interface *ifp, struct arp_state *astate) -{ - struct iarp_state *state; - - if ((state = ARP_STATE(ifp)) != NULL) { - struct arp_state *p, *n; - - TAILQ_FOREACH_SAFE(p, &state->arp_states, next, n) { - if (p != astate) - arp_free(p); - } - } -} - void -arp_free_but(struct arp_state *astate) +arp_freeaddr(struct interface *ifp, const struct in_addr *ia) { + struct arp_state *astate; - arp_free_but1(astate->iface, astate); + astate = arp_find(ifp, ia); + arp_free(astate); } void arp_drop(struct interface *ifp) { - - arp_free_but1(ifp, NULL); - arp_close(ifp); -} - -void -arp_handleifa(int cmd, struct ipv4_addr *addr) -{ struct iarp_state *state; - struct arp_state *astate, *asn; + struct arp_state *astate; - state = ARP_STATE(addr->iface); - if (state == NULL) - return; + while ((state = ARP_STATE(ifp)) != NULL && + (astate = TAILQ_FIRST(&state->arp_states)) != NULL) + arp_free(astate); - TAILQ_FOREACH_SAFE(astate, &state->arp_states, next, asn) { - if (astate->addr.s_addr != addr->addr.s_addr) - continue; - if (cmd == RTM_DELADDR) - arp_free(astate); -#ifdef IN_IFF_DUPLICATED - if (cmd != RTM_NEWADDR) - continue; - if (addr->addr_flags & IN_IFF_DUPLICATED) { - if (astate->conflicted_cb) - astate->conflicted_cb(astate, NULL); - } else if (!(addr->addr_flags & IN_IFF_NOTUSEABLE)) { - if (astate->probed_cb) - astate->probed_cb(astate); - } -#endif - } + /* No need to close because the last free will close */ } diff --git a/contrib/dhcpcd/src/arp.h b/contrib/dhcpcd/src/arp.h index 6b9d5a65e9..0065e0c917 100644 --- a/contrib/dhcpcd/src/arp.h +++ b/contrib/dhcpcd/src/arp.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -63,15 +64,16 @@ struct arp_state { TAILQ_ENTRY(arp_state) next; struct interface *iface; - void (*probed_cb)(struct arp_state *); + void (*found_cb)(struct arp_state *, const struct arp_msg *); + void (*not_found_cb)(struct arp_state *); void (*announced_cb)(struct arp_state *); - void (*conflicted_cb)(struct arp_state *, const struct arp_msg *); + void (*defend_failed_cb)(struct arp_state *); void (*free_cb)(struct arp_state *); struct in_addr addr; int probes; int claims; - struct in_addr failed; + struct timespec defend; }; TAILQ_HEAD(arp_statehead, arp_state); @@ -87,21 +89,14 @@ struct iarp_state { ((const struct iarp_state *)(ifp)->if_data[IF_DATA_ARP]) #ifdef ARP -int arp_open(struct interface *); -ssize_t arp_request(const struct interface *, in_addr_t, in_addr_t); -void arp_probe(struct arp_state *); -void arp_close(struct interface *); -void arp_report_conflicted(const struct arp_state *, const struct arp_msg *); struct arp_state *arp_new(struct interface *, const struct in_addr *); -struct arp_state *arp_find(struct interface *, const struct in_addr *); +void arp_probe(struct arp_state *); void arp_announce(struct arp_state *); -void arp_announceaddr(struct dhcpcd_ctx *, struct in_addr *); -void arp_ifannounceaddr(struct interface *, struct in_addr *); +void arp_announceaddr(struct dhcpcd_ctx *, const struct in_addr *); +void arp_ifannounceaddr(struct interface *, const struct in_addr *); void arp_cancel(struct arp_state *); void arp_free(struct arp_state *); -void arp_free_but(struct arp_state *); +void arp_freeaddr(struct interface *, const struct in_addr *); void arp_drop(struct interface *); - -void arp_handleifa(int, struct ipv4_addr *); #endif /* ARP */ #endif /* ARP_H */ diff --git a/contrib/dhcpcd/src/auth.c b/contrib/dhcpcd/src/auth.c index a272e93d47..8a83530842 100644 --- a/contrib/dhcpcd/src/auth.c +++ b/contrib/dhcpcd/src/auth.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -30,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -117,7 +119,11 @@ dhcp_auth_validate(struct authstate *state, const struct auth *auth, m = vm; data = vdata; - /* Ensure that d is inside m which *may* not be the case for DHPCPv4 */ + /* Ensure that d is inside m which *may* not be the case for DHCPv4. + * This can occur if the authentication option is split using + * DHCP long option from RFC 3399. Section 9 which does infact note that + * implementations should take this into account. + * Fixing this would be problematic, patches welcome. */ if (data < m || data > m + mlen || data + dlen > m + mlen) { errno = ERANGE; return NULL; @@ -354,7 +360,7 @@ gottoken: } free(mm); - if (memcmp(d, &hmac_code, dlen)) { + if (!consttime_memequal(d, &hmac_code, dlen)) { errno = EPERM; return NULL; } diff --git a/contrib/dhcpcd/src/auth.h b/contrib/dhcpcd/src/auth.h index f65d62c39c..3f6b5a572f 100644 --- a/contrib/dhcpcd/src/auth.h +++ b/contrib/dhcpcd/src/auth.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without diff --git a/contrib/dhcpcd/src/bpf.c b/contrib/dhcpcd/src/bpf.c index 537a7a6a2f..06bbf3f1e1 100644 --- a/contrib/dhcpcd/src/bpf.c +++ b/contrib/dhcpcd/src/bpf.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd: BPF arp and bootp filtering - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -84,7 +85,7 @@ size_t bpf_frame_header_len(const struct interface *ifp) { - switch(ifp->family) { + switch (ifp->family) { case ARPHRD_ETHER: return sizeof(struct ether_header); default: @@ -92,6 +93,23 @@ bpf_frame_header_len(const struct interface *ifp) } } +static const uint8_t etherbcastaddr[] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +int +bpf_frame_bcast(const struct interface *ifp, const char *frame) +{ + + switch (ifp->family) { + case ARPHRD_ETHER: + return memcmp(frame + + offsetof(struct ether_header, ether_dhost), + etherbcastaddr, sizeof(etherbcastaddr)); + default: + return -1; + } +} + #ifndef __linux__ /* Linux is a special snowflake for opening, attaching and reading BPF. * See if-linux.c for the Linux specific BPF functions. */ @@ -227,8 +245,12 @@ bpf_read(struct interface *ifp, int fd, void *data, size_t len, if (state->buffer_pos + packet.bh_caplen + packet.bh_hdrlen > state->buffer_len) goto next; /* Packet beyond buffer, drop. */ - payload = state->buffer + state->buffer_pos + - packet.bh_hdrlen + fl; + payload = state->buffer + state->buffer_pos + packet.bh_hdrlen; + if (bpf_frame_bcast(ifp, payload) == 0) + *flags |= BPF_BCAST; + else + *flags &= ~BPF_BCAST; + payload += fl; bytes = (ssize_t)packet.bh_caplen - fl; if ((size_t)bytes > len) bytes = (ssize_t)len; @@ -301,6 +323,7 @@ bpf_close(struct interface *ifp, int fd) /* Normally this is needed by bootp. * Once that uses this again, the ARP guard here can be removed. */ #ifdef ARP +#define BPF_CMP_HWADDR_LEN ((((HWADDR_LEN / 4) + 2) * 2) + 1) static unsigned int bpf_cmp_hwaddr(struct bpf_insn *bpf, size_t bpf_len, size_t off, bool equal, uint8_t *hwaddr, size_t hwaddr_len) @@ -414,7 +437,7 @@ static const struct bpf_insn bpf_arp_ether [] = { sizeof(((struct ether_arp *)0)->arp_sha), 1, 0), BPF_STMT(BPF_RET + BPF_K, 0), }; -#define bpf_arp_ether_len __arraycount(bpf_arp_ether) +#define BPF_ARP_ETHER_LEN __arraycount(bpf_arp_ether) static const struct bpf_insn bpf_arp_filter [] = { /* Make sure this is for IP. */ @@ -425,21 +448,25 @@ static const struct bpf_insn bpf_arp_filter [] = { BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_op)), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0), /* or ARP REPLY. */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 1), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), BPF_STMT(BPF_RET + BPF_K, 0), /* Make sure the protocol length matches. */ BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct arphdr, ar_pln)), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(in_addr_t), 1, 0), BPF_STMT(BPF_RET + BPF_K, 0), }; -#define bpf_arp_filter_len __arraycount(bpf_arp_filter) -#define bpf_arp_extra ((((ARP_ADDRS_MAX + 1) * 2) * 2) + 2) -#define bpf_arp_hw ((((HWADDR_LEN / 4) + 2) * 2) + 1) +#define BPF_ARP_FILTER_LEN __arraycount(bpf_arp_filter) + +#define BPF_ARP_ADDRS_LEN 1 + (ARP_ADDRS_MAX * 2) + 3 + \ + (ARP_ADDRS_MAX * 2) + 1 + +#define BPF_ARP_LEN BPF_ARP_ETHER_LEN + BPF_ARP_FILTER_LEN + \ + BPF_CMP_HWADDR_LEN + BPF_ARP_ADDRS_LEN int bpf_arp(struct interface *ifp, int fd) { - struct bpf_insn bpf[3+ bpf_arp_filter_len + bpf_arp_hw + bpf_arp_extra]; + struct bpf_insn bpf[BPF_ARP_LEN]; struct bpf_insn *bp; struct iarp_state *state; uint16_t arp_len; @@ -452,7 +479,7 @@ bpf_arp(struct interface *ifp, int fd) switch(ifp->family) { case ARPHRD_ETHER: memcpy(bp, bpf_arp_ether, sizeof(bpf_arp_ether)); - bp += bpf_arp_ether_len; + bp += BPF_ARP_ETHER_LEN; arp_len = sizeof(struct ether_header)+sizeof(struct ether_arp); break; default: @@ -462,10 +489,10 @@ bpf_arp(struct interface *ifp, int fd) /* Copy in the main filter. */ memcpy(bp, bpf_arp_filter, sizeof(bpf_arp_filter)); - bp += bpf_arp_filter_len; + bp += BPF_ARP_FILTER_LEN; /* Ensure it's not from us. */ - bp += bpf_cmp_hwaddr(bp, bpf_arp_hw, sizeof(struct arphdr), + bp += bpf_cmp_hwaddr(bp, BPF_CMP_HWADDR_LEN, sizeof(struct arphdr), false, ifp->hwaddr, ifp->hwlen); state = ARP_STATE(ifp); @@ -525,6 +552,21 @@ bpf_arp(struct interface *ifp, int fd) } #endif +#define BPF_M_FHLEN 0 +#define BPF_M_IPHLEN 1 +#define BPF_M_IPLEN 2 +#define BPF_M_UDP 3 +#define BPF_M_UDPLEN 4 + +#ifdef ARPHRD_NONE +static const struct bpf_insn bpf_bootp_none[] = { + /* Set the frame header length to zero. */ + BPF_STMT(BPF_LD + BPF_IMM, 0), + BPF_STMT(BPF_ST, BPF_M_FHLEN), +}; +#define BPF_BOOTP_NONE_LEN __arraycount(bpf_bootp_none) +#endif + static const struct bpf_insn bpf_bootp_ether[] = { /* Make sure this is an IP packet. */ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, @@ -534,17 +576,27 @@ static const struct bpf_insn bpf_bootp_ether[] = { /* Load frame header length into X. */ BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, sizeof(struct ether_header)), - /* Copy to M0. */ - BPF_STMT(BPF_STX, 0), + /* Copy frame header length to memory */ + BPF_STMT(BPF_STX, BPF_M_FHLEN), }; #define BPF_BOOTP_ETHER_LEN __arraycount(bpf_bootp_ether) static const struct bpf_insn bpf_bootp_filter[] = { - /* Make sure it's an optionless IPv4 packet. */ + /* Make sure it's an IPv4 packet. */ BPF_STMT(BPF_LD + BPF_B + BPF_IND, 0), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x45, 1, 0), + BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0xf0), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x40, 1, 0), BPF_STMT(BPF_RET + BPF_K, 0), + /* Ensure IP header length is big enough and + * store the IP header length in memory. */ + BPF_STMT(BPF_LD + BPF_B + BPF_IND, 0), + BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x0f), + BPF_STMT(BPF_ALU + BPF_MUL + BPF_K, 4), + BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ip), 1, 0), + BPF_STMT(BPF_RET + BPF_K, 0), + BPF_STMT(BPF_ST, BPF_M_IPHLEN), + /* Make sure it's a UDP packet. */ BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct ip, ip_p)), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0), @@ -555,39 +607,42 @@ static const struct bpf_insn bpf_bootp_filter[] = { BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 0, 1), BPF_STMT(BPF_RET + BPF_K, 0), - /* Store IP location in M1. */ - BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct ip, ip_len)), - BPF_STMT(BPF_ST, 1), - - /* Store IP length in M2. */ + /* Store IP length. */ BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct ip, ip_len)), - BPF_STMT(BPF_ST, 2), + BPF_STMT(BPF_ST, BPF_M_IPLEN), /* Advance to the UDP header. */ - BPF_STMT(BPF_MISC + BPF_TXA, 0), - BPF_STMT(BPF_ALU + BPF_ADD + BPF_K, sizeof(struct ip)), + BPF_STMT(BPF_LD + BPF_MEM, BPF_M_IPHLEN), + BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0), BPF_STMT(BPF_MISC + BPF_TAX, 0), - /* Store X in M3. */ - BPF_STMT(BPF_STX, 3), + /* Store UDP location */ + BPF_STMT(BPF_STX, BPF_M_UDP), /* Make sure it's from and to the right port. */ BPF_STMT(BPF_LD + BPF_W + BPF_IND, 0), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (BOOTPS << 16) + BOOTPC, 1, 0), BPF_STMT(BPF_RET + BPF_K, 0), - /* Store UDP length in X. */ + /* Store UDP length. */ BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct udphdr, uh_ulen)), + BPF_STMT(BPF_ST, BPF_M_UDPLEN), + + /* Ensure that UDP length + IP header length == IP length */ + /* Copy IP header length to X. */ + BPF_STMT(BPF_LDX + BPF_MEM, BPF_M_IPHLEN), + /* Add UDP length (A) to IP header length (X). */ + BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0), + /* Store result in X. */ BPF_STMT(BPF_MISC + BPF_TAX, 0), - /* Copy IP length in M2 to A. */ - BPF_STMT(BPF_LD + BPF_MEM, 2), - /* Ensure IP length - IP header size == UDP length. */ - BPF_STMT(BPF_ALU + BPF_SUB + BPF_K, sizeof(struct ip)), + /* Copy IP length to A. */ + BPF_STMT(BPF_LD + BPF_MEM, BPF_M_IPLEN), + /* Ensure X == A. */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0), BPF_STMT(BPF_RET + BPF_K, 0), - /* Advance to the BOOTP packet (UDP X is in M3). */ - BPF_STMT(BPF_LD + BPF_MEM, 3), + /* Advance to the BOOTP packet. */ + BPF_STMT(BPF_LD + BPF_MEM, BPF_M_UDP), BPF_STMT(BPF_ALU + BPF_ADD + BPF_K, sizeof(struct udphdr)), BPF_STMT(BPF_MISC + BPF_TAX, 0), @@ -619,6 +674,12 @@ bpf_bootp(struct interface *ifp, int fd) bp = bpf; /* Check frame header. */ switch(ifp->family) { +#ifdef ARPHRD_NONE + case ARPHRD_NONE: + memcpy(bp, bpf_bootp_none, sizeof(bpf_bootp_none)); + bp += BPF_BOOTP_NONE_LEN; + break; +#endif case ARPHRD_ETHER: memcpy(bp, bpf_bootp_ether, sizeof(bpf_bootp_ether)); bp += BPF_BOOTP_ETHER_LEN; @@ -668,11 +729,10 @@ bpf_bootp(struct interface *ifp, int fd) } #endif - /* All passed, return the packet - * (Frame length in M0, IP length in M2). */ - BPF_SET_STMT(bp, BPF_LD + BPF_MEM, 0); + /* All passed, return the packet - frame length + ip length */ + BPF_SET_STMT(bp, BPF_LD + BPF_MEM, BPF_M_FHLEN); bp++; - BPF_SET_STMT(bp, BPF_LDX + BPF_MEM, 2); + BPF_SET_STMT(bp, BPF_LDX + BPF_MEM, BPF_M_IPLEN); bp++; BPF_SET_STMT(bp, BPF_ALU + BPF_ADD + BPF_X, 0); bp++; diff --git a/contrib/dhcpcd/src/bpf.h b/contrib/dhcpcd/src/bpf.h index 2515978dc6..e886f9c3be 100644 --- a/contrib/dhcpcd/src/bpf.h +++ b/contrib/dhcpcd/src/bpf.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd: BPF arp and bootp filtering - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -31,11 +32,13 @@ #define BPF_READING (1U << 0) #define BPF_EOF (1U << 1) #define BPF_PARTIALCSUM (1U << 2) +#define BPF_BCAST (1U << 3) #include "dhcpcd.h" extern const char *bpf_name; size_t bpf_frame_header_len(const struct interface *); +int bpf_frame_bcast(const struct interface *, const char *frame); int bpf_open(struct interface *, int (*)(struct interface *, int)); int bpf_close(struct interface *, int); int bpf_attach(int, void *, unsigned int); diff --git a/contrib/dhcpcd/src/common.c b/contrib/dhcpcd/src/common.c index d4b606dc0d..31998c0ede 100644 --- a/contrib/dhcpcd/src/common.c +++ b/contrib/dhcpcd/src/common.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -56,54 +57,6 @@ /* Most route(4) messages are less than 256 bytes. */ #define IOVEC_BUFSIZ 256 -ssize_t -setvar(char **e, const char *prefix, const char *var, const char *value) -{ - size_t len = strlen(var) + strlen(value) + 3; - - if (prefix) - len += strlen(prefix) + 1; - if ((*e = malloc(len)) == NULL) { - logerr(__func__); - return -1; - } - if (prefix) - snprintf(*e, len, "%s_%s=%s", prefix, var, value); - else - snprintf(*e, len, "%s=%s", var, value); - return (ssize_t)len; -} - -ssize_t -setvard(char **e, const char *prefix, const char *var, size_t value) -{ - - char buffer[32]; - - snprintf(buffer, sizeof(buffer), "%zu", value); - return setvar(e, prefix, var, buffer); -} - -ssize_t -addvar(char ***e, const char *prefix, const char *var, const char *value) -{ - ssize_t len; - - len = setvar(*e, prefix, var, value); - if (len != -1) - (*e)++; - return (ssize_t)len; -} - -ssize_t -addvard(char ***e, const char *prefix, const char *var, size_t value) -{ - char buffer[32]; - - snprintf(buffer, sizeof(buffer), "%zu", value); - return addvar(e, prefix, var, buffer); -} - const char * hwaddr_ntoa(const void *hwaddr, size_t hwlen, char *buf, size_t buflen) { @@ -200,51 +153,3 @@ read_hwaddr_aton(uint8_t **data, const char *path) fclose(fp); return len; } - -ssize_t -recvmsg_realloc(int fd, struct msghdr *msg, int flags) -{ - struct iovec *iov; - ssize_t slen; - size_t len; - void *n; - - assert(msg != NULL); - assert(msg->msg_iov != NULL && msg->msg_iovlen > 0); - assert((flags & (MSG_PEEK | MSG_TRUNC)) == 0); - - /* Assume we are reallocing the last iovec. */ - iov = &msg->msg_iov[msg->msg_iovlen - 1]; - - for (;;) { - /* Passing MSG_TRUNC should return the actual size needed. */ - slen = recvmsg(fd, msg, flags | MSG_PEEK | MSG_TRUNC); - if (slen == -1) - return -1; - if (!(msg->msg_flags & MSG_TRUNC)) - break; - - len = (size_t)slen; - - /* Some kernels return the size of the receive buffer - * on truncation, not the actual size needed. - * So grow the buffer and try again. */ - if (iov->iov_len == len) - len++; - else if (iov->iov_len > len) - break; - len = roundup(len, IOVEC_BUFSIZ); - if ((n = realloc(iov->iov_base, len)) == NULL) - return -1; - iov->iov_base = n; - iov->iov_len = len; - } - - slen = recvmsg(fd, msg, flags); - if (slen != -1 && msg->msg_flags & MSG_TRUNC) { - /* This should not be possible ... */ - errno = ENOBUFS; - return -1; - } - return slen; -} diff --git a/contrib/dhcpcd/src/common.h b/contrib/dhcpcd/src/common.h index 3031fab687..301c8869b5 100644 --- a/contrib/dhcpcd/src/common.h +++ b/contrib/dhcpcd/src/common.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -30,12 +31,9 @@ #include #include +#include #include -#include "config.h" -#include "defs.h" -#include "dhcpcd.h" - #ifndef HOSTNAME_MAX_LEN #define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */ #endif @@ -130,6 +128,37 @@ # endif #endif +/* Needed for rbtree(3) compat */ +#ifndef __RCSID +#define __RCSID(a) +#endif +#ifndef __predict_false +# if __GNUC__ > 2 +# define __predict_true(exp) __builtin_expect((exp) != 0, 1) +# define __predict_false(exp) __builtin_expect((exp) != 0, 0) +#else +# define __predict_true(exp) (exp) +# define __predict_false(exp) (exp) +# endif +#endif +#ifndef __BEGIN_DECLS +# if defined(__cplusplus) +# define __BEGIN_DECLS extern "C" { +# define __END_DECLS }; +# else /* __BEGIN_DECLS */ +# define __BEGIN_DECLS +# define __END_DECLS +# endif /* __BEGIN_DECLS */ +#endif /* __BEGIN_DECLS */ + +#ifndef __fallthrough +# if __GNUC__ >= 7 +# define __fallthrough __attribute__((fallthrough)) +# else +# define __fallthrough +# endif +#endif + /* * Compile Time Assertion. */ @@ -165,14 +194,7 @@ void get_line_free(void); extern int clock_monotonic; int get_monotonic(struct timespec *); -ssize_t setvar(char **, const char *, const char *, const char *); -ssize_t setvard(char **, const char *, const char *, size_t); -ssize_t addvar(char ***, const char *, const char *, const char *); -ssize_t addvard(char ***, const char *, const char *, size_t); - const char *hwaddr_ntoa(const void *, size_t, char *, size_t); size_t hwaddr_aton(uint8_t *, const char *); size_t read_hwaddr_aton(uint8_t **, const char *); - -ssize_t recvmsg_realloc(int, struct msghdr *, int); #endif diff --git a/contrib/dhcpcd/src/control.c b/contrib/dhcpcd/src/control.c index d9bf9498a9..4ae2279316 100644 --- a/contrib/dhcpcd/src/control.c +++ b/contrib/dhcpcd/src/control.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -52,41 +53,26 @@ #endif static void -control_queue_purge(struct dhcpcd_ctx *ctx, char *data) -{ - int found; - struct fd_list *fp; - struct fd_data *fpd; - - /* If no other fd queue has the same data, free it */ - found = 0; - TAILQ_FOREACH(fp, &ctx->control_fds, next) { - TAILQ_FOREACH(fpd, &fp->queue, next) { - if (fpd->data == data) { - found = 1; - break; - } - } - } - if (!found) - free(data); -} - -static void control_queue_free(struct fd_list *fd) { struct fd_data *fdp; while ((fdp = TAILQ_FIRST(&fd->queue))) { TAILQ_REMOVE(&fd->queue, fdp, next); - if (fdp->freeit) - control_queue_purge(fd->ctx, fdp->data); + if (fdp->data_size != 0) + free(fdp->data); free(fdp); } + fd->queue_len = 0; + +#ifdef CTL_FREE_LIST while ((fdp = TAILQ_FIRST(&fd->free_queue))) { TAILQ_REMOVE(&fd->free_queue, fdp, next); + if (fdp->data_size != 0) + free(fdp->data); free(fdp); } +#endif } static void @@ -160,29 +146,33 @@ control_handle1(struct dhcpcd_ctx *ctx, int lfd, unsigned int fd_flags) len = sizeof(run); if ((fd = accept(lfd, (struct sockaddr *)&run, &len)) == -1) - return; + goto error; if ((flags = fcntl(fd, F_GETFD, 0)) == -1 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) - { - close(fd); - return; - } + goto error; if ((flags = fcntl(fd, F_GETFL, 0)) == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) - { - close(fd); - return; - } + goto error; + l = malloc(sizeof(*l)); - if (l) { - l->ctx = ctx; - l->fd = fd; - l->flags = fd_flags; - TAILQ_INIT(&l->queue); - TAILQ_INIT(&l->free_queue); - TAILQ_INSERT_TAIL(&ctx->control_fds, l, next); - eloop_event_add(ctx->eloop, l->fd, control_handle_data, l); - } else + if (l == NULL) + goto error; + + l->ctx = ctx; + l->fd = fd; + l->flags = fd_flags; + TAILQ_INIT(&l->queue); + l->queue_len = 0; +#ifdef CTL_FREE_LIST + TAILQ_INIT(&l->free_queue); +#endif + TAILQ_INSERT_TAIL(&ctx->control_fds, l, next); + eloop_event_add(ctx->eloop, l->fd, control_handle_data, l); + return; + +error: + logerr(__func__); + if (fd != -1) close(fd); } @@ -318,7 +308,7 @@ control_open(const char *ifname) if ((fd = make_sock(&sa, ifname, 0)) != -1) { socklen_t len; - + len = (socklen_t)SUN_LEN(&sa); if (connect(fd, (struct sockaddr *)&sa, len) == -1) { close(fd); @@ -373,40 +363,88 @@ control_writeone(void *arg) } TAILQ_REMOVE(&fd->queue, data, next); - if (data->freeit) - control_queue_purge(fd->ctx, data->data); - data->data = NULL; /* safety */ - data->data_len = 0; + fd->queue_len--; +#ifdef CTL_FREE_LIST TAILQ_INSERT_TAIL(&fd->free_queue, data, next); +#else + if (data->data_size != 0) + free(data->data); + free(data); +#endif if (TAILQ_FIRST(&fd->queue) == NULL) eloop_event_remove_writecb(fd->ctx->eloop, fd->fd); } int -control_queue(struct fd_list *fd, char *data, size_t data_len, uint8_t fit) +control_queue(struct fd_list *fd, void *data, size_t data_len, bool fit) { struct fd_data *d; - size_t n; - d = TAILQ_FIRST(&fd->free_queue); - if (d) { - TAILQ_REMOVE(&fd->free_queue, d, next); - } else { - n = 0; - TAILQ_FOREACH(d, &fd->queue, next) { - if (++n == CONTROL_QUEUE_MAX) { - errno = ENOBUFS; - return -1; + if (data_len == 0) + return 0; + +#ifdef CTL_FREE_LIST + struct fd_data *df; + + d = NULL; + TAILQ_FOREACH(df, &fd->free_queue, next) { + if (!fit) { + if (df->data_size == 0) { + d = df; + break; } + continue; + } + if (d == NULL || d->data_size < df->data_size) { + d = df; + if (d->data_size <= data_len) + break; } - d = malloc(sizeof(*d)); + } + if (d != NULL) + TAILQ_REMOVE(&fd->free_queue, d, next); + else +#endif + { + if (fd->queue_len == CONTROL_QUEUE_MAX) { + errno = ENOBUFS; + return -1; + } + fd->queue_len++; + d = calloc(1, sizeof(*d)); if (d == NULL) return -1; } - d->data = data; + + if (!fit) { +#ifdef CTL_FREE_LIST + if (d->data_size != 0) { + free(d->data); + d->data_size = 0; + } +#endif + d->data = data; + d->data_len = data_len; + goto queue; + } + + if (d->data_size == 0) + d->data = NULL; + if (d->data_size < data_len) { + void *nbuf = realloc(d->data, data_len); + if (nbuf == NULL) { + free(d->data); + free(d); + return -1; + } + d->data = nbuf; + d->data_size = data_len; + } + memcpy(d->data, data, data_len); d->data_len = data_len; - d->freeit = fit; + +queue: TAILQ_INSERT_TAIL(&fd->queue, d, next); eloop_event_add_w(fd->ctx->eloop, fd->fd, control_writeone, fd); return 0; diff --git a/contrib/dhcpcd/src/control.h b/contrib/dhcpcd/src/control.h index 93ac01f62b..e52343dba1 100644 --- a/contrib/dhcpcd/src/control.h +++ b/contrib/dhcpcd/src/control.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -30,14 +31,20 @@ #include "dhcpcd.h" +#if !defined(CTL_FREE_LIST) +#define CTL_FREE_LIST 1 +#elif CTL_FREE_LIST == 0 +#undef CTL_FREE_LIST +#endif + /* Limit queue size per fd */ #define CONTROL_QUEUE_MAX 100 struct fd_data { TAILQ_ENTRY(fd_data) next; - char *data; + void *data; + size_t data_size; size_t data_len; - uint8_t freeit; }; TAILQ_HEAD(fd_data_head, fd_data); @@ -47,7 +54,10 @@ struct fd_list { int fd; unsigned int flags; struct fd_data_head queue; + size_t queue_len; +#ifdef CTL_FREE_LIST struct fd_data_head free_queue; +#endif }; TAILQ_HEAD(fd_list_head, fd_list); @@ -58,7 +68,7 @@ int control_start(struct dhcpcd_ctx *, const char *); int control_stop(struct dhcpcd_ctx *); int control_open(const char *); ssize_t control_send(struct dhcpcd_ctx *, int, char * const *); -int control_queue(struct fd_list *fd, char *data, size_t data_len, uint8_t fit); +int control_queue(struct fd_list *, void *, size_t, bool); void control_close(struct dhcpcd_ctx *ctx); #endif diff --git a/contrib/dhcpcd/src/defs.h b/contrib/dhcpcd/src/defs.h index c68a98c4bb..9bf87366a0 100644 --- a/contrib/dhcpcd/src/defs.h +++ b/contrib/dhcpcd/src/defs.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,7 +29,7 @@ #define CONFIG_H #define PACKAGE "dhcpcd" -#define VERSION "7.0.8" +#define VERSION "8.0.3" #ifndef CONFIG # define CONFIG SYSCONFDIR "/" PACKAGE ".conf" diff --git a/contrib/dhcpcd/src/dev.c b/contrib/dhcpcd/src/dev.c deleted file mode 100644 index 0d3891785e..0000000000 --- a/contrib/dhcpcd/src/dev.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include - -#define _INDEV -#include "common.h" -#include "dev.h" -#include "eloop.h" -#include "dhcpcd.h" -#include "logerr.h" - -int -dev_initialized(struct dhcpcd_ctx *ctx, const char *ifname) -{ - - if (ctx->dev == NULL) - return 1; - return ctx->dev->initialized(ifname); -} - -int -dev_listening(struct dhcpcd_ctx *ctx) -{ - - if (ctx->dev == NULL) - return 0; - return ctx->dev->listening(); -} - -static void -dev_stop1(struct dhcpcd_ctx *ctx, int stop) -{ - - if (ctx->dev) { - if (stop) - logdebugx("dev: unloaded %s", ctx->dev->name); - eloop_event_delete(ctx->eloop, ctx->dev_fd); - ctx->dev->stop(); - free(ctx->dev); - ctx->dev = NULL; - ctx->dev_fd = -1; - } - if (ctx->dev_handle) { - dlclose(ctx->dev_handle); - ctx->dev_handle = NULL; - } -} - -void -dev_stop(struct dhcpcd_ctx *ctx) -{ - - dev_stop1(ctx,!(ctx->options & DHCPCD_FORKED)); -} - -static int -dev_start2(struct dhcpcd_ctx *ctx, const char *name) -{ - char file[PATH_MAX]; - void *h; - void (*fptr)(struct dev *, const struct dev_dhcpcd *); - int r; - struct dev_dhcpcd dev_dhcpcd; - - snprintf(file, sizeof(file), DEVDIR "/%s", name); - h = dlopen(file, RTLD_LAZY); - if (h == NULL) { - logerrx("dlopen: %s", dlerror()); - return -1; - } - fptr = (void (*)(struct dev *, const struct dev_dhcpcd *)) - dlsym(h, "dev_init"); - if (fptr == NULL) { - logerrx("dlsym: %s", dlerror()); - dlclose(h); - return -1; - } - if ((ctx->dev = calloc(1, sizeof(*ctx->dev))) == NULL) { - logerr("%s: calloc", __func__); - dlclose(h); - return -1; - } - dev_dhcpcd.handle_interface = &dhcpcd_handleinterface; - fptr(ctx->dev, &dev_dhcpcd); - if (ctx->dev->start == NULL || (r = ctx->dev->start()) == -1) { - free(ctx->dev); - ctx->dev = NULL; - dlclose(h); - return -1; - } - loginfox("dev: loaded %s", ctx->dev->name); - ctx->dev_handle = h; - return r; -} - -static int -dev_start1(struct dhcpcd_ctx *ctx) -{ - DIR *dp; - struct dirent *d; - int r; - - if (ctx->dev) { - logerrx("dev: already started %s", ctx->dev->name); - return -1; - } - - if (ctx->dev_load) - return dev_start2(ctx, ctx->dev_load); - - dp = opendir(DEVDIR); - if (dp == NULL) { - logdebug("dev: %s", DEVDIR); - return 0; - } - - r = 0; - while ((d = readdir(dp))) { - if (d->d_name[0] == '.') - continue; - - r = dev_start2(ctx, d->d_name); - if (r != -1) - break; - } - closedir(dp); - return r; -} - -static void -dev_handle_data(void *arg) -{ - struct dhcpcd_ctx *ctx; - - ctx = arg; - if (ctx->dev->handle_device(arg) == -1) { - /* XXX: an error occured. should we restart dev? */ - } -} - -int -dev_start(struct dhcpcd_ctx *ctx) -{ - - if (ctx->dev_fd != -1) { - logerrx("%s: already started on fd %d", __func__, ctx->dev_fd); - return ctx->dev_fd; - } - - ctx->dev_fd = dev_start1(ctx); - if (ctx->dev_fd != -1) { - if (eloop_event_add(ctx->eloop, ctx->dev_fd, - dev_handle_data, ctx) == -1) - { - logerr(__func__); - dev_stop1(ctx, 1); - return -1; - } - } - - return ctx->dev_fd; -} diff --git a/contrib/dhcpcd/src/dev.h b/contrib/dhcpcd/src/dev.h index daf4d29561..b2cd804b8d 100644 --- a/contrib/dhcpcd/src/dev.h +++ b/contrib/dhcpcd/src/dev.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/contrib/dhcpcd/src/dhcp-common.c b/contrib/dhcpcd/src/dhcp-common.c index eed3dbf469..dab20a2e92 100644 --- a/contrib/dhcpcd/src/dhcp-common.c +++ b/contrib/dhcpcd/src/dhcp-common.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -36,8 +37,6 @@ #include #include -#include /* after normal includes for sunos */ - #include "config.h" #include "common.h" @@ -46,13 +45,7 @@ #include "if.h" #include "ipv6.h" #include "logerr.h" - -/* Support very old arpa/nameser.h as found in OpenBSD */ -#ifndef NS_MAXDNAME -#define NS_MAXCDNAME MAXCDNAME -#define NS_MAXDNAME MAXDNAME -#define NS_MAXLABEL MAXLABEL -#endif +#include "script.h" const char * dhcp_get_hostname(char *buf, size_t buf_len, const struct if_options *ifo) @@ -624,95 +617,77 @@ dhcp_optlen(const struct dhcp_opt *opt, size_t dl) return (ssize_t)sz; } -/* It's possible for DHCPv4 to contain an IPv6 address */ static ssize_t -ipv6_printaddr(char *s, size_t sl, const uint8_t *d, const char *ifname) -{ - char buf[INET6_ADDRSTRLEN]; - const char *p; - size_t l; - - p = inet_ntop(AF_INET6, d, buf, sizeof(buf)); - if (p == NULL) - return -1; - - l = strlen(p); - if (d[0] == 0xfe && (d[1] & 0xc0) == 0x80) - l += 1 + strlen(ifname); - - if (s == NULL) - return (ssize_t)l; - - if (sl < l) { - errno = ENOMEM; - return -1; - } - - s += strlcpy(s, p, sl); - if (d[0] == 0xfe && (d[1] & 0xc0) == 0x80) { - *s++ = '%'; - s += strlcpy(s, ifname, sl); - } - *s = '\0'; - return (ssize_t)l; -} - -static ssize_t -print_option(char *s, size_t len, const struct dhcp_opt *opt, +print_option(FILE *fp, const char *prefix, const struct dhcp_opt *opt, + int vname, const uint8_t *data, size_t dl, const char *ifname) { + fpos_t fp_pos; const uint8_t *e, *t; uint16_t u16; int16_t s16; uint32_t u32; int32_t s32; struct in_addr addr; - ssize_t bytes = 0, sl; + ssize_t sl; size_t l; -#ifdef INET - char *tmp; -#endif + + /* Ensure a valid length */ + dl = (size_t)dhcp_optlen(opt, dl); + if ((ssize_t)dl == -1) + return 0; + + if (fgetpos(fp, &fp_pos) == -1) + return -1; + if (fprintf(fp, "%s", prefix) == -1) + goto err; + + /* We printed something, so always goto err from now-on + * to terminate the string. */ + if (vname) { + if (fprintf(fp, "_%s", opt->var) == -1) + goto err; + } + if (fputc('=', fp) == EOF) + goto err; + if (dl == 0) + goto done; if (opt->type & OT_RFC1035) { - sl = decode_rfc1035(s, len, data, dl); - if (sl == 0 || sl == -1) - return sl; - if (s != NULL) { - if (valid_domainname(s, opt->type) == -1) - return -1; - } - return sl; + char domain[NS_MAXDNAME]; + + sl = decode_rfc1035(domain, sizeof(domain), data, dl); + if (sl == -1) + goto err; + if (sl == 0) + goto done; + if (valid_domainname(domain, opt->type) == -1) + goto err; + return efprintf(fp, "%s", domain); } #ifdef INET - if (opt->type & OT_RFC3361) { - if ((tmp = decode_rfc3361(data, dl)) == NULL) - return -1; - l = strlen(tmp); - sl = print_string(s, len, opt->type, (uint8_t *)tmp, l); - free(tmp); - return sl; - } + if (opt->type & OT_RFC3361) + return print_rfc3361(fp, data, dl); if (opt->type & OT_RFC3442) - return decode_rfc3442(s, len, data, dl); + return print_rfc3442(fp, data, dl); #endif - if (opt->type & OT_STRING) - return print_string(s, len, opt->type, data, dl); + if (opt->type & OT_STRING) { + char buf[1024]; - if (opt->type & OT_FLAG) { - if (s) { - *s++ = '1'; - *s = '\0'; - } - return 1; + if (print_string(buf, sizeof(buf), opt->type, data, dl) == -1) + goto err; + return efprintf(fp, "%s", buf); } + if (opt->type & OT_FLAG) + return efprintf(fp, "1"); + if (opt->type & OT_BITFLAG) { /* bitflags are a string, MSB first, such as ABCDEFGH * where A is 10000000, B is 01000000, etc. */ - bytes = 0; for (l = 0, sl = sizeof(opt->bitflags) - 1; l < sizeof(opt->bitflags); l++, sl--) @@ -722,109 +697,83 @@ print_option(char *s, size_t len, const struct dhcp_opt *opt, opt->bitflags[l] != '0' && *data & (1 << sl)) { - if (s) - *s++ = opt->bitflags[l]; - bytes++; + if (fputc(opt->bitflags[l], fp) == EOF) + goto err; } } - if (s) - *s = '\0'; - return bytes; - } - - if (!s) { - if (opt->type & OT_UINT8) - l = 3; - else if (opt->type & OT_INT8) - l = 4; - else if (opt->type & OT_UINT16) { - l = 5; - dl /= 2; - } else if (opt->type & OT_INT16) { - l = 6; - dl /= 2; - } else if (opt->type & OT_UINT32) { - l = 10; - dl /= 4; - } else if (opt->type & OT_INT32) { - l = 11; - dl /= 4; - } else if (opt->type & OT_ADDRIPV4) { - l = 16; - dl /= 4; - } else if (opt->type & OT_ADDRIPV6) { - e = data + dl; - l = 0; - while (data < e) { - if (l) - l++; /* space */ - sl = ipv6_printaddr(NULL, 0, data, ifname); - if (sl == -1) - return l == 0 ? -1 : (ssize_t)l; - l += (size_t)sl; - data += 16; - } - return (ssize_t)l; - } else { - errno = EINVAL; - return -1; - } - return (ssize_t)(l * dl); + goto done; } t = data; e = data + dl; while (data < e) { if (data != t) { - *s++ = ' '; - bytes++; - len--; + if (fputc(' ', fp) == EOF) + goto err; } if (opt->type & OT_UINT8) { - sl = snprintf(s, len, "%u", *data); + if (fprintf(fp, "%u", *data) == -1) + goto err; data++; } else if (opt->type & OT_INT8) { - sl = snprintf(s, len, "%d", *data); + if (fprintf(fp, "%d", *data) == -1) + goto err; data++; } else if (opt->type & OT_UINT16) { memcpy(&u16, data, sizeof(u16)); u16 = ntohs(u16); - sl = snprintf(s, len, "%u", u16); + if (fprintf(fp, "%u", u16) == -1) + goto err; data += sizeof(u16); } else if (opt->type & OT_INT16) { memcpy(&u16, data, sizeof(u16)); s16 = (int16_t)ntohs(u16); - sl = snprintf(s, len, "%d", s16); + if (fprintf(fp, "%d", s16) == -1) + goto err; data += sizeof(u16); } else if (opt->type & OT_UINT32) { memcpy(&u32, data, sizeof(u32)); u32 = ntohl(u32); - sl = snprintf(s, len, "%u", u32); + if (fprintf(fp, "%u", u32) == -1) + goto err; data += sizeof(u32); } else if (opt->type & OT_INT32) { memcpy(&u32, data, sizeof(u32)); s32 = (int32_t)ntohl(u32); - sl = snprintf(s, len, "%d", s32); + if (fprintf(fp, "%d", s32) == -1) + goto err; data += sizeof(u32); } else if (opt->type & OT_ADDRIPV4) { memcpy(&addr.s_addr, data, sizeof(addr.s_addr)); - sl = snprintf(s, len, "%s", inet_ntoa(addr)); + if (fprintf(fp, "%s", inet_ntoa(addr)) == -1) + goto err; data += sizeof(addr.s_addr); } else if (opt->type & OT_ADDRIPV6) { - sl = ipv6_printaddr(s, len, data, ifname); + char buf[INET6_ADDRSTRLEN]; + + if (inet_ntop(AF_INET6, data, buf, sizeof(buf)) == NULL) + goto err; + if (fprintf(fp, "%s", buf) == -1) + goto err; + if (data[0] == 0xfe && (data[1] & 0xc0) == 0x80) { + if (fprintf(fp,"%%%s", ifname) == -1) + goto err; + } data += 16; } else { errno = EINVAL; - return -1; + goto err; } - if (sl == -1) - return bytes == 0 ? -1 : bytes; - len -= (size_t)sl; - bytes += sl; - s += sl; } - return bytes; +done: + if (fputc('\0', fp) == EOF) + return -1; + return 1; + +err: + (void)fsetpos(fp, &fp_pos); + return -1; } int @@ -859,61 +808,15 @@ dhcp_set_leasefile(char *leasefile, size_t len, int family, ifp->name, ssid); } -static size_t -dhcp_envoption1(char **env, const char *prefix, - const struct dhcp_opt *opt, int vname, const uint8_t *od, size_t ol, - const char *ifname) -{ - ssize_t len; - size_t e; - char *v, *val; - int r; - - /* Ensure a valid length */ - ol = (size_t)dhcp_optlen(opt, ol); - if ((ssize_t)ol == -1) - return 0; - - len = print_option(NULL, 0, opt, od, ol, ifname); - if (len < 0) - return 0; - if (vname) - e = strlen(opt->var) + 1; - else - e = 0; - if (prefix) - e += strlen(prefix); - e += (size_t)len + 2; - if (env == NULL) - return e; - v = val = *env = malloc(e); - if (v == NULL) - return 0; - if (vname) - r = snprintf(val, e, "%s_%s=", prefix, opt->var); - else - r = snprintf(val, e, "%s=", prefix); - if (r != -1 && len != 0) { - v += r; - if (print_option(v, (size_t)len + 1, opt, od, ol, ifname) == -1) - r = -1; - } - if (r == -1) { - free(val); - return 0; - } - return e; -} - -size_t -dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix, +void +dhcp_envoption(struct dhcpcd_ctx *ctx, FILE *fp, const char *prefix, const char *ifname, struct dhcp_opt *opt, const uint8_t *(*dgetopt)(struct dhcpcd_ctx *, size_t *, unsigned int *, size_t *, const uint8_t *, size_t, struct dhcp_opt **), const uint8_t *od, size_t ol) { - size_t e, i, n, eos, eol; + size_t i, eos, eol; ssize_t eo; unsigned int eoc; const uint8_t *eod; @@ -923,52 +826,36 @@ dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix, /* If no embedded or encapsulated options, it's easy */ if (opt->embopts_len == 0 && opt->encopts_len == 0) { - if (!(opt->type & OT_RESERVED)) { - if (dhcp_envoption1(env == NULL ? NULL : &env[0], - prefix, opt, 1, od, ol, ifname)) - return 1; - else - logerr("%s: %s %d", - ifname, __func__, opt->option); - } - return 0; + if (opt->type & OT_RESERVED) + return; + if (print_option(fp, prefix, opt, 1, od, ol, ifname) == -1) + logerr("%s: %s %d", ifname, __func__, opt->option); + return; } /* Create a new prefix based on the option */ - if (env) { - if (opt->type & OT_INDEX) { - if (opt->index > 999) { - errno = ENOBUFS; - logerr(__func__); - return 0; - } - } - e = strlen(prefix) + strlen(opt->var) + 2 + - (opt->type & OT_INDEX ? 3 : 0); - pfx = malloc(e); - if (pfx == NULL) { - logerr(__func__); - return 0; - } - if (opt->type & OT_INDEX) - snprintf(pfx, e, "%s_%s%d", prefix, - opt->var, ++opt->index); - else - snprintf(pfx, e, "%s_%s", prefix, opt->var); - } else - pfx = NULL; + if (opt->type & OT_INDEX) { + if (asprintf(&pfx, "%s_%s%d", + prefix, opt->var, ++opt->index) == -1) + pfx = NULL; + } else { + if (asprintf(&pfx, "%s_%s", prefix, opt->var) == -1) + pfx = NULL; + } + if (pfx == NULL) { + logerr(__func__); + return; + } /* Embedded options are always processed first as that * is a fixed layout */ - n = 0; for (i = 0, eopt = opt->embopts; i < opt->embopts_len; i++, eopt++) { eo = dhcp_optlen(eopt, ol); if (eo == -1) { - if (env == NULL) - logerrx("%s: %s %d.%d/%zu: " - "malformed embedded option", - ifname, __func__, opt->option, - eopt->option, i); + logerrx("%s: %s %d.%d/%zu: " + "malformed embedded option", + ifname, __func__, opt->option, + eopt->option, i); goto out; } if (eo == 0) { @@ -977,9 +864,9 @@ dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix, * This may not be an error as some options like * DHCP FQDN in RFC4702 have a string as the last * option which is optional. */ - if (env == NULL && - (ol != 0 || !(eopt->type & OT_OPTIONAL))) - logerrx("%s: %s %d.%d/%zu: missing embedded option", + if (ol != 0 || !(eopt->type & OT_OPTIONAL)) + logerrx("%s: %s %d.%d/%zu: " + "missing embedded option", ifname, __func__, opt->option, eopt->option, i); goto out; @@ -989,10 +876,8 @@ dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix, * This avoids new_fqdn_fqdn which would be silly. */ if (!(eopt->type & OT_RESERVED)) { ov = strcmp(opt->var, eopt->var); - if (dhcp_envoption1(env == NULL ? NULL : &env[n], - pfx, eopt, ov, od, (size_t)eo, ifname)) - n++; - else if (env == NULL) + if (print_option(fp, pfx, eopt, ov, od, (size_t)eo, + ifname) == -1) logerr("%s: %s %d.%d/%zu", ifname, __func__, opt->option, eopt->option, i); @@ -1023,19 +908,16 @@ dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix, i < opt->encopts_len; i++, eopt++) { - if (eopt->option == eoc) { - if (eopt->type & OT_OPTION) { - if (oopt == NULL) - /* Report error? */ - continue; - } - n += dhcp_envoption(ctx, - env == NULL ? NULL : &env[n], pfx, - ifname, - eopt->type & OT_OPTION ? oopt:eopt, - dgetopt, eod, eol); - break; + if (eopt->option != eoc) + continue; + if (eopt->type & OT_OPTION) { + if (oopt == NULL) + /* Report error? */ + continue; } + dhcp_envoption(ctx, fp, pfx, ifname, + eopt->type & OT_OPTION ? oopt:eopt, + dgetopt, eod, eol); } od += eos + eol; ol -= eos + eol; @@ -1043,11 +925,7 @@ dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix, } out: - if (env) - free(pfx); - - /* Return number of options found */ - return n; + free(pfx); } void @@ -1070,7 +948,7 @@ dhcp_read_lease_fd(int fd, void **lease) size_t sz; void *buf; ssize_t len; - + if (fstat(fd, &st) != 0) goto out; if (!S_ISREG(st.st_mode)) { @@ -1083,6 +961,8 @@ dhcp_read_lease_fd(int fd, void **lease) } sz = (size_t)st.st_size; + if (sz == 0) + goto out; if ((buf = malloc(sz)) == NULL) goto out; if ((len = read(fd, buf, sz)) == -1) { diff --git a/contrib/dhcpcd/src/dhcp-common.h b/contrib/dhcpcd/src/dhcp-common.h index aea3d85281..2732823afd 100644 --- a/contrib/dhcpcd/src/dhcp-common.h +++ b/contrib/dhcpcd/src/dhcp-common.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -33,9 +34,18 @@ #include +#include /* after normal includes for sunos */ + #include "common.h" #include "dhcpcd.h" +/* Support very old arpa/nameser.h as found in OpenBSD */ +#ifndef NS_MAXDNAME +#define NS_MAXCDNAME MAXCDNAME +#define NS_MAXDNAME MAXDNAME +#define NS_MAXLABEL MAXLABEL +#endif + /* Max MTU - defines dhcp option length */ #define IP_UDP_SIZE 28 #define MTU_MAX 1500 - IP_UDP_SIZE @@ -111,8 +121,8 @@ ssize_t decode_rfc1035(char *, size_t, const uint8_t *, size_t); ssize_t print_string(char *, size_t, int, const uint8_t *, size_t); int dhcp_set_leasefile(char *, size_t, int, const struct interface *); -size_t dhcp_envoption(struct dhcpcd_ctx *, - char **, const char *, const char *, struct dhcp_opt *, +void dhcp_envoption(struct dhcpcd_ctx *, + FILE *, const char *, const char *, struct dhcp_opt *, const uint8_t *(*dgetopt)(struct dhcpcd_ctx *, size_t *, unsigned int *, size_t *, const uint8_t *, size_t, struct dhcp_opt **), diff --git a/contrib/dhcpcd/src/dhcp.c b/contrib/dhcpcd/src/dhcp.c index 1e9fe186db..eafd6e7aa4 100644 --- a/contrib/dhcpcd/src/dhcp.c +++ b/contrib/dhcpcd/src/dhcp.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -47,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -86,9 +88,9 @@ #define IPDEFTTL 64 /* RFC1340 */ #endif -/* NetBSD-7 has an incomplete IP_PKTINFO implementation. */ -#if defined(__NetBSD_Version__) && __NetBSD_Version__ < 800000000 -#undef IP_PKTINFO +/* Support older systems with different defines */ +#if !defined(IP_RECVPKTINFO) && defined(IP_PKTINFO) +#define IP_RECVPKTINFO IP_PKTINFO #endif /* Assert the correct structure size for on wire */ @@ -124,11 +126,15 @@ static const char * const dhcp_params[] = { }; static int dhcp_openbpf(struct interface *); -#ifdef ARP -static void dhcp_arp_conflicted(struct arp_state *, const struct arp_msg *); +static void dhcp_start1(void *); +#if defined(ARP) && (!defined(KERNEL_RFC5227) || defined(ARPING)) +static void dhcp_arp_found(struct arp_state *, const struct arp_msg *); #endif static void dhcp_handledhcp(struct interface *, struct bootp *, size_t, const struct in_addr *); +#ifdef IP_PKTINFO +static void dhcp_handleifudp(void *); +#endif static int dhcp_initstate(struct interface *); void @@ -212,6 +218,12 @@ get_option(struct dhcpcd_ctx *ctx, } l = *p++; + /* Check we can read the option data, if present */ + if (p + l > e) { + errno = EINVAL; + return NULL; + } + if (o == DHO_OPTSOVERLOADED) { /* Ensure we only get this option once by setting * the last bit as well as the value. @@ -246,10 +258,6 @@ get_option(struct dhcpcd_ctx *ctx, bp += ol; } ol = l; - if (p + ol >= e) { - errno = EINVAL; - return NULL; - } op = p; bl += ol; } @@ -334,23 +342,25 @@ get_option_uint8(struct dhcpcd_ctx *ctx, } ssize_t -decode_rfc3442(char *out, size_t len, const uint8_t *p, size_t pl) +print_rfc3442(FILE *fp, const uint8_t *data, size_t data_len) { - const uint8_t *e; - size_t bytes = 0, ocets; - int b; + const uint8_t *p = data, *e; + size_t ocets; uint8_t cidr; struct in_addr addr; - char *o = out; /* Minimum is 5 -first is CIDR and a router length of 4 */ - if (pl < 5) { + if (data_len < 5) { errno = EINVAL; return -1; } - e = p + pl; + e = p + data_len; while (p < e) { + if (p != data) { + if (fputc(' ', fp) == EOF) + return -1; + } cidr = *p++; if (cidr > 32) { errno = EINVAL; @@ -361,45 +371,29 @@ decode_rfc3442(char *out, size_t len, const uint8_t *p, size_t pl) errno = ERANGE; return -1; } - if (!out) { - p += 4 + ocets; - bytes += ((4 * 4) * 2) + 4; - continue; - } - if ((((4 * 4) * 2) + 4) > len) { - errno = ENOBUFS; - return -1; - } - if (o != out) { - *o++ = ' '; - len--; - } /* If we have ocets then we have a destination and netmask */ + addr.s_addr = 0; if (ocets > 0) { - addr.s_addr = 0; memcpy(&addr.s_addr, p, ocets); - b = snprintf(o, len, "%s/%d", inet_ntoa(addr), cidr); p += ocets; - } else - b = snprintf(o, len, "0.0.0.0/0"); - o += b; - len -= (size_t)b; + } + if (fprintf(fp, "%s/%d", inet_ntoa(addr), cidr) == -1) + return -1; /* Finally, snag the router */ memcpy(&addr.s_addr, p, 4); p += 4; - b = snprintf(o, len, " %s", inet_ntoa(addr)); - o += b; - len -= (size_t)b; + if (fprintf(fp, " %s", inet_ntoa(addr)) == -1) + return -1; } - if (out) - return o - out; - return (ssize_t)bytes; + if (fputc('\0', fp) == EOF) + return -1; + return 1; } static int -decode_rfc3442_rt(struct rt_head *routes, struct interface *ifp, +decode_rfc3442_rt(rb_tree_t *routes, struct interface *ifp, const uint8_t *data, size_t dl, const struct bootp *bootp) { const uint8_t *p = data; @@ -447,7 +441,7 @@ decode_rfc3442_rt(struct rt_head *routes, struct interface *ifp, memcpy(&gateway.s_addr, p, 4); p += 4; - /* A host route is normally set by having the + /* An on-link host route is normally set by having the * gateway match the destination or assigned address */ if (gateway.s_addr == dest.s_addr || (gateway.s_addr == bootp->yiaddr || @@ -455,32 +449,25 @@ decode_rfc3442_rt(struct rt_head *routes, struct interface *ifp, { gateway.s_addr = INADDR_ANY; netmask.s_addr = INADDR_BROADCAST; - rt->rt_flags = RTF_HOST; } + if (netmask.s_addr == INADDR_BROADCAST) + rt->rt_flags = RTF_HOST; sa_in_init(&rt->rt_dest, &dest); sa_in_init(&rt->rt_netmask, &netmask); sa_in_init(&rt->rt_gateway, &gateway); - - /* If CIDR is 32 then it's a host route. */ - if (cidr == 32) - rt->rt_flags = RTF_HOST; - - TAILQ_INSERT_TAIL(routes, rt, rt_next); - n++; + if (rt_proto_add(routes, rt)) + n = 1; } return n; } -char * -decode_rfc3361(const uint8_t *data, size_t dl) +ssize_t +print_rfc3361(FILE *fp, const uint8_t *data, size_t dl) { uint8_t enc; - size_t l; - ssize_t r; - char *sip = NULL; + char sip[NS_MAXDNAME]; struct in_addr addr; - char *p; if (dl < 2) { errno = EINVAL; @@ -491,13 +478,10 @@ decode_rfc3361(const uint8_t *data, size_t dl) dl--; switch (enc) { case 0: - if ((r = decode_rfc1035(NULL, 0, data, dl)) > 0) { - l = (size_t)r + 1; - sip = malloc(l); - if (sip == NULL) - return 0; - decode_rfc1035(sip, l, data, dl); - } + if (decode_rfc1035(sip, sizeof(sip), data, dl) == -1) + return -1; + if (efprintf(fp, "%s", sip) == -1) + return -1; break; case 1: if (dl == 0 || dl % 4 != 0) { @@ -505,25 +489,27 @@ decode_rfc3361(const uint8_t *data, size_t dl) break; } addr.s_addr = INADDR_BROADCAST; - l = ((dl / sizeof(addr.s_addr)) * ((4 * 4) + 1)) + 1; - sip = p = malloc(l); - if (sip == NULL) - return 0; - while (dl != 0) { + for (; + dl != 0; + data += sizeof(addr.s_addr), dl -= sizeof(addr.s_addr)) + { memcpy(&addr.s_addr, data, sizeof(addr.s_addr)); - data += sizeof(addr.s_addr); - p += snprintf(p, l - (size_t)(p - sip), - "%s ", inet_ntoa(addr)); - dl -= sizeof(addr.s_addr); + if (fprintf(fp, "%s", inet_ntoa(addr)) == -1) + return -1; + if (dl != 0) { + if (fputc(' ', fp) == EOF) + return -1; + } } - *--p = '\0'; + if (fputc('\0', fp) == EOF) + return -1; break; default: errno = EINVAL; return 0; } - return sip; + return 1; } static char * @@ -579,7 +565,7 @@ route_netmask(uint32_t ip_in) * If we have a CSR then we only use that. * Otherwise we add static routes and then routers. */ static int -get_option_routes(struct rt_head *routes, struct interface *ifp, +get_option_routes(rb_tree_t *routes, struct interface *ifp, const struct bootp *bootp, size_t bootp_len) { struct if_options *ifo = ifp->options; @@ -638,7 +624,7 @@ get_option_routes(struct rt_head *routes, struct interface *ifp, if ((rt = rt_new(ifp)) == NULL) return -1; - /* A host route is normally set by having the + /* A on-link host route is normally set by having the * gateway match the destination or assigned address */ if (gateway.s_addr == dest.s_addr || (gateway.s_addr == bootp->yiaddr || @@ -646,14 +632,16 @@ get_option_routes(struct rt_head *routes, struct interface *ifp, { gateway.s_addr = INADDR_ANY; netmask.s_addr = INADDR_BROADCAST; - rt->rt_flags = RTF_HOST; } else netmask.s_addr = route_netmask(dest.s_addr); + if (netmask.s_addr == INADDR_BROADCAST) + rt->rt_flags = RTF_HOST; + sa_in_init(&rt->rt_dest, &dest); sa_in_init(&rt->rt_netmask, &netmask); sa_in_init(&rt->rt_gateway, &gateway); - TAILQ_INSERT_TAIL(routes, rt, rt_next); - n++; + if (rt_proto_add(routes, rt)) + n++; } } @@ -662,7 +650,7 @@ get_option_routes(struct rt_head *routes, struct interface *ifp, p = get_option(ifp->ctx, bootp, bootp_len, DHO_ROUTER, &len); else p = NULL; - if (p) { + if (p && len % 4 == 0) { e = p + len; dest.s_addr = INADDR_ANY; netmask.s_addr = INADDR_ANY; @@ -674,8 +662,8 @@ get_option_routes(struct rt_head *routes, struct interface *ifp, sa_in_init(&rt->rt_dest, &dest); sa_in_init(&rt->rt_netmask, &netmask); sa_in_init(&rt->rt_gateway, &gateway); - TAILQ_INSERT_TAIL(routes, rt, rt_next); - n++; + if (rt_proto_add(routes, rt)) + n++; } } @@ -702,7 +690,7 @@ dhcp_get_mtu(const struct interface *ifp) /* Grab our routers from the DHCP message and apply any MTU value * the message contains */ int -dhcp_get_routes(struct rt_head *routes, struct interface *ifp) +dhcp_get_routes(rb_tree_t *routes, struct interface *ifp) { const struct dhcp_state *state; @@ -1188,11 +1176,8 @@ read_lease(struct interface *ifp, struct bootp **bootp) bytes = dhcp_read_lease_fd(fd, (void **)&lease); if (fd_opened) close(fd); - if (bytes == 0) { - free(lease); - logerr("%s: dhcp_read_lease_fd", __func__); + if (bytes == 0) return 0; - } /* Ensure the packet is at lease BOOTP sized * with a vendor area of 4 octets @@ -1294,9 +1279,8 @@ dhcp_getoption(struct dhcpcd_ctx *ctx, } ssize_t -dhcp_env(char **env, const char *prefix, - const struct bootp *bootp, size_t bootp_len, - const struct interface *ifp) +dhcp_env(FILE *fenv, const char *prefix, const struct interface *ifp, + const struct bootp *bootp, size_t bootp_len) { const struct if_options *ifo; const uint8_t *p; @@ -1304,109 +1288,73 @@ dhcp_env(char **env, const char *prefix, struct in_addr net; struct in_addr brd; struct dhcp_opt *opt, *vo; - size_t e, i, pl; - char **ep; - char cidr[4], safe[(BOOTP_FILE_LEN * 4) + 1]; + size_t i, pl; + char safe[(BOOTP_FILE_LEN * 4) + 1]; uint8_t overl = 0; uint32_t en; - e = 0; ifo = ifp->options; if (get_option_uint8(ifp->ctx, &overl, bootp, bootp_len, DHO_OPTSOVERLOADED) == -1) overl = 0; - if (env == NULL) { - if (bootp->yiaddr || bootp->ciaddr) - e += 5; - if (*bootp->file && !(overl & 1)) - e++; - if (*bootp->sname && !(overl & 2)) - e++; - for (i = 0, opt = ifp->ctx->dhcp_opts; - i < ifp->ctx->dhcp_opts_len; - i++, opt++) - { - if (has_option_mask(ifo->nomask, opt->option)) - continue; - if (dhcp_getoverride(ifo, opt->option)) - continue; - p = get_option(ifp->ctx, bootp, bootp_len, - opt->option, &pl); - if (!p) - continue; - e += dhcp_envoption(ifp->ctx, NULL, NULL, ifp->name, - opt, dhcp_getoption, p, pl); - } - for (i = 0, opt = ifo->dhcp_override; - i < ifo->dhcp_override_len; - i++, opt++) - { - if (has_option_mask(ifo->nomask, opt->option)) - continue; - p = get_option(ifp->ctx, bootp, bootp_len, - opt->option, &pl); - if (!p) - continue; - e += dhcp_envoption(ifp->ctx, NULL, NULL, ifp->name, - opt, dhcp_getoption, p, pl); - } - return (ssize_t)e; - } - - ep = env; if (bootp->yiaddr || bootp->ciaddr) { /* Set some useful variables that we derive from the DHCP * message but are not necessarily in the options */ addr.s_addr = bootp->yiaddr ? bootp->yiaddr : bootp->ciaddr; - addvar(&ep, prefix, "ip_address", inet_ntoa(addr)); + if (efprintf(fenv, "%s_ip_address=%s", + prefix, inet_ntoa(addr)) == -1) + return -1; if (get_option_addr(ifp->ctx, &net, - bootp, bootp_len, DHO_SUBNETMASK) == -1) - { + bootp, bootp_len, DHO_SUBNETMASK) == -1) { net.s_addr = ipv4_getnetmask(addr.s_addr); - addvar(&ep, prefix, - "subnet_mask", inet_ntoa(net)); + if (efprintf(fenv, "%s_subnet_mask=%s", + prefix, inet_ntoa(net)) == -1) + return -1; } - snprintf(cidr, sizeof(cidr), "%d", inet_ntocidr(net)); - addvar(&ep, prefix, "subnet_cidr", cidr); + if (efprintf(fenv, "%s_subnet_cidr=%d", + prefix, inet_ntocidr(net))== -1) + return -1; if (get_option_addr(ifp->ctx, &brd, bootp, bootp_len, DHO_BROADCAST) == -1) { brd.s_addr = addr.s_addr | ~net.s_addr; - addvar(&ep, prefix, - "broadcast_address", inet_ntoa(brd)); + if (efprintf(fenv, "%s_broadcast_address=%s", + prefix, inet_ntoa(brd)) == -1) + return -1; } addr.s_addr = bootp->yiaddr & net.s_addr; - addvar(&ep, prefix, - "network_number", inet_ntoa(addr)); + if (efprintf(fenv, "%s_network_number=%s", + prefix, inet_ntoa(addr)) == -1) + return -1; } if (*bootp->file && !(overl & 1)) { print_string(safe, sizeof(safe), OT_STRING, bootp->file, sizeof(bootp->file)); - addvar(&ep, prefix, "filename", safe); + if (efprintf(fenv, "%s_filename=%s", prefix, safe) == -1) + return -1; } if (*bootp->sname && !(overl & 2)) { print_string(safe, sizeof(safe), OT_STRING | OT_DOMAIN, bootp->sname, sizeof(bootp->sname)); - addvar(&ep, prefix, "server_name", safe); + if (efprintf(fenv, "%s_server_name=%s", prefix, safe) == -1) + return -1; } /* Zero our indexes */ - if (env) { - for (i = 0, opt = ifp->ctx->dhcp_opts; - i < ifp->ctx->dhcp_opts_len; - i++, opt++) - dhcp_zero_index(opt); - for (i = 0, opt = ifp->options->dhcp_override; - i < ifp->options->dhcp_override_len; - i++, opt++) - dhcp_zero_index(opt); - for (i = 0, opt = ifp->ctx->vivso; - i < ifp->ctx->vivso_len; - i++, opt++) - dhcp_zero_index(opt); - } + for (i = 0, opt = ifp->ctx->dhcp_opts; + i < ifp->ctx->dhcp_opts_len; + i++, opt++) + dhcp_zero_index(opt); + for (i = 0, opt = ifp->options->dhcp_override; + i < ifp->options->dhcp_override_len; + i++, opt++) + dhcp_zero_index(opt); + for (i = 0, opt = ifp->ctx->vivso; + i < ifp->ctx->vivso_len; + i++, opt++) + dhcp_zero_index(opt); for (i = 0, opt = ifp->ctx->dhcp_opts; i < ifp->ctx->dhcp_opts_len; @@ -1419,7 +1367,7 @@ dhcp_env(char **env, const char *prefix, p = get_option(ifp->ctx, bootp, bootp_len, opt->option, &pl); if (p == NULL) continue; - ep += dhcp_envoption(ifp->ctx, ep, prefix, ifp->name, + dhcp_envoption(ifp->ctx, fenv, prefix, ifp->name, opt, dhcp_getoption, p, pl); if (opt->option != DHO_VIVSO || pl <= (int)sizeof(uint32_t)) @@ -1432,7 +1380,7 @@ dhcp_env(char **env, const char *prefix, /* Skip over en + total size */ p += sizeof(en) + 1; pl -= sizeof(en) + 1; - ep += dhcp_envoption(ifp->ctx, ep, prefix, ifp->name, + dhcp_envoption(ifp->ctx, fenv, prefix, ifp->name, vo, dhcp_getoption, p, pl); } @@ -1445,11 +1393,11 @@ dhcp_env(char **env, const char *prefix, p = get_option(ifp->ctx, bootp, bootp_len, opt->option, &pl); if (p == NULL) continue; - ep += dhcp_envoption(ifp->ctx, ep, prefix, ifp->name, + dhcp_envoption(ifp->ctx, fenv, prefix, ifp->name, opt, dhcp_getoption, p, pl); } - return ep - env; + return 1; } static void @@ -1492,7 +1440,7 @@ get_lease(struct interface *ifp, } if (get_option_uint32(ctx, &lease->leasetime, bootp, len, DHO_LEASETIME) != 0) - lease->leasetime = ~0U; /* Default to infinite lease */ + lease->leasetime = DHCP_INFINITE_LIFETIME; if (get_option_uint32(ctx, &lease->renewaltime, bootp, len, DHO_RENEWALTIME) != 0) lease->renewaltime = 0; @@ -1587,6 +1535,11 @@ dhcp_close(struct interface *ifp) state->bpf_fd = -1; state->bpf_flags |= BPF_EOF; } + if (state->udp_fd != -1) { + eloop_event_delete(ifp->ctx->eloop, state->udp_fd); + close(state->udp_fd); + state->udp_fd = -1; + } state->interval = 0; } @@ -1604,11 +1557,15 @@ dhcp_openudp(struct interface *ifp) n = 1; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1) goto eexit; +#ifdef IP_RECVPKTINFO + if (setsockopt(s, IPPROTO_IP, IP_RECVPKTINFO, &n, sizeof(n)) == -1) + goto eexit; +#endif memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(BOOTPC); if (ifp) { - struct dhcp_state *state = D_STATE(ifp); + const struct dhcp_state *state = D_CSTATE(ifp); if (state->addr) sin.sin_addr.s_addr = state->addr->addr.s_addr; @@ -1624,24 +1581,24 @@ eexit: } static uint16_t -checksum(const void *data, size_t len) +in_cksum(const void *data, size_t len, uint32_t *isum) { - const uint8_t *addr = data; - uint32_t sum = 0; + const uint16_t *word = data; + uint32_t sum = isum != NULL ? *isum : 0; - while (len > 1) { - sum += (uint32_t)(addr[0] * 256 + addr[1]); - addr += 2; - len -= 2; - } + for (; len > 1; len -= sizeof(*word)) + sum += *word++; if (len == 1) - sum += (uint32_t)(*addr * 256); + sum += htons((uint16_t)(*(const uint8_t *)word << 8)); + + if (isum != NULL) + *isum = sum; sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); - return (uint16_t)~htons((uint16_t)sum); + return (uint16_t)~sum; } static struct bootp_pkt * @@ -1679,14 +1636,16 @@ dhcp_makeudppacket(size_t *sz, const uint8_t *data, size_t length, udp->uh_dport = htons(BOOTPS); udp->uh_ulen = htons((uint16_t)(sizeof(*udp) + length)); ip->ip_len = udp->uh_ulen; - udp->uh_sum = checksum(udpp, sizeof(*ip) + sizeof(*udp) + length); + udp->uh_sum = in_cksum(udpp, sizeof(*ip) + sizeof(*udp) + length, NULL); ip->ip_v = IPVERSION; ip->ip_hl = sizeof(*ip) >> 2; ip->ip_id = (uint16_t)arc4random_uniform(UINT16_MAX); ip->ip_ttl = IPDEFTTL; ip->ip_len = htons((uint16_t)(sizeof(*ip) + sizeof(*udp) + length)); - ip->ip_sum = checksum(ip, sizeof(*ip)); + ip->ip_sum = in_cksum(ip, sizeof(*ip), NULL); + if (ip->ip_sum == 0) + ip->ip_sum = 0xffff; /* RFC 768 */ *sz = sizeof(*ip) + sizeof(*udp) + length; return udpp; @@ -1699,12 +1658,8 @@ dhcp_sendudp(struct interface *ifp, struct in_addr *to, void *data, size_t len) struct msghdr msg; struct sockaddr_in sin; struct iovec iov[1]; + struct dhcp_state *state = D_STATE(ifp); ssize_t r; -#ifdef IP_PKTINFO - uint8_t cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))]; - struct cmsghdr *cm; - struct in_pktinfo ipi; -#endif iov[0].iov_base = data; iov[0].iov_len = len; @@ -1723,29 +1678,15 @@ dhcp_sendudp(struct interface *ifp, struct in_addr *to, void *data, size_t len) msg.msg_iov = iov; msg.msg_iovlen = 1; -#ifdef IP_PKTINFO - /* Set the outbound interface */ - msg.msg_control = cmsg; - msg.msg_controllen = sizeof(cmsg); - - memset(&ipi, 0, sizeof(ipi)); - ipi.ipi_ifindex = ifp->index; - cm = CMSG_FIRSTHDR(&msg); - if (cm == NULL) { - errno = ESRCH; - return -1; + s = state->udp_fd; + if (s == -1) { + s = dhcp_openudp(ifp); + if (s == -1) + return -1; } - cm->cmsg_level = IPPROTO_IP; - cm->cmsg_type = IP_PKTINFO; - cm->cmsg_len = CMSG_LEN(sizeof(ipi)); - memcpy(CMSG_DATA(cm), &ipi, sizeof(ipi)); -#endif - - s = dhcp_openudp(ifp); - if (s == -1) - return -1; r = sendmsg(s, &msg, 0); - close(s); + if (state->udp_fd == -1) + close(s); return r; } @@ -1764,7 +1705,7 @@ send_message(struct interface *ifp, uint8_t type, if (!callback) { /* No carrier? Don't bother sending the packet. */ - if (ifp->carrier == LINK_DOWN) + if (ifp->carrier <= LINK_DOWN) return; logdebugx("%s: sending %s with xid 0x%x", ifp->name, @@ -1784,7 +1725,7 @@ send_message(struct interface *ifp, uint8_t type, timespecnorm(&tv); /* No carrier? Don't bother sending the packet. * However, we do need to advance the timeout. */ - if (ifp->carrier == LINK_DOWN) + if (ifp->carrier <= LINK_DOWN) goto fail; logdebugx("%s: sending %s (xid 0x%x), next in %0.1f seconds", ifp->name, @@ -1803,7 +1744,7 @@ send_message(struct interface *ifp, uint8_t type, else to.s_addr = INADDR_ANY; - /* If unicasting, try and void sending by BPF so we don't + /* If unicasting, try and avoid sending by BPF so we don't * use a L2 broadcast. */ if (to.s_addr != INADDR_ANY && to.s_addr != INADDR_BROADCAST) { if (dhcp_sendudp(ifp, &to, bootp, len) != -1) @@ -1927,35 +1868,6 @@ dhcp_request(void *arg) send_request(ifp); } -static int -dhcp_leaseextend(struct interface *ifp) -{ - -#ifdef ARP - if (ifp->options->options & DHCPCD_ARP) { - const struct dhcp_state *state; - struct arp_state *astate; - - state = D_CSTATE(ifp); - if ((astate = arp_new(ifp, &state->lease.addr)) == NULL) - return -1; - astate->conflicted_cb = dhcp_arp_conflicted; - -#ifndef KERNEL_RFC5227 - if (arp_open(ifp) == -1) - return -1; -#endif - - logwarnx("%s: extending lease until DaD failure or DHCP", - ifp->name); - return 0; - } -#endif - - logwarnx("%s: extending lease", ifp->name); - return 0; -} - static void dhcp_expire1(struct interface *ifp) { @@ -1965,7 +1877,7 @@ dhcp_expire1(struct interface *ifp) dhcp_drop(ifp, "EXPIRE"); unlink(state->leasefile); state->interval = 0; - if (!(ifp->options->options & DHCPCD_LINK) || ifp->carrier != LINK_DOWN) + if (!(ifp->options->options & DHCPCD_LINK) || ifp->carrier > LINK_DOWN) dhcp_discover(ifp); } @@ -1974,12 +1886,12 @@ dhcp_expire(void *arg) { struct interface *ifp = arg; - logerrx("%s: DHCP lease expired", ifp->name); if (ifp->options->options & DHCPCD_LASTLEASE_EXTEND) { - if (dhcp_leaseextend(ifp) == 0) - return; - logerr(__func__); + logwarnx("%s: DHCP lease expired, extending lease", ifp->name); + return; } + + logerrx("%s: DHCP lease expired", ifp->name); dhcp_expire1(ifp); } @@ -2045,48 +1957,21 @@ dhcp_rebind(void *arg) send_rebind(ifp); } -#ifdef ARP +#if defined(ARP) || defined(IN_IFF_DUPLICATED) static void -dhcp_arp_probed(struct arp_state *astate) +dhcp_finish_dad(struct interface *ifp, struct in_addr *ia) { - struct interface *ifp; - struct dhcp_state *state; - struct if_options *ifo; + struct dhcp_state *state = D_STATE(ifp); - ifp = astate->iface; - state = D_STATE(ifp); - ifo = ifp->options; -#ifdef ARPING - if (ifo->arping_len && state->arping_index < ifo->arping_len) { - /* We didn't find a profile for this - * address or hwaddr, so move to the next - * arping profile */ - if (++state->arping_index < ifo->arping_len) { - astate->addr.s_addr = - ifo->arping[state->arping_index]; - arp_probe(astate); - return; - } - arp_free(astate); -#ifdef KERNEL_RFC5227 - /* As arping is finished, close the ARP socket. - * The kernel will handle ACD from here. */ - arp_close(ifp); -#endif - dhcpcd_startinterface(ifp); + if (state->state != DHS_PROBE) return; - } -#endif - - /* Already bound so DAD has worked */ - if (state->state == DHS_BOUND) + if (state->offer == NULL || state->offer->yiaddr != ia->s_addr) return; - logdebugx("%s: DAD completed for %s", - ifp->name, inet_ntoa(astate->addr)); - if (!(ifo->options & DHCPCD_INFORM)) + logdebugx("%s: DAD completed for %s", ifp->name, inet_ntoa(*ia)); + if (!(ifp->options->options & DHCPCD_INFORM)) dhcp_bind(ifp); -#ifndef IN_IFF_TENTATIVE +#ifndef IN_IFF_DUPLICATED else { struct bootp *bootp; size_t len; @@ -2096,7 +1981,7 @@ dhcp_arp_probed(struct arp_state *astate) state->new = state->offer; state->new_len = state->offer_len; get_lease(ifp, &state->lease, state->new, state->new_len); - ipv4_applyaddr(astate->iface); + ipv4_applyaddr(ifp); state->new = bootp; state->new_len = len; } @@ -2111,23 +1996,82 @@ dhcp_arp_probed(struct arp_state *astate) ipv4ll_drop(ifp); #endif - if (ifo->options & DHCPCD_INFORM) + if (ifp->options->options & DHCPCD_INFORM) dhcp_inform(ifp); } + +static void +dhcp_addr_duplicated(struct interface *ifp, struct in_addr *ia) +{ + struct dhcp_state *state = D_STATE(ifp); +#ifdef IN_IFF_DUPLICATED + struct ipv4_addr *iap; +#endif + + if ((state->offer == NULL || state->offer->yiaddr != ia->s_addr) && + !IN_ARE_ADDR_EQUAL(ia, &state->lease.addr)) + return; + + /* RFC 2131 3.1.5, Client-server interaction */ + logerrx("%s: DAD detected %s", ifp->name, inet_ntoa(*ia)); + unlink(state->leasefile); + if (!(ifp->options->options & DHCPCD_STATIC) && !state->lease.frominfo) + dhcp_decline(ifp); +#ifdef IN_IFF_DUPLICATED + if ((iap = ipv4_iffindaddr(ifp, ia, NULL)) != NULL) + ipv4_deladdr(iap, 0); +#endif + eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); + eloop_timeout_add_sec(ifp->ctx->eloop, + DHCP_RAND_MAX, dhcp_discover, ifp); +} +#endif + +#if defined(ARP) && (!defined(KERNEL_RFC5227) || defined(ARPING)) static void -dhcp_arp_conflicted(struct arp_state *astate, const struct arp_msg *amsg) +dhcp_arp_not_found(struct arp_state *astate) { struct interface *ifp; - struct dhcp_state *state; #ifdef ARPING + struct dhcp_state *state; struct if_options *ifo; #endif ifp = astate->iface; +#ifdef ARPING state = D_STATE(ifp); + ifo = ifp->options; + if (ifo->arping_len && state->arping_index < ifo->arping_len) { + /* We didn't find a profile for this + * address or hwaddr, so move to the next + * arping profile */ + if (++state->arping_index < ifo->arping_len) { + astate->addr.s_addr = + ifo->arping[state->arping_index]; + arp_probe(astate); + return; + } + arp_free(astate); + dhcpcd_startinterface(ifp); + return; + } +#endif + dhcp_finish_dad(ifp, &astate->addr); +} + +static void +dhcp_arp_found(struct arp_state *astate, const struct arp_msg *amsg) +{ + struct in_addr addr; + struct interface *ifp = astate->iface; #ifdef ARPING + struct dhcp_state *state; + struct if_options *ifo; + + state = D_STATE(ifp); + ifo = ifp->options; if (state->arping_index != -1 && state->arping_index < ifo->arping_len && @@ -2136,84 +2080,44 @@ dhcp_arp_conflicted(struct arp_state *astate, const struct arp_msg *amsg) { char buf[HWADDR_LEN * 3]; - astate->failed.s_addr = ifo->arping[state->arping_index]; - arp_report_conflicted(astate, amsg); hwaddr_ntoa(amsg->sha, ifp->hwlen, buf, sizeof(buf)); if (dhcpcd_selectprofile(ifp, buf) == -1 && - dhcpcd_selectprofile(ifp, - inet_ntoa(astate->failed)) == -1) + dhcpcd_selectprofile(ifp, inet_ntoa(amsg->sip)) == -1) { /* We didn't find a profile for this * address or hwaddr, so move to the next * arping profile */ - dhcp_arp_probed(astate); + dhcp_arp_not_found(astate); return; } arp_free(astate); -#ifdef KERNEL_RFC5227 - /* As arping is finished, close the ARP socket. - * The kernel will handle ACD from here. */ - arp_close(ifp); -#endif eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); dhcpcd_startinterface(ifp); return; } +#else + UNUSED(amsg); #endif - /* RFC 2131 3.1.5, Client-server interaction - * NULL amsg means IN_IFF_DUPLICATED */ - if (amsg == NULL || (state->offer && - (amsg->sip.s_addr == state->offer->yiaddr || - (amsg->sip.s_addr == 0 && - amsg->tip.s_addr == state->offer->yiaddr)))) - { -#ifdef IN_IFF_DUPLICATED - struct ipv4_addr *ia; -#endif + addr = astate->addr; + arp_free(astate); + dhcp_addr_duplicated(ifp, &addr); +} - if (amsg) - astate->failed.s_addr = state->offer->yiaddr; - else - astate->failed = astate->addr; - arp_report_conflicted(astate, amsg); - unlink(state->leasefile); -#ifdef ARP - if (!(ifp->options->options & DHCPCD_STATIC) && - !state->lease.frominfo) - dhcp_decline(ifp); -#endif -#ifdef IN_IFF_DUPLICATED - if ((ia = ipv4_iffindaddr(ifp, &astate->addr, NULL)) != NULL) - ipv4_deladdr(ia, 1); -#endif - arp_free(astate); - eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); - eloop_timeout_add_sec(ifp->ctx->eloop, - DHCP_RAND_MAX, dhcp_discover, ifp); - return; - } +#ifdef KERNEL_RFC5227 +static void +dhcp_arp_announced(struct arp_state *state) +{ - /* Bound address */ - if (amsg && state->addr && - amsg->sip.s_addr == state->addr->addr.s_addr) - { - astate->failed = state->addr->addr; - arp_report_conflicted(astate, amsg); - if (state->state == DHS_BOUND) { - /* For now, just report the duplicated address */ - } else { - arp_free(astate); - dhcp_expire1(ifp); - } - return; - } + arp_free(state); } -#endif +#endif /* KERNEL_RFC5227 */ +#endif /* ARP */ void dhcp_bind(struct interface *ifp) { + struct dhcpcd_ctx *ctx = ifp->ctx; struct dhcp_state *state = D_STATE(ifp); struct if_options *ifo = ifp->options; struct dhcp_lease *lease = &state->lease; @@ -2235,17 +2139,17 @@ dhcp_bind(struct interface *ifp) loginfox("%s: using static address %s/%d", ifp->name, inet_ntoa(lease->addr), inet_ntocidr(lease->mask)); - lease->leasetime = ~0U; + lease->leasetime = DHCP_INFINITE_LIFETIME; state->reason = "STATIC"; } else if (ifo->options & DHCPCD_INFORM) { loginfox("%s: received approval for %s", ifp->name, inet_ntoa(lease->addr)); - lease->leasetime = ~0U; + lease->leasetime = DHCP_INFINITE_LIFETIME; state->reason = "INFORM"; } else { if (lease->frominfo) state->reason = "TIMEOUT"; - if (lease->leasetime == ~0U) { + if (lease->leasetime == DHCP_INFINITE_LIFETIME) { lease->renewaltime = lease->rebindtime = lease->leasetime; @@ -2289,10 +2193,10 @@ dhcp_bind(struct interface *ifp) lease->leasetime); } } - if (ifp->ctx->options & DHCPCD_TEST) { + if (ctx->options & DHCPCD_TEST) { state->reason = "TEST"; script_runreason(ifp, state->reason); - eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS); + eloop_exit(ctx->eloop, EXIT_SUCCESS); return; } if (state->reason == NULL) { @@ -2308,29 +2212,51 @@ dhcp_bind(struct interface *ifp) else state->reason = "BOUND"; } - if (lease->leasetime == ~0U) + if (lease->leasetime == DHCP_INFINITE_LIFETIME) lease->renewaltime = lease->rebindtime = lease->leasetime; else { - eloop_timeout_add_sec(ifp->ctx->eloop, + eloop_timeout_add_sec(ctx->eloop, (time_t)lease->renewaltime, dhcp_startrenew, ifp); - eloop_timeout_add_sec(ifp->ctx->eloop, + eloop_timeout_add_sec(ctx->eloop, (time_t)lease->rebindtime, dhcp_rebind, ifp); - eloop_timeout_add_sec(ifp->ctx->eloop, + eloop_timeout_add_sec(ctx->eloop, (time_t)lease->leasetime, dhcp_expire, ifp); logdebugx("%s: renew in %"PRIu32" seconds, rebind in %"PRIu32 " seconds", ifp->name, lease->renewaltime, lease->rebindtime); } state->state = DHS_BOUND; - /* Re-apply the filter because we need to accept any XID anymore. */ - if (bpf_bootp(ifp, state->bpf_fd) == -1) - logerr(__func__); /* try to continue */ if (!state->lease.frominfo && !(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC))) if (write_lease(ifp, state->new, state->new_len) == -1) logerr(__func__); ipv4_applyaddr(ifp); + +#ifdef IP_PKTINFO + /* Close the BPF filter as we can now receive DHCP messages + * on a UDP socket. */ + if (state->udp_fd == -1 || + (state->old != NULL && state->old->yiaddr != state->new->yiaddr)) + { + dhcp_close(ifp); + /* If not in master mode, open an address specific socket. */ + if (ctx->udp_fd == -1) { + state->udp_fd = dhcp_openudp(ifp); + if (state->udp_fd == -1) { + logerr(__func__); + /* Address sharing without master mode is + * not supported. It's also possible another + * DHCP client could be running which is + * even worse. + * We still need to work, so re-open BPF. */ + dhcp_openbpf(ifp); + } else + eloop_event_add(ctx->eloop, + state->udp_fd, dhcp_handleifudp, ifp); + } + } +#endif } static void @@ -2346,12 +2272,6 @@ dhcp_lastlease(void *arg) if (ifp->ctx->options & DHCPCD_FORKED) return; state->interval = 0; - if (ifp->options->options & DHCPCD_LASTLEASE_EXTEND && - dhcp_leaseextend(ifp) == -1) - { - logerr("%s: %s", ifp->name, __func__); - dhcp_expire(ifp); - } dhcp_discover(ifp); } @@ -2384,13 +2304,45 @@ dhcp_message_new(struct bootp **bootp, } #ifdef ARP +#ifndef KERNEL_RFC5227 +static void +dhcp_arp_defend_failed(struct arp_state *astate) +{ + + dhcp_drop(astate->iface, "EXPIRED"); + dhcp_start1(astate->iface); +} +#endif + +#if !defined(KERNEL_RFC5227) || defined(ARPING) +static struct arp_state * +dhcp_arp_new(struct interface *ifp, struct in_addr *addr) +{ + struct arp_state *astate; + + astate = arp_new(ifp, addr); + if (astate == NULL) + return NULL; + + astate->found_cb = dhcp_arp_found; + astate->not_found_cb = dhcp_arp_not_found; +#ifdef KERNEL_RFC5227 + astate->announced_cb = dhcp_arp_announced; +#else + astate->defend_failed_cb = dhcp_arp_defend_failed; +#endif + return astate; +} +#endif +#endif /* ARP */ + +#if defined(ARP) || defined(KERNEL_RFC5227) static int dhcp_arp_address(struct interface *ifp) { struct dhcp_state *state; struct in_addr addr; struct ipv4_addr *ia; - struct arp_state *astate; eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); @@ -2400,12 +2352,7 @@ dhcp_arp_address(struct interface *ifp) /* If the interface already has the address configured * then we can't ARP for duplicate detection. */ ia = ipv4_iffindaddr(ifp, &addr, NULL); - if ((astate = arp_new(ifp, &addr)) == NULL) - return -1; - astate->probed_cb = dhcp_arp_probed; - astate->conflicted_cb = dhcp_arp_conflicted; - -#ifdef IN_IFF_TENTATIVE +#ifdef IN_IFF_NOTUSEABLE if (ia == NULL || ia->addr_flags & IN_IFF_NOTUSEABLE) { state->state = DHS_PROBE; if (ia == NULL) { @@ -2413,16 +2360,25 @@ dhcp_arp_address(struct interface *ifp) get_lease(ifp, &l, state->offer, state->offer_len); /* Add the address now, let the kernel handle DAD. */ - ipv4_addaddr(ifp, &l.addr, &l.mask, &l.brd); + ipv4_addaddr(ifp, &l.addr, &l.mask, &l.brd, + l.leasetime, l.rebindtime); } else loginfox("%s: waiting for DAD on %s", ifp->name, inet_ntoa(addr)); return 0; } #else - if (ifp->options->options & DHCPCD_ARP && ia == NULL) { + if (!(ifp->flags & IFF_NOARP) && + ifp->options->options & DHCPCD_ARP && + ia == NULL) + { + struct arp_state *astate; struct dhcp_lease l; + astate = dhcp_arp_new(ifp, &addr); + if (astate == NULL) + return -1; + state->state = DHS_PROBE; get_lease(ifp, &l, state->offer, state->offer_len); loginfox("%s: probing address %s/%d", @@ -2440,7 +2396,8 @@ static void dhcp_arp_bind(struct interface *ifp) { - if (dhcp_arp_address(ifp) == 1) + if (ifp->ctx->options & DHCPCD_TEST || + dhcp_arp_address(ifp) == 1) dhcp_bind(ifp); } #endif @@ -2470,7 +2427,7 @@ dhcp_static(struct interface *ifp) ia ? &ia->addr : &ifo->req_addr, ia ? &ia->mask : &ifo->req_mask); if (state->offer_len) -#ifdef ARP +#if defined(ARP) || defined(KERNEL_RFC5227) dhcp_arp_bind(ifp); #else dhcp_bind(ifp); @@ -2601,7 +2558,7 @@ dhcp_reboot(struct interface *ifp) state->state = DHS_REBOOT; state->interval = 0; - if (ifo->options & DHCPCD_LINK && ifp->carrier == LINK_DOWN) { + if (ifo->options & DHCPCD_LINK && ifp->carrier <= LINK_DOWN) { loginfox("%s: waiting for carrier", ifp->name); return; } @@ -2631,6 +2588,7 @@ dhcp_reboot(struct interface *ifp) * interface gets the reply. */ ia = ipv4_iffindaddr(ifp, &state->lease.addr, NULL); if (ia != NULL && + !(ifp->ctx->options & DHCPCD_TEST) && #ifdef IN_IFF_NOTUSEABLE !(ia->addr_flags & IN_IFF_NOTUSEABLE) && #endif @@ -2677,9 +2635,14 @@ dhcp_drop(struct interface *ifp, const char *reason) return; } +#ifdef ARP + if (state->addr != NULL) + arp_freeaddr(ifp, &state->addr->addr); +#endif #ifdef ARPING state->arping_index = -1; #endif + if (ifp->options->options & DHCPCD_RELEASE && !(ifp->options->options & DHCPCD_INFORM)) { @@ -2690,7 +2653,7 @@ dhcp_drop(struct interface *ifp, const char *reason) state->state = DHS_RELEASE; unlink(state->leasefile); - if (ifp->carrier != LINK_DOWN && + if (ifp->carrier > LINK_DOWN && state->new != NULL && state->lease.server.s_addr != INADDR_ANY) { @@ -2878,14 +2841,11 @@ dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len, #define LOGDHCP(l, m) \ log_dhcp((l), (m), ifp, bootp, bootp_len, from, 1) - /* Handled in our BPF filter. */ -#if 0 if (bootp->op != BOOTREPLY) { logdebugx("%s: op (%d) is not BOOTREPLY", ifp->name, bootp->op); return; } -#endif if (state->xid != ntohl(bootp->xid)) { if (state->state != DHS_BOUND && state->state != DHS_NONE) @@ -3189,6 +3149,7 @@ dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len, state->reason = "TEST"; script_runreason(ifp, state->reason); eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS); + state->bpf_flags |= BPF_EOF; return; } eloop_timeout_delete(ifp->ctx->eloop, send_discover, ifp); @@ -3259,7 +3220,7 @@ rapidcommit: lease->frominfo = 0; eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); -#ifdef ARP +#if defined(ARP) || defined(KERNEL_RFC5227) dhcp_arp_bind(ifp); #else dhcp_bind(ifp); @@ -3267,75 +3228,108 @@ rapidcommit: } static void * -get_udp_data(void *udp, size_t *len) +get_udp_data(void *packet, size_t *len) { - struct bootp_pkt *p; + const struct ip *ip = packet; + size_t ip_hl = (size_t)ip->ip_hl * 4; + char *p = packet; - p = (struct bootp_pkt *)udp; - *len = (size_t)ntohs(p->ip.ip_len) - sizeof(p->ip) - sizeof(p->udp); - return (char *)udp + offsetof(struct bootp_pkt, bootp); + p += ip_hl + sizeof(struct udphdr); + *len = (size_t)ntohs(ip->ip_len) - sizeof(struct udphdr) - ip_hl; + return p; } static int -valid_udp_packet(void *data, size_t data_len, struct in_addr *from, - int noudpcsum) -{ - struct bootp_pkt *p; - uint16_t bytes; +valid_udp_packet(void *packet, size_t plen, struct in_addr *from, + unsigned int flags) +{ + struct ip *ip = packet; + struct ip pseudo_ip = { + .ip_p = IPPROTO_UDP, + .ip_src = ip->ip_src, + .ip_dst = ip->ip_dst + }; + size_t ip_hlen; + uint16_t ip_len, uh_sum; + struct udphdr *udp; + uint32_t csum; - if (data_len < sizeof(p->ip)) { - if (from) + if (plen < sizeof(*ip)) { + if (from != NULL) from->s_addr = INADDR_ANY; errno = ERANGE; return -1; } - p = (struct bootp_pkt *)data; - if (from) - from->s_addr = p->ip.ip_src.s_addr; - if (checksum(&p->ip, sizeof(p->ip)) != 0) { + + if (from != NULL) + from->s_addr = ip->ip_src.s_addr; + + ip_hlen = (size_t)ip->ip_hl * 4; + if (in_cksum(ip, ip_hlen, NULL) != 0) { errno = EINVAL; return -1; } - bytes = ntohs(p->ip.ip_len); /* Check we have a payload */ - if (bytes <= sizeof(p->ip) + sizeof(p->udp)) { + ip_len = ntohs(ip->ip_len); + if (ip_len <= ip_hlen + sizeof(*udp)) { errno = ERANGE; return -1; } /* Check we don't go beyond the payload */ - if (bytes > data_len) { + if (ip_len > plen) { errno = ENOBUFS; return -1; } - if (noudpcsum == 0) { - uint16_t udpsum, iplen; - - /* This does scribble on the packet, but at this point - * we don't care to keep it. */ - iplen = p->ip.ip_len; - udpsum = p->udp.uh_sum; - p->udp.uh_sum = 0; - p->ip.ip_hl = 0; - p->ip.ip_v = 0; - p->ip.ip_tos = 0; - p->ip.ip_len = p->udp.uh_ulen; - p->ip.ip_id = 0; - p->ip.ip_off = 0; - p->ip.ip_ttl = 0; - p->ip.ip_sum = 0; - if (udpsum && checksum(p, bytes) != udpsum) { - errno = EINVAL; - return -1; - } - p->ip.ip_len = iplen; + if (flags & BPF_PARTIALCSUM) + return 0; + + /* UDP checksum is based on a pseudo IP header alongside + * the UDP header and payload. */ + udp = (struct udphdr *)(void *)((char *)ip + ip_hlen); + if (udp->uh_sum == 0) + return 0; + + uh_sum = udp->uh_sum; + udp->uh_sum = 0; + pseudo_ip.ip_len = udp->uh_ulen; + csum = 0; + in_cksum(&pseudo_ip, sizeof(pseudo_ip), &csum); + csum = in_cksum(udp, ntohs(udp->uh_ulen), &csum); + if (csum != uh_sum) { + errno = EINVAL; + return -1; } return 0; } static void +dhcp_handlebootp(struct interface *ifp, struct bootp *bootp, size_t len, + struct in_addr *from) +{ + size_t v; + + /* udp_len must be correct because the values are checked in + * valid_udp_packet(). */ + if (len < offsetof(struct bootp, vend)) { + logerrx("%s: truncated packet (%zu) from %s", + ifp->name, len, inet_ntoa(*from)); + return; + } + /* To make our IS_DHCP macro easy, ensure the vendor + * area has at least 4 octets. */ + v = len - offsetof(struct bootp, vend); + while (v < 4) { + bootp->vend[v++] = '\0'; + len++; + } + + dhcp_handledhcp(ifp, bootp, len, from); +} + +static void dhcp_handlepacket(struct interface *ifp, uint8_t *data, size_t len) { struct bootp *bootp; @@ -3343,9 +3337,7 @@ dhcp_handlepacket(struct interface *ifp, uint8_t *data, size_t len) size_t udp_len; const struct dhcp_state *state = D_CSTATE(ifp); - if (valid_udp_packet(data, len, &from, - state->bpf_flags & RAW_PARTIALCSUM) == -1) - { + if (valid_udp_packet(data, len, &from, state->bpf_flags) == -1) { if (errno == EINVAL) logerrx("%s: checksum failure from %s", ifp->name, inet_ntoa(from)); @@ -3354,12 +3346,6 @@ dhcp_handlepacket(struct interface *ifp, uint8_t *data, size_t len) ifp->name, inet_ntoa(from)); return; } - if (ifp->flags & IFF_POINTOPOINT && - (state->addr == NULL || state->addr->brd.s_addr != from.s_addr)) - { - logwarnx("%s: server %s is not destination", - ifp->name, inet_ntoa(from)); - } /* * DHCP has a variable option area rather than a fixed vendor area. @@ -3369,22 +3355,7 @@ dhcp_handlepacket(struct interface *ifp, uint8_t *data, size_t len) * dhcpcd can work fine without the vendor area being sent. */ bootp = get_udp_data(data, &udp_len); - /* udp_len must be correct because the values are checked in - * valid_udp_packet(). */ - if (udp_len < offsetof(struct bootp, vend)) { - logerrx("%s: truncated packet (%zu) from %s", - ifp->name, udp_len, inet_ntoa(from)); - return; - } - /* To make our IS_DHCP macro easy, ensure the vendor - * area has at least 4 octets. */ - len = udp_len - offsetof(struct bootp, vend); - while (len < 4) { - bootp->vend[len++] = '\0'; - udp_len++; - } - - dhcp_handledhcp(ifp, bootp, udp_len, &from); + dhcp_handlebootp(ifp, bootp, udp_len, &from); } static void @@ -3420,22 +3391,86 @@ dhcp_readpacket(void *arg) } static void -dhcp_handleudp(void *arg) +dhcp_readudp(struct dhcpcd_ctx *ctx, struct interface *ifp) { - struct dhcpcd_ctx *ctx; - uint8_t buffer[MTU_MAX]; + const struct dhcp_state *state; + struct sockaddr_in from; + unsigned char buf[10 * 1024]; /* Maximum MTU */ + struct iovec iov = { + .iov_base = buf, + .iov_len = sizeof(buf), + }; +#ifdef IP_PKTINFO + unsigned char ctl[CMSG_SPACE(sizeof(struct in_pktinfo))] = { 0 }; + char sfrom[INET_ADDRSTRLEN]; +#endif + struct msghdr msg = { + .msg_name = &from, .msg_namelen = sizeof(from), + .msg_iov = &iov, .msg_iovlen = 1, +#ifdef IP_PKTINFO + .msg_control = ctl, .msg_controllen = sizeof(ctl), +#endif + }; + int s; + ssize_t bytes; - ctx = arg; + if (ifp != NULL) { + state = D_CSTATE(ifp); + s = state->udp_fd; + } else + s = ctx->udp_fd; - /* Just read what's in the UDP fd and discard it as we always read - * from the raw fd */ - if (read(ctx->udp_fd, buffer, sizeof(buffer)) == -1) { + bytes = recvmsg(s, &msg, 0); + if (bytes == -1) { logerr(__func__); - eloop_event_delete(ctx->eloop, ctx->udp_fd); - close(ctx->udp_fd); - ctx->udp_fd = -1; + return; } + +#ifdef IP_PKTINFO + inet_ntop(AF_INET, &from.sin_addr, sfrom, sizeof(sfrom)); + + if (ifp == NULL) { + ifp = if_findifpfromcmsg(ctx, &msg, NULL); + if (ifp == NULL) { + logerr(__func__); + return; + } + state = D_CSTATE(ifp); + if (state == NULL) { + logdebugx("%s: received BOOTP for inactive interface", + ifp->name); + return; + } + } + + if (state->bpf_fd != -1) { + /* Avoid a duplicate read if BPF is open for the interface. */ + return; + } + + dhcp_handlebootp(ifp, (struct bootp *)(void *)buf, (size_t)bytes, + &from.sin_addr); +#endif +} + +static void +dhcp_handleudp(void *arg) +{ + struct dhcpcd_ctx *ctx = arg; + + dhcp_readudp(ctx, NULL); +} + +#ifdef IP_PKTINFO +static void +dhcp_handleifudp(void *arg) +{ + struct interface *ifp = arg; + + dhcp_readudp(ifp->ctx, ifp); + } +#endif static int dhcp_openbpf(struct interface *ifp) @@ -3546,6 +3581,7 @@ dhcp_initstate(struct interface *ifp) state->state = DHS_NONE; /* 0 is a valid fd, so init to -1 */ state->bpf_fd = -1; + state->udp_fd = -1; #ifdef ARPING state->arping_index = -1; #endif @@ -3559,15 +3595,9 @@ dhcp_init(struct interface *ifp) const struct if_options *ifo; uint8_t len; char buf[(sizeof(ifo->clientid) - 1) * 3]; - int r; - r = dhcp_initstate(ifp); - if (r == -1) + if (dhcp_initstate(ifp) == -1) return -1; - else if (r == 1) { - /* Now is a good time to find IPv4 routes */ - if_initrt(ifp->ctx, AF_INET); - } state = D_STATE(ifp); state->state = DHS_INIT; @@ -3636,6 +3666,7 @@ static void dhcp_start1(void *arg) { struct interface *ifp = arg; + struct dhcpcd_ctx *ctx = ifp->ctx; struct if_options *ifo = ifp->options; struct dhcp_state *state; struct stat st; @@ -3646,17 +3677,19 @@ dhcp_start1(void *arg) return; /* Listen on *.*.*.*:bootpc so that the kernel never sends an - * ICMP port unreachable message back to the DHCP server */ - if (ifp->ctx->udp_fd == -1) { - ifp->ctx->udp_fd = dhcp_openudp(NULL); - if (ifp->ctx->udp_fd == -1) { + * ICMP port unreachable message back to the DHCP server. + * Only do this in master mode so we don't swallow messages + * for dhcpcd running on another interface. */ + if (ctx->udp_fd == -1 && ctx->options & DHCPCD_MASTER) { + ctx->udp_fd = dhcp_openudp(NULL); + if (ctx->udp_fd == -1) { /* Don't log an error if some other process * is handling this. */ if (errno != EADDRINUSE) logerr("%s: dhcp_openudp", __func__); } else - eloop_event_add(ifp->ctx->eloop, - ifp->ctx->udp_fd, dhcp_handleudp, ifp->ctx); + eloop_event_add(ctx->eloop, + ctx->udp_fd, dhcp_handleudp, ctx); } if (dhcp_init(ifp) == -1) { @@ -3675,12 +3708,9 @@ dhcp_start1(void *arg) if (ifo->arping_len && state->arping_index < ifo->arping_len) { struct arp_state *astate; - astate = arp_new(ifp, NULL); - if (astate) { - astate->probed_cb = dhcp_arp_probed; - astate->conflicted_cb = dhcp_arp_conflicted; - dhcp_arp_probed(astate); - } + astate = dhcp_arp_new(ifp, NULL); + if (astate) + dhcp_arp_not_found(astate); return; } #endif @@ -3690,19 +3720,11 @@ dhcp_start1(void *arg) return; } - if (ifo->options & DHCPCD_DHCP && dhcp_openbpf(ifp) == -1) - return; - if (ifo->options & DHCPCD_INFORM) { dhcp_inform(ifp); return; } - if (ifp->hwlen == 0 && ifo->clientid[0] == '\0') { - logwarnx("%s: needs a clientid to configure", ifp->name); - dhcp_drop(ifp, "FAIL"); - eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); - return; - } + /* We don't want to read the old lease if we NAK an old test */ nolease = state->offer && ifp->ctx->options & DHCPCD_TEST; if (!nolease && ifo->options & DHCPCD_DHCP) { @@ -3759,7 +3781,7 @@ dhcp_start1(void *arg) state->offer = NULL; state->offer_len = 0; } else if (!(ifo->options & DHCPCD_LASTLEASE_EXTEND) && - state->lease.leasetime != ~0U && + state->lease.leasetime != DHCP_INFINITE_LIFETIME && stat(state->leasefile, &st) == 0) { time_t now; @@ -3934,8 +3956,10 @@ dhcp_handleifa(int cmd, struct ipv4_addr *ia, pid_t pid) return; #ifdef IN_IFF_NOTUSEABLE - if (ia->addr_flags & IN_IFF_NOTUSEABLE) - return; + if (!(ia->addr_flags & IN_IFF_NOTUSEABLE)) + dhcp_finish_dad(ifp, &ia->addr); + else if (ia->addr_flags & IN_IFF_DUPLICATED) + dhcp_addr_duplicated(ifp, &ia->addr); #endif ifo = ifp->options; diff --git a/contrib/dhcpcd/src/dhcp.h b/contrib/dhcpcd/src/dhcp.h index fb2795874a..4b6311a38b 100644 --- a/contrib/dhcpcd/src/dhcp.h +++ b/contrib/dhcpcd/src/dhcp.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -182,6 +183,10 @@ struct dhcp_lease { uint32_t cookie; }; +#ifndef DHCP_INFINITE_LIFETIME +# define DHCP_INFINITE_LIFETIME (~0U) +#endif + enum DHS { DHS_NONE, DHS_INIT, @@ -216,6 +221,7 @@ struct dhcp_state { int bpf_fd; unsigned int bpf_flags; + int udp_fd; struct ipv4_addr *addr; uint8_t added; @@ -228,6 +234,7 @@ struct dhcp_state { #endif }; +#ifdef INET #define D_STATE(ifp) \ ((struct dhcp_state *)(ifp)->if_data[IF_DATA_DHCP]) #define D_CSTATE(ifp) \ @@ -243,16 +250,15 @@ struct dhcp_state { #include "dhcpcd.h" #include "if-options.h" -#ifdef INET -char *decode_rfc3361(const uint8_t *, size_t); -ssize_t decode_rfc3442(char *, size_t, const uint8_t *p, size_t); +ssize_t print_rfc3361(FILE *, const uint8_t *, size_t); +ssize_t print_rfc3442(FILE *, const uint8_t *, size_t); void dhcp_printoptions(const struct dhcpcd_ctx *, const struct dhcp_opt *, size_t); uint16_t dhcp_get_mtu(const struct interface *); -int dhcp_get_routes(struct rt_head *, struct interface *); -ssize_t dhcp_env(char **, const char *, const struct bootp *, size_t, - const struct interface *); +int dhcp_get_routes(rb_tree_t *, struct interface *); +ssize_t dhcp_env(FILE *, const char *, const struct interface *, + const struct bootp *, size_t); void dhcp_handleifa(int, struct ipv4_addr *, pid_t pid); void dhcp_drop(struct interface *, const char *); @@ -266,15 +272,6 @@ void dhcp_reboot_newopts(struct interface *, unsigned long long); void dhcp_close(struct interface *); void dhcp_free(struct interface *); int dhcp_dump(struct interface *); -#else -#define dhcp_start(a) {} -#define dhcp_abort(a) {} -#define dhcp_renew(a) {} -#define dhcp_reboot(a, b) (b = b) -#define dhcp_reboot_newopts(a, b) (b = b) -#define dhcp_close(a) {} -#define dhcp_free(a) {} -#define dhcp_dump(a) (-1) -#endif +#endif /* INET */ -#endif +#endif /* DHCP_H */ diff --git a/contrib/dhcpcd/src/dhcp6.c b/contrib/dhcpcd/src/dhcp6.c index 155bcb0ac1..3c22005c1a 100644 --- a/contrib/dhcpcd/src/dhcp6.c +++ b/contrib/dhcpcd/src/dhcp6.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -168,7 +169,7 @@ static const char * const dhcp6_statuses[] = { "No Prefix Available" }; -static void dhcp6_bind(struct interface *, const char *); +static void dhcp6_bind(struct interface *, const char *, const char *); static void dhcp6_failinform(void *); static int dhcp6_listen(struct dhcpcd_ctx *, struct ipv6_addr *); static void dhcp6_recvaddr(void *); @@ -582,10 +583,14 @@ dhcp6_delegateaddr(struct in6_addr *addr, struct interface *ifp, #define BIT(n) (1UL << (n)) #define BIT_MASK(len) (BIT(len) - 1) - if (ia->sla_max == 0) + if (ia->sla_max == 0) { /* Work out the real sla_max from our bits used */ - ia->sla_max = (uint32_t)BIT_MASK(asla.prefix_len - - prefix->prefix_len); + bits = asla.prefix_len - prefix->prefix_len; + /* Make static analysis happy. + * Bits cannot be bigger than 32 thanks to fls32. */ + assert(bits <= 32); + ia->sla_max = (uint32_t)BIT_MASK(bits); + } } if (ipv6_userprefix(&prefix->prefix, prefix->prefix_len, @@ -797,8 +802,7 @@ dhcp6_makemessage(struct interface *ifp) m = state->new; ml = state->new_len; } - unicast = NULL; - /* Depending on state, get the unicast address */ + switch(state->state) { case DH6S_INIT: /* FALLTHROUGH */ case DH6S_DISCOVER: @@ -806,7 +810,6 @@ dhcp6_makemessage(struct interface *ifp) break; case DH6S_REQUEST: type = DHCP6_REQUEST; - unicast = dhcp6_findmoption(m, ml, D6_OPTION_UNICAST, &uni_len); break; case DH6S_CONFIRM: type = DHCP6_CONFIRM; @@ -816,20 +819,33 @@ dhcp6_makemessage(struct interface *ifp) break; case DH6S_RENEW: type = DHCP6_RENEW; - unicast = dhcp6_findmoption(m, ml, D6_OPTION_UNICAST, &uni_len); break; case DH6S_INFORM: type = DHCP6_INFORMATION_REQ; break; case DH6S_RELEASE: type = DHCP6_RELEASE; - unicast = dhcp6_findmoption(m, ml, D6_OPTION_UNICAST, &uni_len); break; default: errno = EINVAL; return -1; } + switch(state->state) { + case DH6S_REQUEST: /* FALLTHROUGH */ + case DH6S_RENEW: /* FALLTHROUGH */ + case DH6S_RELEASE: + if (has_option_mask(ifo->nomask6, D6_OPTION_UNICAST)) { + unicast = NULL; + break; + } + unicast = dhcp6_findmoption(m, ml, D6_OPTION_UNICAST, &uni_len); + break; + default: + unicast = NULL; + break; + } + /* In non master mode we listen and send from fixed addresses. * We should try and match an address we have to unicast to, * but for now this is the safest policy. */ @@ -1157,9 +1173,12 @@ dhcp6_update_auth(struct interface *ifp, struct dhcp6_message *m, size_t len) static int dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *)) { - struct dhcp6_state *state; - struct dhcpcd_ctx *ctx; - struct sockaddr_in6 dst; + struct dhcp6_state *state = D6_STATE(ifp); + struct dhcpcd_ctx *ctx = ifp->ctx; + struct sockaddr_in6 dst = { + .sin6_family = AF_INET6, + .sin6_port = htons(DHCP6_SERVER_PORT), + }; struct timespec RTprev; double rnd; time_t ms; @@ -1168,18 +1187,22 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *)) const struct in6_addr alldhcp = IN6ADDR_LINKLOCAL_ALLDHCP_INIT; struct ipv6_addr *lla; int s; - - if (!callback && ifp->carrier == LINK_DOWN) + struct iovec iov = { + .iov_base = state->send, .iov_len = state->send_len, + }; + unsigned char ctl[CMSG_SPACE(sizeof(struct in6_pktinfo))] = { 0 }; + struct msghdr msg = { + .msg_name = &dst, .msg_namelen = sizeof(dst), + .msg_iov = &iov, .msg_iovlen = 1, + }; + + if (!callback && ifp->carrier <= LINK_DOWN) return 0; - memset(&dst, 0, sizeof(dst)); - dst.sin6_family = AF_INET6; - dst.sin6_port = htons(DHCP6_SERVER_PORT); #ifdef HAVE_SA_LEN dst.sin6_len = sizeof(dst); #endif - state = D6_STATE(ifp); lla = ipv6_linklocal(ifp); /* We need to ensure we have sufficient scope to unicast the address */ /* XXX FIXME: We should check any added addresses we have like from @@ -1262,7 +1285,7 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *)) } logsend: - if (ifp->carrier != LINK_DOWN) + if (ifp->carrier > LINK_DOWN) logdebugx("%s: %s %s (xid 0x%02x%02x%02x)," " next in %0.1f seconds", ifp->name, @@ -1280,13 +1303,13 @@ logsend: /* Wait the initial delay */ if (state->IMD != 0) { state->IMD = 0; - eloop_timeout_add_tv(ifp->ctx->eloop, + eloop_timeout_add_tv(ctx->eloop, &state->RT, callback, ifp); return 0; } } - if (ifp->carrier == LINK_DOWN) + if (ifp->carrier <= LINK_DOWN) return 0; /* Update the elapsed time */ @@ -1301,31 +1324,21 @@ logsend: } #endif - ctx = ifp->ctx; - ctx->sndhdr.msg_name = (void *)&dst; - ctx->sndhdr.msg_iov[0].iov_base = state->send; - ctx->sndhdr.msg_iov[0].iov_len = state->send_len; - /* Set the outbound interface */ if (IN6_ARE_ADDR_EQUAL(&dst.sin6_addr, &alldhcp)) { struct cmsghdr *cm; - struct in6_pktinfo pi; + struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index }; dst.sin6_scope_id = ifp->index; - cm = CMSG_FIRSTHDR(&ctx->sndhdr); + msg.msg_control = ctl; + msg.msg_controllen = sizeof(ctl); + cm = CMSG_FIRSTHDR(&msg); if (cm == NULL) /* unlikely */ return -1; cm->cmsg_level = IPPROTO_IPV6; cm->cmsg_type = IPV6_PKTINFO; cm->cmsg_len = CMSG_LEN(sizeof(pi)); - memset(&pi, 0, sizeof(pi)); - pi.ipi6_ifindex = ifp->index; memcpy(CMSG_DATA(cm), &pi, sizeof(pi)); - } else { - /* Remove the control buffer as we're not dictating - * which interface to use for outgoing messages. */ - ctx->sndhdr.msg_control = NULL; - ctx->sndhdr.msg_controllen = 0; } if (ctx->dhcp6_fd != -1) @@ -1337,7 +1350,7 @@ logsend: return -1; } - if (sendmsg(s, &ctx->sndhdr, 0) == -1) { + if (sendmsg(s, &msg, 0) == -1) { logerr("%s: %s: sendmsg", __func__, ifp->name); /* Allow DHCPv6 to continue .... the errors * would be rate limited by the protocol. @@ -1345,19 +1358,13 @@ logsend: * associate with an access point. */ } - /* Restore the control buffer assignment. */ - if (!IN6_ARE_ADDR_EQUAL(&dst.sin6_addr, &alldhcp)) { - ctx->sndhdr.msg_control = ctx->sndbuf; - ctx->sndhdr.msg_controllen = sizeof(ctx->sndbuf); - } - state->RTC++; if (callback) { if (state->MRC == 0 || state->RTC < state->MRC) - eloop_timeout_add_tv(ifp->ctx->eloop, + eloop_timeout_add_tv(ctx->eloop, &state->RT, callback, ifp); else if (state->MRC != 0 && state->MRCcallback) - eloop_timeout_add_tv(ifp->ctx->eloop, + eloop_timeout_add_tv(ctx->eloop, &state->RT, state->MRCcallback, ifp); else logwarnx("%s: sent %d times with no reply", @@ -1516,6 +1523,9 @@ dhcp6_dadcallback(void *arg) if (valid) dhcpcd_daemonise(ifp->ctx); } +#ifdef ND6_ADVERTISE + ipv6nd_advertise(ia); +#endif } } } @@ -1649,7 +1659,7 @@ dhcp6_fail(struct interface *ifp) break; } - dhcp6_bind(ifp, NULL); + dhcp6_bind(ifp, NULL, NULL); switch (state->state) { case DH6S_BOUND: @@ -1910,13 +1920,16 @@ static int dhcp6_checkstatusok(const struct interface *ifp, struct dhcp6_message *m, uint8_t *p, size_t len) { + struct dhcp6_state *state; uint8_t *opt; uint16_t opt_len, code; size_t mlen; void * (*f)(void *, size_t, uint16_t, uint16_t *), *farg; char buf[32], *sbuf; const char *status; + logfunc_t *logfunc; + state = D6_STATE(ifp); f = p ? dhcp6_findoption : dhcp6_findmoption; if (p) farg = p; @@ -1924,6 +1937,8 @@ dhcp6_checkstatusok(const struct interface *ifp, farg = m; if ((opt = f(farg, len, D6_OPTION_STATUS_CODE, &opt_len)) == NULL) { //logdebugx("%s: no status", ifp->name); + state->lerror = 0; + errno = ESRCH; return 0; } @@ -1933,8 +1948,11 @@ dhcp6_checkstatusok(const struct interface *ifp, } memcpy(&code, opt, sizeof(code)); code = ntohs(code); - if (code == D6_STATUS_OK) - return 1; + if (code == D6_STATUS_OK) { + state->lerror = 0; + errno = 0; + return 0; + } /* Anything after the code is a message. */ opt += sizeof(code); @@ -1957,9 +1975,15 @@ dhcp6_checkstatusok(const struct interface *ifp, status = sbuf; } - logerrx("%s: DHCPv6 REPLY: %s", ifp->name, status); + if (state->lerror == code || state->state == DH6S_INIT) + logfunc = logdebugx; + else + logfunc = logerrx; + logfunc("%s: DHCPv6 REPLY: %s", ifp->name, status); free(sbuf); - return -1; + state->lerror = code; + errno = 0; + return (int)code; } const struct ipv6_addr * @@ -2015,12 +2039,12 @@ dhcp6_findna(struct interface *ifp, uint16_t ot, const uint8_t *iaid, nd = o + ol; l -= (size_t)(nd - d); d = nd; - if (ol < 24) { + if (ol < sizeof(ia)) { errno = EINVAL; logerrx("%s: IA Address option truncated", ifp->name); continue; } - memcpy(&ia, o, ol); + memcpy(&ia, o, sizeof(ia)); ia.pltime = ntohl(ia.pltime); ia.vltime = ntohl(ia.vltime); /* RFC 3315 22.6 */ @@ -2152,40 +2176,38 @@ dhcp6_findpd(struct interface *ifp, const uint8_t *iaid, state->expire = a->prefix_vltime; i++; - o = dhcp6_findoption(o, ol, D6_OPTION_PD_EXCLUDE, &ol); a->prefix_exclude_len = 0; memset(&a->prefix_exclude, 0, sizeof(a->prefix_exclude)); -#if 0 - if (ex == NULL) { - struct dhcp6_option *w; - uint8_t *wp; - - w = calloc(1, 128); - w->len = htons(2); - wp = D6_OPTION_DATA(w); - *wp++ = 64; - *wp++ = 0x78; - ex = w; - } -#endif + o = dhcp6_findoption(o, ol, D6_OPTION_PD_EXCLUDE, &ol); if (o == NULL) continue; - if (ol < 2) { - logerrx("%s: truncated PD Exclude", ifp->name); + + /* RFC 6603 4.2 says option length MUST be between 2 and 17. + * This allows 1 octet for prefix length and 16 for the + * subnet ID. */ + if (ol < 2 || ol > 17) { + logerrx("%s: invalid PD Exclude option", ifp->name); continue; } - a->prefix_exclude_len = *o++; + + /* RFC 6603 4.2 says prefix length MUST be between the + * length of the IAPREFIX prefix length + 1 and 128. */ + if (*o < a->prefix_len + 1 || *o > 128) { + logerrx("%s: invalid PD Exclude length", ifp->name); + continue; + } + ol--; - if (((a->prefix_exclude_len - a->prefix_len - 1) / NBBY) + 1 - != ol) - { + /* Check option length matches prefix length. */ + if (((*o - a->prefix_len - 1) / NBBY) + 1 != ol) { logerrx("%s: PD Exclude length mismatch", ifp->name); - a->prefix_exclude_len = 0; continue; } - nb = a->prefix_len % NBBY; + a->prefix_exclude_len = *o++; + memcpy(&a->prefix_exclude, &a->prefix, sizeof(a->prefix_exclude)); + nb = a->prefix_len % NBBY; if (nb) ol--; pw = a->prefix_exclude.s6_addr + @@ -2208,7 +2230,7 @@ dhcp6_findia(struct interface *ifp, struct dhcp6_message *m, size_t l, struct dhcp6_option o; uint8_t *d, *p; struct dhcp6_ia_na ia; - int i, e; + int i, e, error; size_t j; uint16_t nl; uint8_t iaid[4]; @@ -2297,7 +2319,9 @@ dhcp6_findia(struct interface *ifp, struct dhcp6_message *m, size_t l, } } else ia.t1 = ia.t2 = 0; /* appease gcc */ - if (dhcp6_checkstatusok(ifp, NULL, p, o.len) == -1) { + if ((error = dhcp6_checkstatusok(ifp, NULL, p, o.len)) != 0) { + if (error == D6_STATUS_NOBINDING) + state->has_no_binding = true; e = 1; continue; } @@ -2398,7 +2422,7 @@ dhcp6_validatelease(struct interface *ifp, const char *sfrom, const struct timespec *acquired) { struct dhcp6_state *state; - int ok, nia; + int nia, ok_errno; struct timespec aq; if (len <= sizeof(*m)) { @@ -2407,8 +2431,10 @@ dhcp6_validatelease(struct interface *ifp, } state = D6_STATE(ifp); - if ((ok = dhcp6_checkstatusok(ifp, m, NULL, len) == -1)) + errno = 0; + if (dhcp6_checkstatusok(ifp, m, NULL, len) != 0) return -1; + ok_errno = errno; state->renew = state->rebind = state->expire = 0; state->lowpl = ND6_INFINITE_LIFETIME; @@ -2416,9 +2442,10 @@ dhcp6_validatelease(struct interface *ifp, clock_gettime(CLOCK_MONOTONIC, &aq); acquired = &aq; } + state->has_no_binding = false; nia = dhcp6_findia(ifp, m, len, sfrom, acquired); if (nia == 0) { - if (state->state != DH6S_CONFIRM && ok != 1) { + if (state->state != DH6S_CONFIRM && ok_errno != 0) { logerrx("%s: no useable IA found in lease", ifp->name); return -1; } @@ -2428,6 +2455,7 @@ dhcp6_validatelease(struct interface *ifp, * IA's must have existed here otherwise we would * have rejected it earlier. */ assert(state->new != NULL && state->new_len != 0); + state->has_no_binding = false; nia = dhcp6_findia(ifp, state->new, state->new_len, sfrom, acquired); } @@ -2460,22 +2488,22 @@ dhcp6_readlease(struct interface *ifp, int validate) struct dhcp6_state *state; struct stat st; int fd; - struct dhcp6_message *lease; time_t now; int retval; - bool fd_opened; + bool read_stdin, fd_opened; #ifdef AUTH uint8_t *o; uint16_t ol; #endif state = D6_STATE(ifp); - if (state->leasefile[0] == '\0') { + read_stdin = state->leasefile[0] == '\0'; + if (read_stdin) { logdebugx("reading standard input"); fd = fileno(stdin); fd_opened = false; } else { - logdebugx("%s: reading lease `%s'", ifp->name, state->leasefile); + logdebugx("%s: reading lease `%s'", ifp->name,state->leasefile); fd = open(state->leasefile, O_RDONLY); if (fd != -1 && fstat(fd, &st) == -1) { close(fd); @@ -2486,19 +2514,19 @@ dhcp6_readlease(struct interface *ifp, int validate) if (fd == -1) return -1; retval = -1; - lease = NULL; free(state->new); - state->new_len = dhcp_read_lease_fd(fd, (void **)&lease); - state->new = lease; + state->new_len = dhcp_read_lease_fd(fd, (void **)&state->new); if (fd_opened) close(fd); - if (state->new_len == 0) - goto ex; - if (ifp->ctx->options & DHCPCD_DUMPLEASE || - state->leasefile[0] == '\0') + if (ifp->ctx->options & DHCPCD_DUMPLEASE || read_stdin) return 0; + if (state->new_len == 0) { + retval = 0; + goto ex; + } + /* If not validating IA's and if they have expired, * skip to the auth check. */ if (!validate) { @@ -2518,14 +2546,12 @@ dhcp6_readlease(struct interface *ifp, int validate) goto ex; if (state->expire != ND6_INFINITE_LIFETIME && - state->leasefile[0] != '\0') + (time_t)state->expire < now - st.st_mtime && + !(ifp->options->options & DHCPCD_LASTLEASE_EXTEND)) { - if ((time_t)state->expire < now - st.st_mtime && - !(ifp->options->options & DHCPCD_LASTLEASE_EXTEND)) { - logdebugx("%s: discarding expired lease", ifp->name); - retval = 0; - goto ex; - } + logdebugx("%s: discarding expired lease", ifp->name); + retval = 0; + goto ex; } auth: @@ -2558,12 +2584,10 @@ auth: ex: dhcp6_freedrop_addrs(ifp, 0, NULL); + unlink(state->leasefile); free(state->new); state->new = NULL; state->new_len = 0; - if (!(ifp->ctx->options & DHCPCD_DUMPLEASE) && - state->leasefile[0] != '\0') - unlink(state->leasefile); return retval; } @@ -2833,26 +2857,11 @@ dhcp6_delegate_prefix(struct interface *ifp) struct dhcp6_state *s = D6_STATE(ifd); ipv6_addaddrs(&s->addrs); - - /* - * Can't add routes here because that will trigger - * interface sorting which may break the current - * enumeration. - * This doesn't really matter thanks to DaD because - * calling the script will be delayed and routes - * will get re-built if needed first. - * This only cause minor confusion when dhcpcd is - * restarted and confirms a lease where prior delegation - * has already been assigned, because it will log it - * added routes after the script has run. - * The routes should still be there and fine though. - */ dhcp6_script_try_run(ifd, 1); } } /* Now all addresses have been added, rebuild the routing table. */ - if_initrt(ifp->ctx, AF_INET6); rt_build(ifp->ctx, AF_INET6); } @@ -2917,7 +2926,6 @@ dhcp6_find_delegates(struct interface *ifp) state = D6_STATE(ifp); state->state = DH6S_DELEGATED; ipv6_addaddrs(&state->addrs); - if_initrt(ifp->ctx, AF_INET6); rt_build(ifp->ctx, AF_INET6); dhcp6_script_try_run(ifp, 1); } @@ -2926,7 +2934,7 @@ dhcp6_find_delegates(struct interface *ifp) #endif static void -dhcp6_bind(struct interface *ifp, const char *op) +dhcp6_bind(struct interface *ifp, const char *op, const char *sfrom) { struct dhcp6_state *state = D6_STATE(ifp); bool has_new = false; @@ -2942,8 +2950,7 @@ dhcp6_bind(struct interface *ifp, const char *op) } lognewinfo = has_new ? loginfox : logdebugx; if (op != NULL) - lognewinfo("%s: %s received from %s", - ifp->name, op, ifp->ctx->sfrom); + lognewinfo("%s: %s received from %s", ifp->name, op, sfrom); state->reason = NULL; if (state->state != DH6S_ITIMEDOUT) @@ -3022,7 +3029,7 @@ dhcp6_bind(struct interface *ifp, const char *op) * unless those values in those fields are 0. */ logwarnx("%s: ignoring T1 %"PRIu32 - " to due address expiry", + " due to address expiry", ifp->name, state->renew); state->renew = state->rebind = 0; } @@ -3156,7 +3163,6 @@ dhcp6_bind(struct interface *ifp, const char *op) else lognewinfo("%s: expire in %"PRIu32" seconds", ifp->name, state->expire); - if_initrt(ifp->ctx, AF_INET6); rt_build(ifp->ctx, AF_INET6); if (!timed_out) dhcp6_writelease(ifp); @@ -3175,7 +3181,8 @@ dhcp6_bind(struct interface *ifp, const char *op) } static void -dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len) +dhcp6_recvif(struct interface *ifp, const char *sfrom, + struct dhcp6_message *r, size_t len) { struct dhcpcd_ctx *ctx; size_t i; @@ -3194,7 +3201,7 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len) ctx = ifp->ctx; state = D6_STATE(ifp); if (state == NULL || state->send == NULL) { - logdebug("%s: DHCPv6 reply received but not running", + logdebugx("%s: DHCPv6 reply received but not running", ifp->name); return; } @@ -3210,8 +3217,7 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len) } if (dhcp6_findmoption(r, len, D6_OPTION_SERVERID, NULL) == NULL) { - logdebugx("%s: no DHCPv6 server ID from %s", - ifp->name, ctx->sfrom); + logdebugx("%s: no DHCPv6 server ID from %s", ifp->name, sfrom); return; } @@ -3224,14 +3230,14 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len) !dhcp6_findmoption(r, len, (uint16_t)opt->option, NULL)) { logwarnx("%s: reject DHCPv6 (no option %s) from %s", - ifp->name, opt->var, ctx->sfrom); + ifp->name, opt->var, sfrom); return; } if (has_option_mask(ifo->rejectmask6, opt->option) && dhcp6_findmoption(r, len, (uint16_t)opt->option, NULL)) { logwarnx("%s: reject DHCPv6 (option %s) from %s", - ifp->name, opt->var, ctx->sfrom); + ifp->name, opt->var, sfrom); return; } } @@ -3244,7 +3250,7 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len) (uint8_t *)r, len, 6, r->type, auth, auth_len) == NULL) { logerr("%s: authentication failed from %s", - ifp->name, ctx->sfrom); + ifp->name, sfrom); return; } if (state->auth.token) @@ -3255,11 +3261,10 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len) } else if (ifo->auth.options & DHCPCD_AUTH_SEND) { if (ifo->auth.options & DHCPCD_AUTH_REQUIRE) { logerr("%s: no authentication from %s", - ifp->name, ctx->sfrom); + ifp->name, sfrom); return; } - logwarnx("%s: no authentication from %s", - ifp->name, ctx->sfrom); + logwarnx("%s: no authentication from %s", ifp->name, sfrom); } #endif @@ -3269,12 +3274,11 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len) case DHCP6_REPLY: switch(state->state) { case DH6S_INFORM: - if (dhcp6_checkstatusok(ifp, r, NULL, len) == -1) + if (dhcp6_checkstatusok(ifp, r, NULL, len) != 0) return; break; case DH6S_CONFIRM: - if (dhcp6_validatelease(ifp, r, len, - ctx->sfrom, NULL) == -1) + if (dhcp6_validatelease(ifp, r, len, sfrom, NULL) == -1) { dhcp6_startdiscover(ifp); return; @@ -3296,17 +3300,34 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len) case DH6S_REQUEST: /* FALLTHROUGH */ case DH6S_RENEW: /* FALLTHROUGH */ case DH6S_REBIND: - if (dhcp6_validatelease(ifp, r, len, - ctx->sfrom, NULL) == -1) + if (dhcp6_validatelease(ifp, r, len, sfrom, NULL) == -1) { -#ifndef SMALL - /* PD doesn't use CONFIRM, so REBIND could - * throw up an invalid prefix if we - * changed link */ - if (state->state == DH6S_REBIND && - dhcp6_hasprefixdelegation(ifp)) + /* + * If we can't use the lease, fallback to + * DISCOVER and try and get a new one. + * + * This is needed become some servers + * renumber the prefix or address + * and deny the current one before it expires + * rather than sending it back with a zero + * lifetime along with the new prefix or + * address to use. + * This behavior is wrong, but moving to the + * DISCOVER phase works around it. + * + * The currently held lease is still valid + * until a new one is found. + */ + if (state->state != DH6S_DISCOVER) dhcp6_startdiscover(ifp); -#endif + return; + } + /* RFC8415 18.2.10.1 */ + if ((state->state == DH6S_RENEW || + state->state == DH6S_REBIND) && + state->has_no_binding) + { + dhcp6_startrequest(ifp); return; } if (state->state == DH6S_DISCOVER) @@ -3355,7 +3376,7 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len) logerrx("%s: invalid INF_MAX_RT %u", ifp->name, max_rt); } - if (dhcp6_validatelease(ifp, r, len, ctx->sfrom, NULL) == -1) + if (dhcp6_validatelease(ifp, r, len, sfrom, NULL) == -1) return; break; case DHCP6_RECONFIGURE: @@ -3363,12 +3384,12 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len) if (auth == NULL) { #endif logerrx("%s: unauthenticated %s from %s", - ifp->name, op, ctx->sfrom); + ifp->name, op, sfrom); if (ifo->auth.options & DHCPCD_AUTH_REQUIRE) return; #ifdef AUTH } - loginfox("%s: %s from %s", ifp->name, op, ctx->sfrom); + loginfox("%s: %s from %s", ifp->name, op, sfrom); o = dhcp6_findmoption(r, len, D6_OPTION_RECONF_MSG, &ol); if (o == NULL) { logerrx("%s: missing Reconfigure Message option", @@ -3405,6 +3426,8 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len) break; } return; +#else + break; #endif default: logerrx("%s: invalid DHCP6 type %s (%d)", @@ -3443,10 +3466,10 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len) ia = TAILQ_FIRST(&state->addrs); if (ia == NULL) loginfox("%s: ADV (no address) from %s", - ifp->name, ctx->sfrom); + ifp->name, sfrom); else loginfox("%s: ADV %s from %s", - ifp->name, ia->saddr, ctx->sfrom); + ifp->name, ia->saddr, sfrom); if (ifp->ctx->options & DHCPCD_TEST) break; dhcp6_startrequest(ifp); @@ -3454,96 +3477,81 @@ dhcp6_recvif(struct interface *ifp, struct dhcp6_message *r, size_t len) } } - dhcp6_bind(ifp, op); + dhcp6_bind(ifp, op, sfrom); } static void dhcp6_recv(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia) { + struct sockaddr_in6 from; + unsigned char buf[64 * 1024]; /* Maximum UDP message size */ + struct iovec iov = { + .iov_base = buf, + .iov_len = sizeof(buf), + }; + unsigned char ctl[CMSG_SPACE(sizeof(struct in6_pktinfo))] = { 0 }; + struct msghdr msg = { + .msg_name = &from, .msg_namelen = sizeof(from), + .msg_iov = &iov, .msg_iovlen = 1, + .msg_control = ctl, .msg_controllen = sizeof(ctl), + }; int s; size_t len; ssize_t bytes; + char sfrom[INET6_ADDRSTRLEN]; struct interface *ifp; struct dhcp6_message *r; const struct dhcp6_state *state; uint8_t *o; uint16_t ol; - ctx->rcvhdr.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); s = ia != NULL ? ia->dhcp6_fd : ctx->dhcp6_fd; - bytes = recvmsg_realloc(s, &ctx->rcvhdr, 0); + bytes = recvmsg(s, &msg, 0); if (bytes == -1) { - logerr("%s: recvmsg_realloc", __func__); + logerr(__func__); return; } len = (size_t)bytes; - ctx->sfrom = inet_ntop(AF_INET6, &ctx->from.sin6_addr, - ctx->ntopbuf, sizeof(ctx->ntopbuf)); + inet_ntop(AF_INET6, &from.sin6_addr, sfrom, sizeof(sfrom)); if (len < sizeof(struct dhcp6_message)) { - logerrx("DHCPv6 packet too short from %s", ctx->sfrom); + logerrx("DHCPv6 packet too short from %s", sfrom); return; } if (ia != NULL) ifp = ia->iface; else { - struct cmsghdr *cm; - struct in6_pktinfo pi; - - pi.ipi6_ifindex = 0; - for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&ctx->rcvhdr); - cm; - cm = (struct cmsghdr *)CMSG_NXTHDR(&ctx->rcvhdr, cm)) - { - if (cm->cmsg_level != IPPROTO_IPV6) - continue; - switch(cm->cmsg_type) { - case IPV6_PKTINFO: - if (cm->cmsg_len == CMSG_LEN(sizeof(pi))) - memcpy(&pi, CMSG_DATA(cm), sizeof(pi)); - break; - } - } - if (pi.ipi6_ifindex == 0) { - logerrx("DHCPv6 reply did not contain index from %s", - ctx->sfrom); - return; - } - - TAILQ_FOREACH(ifp, ctx->ifaces, next) { - if (ifp->index == (unsigned int)pi.ipi6_ifindex) - break; - } + ifp = if_findifpfromcmsg(ctx, &msg, NULL); if (ifp == NULL) { - logerrx("DHCPv6 reply for unexpected interface from %s", - ctx->sfrom); + logerr(__func__); return; } } - r = (struct dhcp6_message *)ctx->rcvhdr.msg_iov[0].iov_base; + r = (struct dhcp6_message *)buf; o = dhcp6_findmoption(r, len, D6_OPTION_CLIENTID, &ol); if (o == NULL || ol != ctx->duid_len || memcmp(o, ctx->duid, ol) != 0) { logdebugx("%s: incorrect client ID from %s", - ifp->name, ctx->sfrom); + ifp->name, sfrom); return; } if (dhcp6_findmoption(r, len, D6_OPTION_SERVERID, NULL) == NULL) { logdebugx("%s: no DHCPv6 server ID from %s", - ifp->name, ctx->sfrom); + ifp->name, sfrom); return; } if (r->type == DHCP6_RECONFIGURE) { - logdebugx("%s: RECONFIGURE recv from %s," + logdebugx("%s: RECONFIGURE6 recv from %s," " sending to all interfaces", - ifp->name, ctx->sfrom); + ifp->name, sfrom); TAILQ_FOREACH(ifp, ctx->ifaces, next) { - if (D6_CSTATE(ifp) != NULL) - dhcp6_recvif(ifp, r, len); + state = D6_CSTATE(ifp); + if (state != NULL && state->send != NULL) + dhcp6_recvif(ifp, sfrom, r, len); } return; } @@ -3577,7 +3585,7 @@ dhcp6_recv(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia) state->send->xid[0], state->send->xid[1], state->send->xid[2], - ctx->sfrom); + sfrom); return; } logdebugx("%s: redirecting DHCP6 message to %s", @@ -3585,7 +3593,7 @@ dhcp6_recv(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia) ifp = ifp1; } - dhcp6_recvif(ifp, r, len); + dhcp6_recvif(ifp, sfrom, r, len); } static void @@ -3778,6 +3786,8 @@ dhcp6_start(struct interface *ifp, enum DH6S init_state) /* No DHCPv6 config, no existing state * so nothing to do. */ return 0; + case DH6S_INFORM: + break; default: init_state = DH6S_INIT; break; @@ -3798,6 +3808,7 @@ dhcp6_start(struct interface *ifp, enum DH6S init_state) gogogo: state->state = init_state; + state->lerror = 0; dhcp_set_leasefile(state->leasefile, sizeof(state->leasefile), AF_INET6, ifp); if (ipv6_linklocal(ifp) == NULL) { @@ -3817,18 +3828,20 @@ dhcp6_reboot(struct interface *ifp) struct dhcp6_state *state; state = D6_STATE(ifp); - if (state) { - switch (state->state) { - case DH6S_BOUND: - dhcp6_startrebind(ifp); - break; - case DH6S_INFORMED: - dhcp6_startinform(ifp); - break; - default: - dhcp6_startdiscover(ifp); - break; - } + if (state == NULL) + return; + + state->lerror = 0; + switch (state->state) { + case DH6S_BOUND: + dhcp6_startrebind(ifp); + break; + case DH6S_INFORMED: + dhcp6_startinform(ifp); + break; + default: + dhcp6_startdiscover(ifp); + break; } } @@ -3927,18 +3940,22 @@ dhcp6_free(struct interface *ifp) } void -dhcp6_dropnondelegates(struct interface *ifp) +dhcp6_abort(struct interface *ifp) { +#ifdef ND6_ADVERTISE + struct dhcp6_state *state; + struct ipv6_addr *ia; +#endif -#ifndef SMALL - if (dhcp6_hasprefixdelegation(ifp)) + eloop_timeout_delete(ifp->ctx->eloop, dhcp6_start1, ifp); +#ifdef ND6_ADVERTISE + state = D6_STATE(ifp); + if (state == NULL) return; + TAILQ_FOREACH(ia, &state->addrs, next) { + ipv6nd_advertise(ia); + } #endif - if (D6_CSTATE(ifp) == NULL) - return; - - loginfox("%s: dropping DHCPv6 due to no valid routers", ifp->name); - dhcp6_drop(ifp, "EXPIRE6"); } void @@ -3961,24 +3978,22 @@ dhcp6_handleifa(int cmd, struct ipv6_addr *ia, pid_t pid) } ssize_t -dhcp6_env(char **env, const char *prefix, const struct interface *ifp, +dhcp6_env(FILE *fp, const char *prefix, const struct interface *ifp, const struct dhcp6_message *m, size_t len) { const struct if_options *ifo; struct dhcp_opt *opt, *vo; const uint8_t *p; struct dhcp6_option o; - size_t i, n; + size_t i; char *pfx; uint32_t en; const struct dhcpcd_ctx *ctx; #ifndef SMALL const struct dhcp6_state *state; const struct ipv6_addr *ap; - char *v, *val; #endif - n = 0; if (m == NULL) goto delegated; @@ -3993,28 +4008,20 @@ dhcp6_env(char **env, const char *prefix, const struct interface *ifp, ctx = ifp->ctx; /* Zero our indexes */ - if (env) { - for (i = 0, opt = ctx->dhcp6_opts; - i < ctx->dhcp6_opts_len; - i++, opt++) - dhcp_zero_index(opt); - for (i = 0, opt = ifp->options->dhcp6_override; - i < ifp->options->dhcp6_override_len; - i++, opt++) - dhcp_zero_index(opt); - for (i = 0, opt = ctx->vivso; - i < ctx->vivso_len; - i++, opt++) - dhcp_zero_index(opt); - i = strlen(prefix) + strlen("_dhcp6") + 1; - pfx = malloc(i); - if (pfx == NULL) { - logerr(__func__); - return -1; - } - snprintf(pfx, i, "%s_dhcp6", prefix); - } else - pfx = NULL; + for (i = 0, opt = ctx->dhcp6_opts; + i < ctx->dhcp6_opts_len; + i++, opt++) + dhcp_zero_index(opt); + for (i = 0, opt = ifp->options->dhcp6_override; + i < ifp->options->dhcp6_override_len; + i++, opt++) + dhcp_zero_index(opt); + for (i = 0, opt = ctx->vivso; + i < ctx->vivso_len; + i++, opt++) + dhcp_zero_index(opt); + if (asprintf(&pfx, "%s_dhcp6", prefix) == -1) + return -1; /* Unlike DHCP, DHCPv6 options *may* occur more than once. * There is also no provision for option concatenation unlike DHCP. */ @@ -4060,15 +4067,13 @@ dhcp6_env(char **env, const char *prefix, const struct interface *ifp, opt = NULL; } if (opt) { - n += dhcp_envoption(ifp->ctx, - env == NULL ? NULL : &env[n], - pfx, ifp->name, + dhcp_envoption(ifp->ctx, + fp, pfx, ifp->name, opt, dhcp6_getoption, p, o.len); } if (vo) { - n += dhcp_envoption(ifp->ctx, - env == NULL ? NULL : &env[n], - pfx, ifp->name, + dhcp_envoption(ifp->ctx, + fp, pfx, ifp->name, vo, dhcp6_getoption, p + sizeof(en), o.len - sizeof(en)); @@ -4080,38 +4085,29 @@ delegated: #ifndef SMALL /* Needed for Delegated Prefixes */ state = D6_CSTATE(ifp); - i = 0; TAILQ_FOREACH(ap, &state->addrs, next) { - if (ap->delegating_prefix) { - i += strlen(ap->saddr) + 1; - } + if (ap->delegating_prefix) + break; } - if (env && i) { - i += strlen(prefix) + strlen("_delegated_dhcp6_prefix="); - v = val = env[n] = malloc(i); - if (v == NULL) { - logerr(__func__); - return -1; - } - v += snprintf(val, i, "%s_delegated_dhcp6_prefix=", prefix); - TAILQ_FOREACH(ap, &state->addrs, next) { - if (ap->delegating_prefix) { - /* Can't use stpcpy(3) due to "security" */ - const char *sap = ap->saddr; - - do - *v++ = *sap; - while (*++sap != '\0'); - *v++ = ' '; - } + if (ap == NULL) + return 1; + if (fprintf(fp, "%s_delegated_dhcp6_prefix=", prefix) == -1) + return -1; + TAILQ_FOREACH(ap, &state->addrs, next) { + if (ap->delegating_prefix == NULL) + continue; + if (ap != TAILQ_FIRST(&state->addrs)) { + if (fputc(' ', fp) == EOF) + return -1; } - *--v = '\0'; + if (fprintf(fp, "%s", ap->saddr) == -1) + return -1; } - if (i) - n++; + if (fputc('\0', fp) == EOF) + return -1; #endif - return (ssize_t)n; + return 1; } int diff --git a/contrib/dhcpcd/src/dhcp6.h b/contrib/dhcpcd/src/dhcp6.h index 0e48ad0e6a..ce6ad8d573 100644 --- a/contrib/dhcpcd/src/dhcp6.h +++ b/contrib/dhcpcd/src/dhcp6.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -55,6 +56,8 @@ #define DHCP6_RECONFIGURE_REQ 18 #define DHCP6_RECONFIGURE_REPLY 19 +#ifdef DHCP6 + #define D6_OPTION_CLIENTID 1 #define D6_OPTION_SERVERID 2 #define D6_OPTION_IA_NA 3 @@ -204,7 +207,8 @@ struct dhcp6_state { /* The +3 is for the possible .pd extension for prefix delegation */ char leasefile[sizeof(LEASEFILE6) + IF_NAMESIZE + (IF_SSIDLEN * 4) +3]; const char *reason; - + uint16_t lerror; /* Last error received from DHCPv6 reply. */ + bool has_no_binding; struct authstate auth; }; @@ -216,7 +220,6 @@ struct dhcp6_state { (D6_CSTATE((ifp)) && \ D6_CSTATE((ifp))->reason && dhcp6_dadcompleted((ifp))) -#ifdef DHCP6 void dhcp6_printoptions(const struct dhcpcd_ctx *, const struct dhcp_opt *, size_t); const struct ipv6_addr *dhcp6_iffindaddr(const struct interface *ifp, @@ -227,28 +230,14 @@ size_t dhcp6_find_delegates(struct interface *); int dhcp6_start(struct interface *, enum DH6S); void dhcp6_reboot(struct interface *); void dhcp6_renew(struct interface *); -ssize_t dhcp6_env(char **, const char *, const struct interface *, +ssize_t dhcp6_env(FILE *, const char *, const struct interface *, const struct dhcp6_message *, size_t); void dhcp6_free(struct interface *); void dhcp6_handleifa(int, struct ipv6_addr *, pid_t); int dhcp6_dadcompleted(const struct interface *); +void dhcp6_abort(struct interface *); void dhcp6_drop(struct interface *, const char *); -void dhcp6_dropnondelegates(struct interface *ifp); int dhcp6_dump(struct interface *); -#else -#define dhcp6_printoptions(a, b, c) {} -#define dhcp6_iffindaddr(a, b, c) (NULL) -#define dhcp6_findaddr(a, b, c) (NULL) -#define dhcp6_find_delegates(a) {} -#define dhcp6_start(a, b) (0) -#define dhcp6_reboot(a) {} -#define dhcp6_renew(a) {} -#define dhcp6_env(a, b, c, d, e) (0) -#define dhcp6_free(a) {} -#define dhcp6_dadcompleted(a) (0) -#define dhcp6_drop(a, b) {} -#define dhcp6_dropnondelegates(a) {} -#define dhcp6_dump(a) (-1) -#endif - -#endif +#endif /* DHCP6 */ + +#endif /* DHCP6_H */ diff --git a/contrib/dhcpcd/src/dhcpcd-definitions.conf b/contrib/dhcpcd/src/dhcpcd-definitions.conf deleted file mode 100644 index e0ab2b79f2..0000000000 --- a/contrib/dhcpcd/src/dhcpcd-definitions.conf +++ /dev/null @@ -1,626 +0,0 @@ -# Copyright (c) 2006-2017 Roy Marples -# All rights reserved - -# DHCP option definitions for dhcpcd(8) -# These are used to translate DHCP options into shell variables -# for use in dhcpcd-run-hooks(8) -# See dhcpcd.conf(5) for details - -############################################################################## -# DHCP RFC2132 options unless otheriwse stated -define 1 request ipaddress subnet_mask -# RFC3442 states that the CSR has to come before all other routes -# For completeness we also specify static routes then routers -define 121 rfc3442 classless_static_routes -# Option 249 is an IANA assigned private number used by Windows DHCP servers -# to provide the exact same information as option 121, classless static routes -define 249 rfc3442 ms_classless_static_routes -define 33 request array ipaddress static_routes -define 3 request array ipaddress routers -define 2 uint32 time_offset -define 4 array ipaddress time_servers -define 5 array ipaddress ien116_name_servers -define 6 array ipaddress domain_name_servers -define 7 array ipaddress log_servers -define 8 array ipaddress cookie_servers -define 9 array ipaddress lpr_servers -define 10 array ipaddress impress_servers -define 11 array ipaddress resource_location_servers -define 12 dname host_name -define 13 uint16 boot_size -define 14 string merit_dump -# Technically domain_name is not an array, but many servers expect clients -# to treat it as one. -define 15 array dname domain_name -define 16 ipaddress swap_server -define 17 string root_path -define 18 string extensions_path -define 19 byte ip_forwarding -define 20 byte non_local_source_routing -define 21 array ipaddress policy_filter -define 22 uint16 max_dgram_reassembly -define 23 byte default_ip_ttl -define 24 uint32 path_mtu_aging_timeout -define 25 array uint16 path_mtu_plateau_table -define 26 uint16 interface_mtu -define 27 byte all_subnets_local -define 28 request ipaddress broadcast_address -define 29 byte perform_mask_discovery -define 30 byte mask_supplier -define 31 byte router_discovery -define 32 ipaddress router_solicitation_address -define 34 byte trailer_encapsulation -define 35 uint32 arp_cache_timeout -define 36 uint16 ieee802_3_encapsulation -define 37 byte default_tcp_ttl -define 38 uint32 tcp_keepalive_interval -define 39 byte tcp_keepalive_garbage -define 40 string nis_domain -define 41 array ipaddress nis_servers -define 42 array ipaddress ntp_servers -define 43 binhex vendor_encapsulated_options -define 44 array ipaddress netbios_name_servers -define 45 ipaddress netbios_dd_server -define 46 byte netbios_node_type -define 47 string netbios_scope -define 48 array ipaddress font_servers -define 49 array ipaddress x_display_manager -define 50 ipaddress dhcp_requested_address -define 51 request uint32 dhcp_lease_time -define 52 byte dhcp_option_overload -define 53 byte dhcp_message_type -define 54 ipaddress dhcp_server_identifier -define 55 array byte dhcp_parameter_request_list -define 56 string dhcp_message -define 57 uint16 dhcp_max_message_size -define 58 request uint32 dhcp_renewal_time -define 59 request uint32 dhcp_rebinding_time -define 60 string vendor_class_identifier -define 61 binhex dhcp_client_identifier -define 64 string nisplus_domain -define 65 array ipaddress nisplus_servers -define 66 dname tftp_server_name -define 67 string bootfile_name -define 68 array ipaddress mobile_ip_home_agent -define 69 array ipaddress smtp_server -define 70 array ipaddress pop_server -define 71 array ipaddress nntp_server -define 72 array ipaddress www_server -define 73 array ipaddress finger_server -define 74 array ipaddress irc_server -define 75 array ipaddress streettalk_server -define 76 array ipaddress streettalk_directory_assistance_server - -# DHCP User Class, RFC3004 -define 77 binhex user_class - -# DHCP SLP Directory Agent, RFC2610 -define 78 embed slp_agent -embed byte mandatory -embed array ipaddress address -define 79 embed slp_service -embed byte mandatory -embed ascii scope_list - -# DHCP Rapid Commit, RFC4039 -define 80 norequest flag rapid_commit - -# DHCP Fully Qualified Domain Name, RFC4702 -define 81 embed fqdn -embed bitflags=0000NEOS flags -embed byte rcode1 -embed byte rcode2 -# dhcpcd always sets the E bit which means the fqdn itself is always -# RFC1035 encoded. -# The server MUST use the encoding as specified by the client as noted -# in RFC4702 Section 2.1. -embed optional domain fqdn - -# Option 82 is for Relay Agents and DHCP servers - -# iSNS, RFC4174 -define 83 embed isns -embed byte reserved1 -embed bitflags=00000SAE functions -embed byte reserved2 -embed bitflags=00fFsSCE dd -embed byte reserved3 -embed bitflags=0000DMHE admin -embed uint16 reserved4 -embed byte reserved5 -embed bitflags=0TXPAMSE server_security -embed array ipaddress servers - -# Option 84 are unused, RFC3679 - -# DHCP Novell Directory Services, RFC2241 -define 85 array ipaddress nds_servers -define 86 raw nds_tree_name -define 87 raw nds_context - -# DHCP Broadcast and Multicast Control Server, RFC4280 -define 88 array domain bcms_controller_names -define 89 array ipaddress bcms_controller_address - -# DHCP Authentication, RFC3118 -define 90 embed auth -embed byte protocol -embed byte algorithm -embed byte rdm -embed binhex:8 replay -embed binhex information - -# DHCP Leasequery, RFC4388 -define 91 uint32 client_last_transaction_time -define 92 array ipaddress associated_ip - -# DHCP Options for Intel Preboot eXecution Environent (PXE), RFC4578 -# Options 93, 94 and 97 are used but of no use to dhcpcd - -# Option 95 used by Apple but never published RFC3679 -# Option 96 is unused, RFC3679 - -# DHCP The Open Group's User Authentication Protocol, RFC2485 -define 98 string uap_servers - -# DHCP Civic Addresses Configuration Information, RFC4776 -define 99 encap geoconf_civic -embed byte what -embed uint16 country_code -# The rest of this option is not supported - -# DHCP Timezone, RFC4883 -define 100 string posix_timezone -define 101 string tzdb_timezone - -# Options 102-115 are unused, RFC3679 - -# DHCP Auto-Configuration, RFC2563 -define 116 byte auto_configure - -# DHCP Name Service Search, RFC2937 -define 117 array uint16 name_service_search - -# DHCP Subnet Selection, RFC3011 -define 118 ipaddress subnet_selection - -# DHCP Domain Search, RFC3397 -define 119 array domain domain_search - -# DHCP Session Initiated Protocol Servers, RFC3361 -define 120 rfc3361 sip_server - -# Option 121 is defined at the top of this file - -# DHCP CableLabs Client, RFC3495 -define 122 encap tsp -encap 1 ipaddress dhcp_server -encap 2 ipaddress dhcp_secondary_server -encap 3 rfc3361 provisioning_server -encap 4 embed as_req_as_rep_backoff -embed uint32 nominal -embed uint32 maximum -embed uint32 retry -encap 5 embed ap_req_ap_rep_backoff -embed uint32 nominal -embed uint32 maximum -embed uint32 retry -encap 6 domain kerberos_realm -encap 7 byte ticket_granting_server_utilization -encap 8 byte provisioning_timer - -# DHCP Coordinate LCI, RFC6225 -# We have no means of expressing 6 bit lengths -define 123 binhex geoconf - -# DHCP Vendor-Identifying Vendor Options, RFC3925 -define 124 binhex vivco -define 125 embed vivso -embed uint32 enterprise_number -# Vendor options are shared between DHCP/DHCPv6 -# Their code is matched to the enterprise number defined above -# see the end of this file for an example - -# Options 126 and 127 are unused, RFC3679 - -# DHCP Options for Intel Preboot eXecution Environent (PXE), RFC4578 -# Options 128-135 are used but of no use to dhcpcd - -# DHCP PANA Authentication Agent, RFC5192 -define 136 array ipaddress pana_agent - -# DHCP Lost Server, RFC5223 -define 137 domain lost_server - -# DHCP CAPWAP, RFC5417 -define 138 array ipaddress capwap_ac - -# DHCP Mobility Services, RFC5678 -define 139 encap mos_ip -encap 1 array ipaddress is -encap 2 array ipaddress cs -encap 3 array ipaddress es -define 140 encap mos_domain -encap 1 domain is -encap 2 domain cs -encap 3 domain es - -# DHCP SIP UA, RFC6011 -define 141 array domain sip_ua_cs_list - -# DHCP ANDSF, RFC6153 -define 142 array ipaddress andsf -define 143 array ip6address andsf6 - -# DHCP Coordinate LCI, RFC6225 -# We have no means of expressing 6 bit lengths -define 144 binhex geoloc - -# DHCP FORCERENEW Nonce Capability, RFC6704 -define 145 array byte forcerenew_nonce_capable - -# DHCP RDNSS Selection for MIF Nodes, RFC6731 -define 146 embed rdnss_selection -embed byte prf -embed ipaddress primary -embed ipaddress secondary -embed array domain domains - -# Options 147, 148 and 149 are unused, RFC3942 - -# DHCP TFTP Server Address, RFC5859 -define 150 array ipaddress tftp_servers - -# DHCP MUD URL, draft-ietf-opsawg-mud -define 161 string mudurl - -# Apart from 161... -# Options 151-157 are used for Lease Query, RFC6926 and not for dhcpcd -# Options 158-174 are unused, RFC3942 - -# Options 175-177 are tentativel assigned for Etherboot -# Options 178-207 are unused, RFC3942 - -# DHCP PXELINUX, RFC5071 -define 208 binhex pxelinux_magic -define 209 string config_file -define 210 string path_prefix -define 211 uint32 reboot_time - -# DHCP IPv6 Rapid Deployment on IPv4 Infrastructures, RFC5969 -define 212 embed sixrd -embed byte mask_len -embed byte prefix_len -embed ip6address prefix -embed array ipaddress brip_address - -# DHCP Access Network Domain Name, RFC5986 -define 213 domain access_domain - -# Options 214-219 are unused, RFC3942 - -# DHCP Subnet Allocation, RFC6656 -# Option 220 looks specific to Cisco hardware. - -# DHCP Virtual Subnet Selection, RFC6607 -define 221 encap vss -encap 0 string nvt -encap 1 binhex vpn_id -encap 255 flag global - -# Options 222 and 223 are unused, RFC3942 - -# Options 224-254 are reserved for Private Use -# However, an expired RFC for Web Proxy Auto Discovery Protocol does define -# Option 252 which is commonly used by major browsers. -# Apparently the code was assigned by agreement of the DHC working group chair. -define 252 string wpad_url - -# Option 255 End - -############################################################################## -# ND6 options, RFC4861 -definend 1 binhex source_address -definend 2 binhex target_address - -definend 3 index embed prefix_information -embed byte length -embed bitflags=LA flags -embed uint32 vltime -embed uint32 pltime -embed uint32 reserved -embed array ip6address prefix - -# option 4 is only for Redirect messages - -definend 5 embed mtu -embed uint16 reserved -embed uint32 mtu - -# ND6 options, RFC6101 -definend 25 index embed rdnss -embed uint16 reserved -embed uint32 lifetime -embed array ip6address servers - -definend 31 index embed dnssl -embed uint16 reserved -embed uint32 lifetime -embed domain search - -############################################################################## -# DHCPv6 options, RFC3315 -define6 1 binhex client_id -define6 2 binhex server_id - -define6 3 norequest index embed ia_na -embed binhex:4 iaid -embed uint32 t1 -embed uint32 t2 -encap 5 option -encap 13 option - -define6 4 norequest index embed ia_ta -embed uint32 iaid -encap 5 option -encap 13 option - -define6 5 norequest index embed ia_addr -embed ip6address ia_addr -embed uint32 pltime -embed uint32 vltime -encap 13 option - -define6 6 array uint16 option_request -define6 7 byte preference -define6 8 uint16 elased_time -define6 9 binhex dhcp_relay_msg - -# Option 10 is unused - -define6 11 embed auth -embed byte protocol -embed byte algorithm -embed byte rdm -embed binhex:8 replay -embed binhex information - -define6 12 ip6address unicast - -define6 13 norequest embed status_code -embed uint16 status_code -embed optional string message - -define6 14 norequest flag rapid_commit -define6 15 binhex user_class - -define6 16 binhex vivco -define6 17 embed vivso -embed uint32 enterprise_number -# Vendor options are shared between DHCP/DHCPv6 -# Their code is matched to the enterprise number defined above -# See the end of this file for an example - -define6 18 binhex interface_id -define6 19 byte reconfigure_msg -define6 20 flag reconfigure_accept - -# DHCPv6 Session Initiation Protocol Options, RFC3319 -define6 21 array domain sip_servers_names -define6 22 array ip6address sip_servers_addresses - -# DHCPv6 DNS Configuration Options, RFC3646 -define6 23 array ip6address name_servers -define6 24 array domain domain_search - -# DHCPv6 Prefix Options, RFC6603 -define6 25 norequest index embed ia_pd -embed binhex:4 iaid -embed uint32 t1 -embed uint32 t2 -encap 26 option -define6 26 index embed prefix -embed uint32 pltime -embed uint32 vltime -embed byte length -embed ip6address prefix -encap 13 option -encap 67 option - -# DHCPv6 Network Information Service Options, RFC3898 -define6 27 array ip6address nis_servers -define6 28 array ip6address nisp_servers -define6 29 string nis_domain_name -define6 30 string nisp_domain_name - -# DHCPv6 Simple Network Time Protocol Servers Option, RFC4075 -define6 31 array ip6address sntp_servers - -# DHCPv6 Information Refresh Time, RFC4242 -define6 32 uint32 info_refresh_time - -# DHCPv6 Broadcast and Multicast Control Server, RFC4280 -define6 33 array domain bcms_server_d -define6 34 array ip6address bcms_server_a - -# DHCP Civic Addresses Configuration Information, RFC4776 -define6 36 encap geoconf_civic -embed byte what -embed uint16 country_code -# The rest of this option is not supported - -# DHCP Relay Agent Remote-ID, RFC4649 -define6 37 embed remote_id -embed uint32 enterprise_number -embed binhex remote_id - -# DHCP Relay Agent Subscriber-ID, RFC4580 -define6 38 binhex subscriber_id - -# DHCPv6 Fully Qualified Domain Name, RFC4704 -define6 39 embed fqdn -embed bitflags=00000NOS flags -embed optional domain fqdn - -# DHCPv6 PANA Authentication Agnet, RC5192 -define6 40 array ip6address pana_agent - -# DHCPv6 Timezone options, RFC4883 -define6 41 string posix_timezone -define6 42 string tzdb_timezone - -# DHCPv6 Relay Agent Echo Request -define6 43 array uint16 ero - -# Options 44-48 are used for Lease Query, RFC5007 and not for dhcpcd - -# DHCPv6 Home Info Discovery in MIPv6, RFC6610 -define6 49 domain mip6_hnidf -define6 50 encap mip6_vdinf -encap 71 option -encap 72 option -encap 73 option - -# DHCPv6 Lost Server, RFC5223 -define6 51 domain lost_server - -# DHCPv6 CAPWAP, RFC5417 -define6 52 array ip6address capwap_ac - -# DHCPv6 Relay-ID, RFC5460 -define6 53 binhex relay_id - -# DHCP Mobility Services, RFC5678 -define6 54 encap mos_ip -encap 1 array ip6address is -encap 2 array ip6address cs -encap 3 array ip6address es -define6 55 encap mos_domain -encap 1 domain is -encap 2 domain cs -encap 3 domain es - -# DHCPv6 Network Time Protocol Server, RFC5908 -define6 56 encap ntp_server -encap 1 ip6address addr -encap 2 ip6address mcast_addr -encap 3 ip6address fqdn - -# DHCPv6 LIS Discovery, RFC5986 -define6 57 domain access_domain - -# DHCPv6 SIP UA, RFC6011 -define6 58 array domain sip_ua_cs_list - -# DHCPv6 Network Boot, RFC5970 -define6 59 string bootfile_url -# We presently cannot decode bootfile_param -define6 60 binhex bootfile_param -define6 61 array uint16 architecture_types -define6 62 embed nii -embed byte type -embed byte major -embed byte minor - -# DHCPv6 Coordinate LCI, RFC6225 -# We have no means of expressing 6 bit lengths -define6 63 binhex geoloc - -# DHCPv6 AFTR-Name, RFC6334 -define6 64 domain aftr_name - -# DHCPv6 Prefix Exclude Option, RFC6603 -define6 67 embed pd_exclude -embed byte prefix_len -embed binhex subnetID - -# DHCPv6 Home Info Discovery in MIPv6, RFC6610 -define6 69 encap mip6_idinf -encap 71 option -encap 72 option -encap 73 option -define6 70 encap mip6_udinf -encap 71 option -encap 72 option -encap 73 option -define6 71 embed mip6_hnp -embed byte prefix_len -embed ip6address prefix -define6 72 ip6address mip6_haa -define6 73 domain mip6_haf - -# DHCPv6 RDNSS Selection for MIF Nodes, RFC6731 -define6 74 embed rdnss_selection -embed ip6address server -embed byte prf -embed array domain domains - -# DHCPv6 Kerberos, RFC6784 -define6 75 string krb_principal_name -define6 76 string krb_realm_name -define6 78 embed krb_kdc -embed uint16 priority -embed uint16 weight -embed byte transport_type -embed uint16 port -embed ip6address address -embed string realm_name - -# DHCPv6 Client Link-Layer Address, RFC6939 -# Section 7 states that clients MUST ignore the option 79 - -# DHCPv6 Relay-Triggered Reconfiguraion, RFC6977 -define6 80 ip6address link_address - -# DHCPv6 Radius, RFC7037 -# Section 7 states that clients MUST ignore the option 81 - -# DHCPv6 SOL_MAX_RT, RFC7083 -define6 82 request uint32 sol_max_rt -define6 83 request uint32 inf_max_rt - -# DHCPv6 Softwire Address and Port-Mapped Clients, RFC7598 -define6 89 embed s46_rule -embed bitflags=0000000F flags -embed byte ea_len -embed byte prefix4_len -embed ipaddress ipv4_prefix -embed ip6address ipv6_prefix -define6 90 ip6address s64_br -define6 91 embed s46_dmr -embed byte prefix_len -embed binhex prefix -define6 92 embed s46_v4v6bind -embed ipaddress ipv4_address -embed byte ipv6_prefix_len -embed binhex ipv6_prefix_and_options -# Cannot decode options after variable length address ... -#encap 93 option -define6 93 embed s46_portparams -embed byte offset -embed byte psid_len -embed uint16 psid -define6 94 embed s46_cont_mape -encap 89 option -encap 90 option -define6 95 embed s46_cont_mapt -encap 89 option -encap 91 option -define6 96 embed s46_cont_lw -encap 90 option -encap 92 option - -# DHCPv6 Address Selection Policy -# Currently not supported - -# DHCPv6 MUD URL, draft-ietf-opsawg-mud -define6 112 string mudurl - -# Options 86-65535 are unasssinged - -############################################################################## -# Vendor-Identifying Vendor Options -# An example: -#vendopt 12345 encap frobozzco -#encap 1 string maze_location -#encap 2 byte grue_probability diff --git a/contrib/dhcpcd/src/dhcpcd-embedded.c b/contrib/dhcpcd/src/dhcpcd-embedded.c new file mode 100644 index 0000000000..a26770389f --- /dev/null +++ b/contrib/dhcpcd/src/dhcpcd-embedded.c @@ -0,0 +1,407 @@ +/* + * DO NOT EDIT! + * Automatically generated from dhcpcd-embedded.conf + * Ths allows us to simply generate DHCP structure without any C programming. + */ + +/* + * dhcpcd - DHCP client daemon + * Copyright (c) 2006-2019 Roy Marples + * All rights reserved + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +const char * const dhcpcd_embedded_conf[] = { +"define 1 request ipaddress subnet_mask", +"define 121 rfc3442 classless_static_routes", +"define 249 rfc3442 ms_classless_static_routes", +"define 33 request array ipaddress static_routes", +"define 3 request array ipaddress routers", +"define 2 uint32 time_offset", +"define 4 array ipaddress time_servers", +"define 5 array ipaddress ien116_name_servers", +"define 6 array ipaddress domain_name_servers", +"define 7 array ipaddress log_servers", +"define 8 array ipaddress cookie_servers", +"define 9 array ipaddress lpr_servers", +"define 10 array ipaddress impress_servers", +"define 11 array ipaddress resource_location_servers", +"define 12 dname host_name", +"define 13 uint16 boot_size", +"define 14 string merit_dump", +"define 15 array dname domain_name", +"define 16 ipaddress swap_server", +"define 17 string root_path", +"define 18 string extensions_path", +"define 19 byte ip_forwarding", +"define 20 byte non_local_source_routing", +"define 21 array ipaddress policy_filter", +"define 22 uint16 max_dgram_reassembly", +"define 23 byte default_ip_ttl", +"define 24 uint32 path_mtu_aging_timeout", +"define 25 array uint16 path_mtu_plateau_table", +"define 26 uint16 interface_mtu", +"define 27 byte all_subnets_local", +"define 28 request ipaddress broadcast_address", +"define 29 byte perform_mask_discovery", +"define 30 byte mask_supplier", +"define 31 byte router_discovery", +"define 32 ipaddress router_solicitation_address", +"define 34 byte trailer_encapsulation", +"define 35 uint32 arp_cache_timeout", +"define 36 uint16 ieee802_3_encapsulation", +"define 37 byte default_tcp_ttl", +"define 38 uint32 tcp_keepalive_interval", +"define 39 byte tcp_keepalive_garbage", +"define 40 string nis_domain", +"define 41 array ipaddress nis_servers", +"define 42 array ipaddress ntp_servers", +"define 43 binhex vendor_encapsulated_options", +"define 44 array ipaddress netbios_name_servers", +"define 45 ipaddress netbios_dd_server", +"define 46 byte netbios_node_type", +"define 47 string netbios_scope", +"define 48 array ipaddress font_servers", +"define 49 array ipaddress x_display_manager", +"define 50 ipaddress dhcp_requested_address", +"define 51 request uint32 dhcp_lease_time", +"define 52 byte dhcp_option_overload", +"define 53 byte dhcp_message_type", +"define 54 ipaddress dhcp_server_identifier", +"define 55 array byte dhcp_parameter_request_list", +"define 56 string dhcp_message", +"define 57 uint16 dhcp_max_message_size", +"define 58 request uint32 dhcp_renewal_time", +"define 59 request uint32 dhcp_rebinding_time", +"define 60 string vendor_class_identifier", +"define 61 binhex dhcp_client_identifier", +"define 64 string nisplus_domain", +"define 65 array ipaddress nisplus_servers", +"define 66 dname tftp_server_name", +"define 67 string bootfile_name", +"define 68 array ipaddress mobile_ip_home_agent", +"define 69 array ipaddress smtp_server", +"define 70 array ipaddress pop_server", +"define 71 array ipaddress nntp_server", +"define 72 array ipaddress www_server", +"define 73 array ipaddress finger_server", +"define 74 array ipaddress irc_server", +"define 75 array ipaddress streettalk_server", +"define 76 array ipaddress streettalk_directory_assistance_server", +"define 77 binhex user_class", +"define 78 embed slp_agent", +"embed byte mandatory", +"embed array ipaddress address", +"define 79 embed slp_service", +"embed byte mandatory", +"embed ascii scope_list", +"define 80 norequest flag rapid_commit", +"define 81 embed fqdn", +"embed bitflags=0000NEOS flags", +"embed byte rcode1", +"embed byte rcode2", +"embed optional domain fqdn", +"define 83 embed isns", +"embed byte reserved1", +"embed bitflags=00000SAE functions", +"embed byte reserved2", +"embed bitflags=00fFsSCE dd", +"embed byte reserved3", +"embed bitflags=0000DMHE admin", +"embed uint16 reserved4", +"embed byte reserved5", +"embed bitflags=0TXPAMSE server_security", +"embed array ipaddress servers", +"define 85 array ipaddress nds_servers", +"define 86 raw nds_tree_name", +"define 87 raw nds_context", +"define 88 array domain bcms_controller_names", +"define 89 array ipaddress bcms_controller_address", +"define 90 embed auth", +"embed byte protocol", +"embed byte algorithm", +"embed byte rdm", +"embed binhex:8 replay", +"embed binhex information", +"define 91 uint32 client_last_transaction_time", +"define 92 array ipaddress associated_ip", +"define 98 string uap_servers", +"define 99 encap geoconf_civic", +"embed byte what", +"embed uint16 country_code", +"define 100 string posix_timezone", +"define 101 string tzdb_timezone", +"define 116 byte auto_configure", +"define 117 array uint16 name_service_search", +"define 118 ipaddress subnet_selection", +"define 119 array domain domain_search", +"define 120 rfc3361 sip_server", +"define 122 encap tsp", +"encap 1 ipaddress dhcp_server", +"encap 2 ipaddress dhcp_secondary_server", +"encap 3 rfc3361 provisioning_server", +"encap 4 embed as_req_as_rep_backoff", +"embed uint32 nominal", +"embed uint32 maximum", +"embed uint32 retry", +"encap 5 embed ap_req_ap_rep_backoff", +"embed uint32 nominal", +"embed uint32 maximum", +"embed uint32 retry", +"encap 6 domain kerberos_realm", +"encap 7 byte ticket_granting_server_utilization", +"encap 8 byte provisioning_timer", +"define 123 binhex geoconf", +"define 124 binhex vivco", +"define 125 embed vivso", +"embed uint32 enterprise_number", +"define 136 array ipaddress pana_agent", +"define 137 domain lost_server", +"define 138 array ipaddress capwap_ac", +"define 139 encap mos_ip", +"encap 1 array ipaddress is", +"encap 2 array ipaddress cs", +"encap 3 array ipaddress es", +"define 140 encap mos_domain", +"encap 1 domain is", +"encap 2 domain cs", +"encap 3 domain es", +"define 141 array domain sip_ua_cs_list", +"define 142 array ipaddress andsf", +"define 143 array ip6address andsf6", +"define 144 binhex geoloc", +"define 145 array byte forcerenew_nonce_capable", +"define 146 embed rdnss_selection", +"embed byte prf", +"embed ipaddress primary", +"embed ipaddress secondary", +"embed array domain domains", +"define 150 array ipaddress tftp_servers", +"define 161 string mudurl", +"define 208 binhex pxelinux_magic", +"define 209 string config_file", +"define 210 string path_prefix", +"define 211 uint32 reboot_time", +"define 212 embed sixrd", +"embed byte mask_len", +"embed byte prefix_len", +"embed ip6address prefix", +"embed array ipaddress brip_address", +"define 213 domain access_domain", +"define 221 encap vss", +"encap 0 string nvt", +"encap 1 binhex vpn_id", +"encap 255 flag global", +"define 252 string wpad_url", +"definend 1 binhex source_address", +"definend 2 binhex target_address", +"definend 3 index embed prefix_information", +"embed byte length", +"embed bitflags=LA flags", +"embed uint32 vltime", +"embed uint32 pltime", +"embed uint32 reserved", +"embed array ip6address prefix", +"definend 5 embed mtu", +"embed uint16 reserved", +"embed uint32 mtu", +"definend 25 index embed rdnss", +"embed uint16 reserved", +"embed uint32 lifetime", +"embed array ip6address servers", +"definend 31 index embed dnssl", +"embed uint16 reserved", +"embed uint32 lifetime", +"embed domain search", +"define6 1 binhex client_id", +"define6 2 binhex server_id", +"define6 3 norequest index embed ia_na", +"embed binhex:4 iaid", +"embed uint32 t1", +"embed uint32 t2", +"encap 5 option", +"encap 13 option", +"define6 4 norequest index embed ia_ta", +"embed uint32 iaid", +"encap 5 option", +"encap 13 option", +"define6 5 norequest index embed ia_addr", +"embed ip6address ia_addr", +"embed uint32 pltime", +"embed uint32 vltime", +"encap 13 option", +"define6 6 array uint16 option_request", +"define6 7 byte preference", +"define6 8 uint16 elased_time", +"define6 9 binhex dhcp_relay_msg", +"define6 11 embed auth", +"embed byte protocol", +"embed byte algorithm", +"embed byte rdm", +"embed binhex:8 replay", +"embed binhex information", +"define6 12 ip6address unicast", +"define6 13 norequest embed status_code", +"embed uint16 status_code", +"embed optional string message", +"define6 14 norequest flag rapid_commit", +"define6 15 binhex user_class", +"define6 16 binhex vivco", +"define6 17 embed vivso", +"embed uint32 enterprise_number", +"define6 18 binhex interface_id", +"define6 19 byte reconfigure_msg", +"define6 20 flag reconfigure_accept", +"define6 21 array domain sip_servers_names", +"define6 22 array ip6address sip_servers_addresses", +"define6 23 array ip6address name_servers", +"define6 24 array domain domain_search", +"define6 25 norequest index embed ia_pd", +"embed binhex:4 iaid", +"embed uint32 t1", +"embed uint32 t2", +"encap 26 option", +"define6 26 index embed prefix", +"embed uint32 pltime", +"embed uint32 vltime", +"embed byte length", +"embed ip6address prefix", +"encap 13 option", +"encap 67 option", +"define6 27 array ip6address nis_servers", +"define6 28 array ip6address nisp_servers", +"define6 29 string nis_domain_name", +"define6 30 string nisp_domain_name", +"define6 31 array ip6address sntp_servers", +"define6 32 uint32 info_refresh_time", +"define6 33 array domain bcms_server_d", +"define6 34 array ip6address bcms_server_a", +"define6 36 encap geoconf_civic", +"embed byte what", +"embed uint16 country_code", +"define6 37 embed remote_id", +"embed uint32 enterprise_number", +"embed binhex remote_id", +"define6 38 binhex subscriber_id", +"define6 39 embed fqdn", +"embed bitflags=00000NOS flags", +"embed optional domain fqdn", +"define6 40 array ip6address pana_agent", +"define6 41 string posix_timezone", +"define6 42 string tzdb_timezone", +"define6 43 array uint16 ero", +"define6 49 domain mip6_hnidf", +"define6 50 encap mip6_vdinf", +"encap 71 option", +"encap 72 option", +"encap 73 option", +"define6 51 domain lost_server", +"define6 52 array ip6address capwap_ac", +"define6 53 binhex relay_id", +"define6 54 encap mos_ip", +"encap 1 array ip6address is", +"encap 2 array ip6address cs", +"encap 3 array ip6address es", +"define6 55 encap mos_domain", +"encap 1 domain is", +"encap 2 domain cs", +"encap 3 domain es", +"define6 56 encap ntp_server", +"encap 1 ip6address addr", +"encap 2 ip6address mcast_addr", +"encap 3 ip6address fqdn", +"define6 57 domain access_domain", +"define6 58 array domain sip_ua_cs_list", +"define6 59 string bootfile_url", +"define6 60 binhex bootfile_param", +"define6 61 array uint16 architecture_types", +"define6 62 embed nii", +"embed byte type", +"embed byte major", +"embed byte minor", +"define6 63 binhex geoloc", +"define6 64 domain aftr_name", +"define6 67 embed pd_exclude", +"embed byte prefix_len", +"embed binhex subnetID", +"define6 69 encap mip6_idinf", +"encap 71 option", +"encap 72 option", +"encap 73 option", +"define6 70 encap mip6_udinf", +"encap 71 option", +"encap 72 option", +"encap 73 option", +"define6 71 embed mip6_hnp", +"embed byte prefix_len", +"embed ip6address prefix", +"define6 72 ip6address mip6_haa", +"define6 73 domain mip6_haf", +"define6 74 embed rdnss_selection", +"embed ip6address server", +"embed byte prf", +"embed array domain domains", +"define6 75 string krb_principal_name", +"define6 76 string krb_realm_name", +"define6 78 embed krb_kdc", +"embed uint16 priority", +"embed uint16 weight", +"embed byte transport_type", +"embed uint16 port", +"embed ip6address address", +"embed string realm_name", +"define6 80 ip6address link_address", +"define6 82 request uint32 sol_max_rt", +"define6 83 request uint32 inf_max_rt", +"define6 89 embed s46_rule", +"embed bitflags=0000000F flags", +"embed byte ea_len", +"embed byte prefix4_len", +"embed ipaddress ipv4_prefix", +"embed ip6address ipv6_prefix", +"define6 90 ip6address s64_br", +"define6 91 embed s46_dmr", +"embed byte prefix_len", +"embed binhex prefix", +"define6 92 embed s46_v4v6bind", +"embed ipaddress ipv4_address", +"embed byte ipv6_prefix_len", +"embed binhex ipv6_prefix_and_options", +"define6 93 embed s46_portparams", +"embed byte offset", +"embed byte psid_len", +"embed uint16 psid", +"define6 94 embed s46_cont_mape", +"encap 89 option", +"encap 90 option", +"define6 95 embed s46_cont_mapt", +"encap 89 option", +"encap 91 option", +"define6 96 embed s46_cont_lw", +"encap 90 option", +"encap 92 option", +"define6 112 string mudurl", +NULL +}; diff --git a/contrib/dhcpcd/src/dhcpcd-embedded.c.in b/contrib/dhcpcd/src/dhcpcd-embedded.c.in deleted file mode 100644 index 6d54dde2ea..0000000000 --- a/contrib/dhcpcd/src/dhcpcd-embedded.c.in +++ /dev/null @@ -1,36 +0,0 @@ -/* - * DO NOT EDIT! - * Automatically generated from dhcpcd-embedded.conf - * Ths allows us to simply generate DHCP structure without any C programming. - */ - -/* - * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples - * All rights reserved - - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include - -const char * const dhcpcd_embedded_conf[] = { diff --git a/contrib/dhcpcd/src/dhcpcd-embedded.h.in b/contrib/dhcpcd/src/dhcpcd-embedded.h similarity index 89% rename from contrib/dhcpcd/src/dhcpcd-embedded.h.in rename to contrib/dhcpcd/src/dhcpcd-embedded.h index 5ece1f9b8e..5e5b5e19ae 100644 --- a/contrib/dhcpcd/src/dhcpcd-embedded.h.in +++ b/contrib/dhcpcd/src/dhcpcd-embedded.h @@ -1,6 +1,6 @@ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -25,8 +25,8 @@ * SUCH DAMAGE. */ -#define INITDEFINES @INITDEFINES@ -#define INITDEFINENDS @INITDEFINENDS@ -#define INITDEFINE6S @INITDEFINE6S@ +#define INITDEFINES 124 +#define INITDEFINENDS 6 +#define INITDEFINE6S 69 extern const char * const dhcpcd_embedded_conf[]; diff --git a/contrib/dhcpcd/src/dhcpcd.8.in b/contrib/dhcpcd/src/dhcpcd.8.in index 235161962f..ae218f937f 100644 --- a/contrib/dhcpcd/src/dhcpcd.8.in +++ b/contrib/dhcpcd/src/dhcpcd.8.in @@ -1,4 +1,6 @@ -.\" Copyright (c) 2006-2018 Roy Marples +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.\" Copyright (c) 2006-2019 Roy Marples .\" All rights reserved .\" .\" Redistribution and use in source and binary forms, with or without @@ -22,7 +24,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd June 5, 2018 +.Dd August 28, 2019 .Dt DHCPCD 8 .Os .Sh NAME @@ -54,7 +56,7 @@ .Op Fl v , Fl Fl vendor Ar code , Ar value .Op Fl W , Fl Fl whitelist Ar address Ns Op Ar /cidr .Op Fl w -.Op Fl Fl waitip Op 4 | 6 +.Op Fl Fl waitip Ns = Ns Op 4 | 6 .Op Fl y , Fl Fl reboot Ar seconds .Op Fl X , Fl Fl blacklist Ar address Ns Op Ar /cidr .Op Fl Z , Fl Fl denyinterfaces Ar pattern @@ -69,7 +71,7 @@ .Fl k , Fl Fl release .Op interface .Nm -.Fl U, Fl Fl dumplease +.Fl U , Fl Fl dumplease .Ar interface .Nm .Fl Fl version @@ -102,7 +104,7 @@ sets the hostname to the one supplied by the DHCP server. .Nm then daemonises and waits for the lease renewal time to lapse. It will then attempt to renew its lease and reconfigure if the new lease -changes when the lease beings to expire or the DHCP server sends message +changes when the lease begins to expire or the DHCP server sends a message to renew early. .Pp If any interface reports a working carrier then @@ -187,8 +189,8 @@ Using a single interface also affects the .Fl n and .Fl x -options where the same interface will need to be specified as a lack of an -interafce will imply Master mode which this is not. +options, where the same interface will need to be specified, as a lack of an +interface will imply Master mode which this is not. To force starting in Master mode with only one interface, the .Fl M , Fl Fl master option can be used. @@ -232,12 +234,11 @@ and need to be copied to .Pa @HOOKDIR@ if you intend to use them. For example, you could install -.Pa 10-wpa_supplicant +.Pa 29-lookup-hostname so that .Nm -can ensure that -.Xr wpa_supplicant 8 -is always running on a hot-plugged wireless interface. +can lookup the hostname of the IP address in DNS if no hostname +is given by the lease and one is not already set. .Ss Fine tuning You can fine-tune the behaviour of .Nm @@ -259,7 +260,7 @@ otheriwse if persistent storage is available then a DUID-LLT (link local address + time) is generated, otherwise DUID-LL is generated (link local address). This, plus the IAID will be used as the -.Fl I, Fl Fl clientid . +.Fl I , Fl Fl clientid . The DUID generated will be held in .Pa @DBDIR@/duid and should not be copied to other hosts. @@ -272,10 +273,10 @@ If cannot obtain a lease, then try to use the last lease acquired for the interface. .It Fl Fl lastleaseextend -Same as the above, but he lease will be retained even if it expires. +Same as the above, but the lease will be retained even if it expires. .Nm will give it up if any other host tries to claim it for their own via ARP. -This is does violate RFC2131 section 3.7 which states the lease should be +This violates RFC 2131, section 3.7, which states the lease should be dropped once it has expired. .It Fl e , Fl Fl env Ar value Push @@ -307,7 +308,7 @@ are disable, none, ptr and both. itself never does any DNS updates. .Nm encodes the FQDN hostname as specified in -.Li RFC1035 . +.Li RFC 1035 . .It Fl f , Fl Fl config Ar file Specify a config to load instead of .Pa @SYSCONFDIR@/dhcpcd.conf . @@ -322,7 +323,7 @@ If is an empty string then the current system hostname is sent. If .Ar hostname -is a FQDN (ie, contains a .) then it will be encoded as such. +is a FQDN (i.e., contains a .) then it will be encoded as such. .It Fl I , Fl Fl clientid Ar clientid Send the .Ar clientid . @@ -352,7 +353,7 @@ rather than .Xr syslog 3 . The .Ar logfile -is s reopened when +is reopened when .Nm receives the .Dv SIGUSR2 @@ -402,10 +403,6 @@ is specified then this applies to all interfaces in Master mode. If .Nm is not running, then it starts up as normal. -This may also cause -.Xr wpa_supplicant 8 -to reload its configuration for each interface as well if the -relevant hook script has been installed. .It Fl N , Fl Fl renew Op Ar interface Notifies .Nm @@ -473,7 +470,7 @@ This option is only needed when .Nm is not processing IPv6RA messages and the need for DHCPv6 Information Request exists. -.It Fl S, Fl Fl static Ar value +.It Fl S , Fl Fl static Ar value Configures a static DHCP .Ar value . If you set @@ -483,7 +480,7 @@ then will not attempt to obtain a lease and just use the value for the address with an infinite lease time. .Pp -Here is an example which configures a static address, routes and dns. +Here is an example which configures a static address, routes and DNS. .D1 dhcpcd -S ip_address=192.168.0.10/24 \e .D1 -S routers=192.168.0.1 \e .D1 -S domain_name_servers=192.168.0.1 \e @@ -538,7 +535,7 @@ then exits before doing any configuration. .It Fl w Wait for an address to be assigned before forking to the background. Does not take an argument, unlike the below option. -.It Fl Fl waitip Op 4 | 6 +.It Fl Fl waitip Ns = Ns Op 4 | 6 Wait for an address to be assigned before forking to the background. 4 means wait for an IPv4 address to be assigned. 6 means wait for an IPv6 address to be assigned. @@ -584,7 +581,7 @@ This has no effect on DHCPv6 other than skipping the reboot phase. .Nm will try to do as much as it can by default. However, there are sometimes situations where you don't want the things to be -configured exactly how the the DHCP server wants. +configured exactly how the DHCP server wants. Here are some options that deal with turning these bits off. .Pp Note that when @@ -628,7 +625,7 @@ Use the last four bytes of the hardware address as the DHCP xid instead of a randomly generated number. .It Fl J , Fl Fl broadcast Instructs the DHCP server to broadcast replies back to the client. -Normally this is only set for non Ethernet interfaces, +Normally this is only set for non-Ethernet interfaces, such as FireWire and InfiniBand. In most instances, .Nm @@ -663,7 +660,7 @@ Quiet .Nm on the command line, only warnings and errors will be displayed. The messages are still logged though. -.It Fl T, Fl Fl test +.It Fl T , Fl Fl test On receipt of DHCP messages just call .Pa @SCRIPT@ with the reason of TEST which echos the DHCP variables found in the message @@ -676,7 +673,7 @@ option is not sent in TEST mode so that the server does not lease an address. To test INFORM the interface needs to be configured with the desired address before starting .Nm . -.It Fl U, Fl Fl dumplease Ar interface +.It Fl U , Fl Fl dumplease Ar interface Dumps the last lease for the .Ar interface to stdout. @@ -686,20 +683,20 @@ Use the or .Fl 6 flags to specify an address family. -.It Fl V, Fl Fl variables +.It Fl V , Fl Fl variables Display a list of option codes, the associated variable and encoding for use in .Xr dhcpcd-run-hooks 8 . Variables are prefixed with new_ and old_ unless the option number is -. Variables without an option are part of the DHCP message and cannot be directly requested. -.It Fl W, Fl Fl whitelist Ar address Ns Op /cidr +.It Fl W , Fl Fl whitelist Ar address Ns Op /cidr Only accept packets from .Ar address Ns Op /cidr . -.Fl X, Fl Fl blacklist +.Fl X , Fl Fl blacklist is ignored if -.Fl W, Fl Fl whitelist +.Fl W , Fl Fl whitelist is set. -.It Fl X, Fl Fl blacklist Ar address Ns Op Ar /cidr +.It Fl X , Fl Fl blacklist Ar address Ns Op Ar /cidr Ignore all packets from .Ar address Ns Op Ar /cidr . .It Fl Z , Fl Fl denyinterfaces Ar pattern @@ -774,6 +771,7 @@ If you always use the same options, put them here. .It Pa @SCRIPT@ Bourne shell script that is run to configure or de-configure an interface. .It Pa @LIBDIR@/dhcpcd/dev +Linux .Pa /dev management modules. .It Pa @HOOKDIR@ @@ -788,11 +786,11 @@ Text file that holds a secret key known only to the host. .It Pa @DBDIR@/ Ns Ar interface Ns Ar -ssid Ns .lease The actual DHCP message sent by the server. We use this when reading the last -lease and use the files mtime as when it was issued. +lease and use the file's mtime as when it was issued. .It Pa @DBDIR@/ Ns Ar interface Ns Ar -ssid Ns .lease6 The actual DHCPv6 message sent by the server. We use this when reading the last -lease and use the files mtime as when it was issued. +lease and use the file's mtime as when it was issued. .It Pa @DBDIR@/rdm_monotonic Stores the monotonic counter used in the .Ar replay @@ -821,7 +819,7 @@ Control socket to per interface daemon. .Xr dhcpcd-run-hooks 8 , .Xr resolvconf 8 .Sh STANDARDS -RFC\ 951, RFC\ 1534, RFC\ 2104, RFC\ 2131, RFC\ 2132, RFC\ 2563, RFC\ 2855, +RFC\ 951, RFC\ 1534, RFC\ 2104, RFC\ 2131, RFC\ 2132, RFC\ 2563, RFC\ 2855, RFC\ 3004, RFC\ 3118, RFC\ 3203, RFC\ 3315, RFC\ 3361, RFC\ 3633, RFC\ 3396, RFC\ 3397, RFC\ 3442, RFC\ 3495, RFC\ 3925, RFC\ 3927, RFC\ 4039, RFC\ 4075, RFC\ 4242, RFC\ 4361, RFC\ 4390, RFC\ 4702, RFC\ 4074, RFC\ 4861, RFC\ 4833, diff --git a/contrib/dhcpcd/src/dhcpcd.c b/contrib/dhcpcd/src/dhcpcd.c index c572c1370f..6d361e49a3 100644 --- a/contrib/dhcpcd/src/dhcpcd.c +++ b/contrib/dhcpcd/src/dhcpcd.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -25,7 +26,7 @@ * SUCH DAMAGE. */ -const char dhcpcd_copyright[] = "Copyright (c) 2006-2018 Roy Marples"; +const char dhcpcd_copyright[] = "Copyright (c) 2006-2019 Roy Marples"; #include #include @@ -52,7 +53,9 @@ const char dhcpcd_copyright[] = "Copyright (c) 2006-2018 Roy Marples"; #include "common.h" #include "control.h" #include "dev.h" +#include "dhcp-common.h" #include "dhcpcd.h" +#include "dhcp.h" #include "dhcp6.h" #include "duid.h" #include "eloop.h" @@ -82,23 +85,29 @@ const int dhcpcd_signals[] = { const size_t dhcpcd_signals_len = __arraycount(dhcpcd_signals); #endif +#define IF_UPANDRUNNING(a) \ + (((a)->flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)) + static void usage(void) { -printf("usage: "PACKAGE"\t[-46ABbDdEGgHJKkLnPpqTVw]\n" +printf("usage: "PACKAGE"\t[-146ABbDdEGgHJKLMNPpqTV]\n" "\t\t[-C, --nohook hook] [-c, --script script]\n" "\t\t[-e, --env value] [-F, --fqdn FQDN] [-f, --config file]\n" "\t\t[-h, --hostname hostname] [-I, --clientid clientid]\n" - "\t\t[-i, --vendorclassid vendorclassid] [-l, --leasetime seconds]\n" - "\t\t[-m, --metric metric] [-O, --nooption option]\n" - "\t\t[-o, --option option] [-Q, --require option]\n" - "\t\t[-r, --request address] [-S, --static value]\n" - "\t\t[-s, --inform address[/cidr]] [-t, --timeout seconds]\n" - "\t\t[-u, --userclass class] [-v, --vendor code, value]\n" - "\t\t[-W, --whitelist address[/cidr]] [-y, --reboot seconds]\n" + "\t\t[-i, --vendorclassid vendorclassid] [-j, --logfile logfile]\n" + "\t\t[-l, --leasetime seconds] [-m, --metric metric]\n" + "\t\t[-O, --nooption option] [-o, --option option]\n" + "\t\t[-Q, --require option] [-r, --request address]\n" + "\t\t[-S, --static value]\n" + "\t\t[-s, --inform address[/cidr[/broadcast_address]]]\n [--inform6]" + "\t\t[-t, --timeout seconds] [-u, --userclass class]\n" + "\t\t[-v, --vendor code, value] [-W, --whitelist address[/cidr]] [-w]\n" + "\t\t[--waitip [4 | 6]] [-y, --reboot seconds]\n" "\t\t[-X, --blacklist address[/cidr]] [-Z, --denyinterfaces pattern]\n" - "\t\t[-z, --allowinterfaces pattern] [interface] [...]\n" + "\t\t[-z, --allowinterfaces pattern] [--inactive] [interface] [...]\n" + " "PACKAGE"\t-n, --rebind [interface]\n" " "PACKAGE"\t-k, --release [interface]\n" " "PACKAGE"\t-U, --dumplease interface\n" " "PACKAGE"\t--version\n" @@ -148,6 +157,7 @@ free_globals(struct dhcpcd_ctx *ctx) free(ctx->nd_opts); ctx->nd_opts = NULL; } +#ifdef DHCP6 if (ctx->dhcp6_opts) { for (opt = ctx->dhcp6_opts; ctx->dhcp6_opts_len > 0; @@ -157,6 +167,7 @@ free_globals(struct dhcpcd_ctx *ctx) ctx->dhcp6_opts = NULL; } #endif +#endif if (ctx->vivso) { for (opt = ctx->vivso; ctx->vivso_len > 0; @@ -202,18 +213,39 @@ int dhcpcd_ifafwaiting(const struct interface *ifp) { unsigned long long opts; + bool foundany = false; if (ifp->active != IF_ACTIVE_USER) return AF_MAX; +#define DHCPCD_WAITALL (DHCPCD_WAITIP4 | DHCPCD_WAITIP6) opts = ifp->options->options; - if (opts & DHCPCD_WAITIP4 && !ipv4_hasaddr(ifp)) - return AF_INET; - if (opts & DHCPCD_WAITIP6 && !ipv6_hasaddr(ifp)) - return AF_INET6; - if (opts & DHCPCD_WAITIP && - !(opts & (DHCPCD_WAITIP4 | DHCPCD_WAITIP6)) && - !ipv4_hasaddr(ifp) && !ipv6_hasaddr(ifp)) +#ifdef INET + if (opts & DHCPCD_WAITIP4 || + (opts & DHCPCD_WAITIP && !(opts & DHCPCD_WAITALL))) + { + bool foundaddr = ipv4_hasaddr(ifp); + + if (opts & DHCPCD_WAITIP4 && !foundaddr) + return AF_INET; + if (foundaddr) + foundany = true; + } +#endif +#ifdef INET6 + if (opts & DHCPCD_WAITIP6 || + (opts & DHCPCD_WAITIP && !(opts & DHCPCD_WAITALL))) + { + bool foundaddr = ipv6_hasaddr(ifp); + + if (opts & DHCPCD_WAITIP6 && !foundaddr) + return AF_INET; + if (foundaddr) + foundany = true; + } +#endif + + if (opts & DHCPCD_WAITIP && !(opts & DHCPCD_WAITALL) && !foundany) return AF_UNSPEC; return AF_MAX; } @@ -230,12 +262,16 @@ dhcpcd_afwaiting(const struct dhcpcd_ctx *ctx) opts = ctx->options; TAILQ_FOREACH(ifp, ctx->ifaces, next) { +#ifdef INET if (opts & (DHCPCD_WAITIP | DHCPCD_WAITIP4) && ipv4_hasaddr(ifp)) opts &= ~(DHCPCD_WAITIP | DHCPCD_WAITIP4); +#endif +#ifdef INET6 if (opts & (DHCPCD_WAITIP | DHCPCD_WAITIP6) && ipv6_hasaddr(ifp)) opts &= ~(DHCPCD_WAITIP | DHCPCD_WAITIP6); +#endif if (!(opts & DHCPCD_WAITOPTS)) break; } @@ -311,13 +347,6 @@ dhcpcd_daemonise(struct dhcpcd_ctx *ctx) return 0; } - /* Store the pid and routing message seq number so we can identify - * the last message successfully sent to the kernel. - * This allows us to ignore all messages we sent after forking - * and detaching. */ - ctx->ppid = getpid(); - ctx->pseq = ctx->sseq; - switch (pid = fork()) { case -1: logerr("%s: fork", __func__); @@ -429,11 +458,10 @@ configure_interface1(struct interface *ifp) ifo->options &= ~DHCPCD_ARP; if (!(ifp->flags & IFF_MULTICAST)) ifo->options &= ~DHCPCD_IPV6RS; - if (!(ifo->options & DHCPCD_INFORM)) + if (!(ifo->options & (DHCPCD_INFORM | DHCPCD_WANTDHCP))) ifo->options |= DHCPCD_STATIC; } - if (ifp->flags & IFF_NOARP || - !(ifo->options & DHCPCD_ARP) || + if (!(ifo->options & DHCPCD_ARP) || ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)) ifo->options &= ~DHCPCD_IPV4LL; @@ -448,6 +476,10 @@ configure_interface1(struct interface *ifp) ifo->options &= ~(DHCPCD_IPV6RS | DHCPCD_DHCP6 | DHCPCD_WAITIP6); + if (!(ifo->options & DHCPCD_IPV6RS)) + ifo->options &= + ~(DHCPCD_IPV6RA_AUTOCONF | DHCPCD_IPV6RA_REQRDNSS); + /* We want to setup INET6 on the interface as soon as possible. */ if (ifp->active == IF_ACTIVE_USER && ifo->options & DHCPCD_IPV6 && @@ -529,7 +561,7 @@ configure_interface1(struct interface *ifp) ifo->options |= DHCPCD_IAID; } -#ifdef INET6 +#ifdef DHCP6 if (ifo->ia_len == 0 && ifo->options & DHCPCD_IPV6 && ifp->name[0] != '\0') { @@ -624,25 +656,6 @@ configure_interface(struct interface *ifp, int argc, char **argv, } static void -dhcpcd_pollup(void *arg) -{ - struct interface *ifp = arg; - int carrier; - - carrier = if_carrier(ifp); /* will set ifp->flags */ - if (carrier == LINK_UP && !(ifp->flags & IFF_UP)) { - struct timespec tv; - - tv.tv_sec = 0; - tv.tv_nsec = IF_POLL_UP * NSEC_PER_MSEC; - eloop_timeout_add_tv(ifp->ctx->eloop, &tv, dhcpcd_pollup, ifp); - return; - } - - dhcpcd_handlecarrier(ifp->ctx, carrier, ifp->flags, ifp->name); -} - -static void dhcpcd_initstate2(struct interface *ifp, unsigned long long options) { struct if_options *ifo; @@ -695,49 +708,42 @@ dhcpcd_handlecarrier(struct dhcpcd_ctx *ctx, int carrier, unsigned int flags, !ifp->active) return; - switch(carrier) { - case LINK_UNKNOWN: - carrier = if_carrier(ifp); /* will set ifp->flags */ - break; - case LINK_UP: - /* we have a carrier! Still need to check for IFF_UP */ - if (flags & IFF_UP) + if (carrier == LINK_UNKNOWN) { + if (ifp->wireless) { + carrier = LINK_DOWN; ifp->flags = flags; - else { - /* So we need to poll for IFF_UP as there is no - * kernel notification when it's set. */ - dhcpcd_pollup(ifp); - return; - } - break; - default: + } else + carrier = if_carrier(ifp); + } else ifp->flags = flags; - } - - /* If we here, we don't need to poll for IFF_UP any longer - * if generated by a kernel event. */ - eloop_timeout_delete(ifp->ctx->eloop, dhcpcd_pollup, ifp); + if (carrier == LINK_UNKNOWN) + carrier = IF_UPANDRUNNING(ifp) ? LINK_UP : LINK_DOWN; - if (carrier == LINK_UNKNOWN) { - if (errno != ENOTTY && errno != ENXIO) { - /* Don't log an error if interface departed */ - logerr("%s: %s", ifp->name, __func__); - } - } else if (carrier == LINK_DOWN || (ifp->flags & IFF_UP) == 0) { + if (carrier == LINK_DOWN || (ifp->flags & IFF_UP) == 0) { if (ifp->carrier != LINK_DOWN) { if (ifp->carrier == LINK_UP) loginfox("%s: carrier lost", ifp->name); - ifp->carrier = LINK_DOWN; +#ifdef NOCARRIER_PRESERVE_IP + if (ifp->flags & IFF_UP) + ifp->carrier = LINK_DOWN_IFFUP; + else +#endif + ifp->carrier = LINK_DOWN; script_runreason(ifp, "NOCARRIER"); #ifdef NOCARRIER_PRESERVE_IP + if (ifp->flags & IFF_UP) { #ifdef ARP - arp_drop(ifp); + arp_drop(ifp); #endif - dhcp_abort(ifp); - ipv6nd_expire(ifp, 0); -#else - dhcpcd_drop(ifp, 0); +#ifdef INET + dhcp_abort(ifp); +#endif +#ifdef DHCP6 + dhcp6_abort(ifp); +#endif + } else #endif + dhcpcd_drop(ifp, 0); } } else if (carrier == LINK_UP && ifp->flags & IFF_UP) { if (ifp->carrier != LINK_UP) { @@ -751,30 +757,36 @@ dhcpcd_handlecarrier(struct dhcpcd_ctx *ctx, int carrier, unsigned int flags, #endif if (ifp->wireless) { uint8_t ossid[IF_SSIDLEN]; -#ifdef NOCARRIER_PRESERVE_IP size_t olen; olen = ifp->ssid_len; -#endif memcpy(ossid, ifp->ssid, ifp->ssid_len); if_getssid(ifp); -#ifdef NOCARRIER_PRESERVE_IP + /* If we changed SSID network, drop leases */ if (ifp->ssid_len != olen || memcmp(ifp->ssid, ossid, ifp->ssid_len)) + { +#ifdef NOCARRIER_PRESERVE_IP dhcpcd_drop(ifp, 0); #endif +#ifdef IPV4LL + ipv4ll_reset(ifp); +#endif + } } dhcpcd_initstate(ifp, 0); script_runreason(ifp, "CARRIER"); +#ifdef INET6 #ifdef NOCARRIER_PRESERVE_IP /* Set any IPv6 Routers we remembered to expire * faster than they would normally as we * maybe on a new network. */ - ipv6nd_expire(ifp, RTR_CARRIER_EXPIRE); + ipv6nd_startexpire(ifp); #endif /* RFC4941 Section 3.5 */ ipv6_gentempifid(ifp); +#endif dhcpcd_startinterface(ifp); } } @@ -819,7 +831,6 @@ dhcpcd_startinterface(void *arg) struct if_options *ifo = ifp->options; char buf[DUID_LEN * 3]; int carrier; - struct timespec tv; if (ifo->options & DHCPCD_LINK) { switch (ifp->carrier) { @@ -831,14 +842,22 @@ dhcpcd_startinterface(void *arg) case LINK_UNKNOWN: /* No media state available. * Loop until both IFF_UP and IFF_RUNNING are set */ - if ((carrier = if_carrier(ifp)) == LINK_UNKNOWN) { - tv.tv_sec = 0; - tv.tv_nsec = IF_POLL_UP * NSEC_PER_MSEC; - eloop_timeout_add_tv(ifp->ctx->eloop, - &tv, dhcpcd_startinterface, ifp); - } else - dhcpcd_handlecarrier(ifp->ctx, carrier, - ifp->flags, ifp->name); + carrier = if_carrier(ifp); + if (carrier == LINK_UNKNOWN) { + if (IF_UPANDRUNNING(ifp)) + carrier = LINK_UP; + else { + struct timespec tv; + + tv.tv_sec = 0; + tv.tv_nsec = IF_POLL_UP * NSEC_PER_MSEC; + eloop_timeout_add_tv(ifp->ctx->eloop, + &tv, dhcpcd_startinterface, ifp); + return; + } + } + dhcpcd_handlecarrier(ifp->ctx, carrier, + ifp->flags, ifp->name); return; } } @@ -880,10 +899,12 @@ dhcpcd_startinterface(void *arg) #endif } +#ifdef INET6 if (ifo->options & DHCPCD_IPV6 && ipv6_start(ifp) == -1) { logerr("%s: ipv6_start", ifp->name); ifo->options &= ~DHCPCD_IPV6; } + if (ifo->options & DHCPCD_IPV6) { if (ifp->active == IF_ACTIVE_USER) { ipv6_startstatic(ifp); @@ -892,7 +913,7 @@ dhcpcd_startinterface(void *arg) ipv6nd_startrs(ifp); } - +#ifdef DHCP6 if (ifo->options & DHCPCD_DHCP6) { dhcp6_find_delegates(ifp); @@ -909,7 +930,9 @@ dhcpcd_startinterface(void *arg) logerr("%s: dhcp6_start", ifp->name); } } +#endif } +#endif #ifdef INET if (ifo->options & DHCPCD_IPV4 && ifp->active == IF_ACTIVE_USER) { @@ -930,20 +953,6 @@ dhcpcd_prestartinterface(void *arg) if_up(ifp) == -1) logerr("%s: %s", __func__, ifp->name); - if (ifp->options->options & DHCPCD_LINK && - ifp->carrier == LINK_UNKNOWN) - { - int carrier; - - if ((carrier = if_carrier(ifp)) != LINK_UNKNOWN) { - dhcpcd_handlecarrier(ifp->ctx, carrier, - ifp->flags, ifp->name); - return; - } - loginfox("%s: unknown carrier, waiting for interface flags", - ifp->name); - } - dhcpcd_startinterface(ifp); } @@ -986,6 +995,7 @@ dhcpcd_handleinterface(void *arg, int action, const char *ifname) struct if_head *ifs; struct interface *ifp, *iff; const char * const argv[] = { ifname }; + int e; ctx = arg; if (action == -1) { @@ -1009,13 +1019,17 @@ dhcpcd_handleinterface(void *arg, int action, const char *ifname) logerr(__func__); return -1; } + ifp = if_find(ifs, ifname); if (ifp == NULL) { /* This can happen if an interface is quickly added * and then removed. */ errno = ENOENT; - return -1; + e = -1; + goto out; } + e = 1; + /* Check if we already have the interface */ iff = if_find(ctx->ifaces, ifp->name); @@ -1044,6 +1058,7 @@ dhcpcd_handleinterface(void *arg, int action, const char *ifname) dhcpcd_prestartinterface(iff); } +out: /* Free our discovered list */ while ((ifp = TAILQ_FIRST(ifs))) { TAILQ_REMOVE(ifs, ifp, next); @@ -1051,7 +1066,7 @@ dhcpcd_handleinterface(void *arg, int action, const char *ifname) } free(ifs); - return 1; + return e; } static void @@ -1064,7 +1079,8 @@ dhcpcd_handlelink(void *arg) dhcpcd_linkoverflow(ctx); return; } - logerr(__func__); + if (errno != ENOTSUP) + logerr(__func__); } } @@ -1076,6 +1092,22 @@ dhcpcd_checkcarrier(void *arg) dhcpcd_handlecarrier(ifp->ctx, LINK_UNKNOWN, ifp->flags, ifp->name); } +#ifndef SMALL +static void +dhcpcd_setlinkrcvbuf(struct dhcpcd_ctx *ctx) +{ + socklen_t socklen; + + if (ctx->link_rcvbuf == 0) + return; + + socklen = sizeof(ctx->link_rcvbuf); + if (setsockopt(ctx->link_fd, SOL_SOCKET, + SO_RCVBUF, &ctx->link_rcvbuf, socklen) == -1) + logerr(__func__); +} +#endif + void dhcpcd_linkoverflow(struct dhcpcd_ctx *ctx) { @@ -1096,10 +1128,17 @@ dhcpcd_linkoverflow(struct dhcpcd_ctx *ctx) eloop_exit(ctx->eloop, EXIT_FAILURE); return; } +#ifndef SMALL + dhcpcd_setlinkrcvbuf(ctx); +#endif eloop_event_add(ctx->eloop, ctx->link_fd, dhcpcd_handlelink, ctx); /* Work out the current interfaces. */ ifaces = if_discover(ctx, &ifaddrs, ctx->ifc, ctx->ifv); + if (ifaces == NULL) { + logerr(__func__); + return; + } /* Punt departed interfaces */ TAILQ_FOREACH_SAFE(ifp, ctx->ifaces, next, ifn) { @@ -1109,21 +1148,23 @@ dhcpcd_linkoverflow(struct dhcpcd_ctx *ctx) } /* Add new interfaces */ - TAILQ_FOREACH_SAFE(ifp, ifaces, next, ifn) { + while ((ifp = TAILQ_FIRST(ifaces)) != NULL ) { + TAILQ_REMOVE(ifaces, ifp, next); ifp1 = if_find(ctx->ifaces, ifp->name); if (ifp1 != NULL) { /* If the interface already exists, * check carrier state. */ eloop_timeout_add_sec(ctx->eloop, 0, dhcpcd_checkcarrier, ifp1); + if_free(ifp); continue; } - TAILQ_REMOVE(ifaces, ifp, next); TAILQ_INSERT_TAIL(ctx->ifaces, ifp, next); if (ifp->active) eloop_timeout_add_sec(ctx->eloop, 0, dhcpcd_prestartinterface, ifp); } + free(ifaces); /* Update address state. */ if_markaddrsstale(ctx->ifaces); @@ -1163,13 +1204,19 @@ dhcpcd_handlehwaddr(struct dhcpcd_ctx *ctx, const char *ifname, static void if_reboot(struct interface *ifp, int argc, char **argv) { +#ifdef INET unsigned long long oldopts; oldopts = ifp->options->options; +#endif script_runreason(ifp, "RECONFIGURE"); dhcpcd_initstate1(ifp, argc, argv, 0); +#ifdef INET dhcp_reboot_newopts(ifp, oldopts); +#endif +#ifdef DHCP6 dhcp6_reboot(ifp); +#endif dhcpcd_prestartinterface(ifp); } @@ -1207,8 +1254,10 @@ reconf_reboot(struct dhcpcd_ctx *ctx, int action, int argc, char **argv, int oi) if (ifp->active == IF_ACTIVE_USER) { if (action) if_reboot(ifp, argc, argv); +#ifdef INET else ipv4_applyaddr(ifp); +#endif } else if (i != argc) { ifp->active = IF_ACTIVE_USER; dhcpcd_initstate1(ifp, argc, argv, 0); @@ -1247,11 +1296,17 @@ dhcpcd_ifrenew(struct interface *ifp) ifp->carrier == LINK_DOWN) return; +#ifdef INET dhcp_renew(ifp); +#endif +#ifdef INET6 #define DHCPCD_RARENEW (DHCPCD_IPV6 | DHCPCD_IPV6RS) if ((ifp->options->options & DHCPCD_RARENEW) == DHCPCD_RARENEW) ipv6nd_startrs(ifp); +#endif +#ifdef DHCP6 dhcp6_renew(ifp); +#endif } static void @@ -1333,16 +1388,24 @@ dhcpcd_getinterfaces(void *arg) if (!ifp->active) continue; len++; +#ifdef INET if (D_STATE_RUNNING(ifp)) len++; +#endif +#ifdef IPV4LL if (IPV4LL_STATE_RUNNING(ifp)) len++; +#endif +#ifdef INET6 if (IPV6_STATE_RUNNING(ifp)) len++; if (RS_STATE_RUNNING(ifp)) len++; +#endif +#ifdef DHCP6 if (D6_STATE_RUNNING(ifp)) len++; +#endif } if (write(fd->fd, &len, sizeof(len)) != sizeof(len)) return; @@ -1371,10 +1434,10 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd, * write callback on the fd */ if (strcmp(*argv, "--version") == 0) { return control_queue(fd, UNCONST(VERSION), - strlen(VERSION) + 1, 0); + strlen(VERSION) + 1, false); } else if (strcmp(*argv, "--getconfigfile") == 0) { return control_queue(fd, UNCONST(fd->ctx->cffile), - strlen(fd->ctx->cffile) + 1, 0); + strlen(fd->ctx->cffile) + 1, false); } else if (strcmp(*argv, "--getinterfaces") == 0) { eloop_event_add_w(fd->ctx->eloop, fd->fd, dhcpcd_getinterfaces, fd); @@ -1535,9 +1598,6 @@ main(int argc, char **argv) ctx.cffile = CONFIG; ctx.control_fd = ctx.control_unpriv_fd = ctx.link_fd = -1; ctx.pf_inet_fd = -1; -#ifdef IFLR_ACTIVE - ctx.pf_link_fd = -1; -#endif TAILQ_INIT(&ctx.control_fds); #ifdef PLUGIN_DEV @@ -1661,9 +1721,11 @@ main(int argc, char **argv) printf("\nND options:\n"); ipv6nd_printoptions(&ctx, ifo->nd_override, ifo->nd_override_len); +#ifdef DHCP6 printf("\nDHCPv6 options:\n"); dhcp6_printoptions(&ctx, ifo->dhcp6_override, ifo->dhcp6_override_len); +#endif } #endif goto exit_success; @@ -1790,12 +1852,22 @@ printpidfile: configure_interface(ifp, ctx.argc, ctx.argv, 0); i = 0; if (family == 0 || family == AF_INET) { +#ifdef INET if (dhcp_dump(ifp) == -1) i = -1; +#else + if (family == AF_INET) + logerrx("No INET support"); +#endif } if (family == 0 || family == AF_INET6) { +#ifdef DHCP6 if (dhcp6_dump(ifp) == -1) i = -1; +#else + if (family == AF_INET6) + logerrx("No DHCP6 support"); +#endif } if (i == -1) goto exit_failure; @@ -1912,6 +1984,9 @@ printpidfile: logerr("%s: if_opensockets", __func__); goto exit_failure; } +#ifndef SMALL + dhcpcd_setlinkrcvbuf(&ctx); +#endif /* When running dhcpcd against a single interface, we need to retain * the old behaviour of waiting for an IP address */ @@ -2011,7 +2086,6 @@ printpidfile: free_options(&ctx, ifo); ifo = NULL; - if_sortinterfaces(&ctx); TAILQ_FOREACH(ifp, ctx.ifaces, next) { if (ifp->active) eloop_timeout_add_sec(ctx.eloop, 0, @@ -2046,6 +2120,12 @@ exit1: free(ctx.ifaces); } free_options(&ctx, ifo); +#ifdef HAVE_OPEN_MEMSTREAM + if (ctx.script_fp) + fclose(ctx.script_fp); +#endif + free(ctx.script_buf); + free(ctx.script_env); rt_dispose(&ctx); free(ctx.duid); if (ctx.link_fd != -1) { @@ -2054,10 +2134,11 @@ exit1: } if_closesockets(&ctx); free_globals(&ctx); +#ifdef INET6 ipv6_ctxfree(&ctx); +#endif dev_stop(&ctx); eloop_free(ctx.eloop); - free(ctx.iov[0].iov_base); if (ctx.options & DHCPCD_STARTED && !(ctx.options & DHCPCD_FORKED)) loginfox(PACKAGE " exited"); diff --git a/contrib/dhcpcd/src/dhcpcd.conf.5.in b/contrib/dhcpcd/src/dhcpcd.conf.5.in index c3e01d6b90..cf8036359c 100644 --- a/contrib/dhcpcd/src/dhcpcd.conf.5.in +++ b/contrib/dhcpcd/src/dhcpcd.conf.5.in @@ -1,4 +1,6 @@ -.\" Copyright (c) 2006-2018 Roy Marples +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.\" Copyright (c) 2006-2019 Roy Marples .\" All rights reserved .\" .\" Redistribution and use in source and binary forms, with or without @@ -22,7 +24,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd June 5, 2018 +.Dd August 28, 2019 .Dt DHCPCD.CONF 5 .Os .Sh NAME @@ -61,7 +63,7 @@ which is a space or comma separated list of patterns passed to .Nm dhcpcd will arping each address in order before attempting DHCP. If an address is found, we will select the replying hardware address as the -profile, otherwise the ip address. +profile, otherwise the IP address. Example: .Pp .D1 interface bge0 @@ -110,7 +112,7 @@ then it means that .Nm dhcpcd could not find the correct authentication token in your configuration. .It Ic background -Background immediately. +Fork to the background immediately. This is useful for startup scripts which don't disable link messages for carrier status. .It Ic blacklist Ar address Ns Op /cidr @@ -130,7 +132,7 @@ interact with a BOOTP server. All other DHCP options still work. .It Ic broadcast Instructs the DHCP server to broadcast replies back to the client. -Normally this is only set for non Ethernet interfaces, +Normally this is only set for non-Ethernet interfaces, such as FireWire and InfiniBand. In most cases, .Nm dhcpcd @@ -164,7 +166,7 @@ should use with .Va wpa_supplicant_driver=nl80211 .Pp If the hostname is set, it will be will set to the FQDN if possible as per -RFC 4702 section 3.1. +RFC 4702, section 3.1. If the FQDN option is missing, .Nm dhcpcd will still try and set a FQDN from the hostname and domain options for @@ -172,7 +174,9 @@ consistency. To override this, set .Ic env .Va hostname_fqdn=[YES|NO|SERVER] . -A value of server means just what the server says, don't manipulate it. +A value of +.Va SERVER +means just what the server says, don't manipulate it. This could lead to an inconsistent hostname on a DHCPv4 and DHCPv6 network where the DHCPv4 hostname is short and the DHCPv6 has an FQDN. DHCPv6 has no hostname option. @@ -252,11 +256,11 @@ back on IPv4LL. .It Ic inform6 Performs a DHCPv6 Information Request. No address is requested or specified, but all other DHCPv6 options are allowed. -This is normally performed automatically when the IPv6 Router Advertises -that the client should perform this operation. +This is normally performed automatically when an IPv6 Router Advertisement +indicates that the client should perform this operation. This option is only needed when .Nm dhcpcd -is not processing IPv6RA messages and the need for DHCPv6 Information Request +is not processing IPv6 RA messages and the need for a DHCPv6 Information Request exists. .It Ic persistent .Nm dhcpcd @@ -266,7 +270,7 @@ NFS or SSH clients connect to this host and they need to be notified of the host shutting down. You can use this option to stop this from happening. .It Ic fallback Ar profile -Fallback to using this profile if DHCP fails. +Fall back to using this profile if DHCP fails. This allows you to configure a static profile instead of using ZeroConf. .It Ic hostname Ar name Sends the hostname @@ -277,7 +281,7 @@ If is an empty string then the current system hostname is sent. If .Ar name -is a FQDN (ie, contains a .) then it will be encoded as such. +is a FQDN (i.e., contains a .) then it will be encoded as such. .It Ic hostname_short Sends the short hostname to the DHCP server instead of the FQDN. This is useful because DHCP servers will not register the FQDN in their @@ -327,10 +331,10 @@ Each assigned address will have a defaulting to 1. If the .Ar suffix -is 0 then a slaac address is assigned. +is 0 then a SLAAC address is assigned. You cannot assign a prefix to the requesting interface unless the -DHCPv6 server supports -.Li RFC6603 +DHCPv6 server supports the +.Li RFC 6603 Prefix Exclude Option. .Nm dhcpcd has to be running for all the interfaces it is delegating to. @@ -374,8 +378,7 @@ IPv6 address configuration for the internal network. noipv6rs # disable routing solicitation denyinterfaces eth2 # Don't touch eth2 at all interface eth0 - ipv6rs # enable routing solicitation get the - # default IPv6 route + ipv6rs # enable routing solicitation for eth0 ia_na 1 # request an IPv6 address ia_pd 2 eth1/0 # request a PD and assign it to eth1 ia_pd 3 eth2/1 eth3/2 # req a PD and assign it to eth2 and eth3 @@ -385,21 +388,27 @@ Only configure IPv4. .It Ic ipv6only Only configure IPv6. .It Ic fqdn Op disable | none | ptr | both -none will not ask the DHCP server to update DNS. -ptr just asks the DHCP server to update the PTR -record of the host in DNS whereas both also updates the A record. -disable will disable the FQDN option. -The default is both. +.Ar none +will not ask the DHCP server to update DNS. +.Ar ptr +just asks the DHCP server to update the PTR +record of the host in DNS, whereas +.Ar both +also updates the A record. +.Ar disable +will disable the FQDN option. +The default is +.Ar both . .Nm dhcpcd itself never does any DNS updates. .Nm dhcpcd encodes the FQDN hostname as specified in -.Li RFC1035 . +.Li RFC 1035 . .It Ic interface Ar interface Subsequent options are only parsed for this .Ar interface . .It Ic ipv6ra_autoconf -Generate SLAAC addresses for each Prefix advertised by a +Generate SLAAC addresses for each Prefix advertised by an IPv6 Router Advertisement message with the Auto flag set. On by default. .It Ic ipv6ra_noautoconf @@ -407,7 +416,7 @@ Disables the above option. .It Ic ipv6ra_fork By default, when .Nm dhcpcd -receives an IPv6 RA, +receives an IPv6 Router Advertisement, .Nm dhcpcd will only fork to the background if the RA contains at least one unexpired RDNSS option and a valid prefix or no DHCPv6 instruction. @@ -421,6 +430,12 @@ globally but needs to be enabled for one interface. .It Ic leasetime Ar seconds Request a leasetime of .Ar seconds . +.It Ic link_rcvbuf Ar size +Override the size of the link receive buffer from the kernel default. +While +.Nm dhcpcd +will recover from link buffer overflows, +this may not be desirable on heavily loaded systems. .It Ic logfile Ar logfile Writes to the specified .Ar logfile @@ -440,13 +455,13 @@ will supply a default metric of 200 + .Xr if_nametoindex 3 . An extra 100 will be added for wireless interfaces. .It Ic mudurl Ar url -Specifies the URL for a manufacturer usage description (MUD). +Specifies the URL for a Manufacturer Usage Description (MUD). The description is used by upstream network devices to instantiate any desired access lists. See draft-ietf-opsawg-mud for more information. .It Ic noalias -Any pre-existing IPv4 addresses existing address will be removed from the -interface when adding a new IPv4 address. +Any pre-existing IPv4 addresses will be removed from the interface when +adding a new IPv4 address. .It Ic noarp Don't send any ARP requests. This also disables IPv4LL. @@ -464,7 +479,8 @@ Don't start DHCP or listen to DHCP messages. This is only useful when allowing IPv4LL. .It Ic nodhcp6 Don't start DHCPv6 or listen to DHCPv6 messages. -Normally DHCPv6 is started by a RA instruction or configuration. +Normally DHCPv6 is started by an IPv6 Router Advertisement instruction or +configuration. .It Ic nogateway Don't install any default routes. .It Ic gateway @@ -513,7 +529,6 @@ You can specify more separated by commas, spaces or more .Ic option lines. -.Ar option Prepend dhcp6_ to .Ar option to request a DHCPv6 option. @@ -545,7 +560,7 @@ To enforce that only responds to DHCP servers and not BOOTP servers, you can .Ic require .Ar dhcp_message_type . -This isn't an exact science though because a BOOTP server can send DHCP like +This isn't an exact science though because a BOOTP server can send DHCP-like options. .It Ic reject Ar option Reject a message that contains the @@ -594,7 +609,7 @@ Subsequent options are only parsed for this wireless Selects the interface identifier used for SLAAC generated IPv6 addresses. If .Ar private -is used, a RFC7217 address is generated. +is used, an RFC 7217 address is generated. .It Ic static Ar value Configures a static .Ar value . @@ -602,12 +617,12 @@ If you set .Ic ip_address then .Nm dhcpcd -will not attempt to obtain a lease and just use the value for the address with -an infinite lease time. +will not attempt to obtain a lease and will just use the value for the address +with an infinite lease time. If you set .Ic ip6_address , .Nm dhcpcd -will continue auto-configuation as normal. +will continue auto-configuration as normal. .Pp Here is an example which configures two static address, overriding the default IPv4 broadcast address, an IPv4 router, DNS and disables IPv6 auto-configuration. @@ -626,13 +641,15 @@ option instead of setting a static address. .D1 static domain_name_servers=192.168.0.1 fd51:42f8:caae:d92e::1 .Pp Here is an example for PPP which gives the destination a default route. -It uses the special destination keyword to insert the destination address +It uses the special +.Ar destination +keyword to insert the destination address into the value. .D1 interface ppp0 .D1 static ip_address= .D1 destination routers .It Ic timeout Ar seconds -Timeout after +Time out after .Ar seconds , instead of the default 30. A setting of 0 @@ -673,7 +690,7 @@ Set un-encapsulated vendor option to hello world. .D1 vendor ,"hello world" .It Ic vendorclassid Ar string Set the DHCP Vendor Class. -DHCPv6 has it's own option as shown below. +DHCPv6 has its own option as shown below. The default is dhcpcd-:::. For example @@ -688,9 +705,7 @@ Number with the .Ar data . This option can be set more than once to add more data, but the behaviour, -as per -.Xr RFC 3925 -is undefined if the Enterprise Number differs. +as per RFC 3925 is undefined if the Enterprise Number differs. .It Ic waitip Op 4 | 6 Wait for an address to be assigned before forking to the background. 4 means wait for an IPv4 address to be assigned. @@ -706,12 +721,14 @@ Use the last four bytes of the hardware address as the DHCP xid instead of a randomly generated number. .El .Ss Defining new options -DHCP, ND and DHCPv6 allow for the use of custom options. +DHCP, ND and DHCPv6 allow for the use of custom options, and RFC 3925 vendor +options for DHCP can also be supplied. Each option needs to be started with the .Ic define , -.If definend -or +.Ic definend, .Ic define6 +or +.Ic vendopt directive. This can optionally be followed by both .Ic embed @@ -797,12 +814,12 @@ You can use more than one, but they must appear in the order listed below. .Bl -tag -width -indent .It Ic request Requests the option by default without having to be specified in user -configuration +configuration. .It Ic norequest -This option cannot be requested, regardless of user configuration +This option cannot be requested, regardless of user configuration. .It Ic optional This option is optional. -Only makes sense for embedded options where like the client FQDN option where +Only makes sense for embedded options like the client FQDN option, where the FQDN string itself is optional. .It Ic index The option can appear more than once and will be indexed. @@ -842,9 +859,9 @@ An unsigned 32bit integer, 4 bytes. .It Ic flag A fixed value (1) to indicate that the option is present, 0 bytes. .It Ic domain -A RFC 3397 encoded string. +An RFC 3397 encoded string. .It Ic dname -A RFC 1035 validated string. +An RFC 1035 validated string. .It Ic binhex Op : Ic length Binary data expressed as hexadecimal. .It Ic embed @@ -855,14 +872,14 @@ Contains encapsulated options (implies embed as well). References an option from the global definition. .El .Ss Example definition -.D1 # DHCP option 81, Fully Qualified Domain Name, RFC4702 +.D1 # DHCP option 81, Fully Qualified Domain Name, RFC 4702 .D1 define 81 embed fqdn .D1 embed byte flags .D1 embed byte rcode1 .D1 embed byte rcode2 .D1 embed domain fqdn .Pp -.D1 # DHCP option 125, Vendor Specific Information Option, RFC3925 +.D1 # DHCP option 125, Vendor Specific Information Option, RFC 3925 .D1 define 125 encap vsio .D1 embed uint32 enterprise_number .D1 # Options defined for the enterprise number @@ -872,8 +889,10 @@ References an option from the global definition. .It Ic token Sends a plain text token the server expects and matches a token sent by the server. -The tokens to not have to be the same. -If unspecified, the token with secretid of 0 will be used in sending messages +The tokens do not have to be the same. +If unspecified, the token with a +.Ar secretid +of 0 will be used in sending messages and validating received messages. .It Ic delayedrealm Delayed Authentication. @@ -886,7 +905,10 @@ and .Ar secretid in it. .Nm dhcpcd -will then look for a non-expired token with a matching realm and secretid. +will then look for an unexpired token with a matching +.Ar realm +and +.Ar secretid . This token is used to authenticate all other messages. .It Ic delayed Same as above, but without a realm. @@ -903,8 +925,8 @@ If none specified, .Ic monotonic is the default. If this is changed from what was previously used, -or the means of calculating or storing it is broken then the DHCP server -will probably have to have its notion of the clients Replay Detection Value +or the means of calculating or storing it is broken, then the DHCP server +will probably have to have its notion of the client's Replay Detection Value reset. .Bl -tag -width -indent .It Ic monocounter @@ -912,7 +934,7 @@ Read the number in the file .Pa @DBDIR@/dhcpcd-rdm.monotonic and add one to it. .It Ic monotime -Create a NTP timestamp from the system time. +Create an NTP timestamp from the system time. .It Ic monotonic Same as .Ic monotime . diff --git a/contrib/dhcpcd/src/dhcpcd.h b/contrib/dhcpcd/src/dhcpcd.h index 4f1c2101d5..b28cc7cf29 100644 --- a/contrib/dhcpcd/src/dhcpcd.h +++ b/contrib/dhcpcd/src/dhcpcd.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -49,9 +50,10 @@ #define IF_ACTIVE 1 #define IF_ACTIVE_USER 2 -#define LINK_UP 1 -#define LINK_UNKNOWN 0 -#define LINK_DOWN -1 +#define LINK_UP 1 +#define LINK_UNKNOWN 0 +#define LINK_DOWN -1 +#define LINK_DOWN_IFFUP -2 #define IF_DATA_IPV4 0 #define IF_DATA_ARP 1 @@ -84,8 +86,8 @@ struct interface { unsigned short vlanid; unsigned int metric; int carrier; - int wireless; - uint8_t ssid[IF_SSIDLEN + 1]; /* NULL terminated */ + bool wireless; + uint8_t ssid[IF_SSIDLEN]; unsigned int ssid_len; char profile[PROFILE_LEN]; @@ -135,25 +137,34 @@ struct dhcpcd_ctx { size_t duid_len; struct if_head *ifaces; - struct rt_head routes; /* our routes */ - struct rt_head kroutes; /* all kernel routes */ - struct rt_head froutes; /* free routes for re-use */ + rb_tree_t routes; /* our routes */ +#ifdef RT_FREE_ROUTE_TABLE + rb_tree_t froutes; /* free routes for re-use */ +#endif + size_t rt_order; /* route order storage */ int pf_inet_fd; -#ifdef IFLR_ACTIVE - int pf_link_fd; -#endif void *priv; int link_fd; +#ifndef SMALL + int link_rcvbuf; +#endif int seq; /* route message sequence no */ int sseq; /* successful seq no sent */ - struct iovec iov[1]; /* generic iovec buffer */ #ifdef USE_SIGNALS sigset_t sigset; #endif struct eloop *eloop; +#ifdef HAVE_OPEN_MEMSTREAM + FILE *script_fp; +#endif + char *script_buf; + size_t script_buflen; + char **script_env; + size_t script_envlen; + int control_fd; int control_unpriv_fd; struct fd_list_head control_fds; @@ -166,13 +177,6 @@ struct dhcpcd_ctx { char *randomstate; /* original state */ - /* Used to track the last routing message, - * so we can ignore messages the parent process sent - * but the child receives when forking. - * getppid(2) is unreliable because we detach. */ - pid_t ppid; /* parent pid */ - int pseq; /* last seq in parent */ - #ifdef INET struct dhcp_opt *dhcp_opts; size_t dhcp_opts_len; @@ -189,24 +193,19 @@ struct dhcpcd_ctx { uint8_t *secret; size_t secret_len; - unsigned char ctlbuf[IP6BUFLEN]; - struct sockaddr_in6 from; - struct msghdr sndhdr; - struct iovec sndiov[1]; - unsigned char sndbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; - struct msghdr rcvhdr; - char ntopbuf[INET6_ADDRSTRLEN]; - const char *sfrom; - +#ifndef __sun int nd_fd; +#endif struct ra_head *ra_routers; int dhcp6_fd; struct dhcp_opt *nd_opts; size_t nd_opts_len; +#ifdef DHCP6 struct dhcp_opt *dhcp6_opts; size_t dhcp6_opts_len; +#endif #ifndef __linux__ int ra_global; diff --git a/contrib/dhcpcd/src/duid.c b/contrib/dhcpcd/src/duid.c index cc53f9bdc5..d6c2b498e5 100644 --- a/contrib/dhcpcd/src/duid.c +++ b/contrib/dhcpcd/src/duid.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -51,10 +52,6 @@ #include #include -#ifndef ARPHRD_NETROM -# define ARPHRD_NETROM 0 -#endif - #include "common.h" #include "dhcpcd.h" #include "duid.h" @@ -88,8 +85,9 @@ duid_machineuuid(char *uuid, size_t uuid_len) } len = strlen(uuid) + 1; fclose(fp); - r = 0; + r = len == 1 ? -1 : 0; #else + UNUSED(uuid); r = -1; errno = ENOSYS; #endif @@ -187,10 +185,10 @@ duid_get(uint8_t **d, const struct interface *ifp) return len; /* No UUID? OK, lets make one based on our interface */ - if (ifp->family == ARPHRD_NETROM) { - logwarnx("%s: is a NET/ROM pseudo interface", ifp->name); + if (ifp->hwlen == 0) { + logwarnx("%s: does not have hardware address", ifp->name); TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { - if (ifp2->family != ARPHRD_NETROM) + if (ifp2->hwlen != 0) break; } if (ifp2) { diff --git a/contrib/dhcpcd/src/duid.h b/contrib/dhcpcd/src/duid.h index 05857844f7..23a714f09a 100644 --- a/contrib/dhcpcd/src/duid.h +++ b/contrib/dhcpcd/src/duid.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon * Copyright (c) 2006-2015 Roy Marples diff --git a/contrib/dhcpcd/src/eloop.c b/contrib/dhcpcd/src/eloop.c index 4d744821e4..e8be9a34ac 100644 --- a/contrib/dhcpcd/src/eloop.c +++ b/contrib/dhcpcd/src/eloop.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * eloop - portable event based main loop. - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved. * Redistribution and use in source and binary forms, with or without diff --git a/contrib/dhcpcd/src/eloop.h b/contrib/dhcpcd/src/eloop.h index cbacc90417..a98d57ed7e 100644 --- a/contrib/dhcpcd/src/eloop.h +++ b/contrib/dhcpcd/src/eloop.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without diff --git a/contrib/dhcpcd/src/genembedc b/contrib/dhcpcd/src/genembedc deleted file mode 100755 index 27b5853994..0000000000 --- a/contrib/dhcpcd/src/genembedc +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh -set -e - -: ${TOOL_CAT:=cat} -: ${TOOL_SED:=sed} -CONF=${1:-dhcpcd-definitions.conf} -C=${2:-dhcpcd-embedded.c.in} - -$TOOL_CAT $C -$TOOL_SED \ - -e 's/#.*$//' \ - -e '/^$/d' \ - -e 's/^/"/g' \ - -e 's/$/\",/g' \ - -e 's/ [ ]*/ /g' \ - -e 's/ [ ]*/ /g' \ - $CONF -printf "%s\n%s\n" "NULL" "};" diff --git a/contrib/dhcpcd/src/genembedh b/contrib/dhcpcd/src/genembedh deleted file mode 100755 index cd97cbb44d..0000000000 --- a/contrib/dhcpcd/src/genembedh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -set -e - -: ${TOOL_SED:=sed} -: ${TOOL_GREP:=grep} -: ${TOOL_WC:=wc} -CONF=${1:-dhcpcd-definitions.conf} -H=${2:-dhcpcd-embedded.h.in} - -INITDEFINES=$($TOOL_GREP "^define " $CONF | $TOOL_WC -l) -INITDEFINENDS=$($TOOL_GREP "^definend " $CONF | $TOOL_WC -l) -INITDEFINE6S=$($TOOL_GREP "^define6 " $CONF | $TOOL_WC -l) -$TOOL_SED \ - -e "s/@INITDEFINES@/$INITDEFINES/" \ - -e "s/@INITDEFINENDS@/$INITDEFINENDS/" \ - -e "s/@INITDEFINE6S@/$INITDEFINE6S/" \ - $H diff --git a/contrib/dhcpcd/src/if-bsd.c b/contrib/dhcpcd/src/if-bsd.c index cdd959a6a4..8348d42dcf 100644 --- a/contrib/dhcpcd/src/if-bsd.c +++ b/contrib/dhcpcd/src/if-bsd.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * BSD interface driver for dhcpcd - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -50,6 +51,8 @@ #include #ifdef __NetBSD__ #include /* Needs netinet/if_ether.h */ +#elif defined(__DragonFly__) +#include #else #include #endif @@ -99,14 +102,20 @@ #endif #ifdef INET6 -static void -ifa_scope(struct sockaddr_in6 *, unsigned int); +static void ifa_setscope(struct sockaddr_in6 *, unsigned int); +static unsigned int ifa_getscope(const struct sockaddr_in6 *); #endif struct priv { int pf_inet6_fd; }; +struct rtm +{ + struct rt_msghdr hdr; + char buffer[sizeof(struct sockaddr_storage) * RTAX_MAX]; +}; + int if_init(__unused struct interface *iface) { @@ -125,13 +134,14 @@ int if_opensockets_os(struct dhcpcd_ctx *ctx) { struct priv *priv; + int n; #if defined(RO_MSGFILTER) || defined(ROUTE_MSGFILTER) unsigned char msgfilter[] = { RTM_IFINFO, #ifdef RTM_IFANNOUNCE RTM_IFANNOUNCE, #endif - RTM_ADD, RTM_CHANGE, RTM_DELETE, + RTM_ADD, RTM_CHANGE, RTM_DELETE, RTM_MISS, #ifdef RTM_CHGADDR RTM_CHGADDR, #endif @@ -161,6 +171,14 @@ if_opensockets_os(struct dhcpcd_ctx *ctx) if (ctx->link_fd == -1) return -1; + /* Ignore our own route(4) messages. + * Sadly there is no way of doing this for route(4) messages + * generated from addresses we add/delete. */ + n = 0; + if (setsockopt(ctx->link_fd, SOL_SOCKET, SO_USELOOPBACK, + &n, sizeof(n)) == -1) + logerr("%s: SO_USELOOPBACK", __func__); + #if defined(RO_MSGFILTER) if (setsockopt(ctx->link_fd, PF_ROUTE, RO_MSGFILTER, &msgfilter, sizeof(msgfilter)) == -1) @@ -173,6 +191,8 @@ if_opensockets_os(struct dhcpcd_ctx *ctx) if (setsockopt(ctx->link_fd, PF_ROUTE, ROUTE_MSGFILTER, &msgfilter_mask, sizeof(msgfilter_mask)) == -1) logerr(__func__); +#else +#warning kernel does not support route message filtering #endif return 0; @@ -188,7 +208,19 @@ if_closesockets_os(struct dhcpcd_ctx *ctx) close(priv->pf_inet6_fd); } -#if defined(INET) || defined(INET6) +int +if_carrier(struct interface *ifp) +{ + struct ifmediareq ifmr = { .ifm_status = 0 }; + + strlcpy(ifmr.ifm_name, ifp->name, sizeof(ifmr.ifm_name)); + if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFMEDIA, &ifmr) == -1 || + !(ifmr.ifm_status & IFM_AVALID)) + return LINK_UNKNOWN; + + return (ifmr.ifm_status & IFM_ACTIVE) ? LINK_UP : LINK_DOWN; +} + static void if_linkaddr(struct sockaddr_dl *sdl, const struct interface *ifp) { @@ -199,7 +231,6 @@ if_linkaddr(struct sockaddr_dl *sdl, const struct interface *ifp) sdl->sdl_nlen = sdl->sdl_alen = sdl->sdl_slen = 0; sdl->sdl_index = (unsigned short)ifp->index; } -#endif #if defined(SIOCG80211NWID) || defined(SIOCGETVLAN) static int if_direct_ioctl(int s, const char *ifname, @@ -334,23 +365,30 @@ if_vlanid(const struct interface *ifp) #endif } -static void -get_addrs(int type, const void *data, const struct sockaddr **sa) +static int +get_addrs(int type, const void *data, size_t data_len, + const struct sockaddr **sa) { - const char *cp; + const char *cp, *ep; int i; cp = data; + ep = cp + data_len; for (i = 0; i < RTAX_MAX; i++) { if (type & (1 << i)) { + if (cp >= ep) { + errno = EINVAL; + return -1; + } sa[i] = (const struct sockaddr *)cp; RT_ADVANCE(cp, sa[i]); } else sa[i] = NULL; } + + return 0; } -#if defined(INET) || defined(INET6) static struct interface * if_findsdl(struct dhcpcd_ctx *ctx, const struct sockaddr_dl *sdl) { @@ -412,9 +450,13 @@ if_findsa(struct dhcpcd_ctx *ctx, const struct sockaddr *sa) case AF_INET6: { const struct sockaddr_in6 *sin; + unsigned int scope; struct ipv6_addr *ia; sin = (const void *)sa; + scope = ifa_getscope(sin); + if (scope != 0) + return if_findindex(ctx->ifaces, scope); if ((ia = ipv6_findmaskaddr(ctx, &sin->sin6_addr))) return ia->iface; break; @@ -452,24 +494,17 @@ int if_route(unsigned char cmd, const struct rt *rt) { struct dhcpcd_ctx *ctx; - struct rtm - { - struct rt_msghdr hdr; - char buffer[sizeof(struct sockaddr_storage) * RTAX_MAX]; - } rtmsg; + struct rtm rtmsg; struct rt_msghdr *rtm = &rtmsg.hdr; char *bp = rtmsg.buffer; struct sockaddr_dl sdl; bool gateway_unspec; assert(rt != NULL); + assert(rt->rt_ifp != NULL); + assert(rt->rt_ifp->ctx != NULL); ctx = rt->rt_ifp->ctx; - if ((cmd == RTM_ADD || cmd == RTM_DELETE || cmd == RTM_CHANGE) && - ctx->options & DHCPCD_DAEMONISE && - !(ctx->options & DHCPCD_DAEMONISED)) - ctx->options |= DHCPCD_RTM_PPID; - #define ADDSA(sa) do { \ memcpy(bp, (sa), (sa)->sa_len); \ bp += RT_ROUNDUP((sa)->sa_len); \ @@ -500,8 +535,20 @@ if_route(unsigned char cmd, const struct rt *rt) !sa_is_loopback(&rt->rt_gateway)) { rtm->rtm_index = (unsigned short)rt->rt_ifp->index; - if (!gateway_unspec) - rtm->rtm_addrs |= RTA_IFP; +/* + * OpenBSD rejects the message for on-link routes. + * FreeBSD-12 kernel apparently panics. + * I can't replicate the panic, but better safe than sorry! + * https://roy.marples.name/archives/dhcpcd-discuss/0002286.html + * + * Neither OS currently allows IPv6 address sharing anyway, so let's + * try to encourage someone to fix that by logging a waring during compile. + */ +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#warning kernel does not allow IPv6 address sharing + if (!gateway_unspec || rt->rt_dest.sa_family!=AF_INET6) +#endif + rtm->rtm_addrs |= RTA_IFP; if (!sa_is_unspecified(&rt->rt_ifa)) rtm->rtm_addrs |= RTA_IFA; } @@ -564,7 +611,7 @@ if_route(unsigned char cmd, const struct rt *rt) if_copysa(&gateway.sa, &rt->rt_gateway); #ifdef INET6 if (gateway.sa.sa_family == AF_INET6) - ifa_scope(&gateway.sin6, rt->rt_ifp->index); + ifa_setscope(&gateway.sin6, rt->rt_ifp->index); #endif ADDSA(&gateway.sa); } @@ -584,7 +631,6 @@ if_route(unsigned char cmd, const struct rt *rt) rtm->rtm_msglen = (unsigned short)(bp - (char *)rtm); if (write(ctx->link_fd, rtm, rtm->rtm_msglen) == -1) return -1; - ctx->sseq = ctx->seq; return 0; } @@ -593,22 +639,36 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm) { const struct sockaddr *rti_info[RTAX_MAX]; - if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY)) + if (!(rtm->rtm_addrs & RTA_DST)) { + errno = EINVAL; + return -1; + } + if (rtm->rtm_type != RTM_MISS && !(rtm->rtm_addrs & RTA_GATEWAY)) { + errno = EINVAL; return -1; + } #ifdef RTF_CLONED - if (rtm->rtm_flags & RTF_CLONED) + if (rtm->rtm_flags & RTF_CLONED) { + errno = ENOTSUP; return -1; + } #endif #ifdef RTF_LOCAL - if (rtm->rtm_flags & RTF_LOCAL) + if (rtm->rtm_flags & RTF_LOCAL) { + errno = ENOTSUP; return -1; + } #endif #ifdef RTF_BROADCAST - if (rtm->rtm_flags & RTF_BROADCAST) + if (rtm->rtm_flags & RTF_BROADCAST) { + errno = ENOTSUP; return -1; + } #endif - get_addrs(rtm->rtm_addrs, rtm + 1, rti_info); + if (get_addrs(rtm->rtm_addrs, (const char *)rtm + sizeof(*rtm), + rtm->rtm_msglen - sizeof(*rtm), rti_info) == -1) + return -1; memset(rt, 0, sizeof(*rt)); rt->rt_flags = (unsigned int)rtm->rtm_flags; @@ -618,12 +678,24 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm) if (rt->rt_netmask.sa_family == 255) /* Why? */ rt->rt_netmask.sa_family = rt->rt_dest.sa_family; } - /* dhcpcd likes an unspecified gateway to indicate via the link. */ - if (rt->rt_flags & RTF_GATEWAY && - rti_info[RTAX_GATEWAY]->sa_family != AF_LINK) - if_copysa(&rt->rt_gateway, rti_info[RTAX_GATEWAY]); + + /* dhcpcd likes an unspecified gateway to indicate via the link. + * However we need to know if gateway was a link with an address. */ + if (rtm->rtm_addrs & RTA_GATEWAY) { + if (rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) { + const struct sockaddr_dl *sdl; + + sdl = (const struct sockaddr_dl*) + (const void *)rti_info[RTAX_GATEWAY]; + if (sdl->sdl_alen != 0) + rt->rt_dflags |= RTDF_GATELINK; + } else if (rtm->rtm_flags & RTF_GATEWAY) + if_copysa(&rt->rt_gateway, rti_info[RTAX_GATEWAY]); + } + if (rtm->rtm_addrs & RTA_IFA) if_copysa(&rt->rt_ifa, rti_info[RTAX_IFA]); + rt->rt_mtu = (unsigned int)rtm->rtm_rmx.rmx_mtu; if (rtm->rtm_index) @@ -635,6 +707,9 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm) else rt->rt_ifp = if_findsa(ctx, rti_info[RTAX_DST]); + if (rt->rt_ifp == NULL && rtm->rtm_type == RTM_MISS) + rt->rt_ifp = if_find(ctx->ifaces, "lo0"); + if (rt->rt_ifp == NULL) { errno = ESRCH; return -1; @@ -643,15 +718,13 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm) } int -if_initrt(struct dhcpcd_ctx *ctx, int af) +if_initrt(struct dhcpcd_ctx *ctx, rb_tree_t *kroutes, int af) { struct rt_msghdr *rtm; int mib[6]; size_t needed; char *buf, *p, *end; - struct rt rt; - - rt_headclear(&ctx->kroutes, af); + struct rt rt, *rtn; mib[0] = CTL_NET; mib[1] = PF_ROUTE; @@ -674,17 +747,24 @@ if_initrt(struct dhcpcd_ctx *ctx, int af) end = buf + needed; for (p = buf; p < end; p += rtm->rtm_msglen) { rtm = (void *)p; - if (if_copyrt(ctx, &rt, rtm) == 0) { - rt.rt_dflags |= RTDF_INIT; - rt_recvrt(RTM_ADD, &rt); + if (p + rtm->rtm_msglen >= end) { + errno = EINVAL; + break; + } + if (if_copyrt(ctx, &rt, rtm) != 0) + continue; + if ((rtn = rt_new(rt.rt_ifp)) == NULL) { + logerr(__func__); + break; } + memcpy(rtn, &rt, sizeof(*rtn)); + if (rb_tree_insert_node(kroutes, rtn) != rtn) + rt_free(rtn); } free(buf); - return 0; + return p == end ? 0 : -1; } -#endif - #ifdef INET int if_address(unsigned char cmd, const struct ipv4_addr *ia) @@ -741,7 +821,7 @@ if_addrflags(const struct interface *ifp, const struct in_addr *addr, #ifdef INET6 static void -ifa_scope(struct sockaddr_in6 *sin, unsigned int ifindex) +ifa_setscope(struct sockaddr_in6 *sin, unsigned int ifindex) { #ifdef __KAME__ @@ -761,6 +841,23 @@ ifa_scope(struct sockaddr_in6 *sin, unsigned int ifindex) #endif } +static unsigned int +ifa_getscope(const struct sockaddr_in6 *sin) +{ +#ifdef __KAME__ + uint16_t scope; +#endif + + if (!IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) + return 0; +#ifdef __KAME__ + memcpy(&scope, &sin->sin6_addr.s6_addr[2], sizeof(scope)); + return (unsigned int)ntohs(scope); +#else + return (unsigned int)sin->sin6_scope_id; +#endif +} + int if_address6(unsigned char cmd, const struct ipv6_addr *ia) { @@ -784,6 +881,10 @@ if_address6(unsigned char cmd, const struct ipv6_addr *ia) if (ia->autoconf) ifa.ifra_flags |= IN6_IFF_AUTOCONF; #endif +#if defined(__FreeBSD__) || defined(__DragonFly__) + if (ia->addr_flags & IN6_IFF_TENTATIVE) + ifa.ifra_flags |= IN6_IFF_TENTATIVE; +#endif #ifdef IPV6_MANGETEMPADDR if (ia->flags & IPV6_AF_TEMPORARY) ifa.ifra_flags |= IN6_IFF_TEMPORARY; @@ -796,7 +897,7 @@ if_address6(unsigned char cmd, const struct ipv6_addr *ia) } ADDADDR(&ifa.ifra_addr, &ia->addr); - ifa_scope(&ifa.ifra_addr, ia->iface->index); + ifa_setscope(&ifa.ifra_addr, ia->iface->index); ipv6_mask(&mask, ia->prefix_len); ADDADDR(&ifa.ifra_prefixmask, &mask); @@ -861,7 +962,6 @@ if_address6(unsigned char cmd, const struct ipv6_addr *ia) cmd == RTM_DELADDR ? SIOCDIFADDR_IN6 : SIOCAIFADDR_IN6, &ifa); } -#if !(defined(HAVE_IFADDRS_ADDRFLAGS) && defined(HAVE_IFAM_ADDRFLAGS)) int if_addrflags6(const struct interface *ifp, const struct in6_addr *addr, __unused const char *alias) @@ -874,7 +974,7 @@ if_addrflags6(const struct interface *ifp, const struct in6_addr *addr, strlcpy(ifr6.ifr_name, ifp->name, sizeof(ifr6.ifr_name)); ifr6.ifr_addr.sin6_family = AF_INET6; ifr6.ifr_addr.sin6_addr = *addr; - ifa_scope(&ifr6.ifr_addr, ifp->index); + ifa_setscope(&ifr6.ifr_addr, ifp->index); priv = (struct priv *)ifp->ctx->priv; if (ioctl(priv->pf_inet6_fd, SIOCGIFAFLAG_IN6, &ifr6) != -1) flags = ifr6.ifr_ifru.ifru_flags6; @@ -882,7 +982,6 @@ if_addrflags6(const struct interface *ifp, const struct in6_addr *addr, flags = -1; return flags; } -#endif int if_getlifetime6(struct ipv6_addr *ia) @@ -896,14 +995,19 @@ if_getlifetime6(struct ipv6_addr *ia) strlcpy(ifr6.ifr_name, ia->iface->name, sizeof(ifr6.ifr_name)); ifr6.ifr_addr.sin6_family = AF_INET6; ifr6.ifr_addr.sin6_addr = ia->addr; - ifa_scope(&ifr6.ifr_addr, ia->iface->index); + ifa_setscope(&ifr6.ifr_addr, ia->iface->index); priv = (struct priv *)ia->iface->ctx->priv; if (ioctl(priv->pf_inet6_fd, SIOCGIFALIFETIME_IN6, &ifr6) == -1) return -1; + clock_gettime(CLOCK_MONOTONIC, &ia->created); +#if defined(__FreeBSD__) || defined(__DragonFly__) + t = ia->created.tv_sec; +#else t = time(NULL); - lifetime = &ifr6.ifr_ifru.ifru_lifetime; +#endif + lifetime = &ifr6.ifr_ifru.ifru_lifetime; if (lifetime->ia6t_preferred) ia->prefix_pltime = (uint32_t)(lifetime->ia6t_preferred - MIN(t, lifetime->ia6t_preferred)); @@ -913,7 +1017,6 @@ if_getlifetime6(struct ipv6_addr *ia) ia->prefix_vltime = (uint32_t)(lifetime->ia6t_expire - MIN(t, lifetime->ia6t_expire)); /* Calculate the created time */ - clock_gettime(CLOCK_MONOTONIC, &ia->created); ia->created.tv_sec -= lifetime->ia6t_vltime - ia->prefix_vltime; } else ia->prefix_vltime = ND6_INFINITE_LIFETIME; @@ -921,117 +1024,99 @@ if_getlifetime6(struct ipv6_addr *ia) } #endif -static void +static int if_announce(struct dhcpcd_ctx *ctx, const struct if_announcemsghdr *ifan) { + if (ifan->ifan_msglen < sizeof(*ifan)) { + errno = EINVAL; + return -1; + } + switch(ifan->ifan_what) { case IFAN_ARRIVAL: - dhcpcd_handleinterface(ctx, 1, ifan->ifan_name); - break; + return dhcpcd_handleinterface(ctx, 1, ifan->ifan_name); case IFAN_DEPARTURE: - dhcpcd_handleinterface(ctx, -1, ifan->ifan_name); - break; + return dhcpcd_handleinterface(ctx, -1, ifan->ifan_name); } + + return 0; } -static void +static int if_ifinfo(struct dhcpcd_ctx *ctx, const struct if_msghdr *ifm) { struct interface *ifp; int link_state; + if (ifm->ifm_msglen < sizeof(*ifm)) { + errno = EINVAL; + return -1; + } + if ((ifp = if_findindex(ctx->ifaces, ifm->ifm_index)) == NULL) - return; + return 0; + switch (ifm->ifm_data.ifi_link_state) { - case LINK_STATE_DOWN: - link_state = LINK_DOWN; + case LINK_STATE_UNKNOWN: + link_state = LINK_UNKNOWN; break; case LINK_STATE_UP: - /* dhcpcd considers the link down if IFF_UP is not set. */ - link_state = ifm->ifm_flags & IFF_UP ? LINK_UP : LINK_DOWN; + link_state = LINK_UP; break; default: - /* handle_carrier will re-load the interface flags and check for - * IFF_RUNNING as some drivers that don't handle link state also - * don't set IFF_RUNNING when this routing message is generated. - * As such, it is a race ...*/ - link_state = LINK_UNKNOWN; + link_state = LINK_DOWN; break; } + dhcpcd_handlecarrier(ctx, link_state, (unsigned int)ifm->ifm_flags, ifp->name); -} - -static int -if_ownmsgpid(struct dhcpcd_ctx *ctx, pid_t pid, int seq) -{ - - /* Ignore messages generated by us */ - if (getpid() == pid) { - ctx->options &= ~DHCPCD_RTM_PPID; - return 1; - } - - /* Ignore messages sent by the parent after forking */ - if ((ctx->options & - (DHCPCD_RTM_PPID | DHCPCD_DAEMONISED)) == - (DHCPCD_RTM_PPID | DHCPCD_DAEMONISED) && - ctx->ppid == pid) - { - /* If this is the last successful message sent, - * clear the check flag as it's possible another - * process could re-use the same pid and also - * manipulate the routing table. */ - if (ctx->pseq == seq) - ctx->options &= ~DHCPCD_RTM_PPID; - return 1; - } - - /* Not a message we made. */ return 0; } -static void +static int if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm) { struct rt rt; - if (if_ownmsgpid(ctx, rtm->rtm_pid, rtm->rtm_seq)) - return; + if (rtm->rtm_msglen < sizeof(*rtm)) { + errno = EINVAL; + return -1; + } /* Ignore errors. */ if (rtm->rtm_errno != 0) - return; + return 0; if (if_copyrt(ctx, &rt, rtm) == -1) - return; + return errno == ENOTSUP ? 0 : -1; #ifdef INET6 /* * BSD announces host routes. * As such, we should be notified of reachability by its * existance with a hardware address. + * Ensure we don't call this for a newly incomplete state. */ - if (rt.rt_dest.sa_family == AF_INET6 && rt.rt_flags & RTF_HOST) { - struct sockaddr_in6 dest; - struct sockaddr_dl sdl; + if (rt.rt_dest.sa_family == AF_INET6 && + (rt.rt_flags & RTF_HOST || rtm->rtm_type == RTM_MISS) && + !(rtm->rtm_type == RTM_ADD && !(rt.rt_dflags & RTDF_GATELINK))) + { + bool reachable; - memcpy(&dest, &rt.rt_dest, rt.rt_dest.sa_len); - if (rt.rt_gateway.sa_family == AF_LINK) - memcpy(&sdl, &rt.rt_gateway, rt.rt_gateway.sa_len); - else - sdl.sdl_alen = 0; - ipv6nd_neighbour(ctx, &dest.sin6_addr, - rtm->rtm_type != RTM_DELETE && sdl.sdl_alen ? - IPV6ND_REACHABLE : 0); + reachable = (rtm->rtm_type == RTM_ADD || + rtm->rtm_type == RTM_CHANGE) && + rt.rt_dflags & RTDF_GATELINK; + ipv6nd_neighbour(ctx, &rt.rt_ss_dest.sin6.sin6_addr, reachable); } #endif - rt_recvrt(rtm->rtm_type, &rt); + if (rtm->rtm_type != RTM_MISS) + rt_recvrt(rtm->rtm_type, &rt, rtm->rtm_pid); + return 0; } -static void +static int if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam) { struct interface *ifp; @@ -1039,33 +1124,20 @@ if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam) int addrflags; pid_t pid; + if (ifam->ifam_msglen < sizeof(*ifam)) { + errno = EINVAL; + return -1; + } + if (~ifam->ifam_addrs & RTA_IFA) + return 0; if ((ifp = if_findindex(ctx->ifaces, ifam->ifam_index)) == NULL) - return; - get_addrs(ifam->ifam_addrs, ifam + 1, rti_info); - if (rti_info[RTAX_IFA] == NULL) - return; + return 0; + + if (get_addrs(ifam->ifam_addrs, (const char *)ifam + sizeof(*ifam), + ifam->ifam_msglen - sizeof(*ifam), rti_info) == -1) + return -1; #ifdef HAVE_IFAM_PID - if (if_ownmsgpid(ctx, ifam->ifam_pid, 0)) { -#ifdef HAVE_IFAM_ADDRFLAGS - /* If the kernel isn't doing DAD for the newly added - * address we need to let it through. */ - if (ifam->ifam_type != RTM_NEWADDR) - return; - switch (rti_info[RTAX_IFA]->sa_family) { - case AF_INET: - if (ifam->ifam_addrflags & IN_IFF_TENTATIVE) - return; - break; - case AF_INET6: - if (ifam->ifam_addrflags & IN6_IFF_TENTATIVE) - return; - break; - default: - return; - } -#endif - } pid = ifam->ifam_pid; #else pid = 0; @@ -1103,12 +1175,27 @@ if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam) sin = (const void *)rti_info[RTAX_NETMASK]; mask.s_addr = sin != NULL && sin->sin_family == AF_INET ? sin->sin_addr.s_addr : INADDR_ANY; + sin = (const void *)rti_info[RTAX_BRD]; + bcast.s_addr = sin != NULL && sin->sin_family == AF_INET ? + sin->sin_addr.s_addr : INADDR_ANY; #if defined(__NetBSD_Version__) && __NetBSD_Version__ < 800000000 - /* NetBSD-7 and older send an invalid broadcast address. + /* + * NetBSD-7 and older send an invalid broadcast address. * So we need to query the actual address to get - * the right one. */ + * the right one. + */ { +#else + /* + * If the address was deleted, lets check if it's + * a late message and it still exists (maybe modified). + * If so, ignore it as deleting an address causes + * dhcpcd to drop any lease to which it belongs. + */ + if (ifam->ifam_type == RTM_DELADDR) { +#endif +#ifdef SIOCGIFALIAS struct in_aliasreq ifra; memset(&ifra, 0, sizeof(ifra)); @@ -1118,40 +1205,39 @@ if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam) ifra.ifra_addr.sin_len = sizeof(ifra.ifra_addr); ifra.ifra_addr.sin_addr = addr; if (ioctl(ctx->pf_inet_fd, SIOCGIFALIAS, &ifra) == -1) { - if (errno != EADDRNOTAVAIL) + if (errno != ENXIO && errno != EADDRNOTAVAIL) logerr("%s: SIOCGIFALIAS", __func__); - break; + if (ifam->ifam_type != RTM_DELADDR) + break; } - bcast = ifra.ifra_broadaddr.sin_addr; - } -#else - sin = (const void *)rti_info[RTAX_BRD]; - bcast.s_addr = sin != NULL && sin->sin_family == AF_INET ? - sin->sin_addr.s_addr : INADDR_ANY; +#if defined(__NetBSD_Version__) && __NetBSD_Version__ < 800000000 + else + bcast = ifra.ifra_broadaddr.sin_addr; #endif - -#if defined(__FreeBSD__) || defined(__DragonFly__) - /* FreeBSD sends RTM_DELADDR for each assigned address - * to an interface just brought down. - * This is wrong, because the address still exists. - * So we need to ignore it. - * Oddly enough this only happens for INET addresses. */ - if (ifam->ifam_type == RTM_DELADDR) { - struct ifreq ifr; - struct sockaddr_in *ifr_sin; - - memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name)); - ifr_sin = (void *)&ifr.ifr_addr; - ifr_sin->sin_family = AF_INET; - ifr_sin->sin_addr = addr; - if (ioctl(ctx->pf_inet_fd, SIOCGIFADDR, &ifr) == 0) { - logwarnx("%s: ignored false RTM_DELADDR for %s", - ifp->name, inet_ntoa(addr)); - break; +#else +#warning No SIOCGIFALIAS support + /* + * No SIOCGIFALIAS? That sucks! + * This makes this call very heavy weight, but we + * really need to know if the message is late or not. + */ + const struct sockaddr *sa; + struct ifaddrs *ifaddrs = NULL, *ifa; + + sa = rti_info[RTAX_IFA]; + getifaddrs(&ifaddrs); + for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) + continue; + if (sa_cmp(ifa->ifa_addr, sa) == 0 && + strcmp(ifa->ifa_name, ifp->name) == 0) + break; } - } + freeifaddrs(ifaddrs); + if (ifa != NULL) + return 0; #endif + } #ifndef HAVE_IFAM_ADDRFLAGS if (ifam->ifam_type == RTM_DELADDR) @@ -1173,15 +1259,26 @@ if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam) { struct in6_addr addr6, mask6; const struct sockaddr_in6 *sin6; + int flags; sin6 = (const void *)rti_info[RTAX_IFA]; addr6 = sin6->sin6_addr; sin6 = (const void *)rti_info[RTAX_NETMASK]; mask6 = sin6->sin6_addr; + /* + * If the address was deleted, lets check if it's + * a late message and it still exists (maybe modified). + * If so, ignore it as deleting an address causes + * dhcpcd to drop any lease to which it belongs. + */ + if (ifam->ifam_type == RTM_DELADDR) { + flags = if_addrflags6(ifp, &addr6, NULL); + if (flags != -1) + break; + addrflags = 0; + } #ifndef HAVE_IFAM_ADDRFLAGS - if (ifam->ifam_type == RTM_DELADDR) - addrflags = 0; else if ((addrflags = if_addrflags6(ifp, &addr6, NULL)) == -1) { if (errno != EADDRNOTAVAIL) logerr("%s: if_addrflags6", __func__); @@ -1201,60 +1298,74 @@ if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam) } #endif } + + return 0; } -static void +static int if_dispatch(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm) { if (rtm->rtm_version != RTM_VERSION) - return; + return 0; switch(rtm->rtm_type) { #ifdef RTM_IFANNOUNCE case RTM_IFANNOUNCE: - if_announce(ctx, (const void *)rtm); - break; + return if_announce(ctx, (const void *)rtm); #endif case RTM_IFINFO: - if_ifinfo(ctx, (const void *)rtm); - break; + return if_ifinfo(ctx, (const void *)rtm); case RTM_ADD: /* FALLTHROUGH */ case RTM_CHANGE: /* FALLTHROUGH */ - case RTM_DELETE: - if_rtm(ctx, (const void *)rtm); - break; + case RTM_DELETE: /* FALLTHROUGH */ + case RTM_MISS: + return if_rtm(ctx, (const void *)rtm); #ifdef RTM_CHGADDR case RTM_CHGADDR: /* FALLTHROUGH */ #endif case RTM_DELADDR: /* FALLTHROUGH */ case RTM_NEWADDR: - if_ifa(ctx, (const void *)rtm); - break; + return if_ifa(ctx, (const void *)rtm); #ifdef RTM_DESYNC case RTM_DESYNC: dhcpcd_linkoverflow(ctx); - break; +#elif !defined(SO_RERROR) +#warning cannot detect route socket overflow within kernel #endif } + + return 0; } +__CTASSERT(offsetof(struct rt_msghdr, rtm_msglen) == 0); int if_handlelink(struct dhcpcd_ctx *ctx) { - struct msghdr msg; + struct rtm rtm; ssize_t len; - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = ctx->iov; - msg.msg_iovlen = 1; - - len = recvmsg_realloc(ctx->link_fd, &msg, 0); + len = read(ctx->link_fd, &rtm, sizeof(rtm)); if (len == -1) return -1; - if (len != 0) - if_dispatch(ctx, ctx->iov[0].iov_base); - return 0; + if (len == 0) + return 0; + if ((size_t)len < sizeof(rtm.hdr.rtm_msglen) || + len != rtm.hdr.rtm_msglen) + { + errno = EINVAL; + return -1; + } + /* + * Coverity thinks that the data could be tainted from here. + * I have no idea how because the length of the data we read + * is guarded by len and checked to match rtm_msglen. + * The issue seems to be related to extracting the addresses + * at the end of the header, but seems to have no issues with the + * equivalent call in if_initrt. + */ + /* coverity[tainted_data] */ + return if_dispatch(ctx, &rtm.hdr); } #ifndef SYS_NMLN /* OSX */ @@ -1280,7 +1391,8 @@ if_machinearch(char *str, size_t len) #ifdef INET6 #if (defined(IPV6CTL_ACCEPT_RTADV) && !defined(ND6_IFF_ACCEPT_RTADV)) || \ - defined(IPV6CTL_USETEMPADDR) || defined(IPV6CTL_TEMPVLTIME) + defined(IPV6CTL_USETEMPADDR) || defined(IPV6CTL_TEMPVLTIME) || \ + defined(IPV6CTL_FORWARDING) #define get_inet6_sysctl(code) inet6_sysctl(code, 0, 0) #define set_inet6_sysctl(code, val) inet6_sysctl(code, val, 1) static int @@ -1364,6 +1476,19 @@ ip6_temp_valid_lifetime(__unused const char *ifname) } #endif +int +ip6_forwarding(__unused const char *ifname) +{ + int val; + +#ifdef IPV6CTL_FORWARDING + val = get_inet6_sysctl(IPV6CTL_FORWARDING); +#else + val = get_inet6_sysctlbyname("net.inet6.ip6.forwarding"); +#endif + return val < 0 ? 0 : val; +} + #ifdef SIOCIFAFATTACH static int af_attach(int s, const struct interface *ifp, int af) @@ -1393,8 +1518,20 @@ set_ifxflags(int s, const struct interface *ifp) /* * If not doing autoconf, don't disable the kernel from doing it. * If we need to, we should have another option actively disable it. + * + * OpenBSD moved from kernel based SLAAC to userland via slaacd(8). + * It has a similar featureset to dhcpcd such as stable private + * addresses, but lacks the ability to handle DNS inside the RA + * which is a serious shortfall in this day and age. + * Appease their user base by working alongside slaacd(8) if + * dhcpcd is instructed not to do auto configuration of addresses. */ - if (ifp->options->options & DHCPCD_IPV6RS) +#if defined(ND6_IFF_ACCEPT_RTADV) +#define BSD_AUTOCONF DHCPCD_IPV6RS +#else +#define BSD_AUTOCONF DHCPCD_IPV6RA_AUTOCONF +#endif + if (ifp->options->options & BSD_AUTOCONF) flags &= ~IFXF_AUTOCONF6; if (ifr.ifr_flags == flags) return 0; @@ -1504,13 +1641,14 @@ if_setup_inet6(const struct interface *ifp) * and prefixes so the kernel does not expire prefixes * and default routes we are trying to own. */ if (ifp->options->options & DHCPCD_IPV6RS) { - char ifname[IFNAMSIZ + 8]; + struct in6_ifreq ifr; - strlcpy(ifname, ifp->name, sizeof(ifname)); - if (ioctl(s, SIOCSRTRFLUSH_IN6, (void *)&ifname) == -1 && + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name)); + if (ioctl(s, SIOCSRTRFLUSH_IN6, &ifr) == -1 && errno != ENOTSUP) logwarn("SIOCSRTRFLUSH_IN6"); - if (ioctl(s, SIOCSPFXFLUSH_IN6, (void *)&ifname) == -1 && + if (ioctl(s, SIOCSPFXFLUSH_IN6, &ifr) == -1 && errno != ENOTSUP) logwarn("SIOCSPFXFLUSH_IN6"); } diff --git a/contrib/dhcpcd/src/if-options.c b/contrib/dhcpcd/src/if-options.c index b017bad53a..f0165c13b7 100644 --- a/contrib/dhcpcd/src/if-options.c +++ b/contrib/dhcpcd/src/if-options.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -64,7 +65,7 @@ #define O_IPV6RS O_BASE + 4 #define O_NOIPV6RS O_BASE + 5 #define O_IPV6RA_FORK O_BASE + 6 -// unused O_BASE + 7 +#define O_LINK_RCVBUF O_BASE + 7 // unused O_BASE + 8 #define O_NOALIAS O_BASE + 9 #define O_IA_NA O_BASE + 10 @@ -204,14 +205,16 @@ const struct option cf_options[] = { {"lastleaseextend", no_argument, NULL, O_LASTLEASE_EXTEND}, {"inactive", no_argument, NULL, O_INACTIVE}, {"mudurl", required_argument, NULL, O_MUDURL}, + {"link_rcvbuf", required_argument, NULL, O_LINK_RCVBUF}, {NULL, 0, NULL, '\0'} }; +static const char *default_script = SCRIPT; + static char * -add_environ(struct if_options *ifo, const char *value, int uniq) +add_environ(char ***array, const char *value, int uniq) { - char **newlist; - char **lst = ifo->environ; + char **newlist, **list = *array; size_t i = 0, l, lv; char *match = NULL, *p, *n; @@ -229,8 +232,8 @@ add_environ(struct if_options *ifo, const char *value, int uniq) *p++ = '\0'; l = strlen(match); - while (lst && lst[i]) { - if (match && strncmp(lst[i], match, l) == 0) { + while (list && list[i]) { + if (match && strncmp(list[i], match, l) == 0) { if (uniq) { n = strdup(value); if (n == NULL) { @@ -238,25 +241,25 @@ add_environ(struct if_options *ifo, const char *value, int uniq) free(match); return NULL; } - free(lst[i]); - lst[i] = n; + free(list[i]); + list[i] = n; } else { /* Append a space and the value to it */ - l = strlen(lst[i]); + l = strlen(list[i]); lv = strlen(p); - n = realloc(lst[i], l + lv + 2); + n = realloc(list[i], l + lv + 2); if (n == NULL) { logerr(__func__); free(match); return NULL; } - lst[i] = n; - lst[i][l] = ' '; - memcpy(lst[i] + l + 1, p, lv); - lst[i][l + lv + 1] = '\0'; + list[i] = n; + list[i][l] = ' '; + memcpy(list[i] + l + 1, p, lv); + list[i][l + lv + 1] = '\0'; } free(match); - return lst[i]; + return list[i]; } i++; } @@ -267,7 +270,7 @@ add_environ(struct if_options *ifo, const char *value, int uniq) logerr(__func__); return NULL; } - newlist = reallocarray(lst, i + 2, sizeof(char *)); + newlist = reallocarray(list, i + 2, sizeof(char *)); if (newlist == NULL) { logerr(__func__); free(n); @@ -275,26 +278,31 @@ add_environ(struct if_options *ifo, const char *value, int uniq) } newlist[i] = n; newlist[i + 1] = NULL; - ifo->environ = newlist; + *array = newlist; return newlist[i]; } -#define parse_string(buf, len, arg) parse_string_hwaddr(buf, len, arg, 0) +#define PARSE_STRING 0 +#define PARSE_STRING_NULL 1 +#define PARSE_HWADDR 2 +#define parse_string(a, b, c) parse_str((a), (b), (c), PARSE_STRING) +#define parse_hwaddr(a, b, c) parse_str((a), (b), (c), PARSE_HWADDR) static ssize_t -parse_string_hwaddr(char *sbuf, size_t slen, const char *str, int clid) +parse_str(char *sbuf, size_t slen, const char *str, int flags) { size_t l; - const char *p; - int i, punt_last = 0; + const char *p, *end; + int i; char c[4], cmd; + end = str + strlen(str); /* If surrounded by quotes then it's a string */ if (*str == '"') { - str++; - l = strlen(str); - p = str + l - 1; - if (*p == '"') - punt_last = 1; + p = end - 1; + if (*p == '"') { + str++; + end = p; + } } else { l = (size_t)hwaddr_aton(NULL, str); if ((ssize_t) l != -1 && l > 1) { @@ -311,13 +319,13 @@ parse_string_hwaddr(char *sbuf, size_t slen, const char *str, int clid) l = 0; /* If processing a string on the clientid, first byte should be * 0 to indicate a non hardware type */ - if (clid && *str) { + if (flags == PARSE_HWADDR && *str) { if (sbuf) *sbuf++ = 0; l++; } c[3] = '\0'; - while (*str) { + while (str < end) { if (++l > slen && sbuf) { errno = ENOBUFS; return -1; @@ -385,11 +393,8 @@ parse_string_hwaddr(char *sbuf, size_t slen, const char *str, int clid) str++; } } - if (punt_last) { - if (sbuf) - *--sbuf = '\0'; - l--; - } + if (flags == PARSE_STRING_NULL && sbuf) + *sbuf = '\0'; return (ssize_t)l; } @@ -541,6 +546,7 @@ set_option_space(struct dhcpcd_ctx *ctx, return arg + strlen("nd_"); } +#ifdef DHCP6 if (strncmp(arg, "dhcp6_", strlen("dhcp6_")) == 0) { *d = ctx->dhcp6_opts; *dl = ctx->dhcp6_opts_len; @@ -553,6 +559,7 @@ set_option_space(struct dhcpcd_ctx *ctx, return arg + strlen("dhcp6_"); } #endif +#endif #ifdef INET *d = ctx->dhcp_opts; @@ -652,7 +659,7 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, int e, i, t; long l; unsigned long u; - char *p = NULL, *bp, *fp, *np, **nconf; + char *p = NULL, *bp, *fp, *np; ssize_t s; struct in_addr addr, addr2; in_addr_t *naddr; @@ -706,17 +713,34 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, break; case 'c': ARG_REQUIRED; - free(ifo->script); - ifo->script = strdup(arg); - if (ifo->script == NULL) + if (ifo->script != default_script) + free(ifo->script); + s = parse_str(NULL, 0, arg, PARSE_STRING_NULL); + if (s == 0) { + ifo->script = NULL; + break; + } + dl = (size_t)s; + if (s == -1 || (ifo->script = malloc(dl)) == NULL) { + ifo->script = NULL; logerr(__func__); + return -1; + } + s = parse_str(ifo->script, dl, arg, PARSE_STRING_NULL); + if (s == -1 || + ifo->script[0] == '\0' || + strcmp(ifo->script, "/dev/null") == 0) + { + free(ifo->script); + ifo->script = NULL; + } break; case 'd': ifo->options |= DHCPCD_DEBUG; break; case 'e': ARG_REQUIRED; - add_environ(ifo, arg, 1); + add_environ(&ifo->environ, arg, 1); break; case 'h': if (!arg) { @@ -967,7 +991,7 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, return -1; } snprintf(p, dl, "skip_hooks=%s", arg); - add_environ(ifo, p, 0); + add_environ(&ifo->environ, p, 0); free(p); break; case 'D': @@ -1004,8 +1028,8 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, /* Strings have a type of 0 */; ifo->clientid[1] = 0; if (arg) - s = parse_string_hwaddr((char *)ifo->clientid + 1, - CLIENTID_MAX_LEN, arg, 1); + s = parse_hwaddr((char *)ifo->clientid + 1, + CLIENTID_MAX_LEN, arg); else s = 0; if (s == -1) { @@ -1101,15 +1125,14 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, *fp = ' '; return -1; } - if ((rt = rt_new0(ctx)) == NULL) { - *fp = ' '; + *fp = ' '; + if ((rt = rt_new0(ctx)) == NULL) return -1; - } sa_in_init(&rt->rt_dest, &addr); sa_in_init(&rt->rt_netmask, &addr2); sa_in_init(&rt->rt_gateway, &addr3); - TAILQ_INSERT_TAIL(&ifo->routes, rt, rt_next); - *fp = ' '; + if (rt_proto_add_ctx(&ifo->routes, rt, ctx)) + add_environ(&ifo->config, arg, 0); } else if (strncmp(arg, "routers=", strlen("routers=")) == 0) { if (parse_addr(&addr, NULL, p) == -1) return -1; @@ -1119,7 +1142,8 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, sa_in_init(&rt->rt_dest, &addr2); sa_in_init(&rt->rt_netmask, &addr2); sa_in_init(&rt->rt_gateway, &addr); - TAILQ_INSERT_TAIL(&ifo->routes, rt, rt_next); + if (rt_proto_add_ctx(&ifo->routes, rt, ctx)) + add_environ(&ifo->config, arg, 0); } else if (strncmp(arg, "interface_mtu=", strlen("interface_mtu=")) == 0 || strncmp(arg, "mtu=", strlen("mtu=")) == 0) @@ -1147,40 +1171,8 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, } else ifo->req_prefix_len = 128; } - } else { - dl = 0; - if (ifo->config != NULL) { - while (ifo->config[dl] != NULL) { - if (strncmp(ifo->config[dl], arg, - (size_t)(p - arg)) == 0) - { - p = strdup(arg); - if (p == NULL) { - logerr(__func__); - return -1; - } - free(ifo->config[dl]); - ifo->config[dl] = p; - return 1; - } - dl++; - } - } - p = strdup(arg); - if (p == NULL) { - logerr(__func__); - return -1; - } - nconf = reallocarray(ifo->config, dl+2, sizeof(char *)); - if (nconf == NULL) { - logerr(__func__); - free(p); - return -1; - } - ifo->config = nconf; - ifo->config[dl] = p; - ifo->config[dl + 1] = NULL; - } + } else + add_environ(&ifo->config, arg, 1); break; case 'W': if (parse_addr(&addr, &addr2, arg) != 0) @@ -1329,7 +1321,7 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, return -1; #else if (ifname == NULL) { - logerr("IA PD must belong in an " + logerrx("IA PD must belong in an " "interface block"); return -1; } @@ -1420,9 +1412,13 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, ia->sla = NULL; #endif } + +#ifdef SMALL + break; +#else if (ia->ia_type != D6_OPTION_IA_PD) break; -#ifndef SMALL + for (p = fp; p; p = fp) { fp = strwhite(p); if (fp) { @@ -1873,6 +1869,7 @@ err_sla: ifo->vivco_len + 1, sizeof(*ifo->vivco)); if (vivco == NULL) { logerr( __func__); + free(np); return -1; } ifo->vivco = vivco; @@ -2010,7 +2007,7 @@ err_sla: arg = fp; fp = strend(arg); if (fp == NULL) { - logerrx("authtoken requies an an expiry date"); + logerrx("authtoken requies an expiry date"); free(token->realm); free(token); return -1; @@ -2065,7 +2062,7 @@ err_sla: ifo->auth.options &= ~DHCPCD_AUTH_REQUIRE; break; case O_DHCP: - ifo->options |= DHCPCD_DHCP | DHCPCD_IPV4; + ifo->options |= DHCPCD_DHCP | DHCPCD_WANTDHCP | DHCPCD_IPV4; break; case O_NODHCP: ifo->options &= ~DHCPCD_DHCP; @@ -2165,6 +2162,16 @@ err_sla: } *ifo->mudurl = (uint8_t)s; break; + case O_LINK_RCVBUF: +#ifndef SMALL + ARG_REQUIRED; + ctx->link_rcvbuf = (int)strtoi(arg, NULL, 0, 0, INT32_MAX, &e); + if (e) { + logerrx("failed to convert link_rcvbuf %s", arg); + return -1; + } +#endif + break; default: return 0; } @@ -2271,9 +2278,10 @@ default_config(struct dhcpcd_ctx *ctx) ifo->options |= DHCPCD_IF_UP | DHCPCD_LINK | DHCPCD_INITIAL_DELAY; ifo->timeout = DEFAULT_TIMEOUT; ifo->reboot = DEFAULT_REBOOT; + ifo->script = UNCONST(default_script); ifo->metric = -1; ifo->auth.options |= DHCPCD_AUTH_REQUIRE; - TAILQ_INIT(&ifo->routes); + rb_tree_init(&ifo->routes, &rt_compare_list_ops); #ifdef AUTH TAILQ_INIT(&ifo->auth.tokens); #endif @@ -2331,6 +2339,9 @@ read_config(struct dhcpcd_ctx *ctx, buf = NULL; buflen = 0; + /* Reset route order */ + ctx->rt_order = 0; + /* Parse our embedded options file */ if (ifname == NULL && !(ctx->options & DHCPCD_PRINT_PIDFILE)) { /* Space for initial estimates */ @@ -2430,8 +2441,10 @@ read_config(struct dhcpcd_ctx *ctx, #ifdef INET6 ctx->nd_opts = ifo->nd_override; ctx->nd_opts_len = ifo->nd_override_len; +#ifdef DHCP6 ctx->dhcp6_opts = ifo->dhcp6_override; ctx->dhcp6_opts_len = ifo->dhcp6_override_len; +#endif #else for (i = 0, opt = ifo->nd_override; i < ifo->nd_override_len; @@ -2598,72 +2611,92 @@ void free_options(struct dhcpcd_ctx *ctx, struct if_options *ifo) { size_t i; +#ifdef RT_FREE_ROUTE_TABLE + struct interface *ifp; + struct rt *rt; +#endif struct dhcp_opt *opt; struct vivco *vo; #ifdef AUTH struct token *token; #endif - if (ifo) { - if (ifo->environ) { - i = 0; - while (ifo->environ[i]) - free(ifo->environ[i++]); - free(ifo->environ); - } - if (ifo->config) { - i = 0; - while (ifo->config[i]) - free(ifo->config[i++]); - free(ifo->config); + if (ifo == NULL) + return; + + if (ifo->environ) { + i = 0; + while (ifo->environ[i]) + free(ifo->environ[i++]); + free(ifo->environ); + } + if (ifo->config) { + i = 0; + while (ifo->config[i]) + free(ifo->config[i++]); + free(ifo->config); + } + +#ifdef RT_FREE_ROUTE_TABLE + /* Stupidly, we don't know the interface when creating the options. + * As such, make sure each route has one so they can goto the + * free list. */ + ifp = ctx->ifaces != NULL ? TAILQ_FIRST(ctx->ifaces) : NULL; + if (ifp != NULL) { + RB_TREE_FOREACH(rt, &ifo->routes) { + if (rt->rt_ifp == NULL) + rt->rt_ifp = ifp; } - rt_headclear0(ctx, &ifo->routes, AF_UNSPEC); - free(ifo->script); - free(ifo->arping); - free(ifo->blacklist); - free(ifo->fallback); + } +#endif + rt_headclear0(ctx, &ifo->routes, AF_UNSPEC); - for (opt = ifo->dhcp_override; - ifo->dhcp_override_len > 0; - opt++, ifo->dhcp_override_len--) - free_dhcp_opt_embenc(opt); - free(ifo->dhcp_override); - for (opt = ifo->nd_override; - ifo->nd_override_len > 0; - opt++, ifo->nd_override_len--) - free_dhcp_opt_embenc(opt); - free(ifo->nd_override); - for (opt = ifo->dhcp6_override; - ifo->dhcp6_override_len > 0; - opt++, ifo->dhcp6_override_len--) - free_dhcp_opt_embenc(opt); - free(ifo->dhcp6_override); - for (vo = ifo->vivco; - ifo->vivco_len > 0; - vo++, ifo->vivco_len--) - free(vo->data); - free(ifo->vivco); - for (opt = ifo->vivso_override; - ifo->vivso_override_len > 0; - opt++, ifo->vivso_override_len--) - free_dhcp_opt_embenc(opt); - free(ifo->vivso_override); + if (ifo->script != default_script) + free(ifo->script); + free(ifo->arping); + free(ifo->blacklist); + free(ifo->fallback); + + for (opt = ifo->dhcp_override; + ifo->dhcp_override_len > 0; + opt++, ifo->dhcp_override_len--) + free_dhcp_opt_embenc(opt); + free(ifo->dhcp_override); + for (opt = ifo->nd_override; + ifo->nd_override_len > 0; + opt++, ifo->nd_override_len--) + free_dhcp_opt_embenc(opt); + free(ifo->nd_override); + for (opt = ifo->dhcp6_override; + ifo->dhcp6_override_len > 0; + opt++, ifo->dhcp6_override_len--) + free_dhcp_opt_embenc(opt); + free(ifo->dhcp6_override); + for (vo = ifo->vivco; + ifo->vivco_len > 0; + vo++, ifo->vivco_len--) + free(vo->data); + free(ifo->vivco); + for (opt = ifo->vivso_override; + ifo->vivso_override_len > 0; + opt++, ifo->vivso_override_len--) + free_dhcp_opt_embenc(opt); + free(ifo->vivso_override); #if defined(INET6) && !defined(SMALL) - for (; ifo->ia_len > 0; ifo->ia_len--) - free(ifo->ia[ifo->ia_len - 1].sla); + for (; ifo->ia_len > 0; ifo->ia_len--) + free(ifo->ia[ifo->ia_len - 1].sla); #endif - free(ifo->ia); + free(ifo->ia); #ifdef AUTH - while ((token = TAILQ_FIRST(&ifo->auth.tokens))) { - TAILQ_REMOVE(&ifo->auth.tokens, token, next); - if (token->realm_len) - free(token->realm); - free(token->key); - free(token); - } -#endif - free(ifo); + while ((token = TAILQ_FIRST(&ifo->auth.tokens))) { + TAILQ_REMOVE(&ifo->auth.tokens, token, next); + if (token->realm_len) + free(token->realm); + free(token->key); + free(token); } +#endif + free(ifo); } diff --git a/contrib/dhcpcd/src/if-options.h b/contrib/dhcpcd/src/if-options.h index 8dfe3486e9..79947885ce 100644 --- a/contrib/dhcpcd/src/if-options.h +++ b/contrib/dhcpcd/src/if-options.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -60,7 +61,7 @@ #define DHCPCD_ARP (1ULL << 0) #define DHCPCD_RELEASE (1ULL << 1) -// unused (1ULL << 2) +#define DHCPCD_RTBUILD (1ULL << 2) #define DHCPCD_GATEWAY (1ULL << 3) #define DHCPCD_STATIC (1ULL << 4) #define DHCPCD_DEBUG (1ULL << 5) @@ -109,7 +110,7 @@ #define DHCPCD_DHCP6 (1ULL << 50) #define DHCPCD_IF_UP (1ULL << 51) #define DHCPCD_INFORM6 (1ULL << 52) -#define DHCPCD_RTM_PPID (1ULL << 53) +#define DHCPCD_WANTDHCP (1ULL << 53) #define DHCPCD_IPV6RA_AUTOCONF (1ULL << 54) #define DHCPCD_ROUTER_HOST_ROUTE_WARNED (1ULL << 55) #define DHCPCD_LASTLEASE_EXTEND (1ULL << 56) @@ -181,7 +182,7 @@ struct if_options { struct in_addr req_addr; struct in_addr req_mask; struct in_addr req_brd; - struct rt_head routes; + rb_tree_t routes; struct in6_addr req_addr6; uint8_t req_prefix_len; unsigned int mtu; diff --git a/contrib/dhcpcd/src/if.c b/contrib/dhcpcd/src/if.c index 1835620638..c483f046ff 100644 --- a/contrib/dhcpcd/src/if.c +++ b/contrib/dhcpcd/src/if.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -59,7 +60,6 @@ #include #include #include -#include #include "common.h" #include "dev.h" @@ -72,18 +72,32 @@ #include "ipv6nd.h" #include "logerr.h" +#ifdef __sun +/* It has the ioctl, but the member is missing from the struct? + * No matter, our getifaddrs foo in if-sun.c will DTRT. */ +#undef SIOCGIFHWADDR +#endif + void if_free(struct interface *ifp) { if (ifp == NULL) return; +#ifdef IPV4LL ipv4ll_free(ifp); +#endif +#ifdef INET dhcp_free(ifp); ipv4_free(ifp); +#endif +#ifdef DHCP6 dhcp6_free(ifp); +#endif +#ifdef INET6 ipv6nd_free(ifp); ipv6_free(ifp); +#endif rt_freeif(ifp); free_options(ifp->ctx, ifp->options); free(ifp); @@ -101,12 +115,6 @@ if_opensockets(struct dhcpcd_ctx *ctx) if (ctx->pf_inet_fd == -1) return -1; -#ifdef IFLR_ACTIVE - ctx->pf_link_fd = xsocket(PF_LINK, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (ctx->pf_link_fd == -1) - return -1; -#endif - return 0; } @@ -116,10 +124,6 @@ if_closesockets(struct dhcpcd_ctx *ctx) if (ctx->pf_inet_fd != -1) close(ctx->pf_inet_fd); -#ifdef IFLR_ACTIVE - if (ctx->pf_link_fd != -1) - close(ctx->pf_link_fd); -#endif if (ctx->priv) { if_closesockets_os(ctx); @@ -128,54 +132,37 @@ if_closesockets(struct dhcpcd_ctx *ctx) } int -if_carrier(struct interface *ifp) +if_getflags(struct interface *ifp) { - int r; - struct ifreq ifr; -#ifdef SIOCGIFMEDIA - struct ifmediareq ifmr; -#endif + struct ifreq ifr = { .ifr_flags = 0 }; - memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name)); if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFFLAGS, &ifr) == -1) - return LINK_UNKNOWN; + return -1; ifp->flags = (unsigned int)ifr.ifr_flags; - -#ifdef SIOCGIFMEDIA - memset(&ifmr, 0, sizeof(ifmr)); - strlcpy(ifmr.ifm_name, ifp->name, sizeof(ifmr.ifm_name)); - if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFMEDIA, &ifmr) != -1 && - ifmr.ifm_status & IFM_AVALID) - r = (ifmr.ifm_status & IFM_ACTIVE) ? LINK_UP : LINK_DOWN; - else - r = ifr.ifr_flags & IFF_RUNNING ? LINK_UP : LINK_UNKNOWN; -#else - r = ifr.ifr_flags & IFF_RUNNING ? LINK_UP : LINK_DOWN; -#endif - return r; + return 0; } int if_setflag(struct interface *ifp, short flag) { - struct ifreq ifr; - int r; + struct ifreq ifr = { .ifr_flags = 0 }; + short f; + + if (if_getflags(ifp) == -1) + return -1; + + f = (short)ifp->flags; + if ((f & flag) == flag) + return 0; - memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name)); - r = -1; - if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFFLAGS, &ifr) == 0) { - if (flag == 0 || (ifr.ifr_flags & flag) == flag) - r = 0; - else { - ifr.ifr_flags |= flag; - if (ioctl(ifp->ctx->pf_inet_fd, SIOCSIFFLAGS, &ifr) ==0) - r = 0; - } - ifp->flags = (unsigned int)ifr.ifr_flags; - } - return r; + ifr.ifr_flags = f | flag; + if (ioctl(ifp->ctx->pf_inet_fd, SIOCSIFFLAGS, &ifr) == -1) + return -1; + + ifp->flags = (unsigned int)ifr.ifr_flags; + return 0; } static int @@ -254,6 +241,7 @@ if_learnaddrs(struct dhcpcd_ctx *ctx, struct if_head *ifs, case AF_INET6: sin6 = (void *)ifa->ifa_addr; net6 = (void *)ifa->ifa_netmask; + #ifdef __KAME__ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) /* Remove the scope from the address */ @@ -326,29 +314,36 @@ if_discover(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs, struct if_spec spec; #ifdef AF_LINK const struct sockaddr_dl *sdl; -#ifdef SIOCGIFPRIORITY - struct ifreq ifr; -#endif #ifdef IFLR_ACTIVE - struct if_laddrreq iflr; -#endif - -#ifdef IFLR_ACTIVE - memset(&iflr, 0, sizeof(iflr)); + struct if_laddrreq iflr = { .flags = IFLR_PREFIX }; + int link_fd; #endif #elif AF_PACKET const struct sockaddr_ll *sll; #endif +#if defined(SIOCGIFPRIORITY) || defined(SIOCGIFHWADDR) + struct ifreq ifr; +#endif if ((ifs = malloc(sizeof(*ifs))) == NULL) { logerr(__func__); return NULL; } - TAILQ_INIT(ifs); if (getifaddrs(ifaddrs) == -1) { logerr(__func__); - goto out; + free(ifs); + return NULL; } + TAILQ_INIT(ifs); + +#ifdef IFLR_ACTIVE + link_fd = xsocket(PF_LINK, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (link_fd == -1) { + logerr(__func__); + free(ifs); + return NULL; + } +#endif for (ifa = *ifaddrs; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr != NULL) { @@ -445,7 +440,7 @@ if_discover(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs, MIN(ifa->ifa_addr->sa_len, sizeof(iflr.addr))); iflr.flags = IFLR_PREFIX; iflr.prefixlen = (unsigned int)sdl->sdl_alen * NBBY; - if (ioctl(ctx->pf_link_fd, SIOCGLIFADDR, &iflr) == -1 || + if (ioctl(link_fd, SIOCGLIFADDR, &iflr) == -1 || !(iflr.flags & IFLR_ACTIVE)) { if_free(ifp); @@ -462,7 +457,7 @@ if_discover(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs, case IFT_PPP: /* FALLTHROUGH */ #endif #ifdef IFT_PROPVIRTUAL - case IFT_PROPVIRTUAL: /* FALLTHROUGH */ + case IFT_PROPVIRTUAL: #endif #if defined(IFT_BRIDGE) || defined(IFT_PPP) || defined(IFT_PROPVIRTUAL) /* Don't allow unless explicit */ @@ -476,6 +471,7 @@ if_discover(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs, ifp->name); active = IF_INACTIVE; } + __fallthrough; /* Appease gcc-7 */ /* FALLTHROUGH */ #endif #ifdef IFT_L2VLAN @@ -522,10 +518,21 @@ if_discover(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs, memcpy(ifp->hwaddr, sll->sll_addr, ifp->hwlen); #endif } -#ifdef __linux__ - /* PPP addresses on Linux don't have hardware addresses */ - else - ifp->index = if_nametoindex(ifp->name); +#ifdef SIOCGIFHWADDR + else { + /* This is a huge bug in getifaddrs(3) as there + * is no reason why this can't be returned in + * ifa_addr. */ + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifa->ifa_name, + sizeof(ifr.ifr_name)); + if (ioctl(ctx->pf_inet_fd, SIOCGIFHWADDR, &ifr) == -1) + logerr("%s: SIOCGIFHWADDR", ifa->ifa_name); + ifp->family = ifr.ifr_hwaddr.sa_family; + if (ioctl(ctx->pf_inet_fd, SIOCGIFINDEX, &ifr) == -1) + logerr("%s: SIOCGIFINDEX", ifa->ifa_name); + ifp->index = (unsigned int)ifr.ifr_ifindex; + } #endif /* Ensure hardware address is valid. */ @@ -546,6 +553,9 @@ if_discover(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs, #ifdef ARPHRD_PPP case ARPHRD_PPP: #endif +#ifdef ARPHRD_NONE + case ARPHRD_NONE: +#endif /* We don't warn for supported families */ break; @@ -584,7 +594,7 @@ if_discover(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs, * we can work them out. */ ifp->metric = 200 + ifp->index; if (if_getssid(ifp) != -1) { - ifp->wireless = 1; + ifp->wireless = true; ifp->metric += 100; } #endif @@ -597,7 +607,9 @@ if_discover(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs, TAILQ_INSERT_TAIL(ifs, ifp, next); } -out: +#ifdef IFLR_ACTIVE + close(link_fd); +#endif return ifs; } @@ -698,6 +710,11 @@ if_domtu(const struct interface *ifp, short int mtu) int r; struct ifreq ifr; +#ifdef __sun + if (mtu == 0) + return if_mtu_os(ifp); +#endif + memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name)); ifr.ifr_mtu = mtu; @@ -707,80 +724,77 @@ if_domtu(const struct interface *ifp, short int mtu) return ifr.ifr_mtu; } -/* Interface comparer for working out ordering. */ -static int -if_cmp(const struct interface *si, const struct interface *ti) +#ifdef ALIAS_ADDR +int +if_makealias(char *alias, size_t alias_len, const char *ifname, int lun) { -#ifdef INET - int r; -#endif - - /* Check active first */ - if (si->active > ti->active) - return -1; - if (si->active < ti->active) - return 1; - - /* Check carrier status next */ - if (si->carrier > ti->carrier) - return -1; - if (si->carrier < ti->carrier) - return 1; - - if (D_STATE_RUNNING(si) && !D_STATE_RUNNING(ti)) - return -1; - if (!D_STATE_RUNNING(si) && D_STATE_RUNNING(ti)) - return 1; - if (RS_STATE_RUNNING(si) && !RS_STATE_RUNNING(ti)) - return -1; - if (!RS_STATE_RUNNING(si) && RS_STATE_RUNNING(ti)) - return 1; - if (D6_STATE_RUNNING(si) && !D6_STATE_RUNNING(ti)) - return -1; - if (!D6_STATE_RUNNING(si) && D6_STATE_RUNNING(ti)) - return 1; -#ifdef INET - /* Special attention needed here due to states and IPv4LL. */ - if ((r = ipv4_ifcmp(si, ti)) != 0) - return r; -#endif - - /* Finally, metric */ - if (si->metric < ti->metric) - return -1; - if (si->metric > ti->metric) - return 1; - return 0; + if (lun == 0) + return strlcpy(alias, ifname, alias_len); + return snprintf(alias, alias_len, "%s:%u", ifname, lun); } +#endif -/* Sort the interfaces into a preferred order - best first, worst last. */ -void -if_sortinterfaces(struct dhcpcd_ctx *ctx) +struct interface * +if_findifpfromcmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg, int *hoplimit) { - struct if_head sorted; - struct interface *ifp, *ift; - - if (ctx->ifaces == NULL || - (ifp = TAILQ_FIRST(ctx->ifaces)) == NULL || - TAILQ_NEXT(ifp, next) == NULL) - return; + struct cmsghdr *cm; + unsigned int ifindex = 0; + struct interface *ifp; +#if defined(INET) && defined(IP_PKTINFO) + struct in_pktinfo ipi; +#endif +#ifdef INET6 + struct in6_pktinfo ipi6; +#else + UNUSED(hoplimit); +#endif - TAILQ_INIT(&sorted); - TAILQ_REMOVE(ctx->ifaces, ifp, next); - TAILQ_INSERT_HEAD(&sorted, ifp, next); - while ((ifp = TAILQ_FIRST(ctx->ifaces))) { - TAILQ_REMOVE(ctx->ifaces, ifp, next); - TAILQ_FOREACH(ift, &sorted, next) { - if (if_cmp(ifp, ift) == -1) { - TAILQ_INSERT_BEFORE(ift, ifp, next); + for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(msg); + cm; + cm = (struct cmsghdr *)CMSG_NXTHDR(msg, cm)) + { +#if defined(INET) && defined(IP_PKTINFO) + if (cm->cmsg_level == IPPROTO_IP) { + switch(cm->cmsg_type) { + case IP_PKTINFO: + if (cm->cmsg_len != CMSG_LEN(sizeof(ipi))) + continue; + memcpy(&ipi, CMSG_DATA(cm), sizeof(ipi)); + ifindex = (unsigned int)ipi.ipi_ifindex; break; } } - if (ift == NULL) - TAILQ_INSERT_TAIL(&sorted, ifp, next); +#endif +#ifdef INET6 + if (cm->cmsg_level == IPPROTO_IPV6) { + switch(cm->cmsg_type) { + case IPV6_PKTINFO: + if (cm->cmsg_len != CMSG_LEN(sizeof(ipi6))) + continue; + memcpy(&ipi6, CMSG_DATA(cm), sizeof(ipi6)); + ifindex = (unsigned int)ipi6.ipi6_ifindex; + break; + case IPV6_HOPLIMIT: + if (cm->cmsg_len != CMSG_LEN(sizeof(int))) + continue; + if (hoplimit == NULL) + break; + memcpy(hoplimit, CMSG_DATA(cm), sizeof(int)); + break; + } + } +#endif } - TAILQ_CONCAT(ctx->ifaces, &sorted, next); + + /* Find the receiving interface */ + TAILQ_FOREACH(ifp, ctx->ifaces, next) { + if (ifp->index == ifindex) + break; + } + if (ifp == NULL) + errno = ESRCH; + return ifp; } int @@ -790,6 +804,9 @@ xsocket(int domain, int type, int protocol) #if !defined(HAVE_SOCK_CLOEXEC) || !defined(HAVE_SOCK_NONBLOCK) int xflags, xtype = type; #endif +#ifdef SO_RERROR + int on; +#endif #ifndef HAVE_SOCK_CLOEXEC if (xtype & SOCK_CLOEXEC) @@ -814,6 +831,13 @@ xsocket(int domain, int type, int protocol) goto out; #endif +#ifdef SO_RERROR + /* Tell recvmsg(2) to return ENOBUFS if the receiving socket overflows. */ + on = 1; + if (setsockopt(s, SOL_SOCKET, SO_RERROR, &on, sizeof(on)) == -1) + logerr("%s: SO_RERROR", __func__); +#endif + return s; #if !defined(HAVE_SOCK_CLOEXEC) || !defined(HAVE_SOCK_NONBLOCK) diff --git a/contrib/dhcpcd/src/if.h b/contrib/dhcpcd/src/if.h index 4f6c6fb687..4126fd33e4 100644 --- a/contrib/dhcpcd/src/if.h +++ b/contrib/dhcpcd/src/if.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -90,9 +91,6 @@ ((addr & IN_CLASSB_NET) == 0xc0a80000)) #endif -#define RAW_EOF 1 << 0 -#define RAW_PARTIALCSUM 2 << 0 - #ifndef CLLADDR #ifdef AF_LINK # define CLLADDR(sdl) (const void *)((sdl)->sdl_data + (sdl)->sdl_nlen) @@ -109,8 +107,10 @@ struct ifaddrs; int if_getifaddrs(struct ifaddrs **); #define getifaddrs if_getifaddrs +int if_getsubnet(struct dhcpcd_ctx *, const char *, int, void *, size_t); #endif +int if_getflags(struct interface *ifp); int if_setflag(struct interface *ifp, short flag); #define if_up(ifp) if_setflag((ifp), (IFF_UP | IFF_RUNNING)) bool if_valid_hwaddr(const uint8_t *, size_t); @@ -122,13 +122,18 @@ void if_deletestaleaddrs(struct if_head *); struct interface *if_find(struct if_head *, const char *); struct interface *if_findindex(struct if_head *, unsigned int); struct interface *if_loopback(struct dhcpcd_ctx *); -void if_sortinterfaces(struct dhcpcd_ctx *); void if_free(struct interface *); int if_domtu(const struct interface *, short int); #define if_getmtu(ifp) if_domtu((ifp), 0) #define if_setmtu(ifp, mtu) if_domtu((ifp), (mtu)) int if_carrier(struct interface *); +#ifdef ALIAS_ADDR +int if_makealias(char *, size_t, const char *, int); +#endif + +int if_mtu_os(const struct interface *); + /* * Helper to decode an interface name of bge0:1 to * devname = bge0, drvname = bge0, ppa = 0, lun = 1. @@ -179,7 +184,7 @@ int if_handlelink(struct dhcpcd_ctx *); #endif int if_route(unsigned char, const struct rt *rt); -int if_initrt(struct dhcpcd_ctx *, int); +int if_initrt(struct dhcpcd_ctx *, rb_tree_t *, int); #ifdef INET int if_address(unsigned char, const struct ipv4_addr *); @@ -197,6 +202,7 @@ int ip6_temp_valid_lifetime(const char *ifname); #else #define ip6_use_tempaddr(a) (0) #endif +int ip6_forwarding(const char *ifname); int if_address6(unsigned char, const struct ipv6_addr *); int if_addrflags6(const struct interface *, const struct in6_addr *, @@ -208,5 +214,7 @@ int if_getlifetime6(struct ipv6_addr *); #endif int if_machinearch(char *, size_t); +struct interface *if_findifpfromcmsg(struct dhcpcd_ctx *, + struct msghdr *, int *); int xsocket(int, int, int); #endif diff --git a/contrib/dhcpcd/src/ipv4.c b/contrib/dhcpcd/src/ipv4.c index 8008f9e658..05fc8066af 100644 --- a/contrib/dhcpcd/src/ipv4.c +++ b/contrib/dhcpcd/src/ipv4.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -38,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -200,8 +202,10 @@ ipv4_hasaddr(const struct interface *ifp) { const struct dhcp_state *dstate; +#ifdef IPV4LL if (IPV4LL_STATE_RUNNING(ifp)) return 1; +#endif dstate = D_CSTATE(ifp); return (dstate && @@ -245,10 +249,10 @@ ipv4_ifcmp(const struct interface *si, const struct interface *ti) } static int -inet_dhcproutes(struct rt_head *routes, struct interface *ifp) +inet_dhcproutes(rb_tree_t *routes, struct interface *ifp, bool *have_default) { const struct dhcp_state *state; - struct rt_head nroutes; + rb_tree_t nroutes; struct rt *rt, *r = NULL; struct in_addr in; uint16_t mtu; @@ -261,16 +265,15 @@ inet_dhcproutes(struct rt_head *routes, struct interface *ifp) /* An address does have to exist. */ assert(state->addr); - TAILQ_INIT(&nroutes); + rb_tree_init(&nroutes, &rt_compare_proto_ops); /* First, add a subnet route. */ - if (!(ifp->flags & IFF_POINTOPOINT) && + if (state->addr->mask.s_addr != INADDR_ANY #ifndef BSD /* BSD adds a route in this instance */ - state->addr->mask.s_addr != INADDR_BROADCAST && + && state->addr->mask.s_addr != INADDR_BROADCAST #endif - state->addr->mask.s_addr != INADDR_ANY) - { + ) { if ((rt = rt_new(ifp)) == NULL) return -1; rt->rt_dflags |= RTDF_IFA_ROUTE; @@ -281,12 +284,12 @@ inet_dhcproutes(struct rt_head *routes, struct interface *ifp) //in.s_addr = INADDR_ANY; //sa_in_init(&rt->rt_gateway, &in); rt->rt_gateway.sa_family = AF_UNSPEC; - TAILQ_INSERT_HEAD(&nroutes, rt, rt_next); + rt_proto_add(&nroutes, rt); } /* If any set routes, grab them, otherwise DHCP routes. */ - if (TAILQ_FIRST(&ifp->options->routes)) { - TAILQ_FOREACH(r, &ifp->options->routes, rt_next) { + if (RB_TREE_MIN(&ifp->options->routes)) { + RB_TREE_FOREACH(r, &ifp->options->routes) { if (sa_is_unspecified(&r->rt_gateway)) break; if ((rt = rt_new0(ifp->ctx)) == NULL) @@ -294,14 +297,14 @@ inet_dhcproutes(struct rt_head *routes, struct interface *ifp) memcpy(rt, r, sizeof(*rt)); rt_setif(rt, ifp); rt->rt_dflags = RTDF_STATIC; - TAILQ_INSERT_TAIL(&nroutes, rt, rt_next); + rt_proto_add(&nroutes, rt); } } else { if (dhcp_get_routes(&nroutes, ifp) == -1) return -1; } - /* If configured, Install a gateway to the desintion + /* If configured, install a gateway to the desintion * for P2P interfaces. */ if (ifp->flags & IFF_POINTOPOINT && has_option_mask(ifp->options->dstmask, DHO_ROUTER)) @@ -313,20 +316,26 @@ inet_dhcproutes(struct rt_head *routes, struct interface *ifp) sa_in_init(&rt->rt_netmask, &in); sa_in_init(&rt->rt_gateway, &state->addr->brd); sa_in_init(&rt->rt_ifa, &state->addr->addr); - TAILQ_INSERT_HEAD(routes, rt, rt_next); + rt_proto_add(&nroutes, rt); } /* Copy our address as the source address and set mtu */ mtu = dhcp_get_mtu(ifp); n = 0; - TAILQ_FOREACH(rt, &nroutes, rt_next) { + while ((rt = RB_TREE_MIN(&nroutes)) != NULL) { + rb_tree_remove_node(&nroutes, rt); rt->rt_mtu = mtu; if (!(rt->rt_dflags & RTDF_STATIC)) rt->rt_dflags |= RTDF_DHCP; sa_in_init(&rt->rt_ifa, &state->addr->addr); - n++; + if (rb_tree_insert_node(routes, rt) != rt) { + rt_free(rt); + continue; + } + if (rt_is_default(rt)) + *have_default = true; + n = 1; } - TAILQ_CONCAT(routes, &nroutes, rt_next); return n; } @@ -334,20 +343,23 @@ inet_dhcproutes(struct rt_head *routes, struct interface *ifp) /* We should check to ensure the routers are on the same subnet * OR supply a host route. If not, warn and add a host route. */ static int -inet_routerhostroute(struct rt_head *routes, struct interface *ifp) +inet_routerhostroute(rb_tree_t *routes, struct interface *ifp) { - struct rt *rt, *rth; + struct rt *rt, *rth, *rtp; struct sockaddr_in *dest, *netmask, *gateway; const char *cp, *cp2, *cp3, *cplim; struct if_options *ifo; const struct dhcp_state *state; struct in_addr in; + rb_tree_t troutes; /* Don't add a host route for these interfaces. */ if (ifp->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) return 0; - TAILQ_FOREACH(rt, routes, rt_next) { + rb_tree_init(&troutes, &rt_compare_proto_ops); + + RB_TREE_FOREACH(rt, routes) { if (rt->rt_dest.sa_family != AF_INET) continue; if (!sa_is_unspecified(&rt->rt_dest) || @@ -355,13 +367,14 @@ inet_routerhostroute(struct rt_head *routes, struct interface *ifp) continue; gateway = satosin(&rt->rt_gateway); /* Scan for a route to match */ - TAILQ_FOREACH(rth, routes, rt_next) { + RB_TREE_FOREACH(rth, routes) { if (rth == rt) break; /* match host */ if (sa_cmp(&rth->rt_dest, &rt->rt_gateway) == 0) break; /* match subnet */ + /* XXX ADD TO RT_COMARE? XXX */ cp = (const char *)&gateway->sin_addr.s_addr; dest = satosin(&rth->rt_dest); cp2 = (const char *)&dest->sin_addr.s_addr; @@ -406,6 +419,7 @@ inet_routerhostroute(struct rt_head *routes, struct interface *ifp) ifp->name, sa_addrtop(&rt->rt_gateway, buf, sizeof(buf))); } + if ((rth = rt_new(ifp)) == NULL) return -1; rth->rt_flags |= RTF_HOST; @@ -416,40 +430,54 @@ inet_routerhostroute(struct rt_head *routes, struct interface *ifp) sa_in_init(&rth->rt_gateway, &in); rth->rt_mtu = dhcp_get_mtu(ifp); sa_in_init(&rth->rt_ifa, &state->addr->addr); - TAILQ_INSERT_BEFORE(rt, rth, rt_next); + + /* We need to insert the host route just before the router. */ + while ((rtp = RB_TREE_MAX(routes)) != NULL) { + rb_tree_remove_node(routes, rtp); + rt_proto_add(&troutes, rtp); + if (rtp == rt) + break; + } + rt_proto_add(routes, rth); + /* troutes is now reversed, so add backwards again. */ + while ((rtp = RB_TREE_MAX(&troutes)) != NULL) { + rb_tree_remove_node(&troutes, rtp); + rt_proto_add(routes, rtp); + } } return 0; } bool -inet_getroutes(struct dhcpcd_ctx *ctx, struct rt_head *routes) +inet_getroutes(struct dhcpcd_ctx *ctx, rb_tree_t *routes) { struct interface *ifp; - struct rt def; - bool have_default; + bool have_default = false; TAILQ_FOREACH(ifp, ctx->ifaces, next) { if (!ifp->active) continue; - if (inet_dhcproutes(routes, ifp) == -1) + if (inet_dhcproutes(routes, ifp, &have_default) == -1) return false; +#ifdef IPV4LL if (ipv4ll_subnetroute(routes, ifp) == -1) return false; +#endif if (inet_routerhostroute(routes, ifp) == -1) return false; } +#ifdef IPV4LL /* If there is no default route, see if we can use an IPv4LL one. */ - memset(&def, 0, sizeof(def)); - def.rt_dest.sa_family = AF_INET; - have_default = (rt_find(routes, &def) != NULL); - if (!have_default) { - TAILQ_FOREACH(ifp, ctx->ifaces, next) { - if (ifp->active && - ipv4ll_defaultroute(routes, ifp) == 1) - break; - } + if (have_default) + return true; + + TAILQ_FOREACH(ifp, ctx->ifaces, next) { + if (ifp->active && + ipv4ll_defaultroute(routes, ifp) == 1) + break; } +#endif return true; } @@ -460,11 +488,6 @@ ipv4_deladdr(struct ipv4_addr *addr, int keeparp) int r; struct ipv4_state *state; struct ipv4_addr *ap; -#ifdef ARP - struct arp_state *astate; -#else - UNUSED(keeparp); -#endif logdebugx("%s: deleting IP address %s", addr->iface->name, addr->saddr); @@ -476,8 +499,10 @@ ipv4_deladdr(struct ipv4_addr *addr, int keeparp) logerr("%s: %s", addr->iface->name, __func__); #ifdef ARP - if (!keeparp && (astate = arp_find(addr->iface, &addr->addr)) != NULL) - arp_free(astate); + if (!keeparp) + arp_freeaddr(addr->iface, &addr->addr); +#else + UNUSED(keeparp); #endif state = IPV4_STATE(addr->iface); @@ -515,6 +540,9 @@ delete_address(struct interface *ifp) ifo->options & DHCPCD_INFORM || (ifo->options & DHCPCD_STATIC && ifo->req_addr.s_addr == 0)) return 0; +#ifdef ARP + arp_freeaddr(ifp, &state->addr->addr); +#endif r = ipv4_deladdr(state->addr, 0); return r; } @@ -555,10 +583,12 @@ ipv4_aliasaddr(struct ipv4_addr *ia, struct ipv4_addr **repl) lun = 0; state = IPV4_STATE(ia->iface); find_lun: - if (lun == 0) - strlcpy(alias, ia->iface->name, sizeof(alias)); - else - snprintf(alias, sizeof(alias), "%s:%u", ia->iface->name, lun); + if (if_makealias(alias, IF_NAMESIZE, ia->iface->name, lun) >= + IF_NAMESIZE) + { + errno = ENOMEM; + return -1; + } TAILQ_FOREACH(iap, &state->addrs, next) { if (iap->alias[0] != '\0' && iap->addr.s_addr == INADDR_ANY) { /* No address assigned? Lets use it. */ @@ -587,7 +617,8 @@ find_lun: struct ipv4_addr * ipv4_addaddr(struct interface *ifp, const struct in_addr *addr, - const struct in_addr *mask, const struct in_addr *bcast) + const struct in_addr *mask, const struct in_addr *bcast, + uint32_t vltime, uint32_t pltime) { struct ipv4_state *state; struct ipv4_addr *ia; @@ -627,6 +658,13 @@ ipv4_addaddr(struct interface *ifp, const struct in_addr *addr, ia->mask = *mask; ia->brd = *bcast; +#ifdef IP_LIFETIME + ia->vltime = vltime; + ia->pltime = pltime; +#else + UNUSED(vltime); + UNUSED(pltime); +#endif snprintf(ia->saddr, sizeof(ia->saddr), "%s/%d", inet_ntoa(*addr), inet_ntocidr(*mask)); @@ -641,8 +679,10 @@ ipv4_addaddr(struct interface *ifp, const struct in_addr *addr, logdebugx("%s: aliased %s", ia->alias, ia->saddr); #endif - logdebugx("%s: adding IP address %s broadcast %s", - ifp->name, ia->saddr, inet_ntoa(*bcast)); + logdebugx("%s: adding IP address %s %s %s", + ifp->name, ia->saddr, + ifp->flags & IFF_POINTOPOINT ? "destination" : "broadcast", + inet_ntoa(*bcast)); if (if_address(RTM_NEWADDR, ia) == -1) { if (errno != EEXIST) logerr("%s: if_addaddress", @@ -669,7 +709,8 @@ ipv4_daddaddr(struct interface *ifp, const struct dhcp_lease *lease) struct dhcp_state *state; struct ipv4_addr *ia; - ia = ipv4_addaddr(ifp, &lease->addr, &lease->mask, &lease->brd); + ia = ipv4_addaddr(ifp, &lease->addr, &lease->mask, &lease->brd, + lease->leasetime, lease->rebindtime); if (ia == NULL) return -1; @@ -687,7 +728,6 @@ ipv4_applyaddr(void *arg) struct dhcp_lease *lease; struct if_options *ifo = ifp->options; struct ipv4_addr *ia; - int r; if (state == NULL) return; @@ -698,15 +738,13 @@ ipv4_applyaddr(void *arg) (DHCPCD_EXITING | DHCPCD_PERSISTENT)) { if (state->added) { - struct in_addr addr; - - addr = lease->addr; delete_address(ifp); rt_build(ifp->ctx, AF_INET); #ifdef ARP /* Announce the preferred address to * kick ARP caches. */ - arp_announceaddr(ifp->ctx, &addr); + if (!(ifp->flags & IFF_NOARP)) + arp_announceaddr(ifp->ctx,&lease->addr); #endif } script_runreason(ifp, state->reason); @@ -715,25 +753,36 @@ ipv4_applyaddr(void *arg) return; } - /* If the netmask or broadcast is different, re-add the addresss */ ia = ipv4_iffindaddr(ifp, &lease->addr, NULL); - if (ia && + /* If the netmask or broadcast is different, re-add the addresss. + * If IP addresses do not have lifetimes, there is a very real chance + * that re-adding them will scrub the subnet route temporarily + * which is a bad thing, so avoid it. */ + if (ia != NULL && ia->mask.s_addr == lease->mask.s_addr && ia->brd.s_addr == lease->brd.s_addr) + { +#ifndef IP_LIFETIME logdebugx("%s: IP address %s already exists", ifp->name, ia->saddr); - else { -#if __linux__ +#endif + } else { +#ifdef __linux__ /* Linux does not change netmask/broadcast address * for re-added addresses, so we need to delete the old one * first. */ if (ia != NULL) ipv4_deladdr(ia, 0); #endif - r = ipv4_daddaddr(ifp, lease); - if (r == -1 && errno != EEXIST) +#ifndef IP_LIFETIME + if (ipv4_daddaddr(ifp, lease) == -1 && errno != EEXIST) return; +#endif } +#ifdef IP_LIFETIME + if (ipv4_daddaddr(ifp, lease) == -1 && errno != EEXIST) + return; +#endif ia = ipv4_iffindaddr(ifp, &lease->addr, NULL); if (ia == NULL) { @@ -754,14 +803,11 @@ ipv4_applyaddr(void *arg) state->addr = ia; state->added = STATE_ADDED; - /* Find any freshly added routes, such as the subnet route. - * We do this because we cannot rely on recieving the kernel - * notification right now via our link socket. */ - if_initrt(ifp->ctx, AF_INET); rt_build(ifp->ctx, AF_INET); #ifdef ARP - arp_announceaddr(ifp->ctx, &state->addr->addr); + if (!(ifp->flags & IFF_NOARP)) + arp_announceaddr(ifp->ctx, &state->addr->addr); #endif if (state->state == DHS_BOUND) { @@ -880,6 +926,9 @@ ipv4_handleifa(struct dhcpcd_ctx *ctx, case RTM_DELADDR: if (ia == NULL) return; + if (mask->s_addr != INADDR_ANY && + mask->s_addr != ia->mask.s_addr) + return; TAILQ_REMOVE(&state->addrs, ia, next); break; default: @@ -887,10 +936,10 @@ ipv4_handleifa(struct dhcpcd_ctx *ctx, } if (addr->s_addr != INADDR_ANY && addr->s_addr != INADDR_BROADCAST) { -#ifdef ARP - arp_handleifa(cmd, ia); -#endif dhcp_handleifa(cmd, ia, pid); +#ifdef IPV4LL + ipv4ll_handleifa(cmd, ia, pid); +#endif } if (cmd == RTM_DELADDR) diff --git a/contrib/dhcpcd/src/ipv4.h b/contrib/dhcpcd/src/ipv4.h index b41958ec89..eaa0dcce9a 100644 --- a/contrib/dhcpcd/src/ipv4.h +++ b/contrib/dhcpcd/src/ipv4.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -62,9 +63,9 @@ * While it supports DaD, to seems to only expose IFF_DUPLICATE * so we have no way of knowing if it's tentative or not. * I don't even know if Solaris has any special treatment for tentative. */ -# define IN_IFF_TENTATIVE 0 +# define IN_IFF_TENTATIVE 0x01 # define IN_IFF_DUPLICATED 0x02 -# define IN_IFF_DETACHED 0 +# define IN_IFF_DETACHED 0x00 #endif #ifdef IN_IFF_TENTATIVE @@ -72,6 +73,13 @@ (IN_IFF_TENTATIVE | IN_IFF_DUPLICATED | IN_IFF_DETACHED) #endif +#define IN_ARE_ADDR_EQUAL(a, b) ((a)->s_addr == (b)->s_addr) +#define IN_IS_ADDR_UNSPECIFIED(a) ((a)->s_addr == INADDR_ANY) + +#ifdef __linux__ +#define IP_LIFETIME +#endif + struct ipv4_addr { TAILQ_ENTRY(ipv4_addr) next; struct in_addr addr; @@ -80,6 +88,10 @@ struct ipv4_addr { struct interface *iface; int addr_flags; unsigned int flags; +#ifdef IP_LIFETIME + uint32_t vltime; + uint32_t pltime; +#endif char saddr[INET_ADDRSTRLEN + 3]; #ifdef ALIAS_ADDR char alias[IF_NAMESIZE]; @@ -116,14 +128,15 @@ int inet_cidrtoaddr(int, struct in_addr *); uint32_t ipv4_getnetmask(uint32_t); int ipv4_hasaddr(const struct interface *); -bool inet_getroutes(struct dhcpcd_ctx *, struct rt_head *); +bool inet_getroutes(struct dhcpcd_ctx *, rb_tree_t *); #define STATE_ADDED 0x01 #define STATE_FAKE 0x02 int ipv4_deladdr(struct ipv4_addr *, int); struct ipv4_addr *ipv4_addaddr(struct interface *, - const struct in_addr *, const struct in_addr *, const struct in_addr *); + const struct in_addr *, const struct in_addr *, const struct in_addr *, + uint32_t, uint32_t); void ipv4_applyaddr(void *); struct ipv4_addr *ipv4_iffindaddr(struct interface *, @@ -139,11 +152,6 @@ void ipv4_handleifa(struct dhcpcd_ctx *, int, struct if_head *, const char *, int, pid_t); void ipv4_free(struct interface *); -#else -#define ipv4_sortinterfaces(a) {} -#define ipv4_applyaddr(a) {} -#define ipv4_free(a) {} -#define ipv4_hasaddr(a) (0) -#endif +#endif /* INET */ -#endif +#endif /* IPV4_H */ diff --git a/contrib/dhcpcd/src/ipv4ll.c b/contrib/dhcpcd/src/ipv4ll.c index 4d1d1c0a4c..4b0038b76f 100644 --- a/contrib/dhcpcd/src/ipv4ll.c +++ b/contrib/dhcpcd/src/ipv4ll.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -30,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +40,7 @@ #include "config.h" #include "arp.h" #include "common.h" +#include "dhcp.h" #include "eloop.h" #include "if.h" #include "if-options.h" @@ -47,7 +50,6 @@ #include "sa.h" #include "script.h" -#ifdef IPV4LL static const struct in_addr inaddr_llmask = { .s_addr = HTONL(LINKLOCAL_MASK) }; @@ -55,18 +57,21 @@ static const struct in_addr inaddr_llbcast = { .s_addr = HTONL(LINKLOCAL_BCAST) }; +static void ipv4ll_start1(struct interface *, struct arp_state *); + static in_addr_t -ipv4ll_pickaddr(struct arp_state *astate) +ipv4ll_pickaddr(struct interface *ifp) { struct in_addr addr; - struct ipv4ll_state *istate; + struct ipv4ll_state *state; - istate = IPV4LL_STATE(astate->iface); - setstate(istate->randomstate); + state = IPV4LL_STATE(ifp); + setstate(state->randomstate); do { long r; +again: /* RFC 3927 Section 2.1 states that the first 256 and * last 256 addresses are reserved for future use. * See ipv4ll_start for why we don't use arc4random. */ @@ -76,18 +81,18 @@ ipv4ll_pickaddr(struct arp_state *astate) ((uint32_t)(r % 0xFD00) + 0x0100)); /* No point using a failed address */ - if (addr.s_addr == astate->failed.s_addr) - continue; + if (IN_ARE_ADDR_EQUAL(&addr, &state->pickedaddr)) + goto again; /* Ensure we don't have the address on another interface */ - } while (ipv4_findaddr(astate->iface->ctx, &addr) != NULL); + } while (ipv4_findaddr(ifp->ctx, &addr) != NULL); /* Restore the original random state */ - setstate(istate->arp->iface->ctx->randomstate); + setstate(ifp->ctx->randomstate); return addr.s_addr; } int -ipv4ll_subnetroute(struct rt_head *routes, struct interface *ifp) +ipv4ll_subnetroute(rb_tree_t *routes, struct interface *ifp) { struct ipv4ll_state *state; struct rt *rt; @@ -108,12 +113,11 @@ ipv4ll_subnetroute(struct rt_head *routes, struct interface *ifp) in.s_addr = INADDR_ANY; sa_in_init(&rt->rt_gateway, &in); sa_in_init(&rt->rt_ifa, &state->addr->addr); - TAILQ_INSERT_TAIL(routes, rt, rt_next); - return 1; + return rt_proto_add(routes, rt) ? 1 : 0; } int -ipv4ll_defaultroute(struct rt_head *routes, struct interface *ifp) +ipv4ll_defaultroute(rb_tree_t *routes, struct interface *ifp) { struct ipv4ll_state *state; struct rt *rt; @@ -132,12 +136,11 @@ ipv4ll_defaultroute(struct rt_head *routes, struct interface *ifp) sa_in_init(&rt->rt_netmask, &in); sa_in_init(&rt->rt_gateway, &in); sa_in_init(&rt->rt_ifa, &state->addr->addr); - TAILQ_INSERT_TAIL(routes, rt, rt_next); - return 1; + return rt_proto_add(routes, rt) ? 1 : 0; } ssize_t -ipv4ll_env(char **env, const char *prefix, const struct interface *ifp) +ipv4ll_env(FILE *fp, const char *prefix, const struct interface *ifp) { const struct ipv4ll_state *state; const char *pf = prefix == NULL ? "" : "_"; @@ -147,204 +150,185 @@ ipv4ll_env(char **env, const char *prefix, const struct interface *ifp) if ((state = IPV4LL_CSTATE(ifp)) == NULL || state->addr == NULL) return 0; - if (env == NULL) - return 5; - /* Emulate a DHCP environment */ - if (asprintf(&env[0], "%s%sip_address=%s", + if (efprintf(fp, "%s%sip_address=%s", prefix, pf, inet_ntoa(state->addr->addr)) == -1) return -1; - if (asprintf(&env[1], "%s%ssubnet_mask=%s", + if (efprintf(fp, "%s%ssubnet_mask=%s", prefix, pf, inet_ntoa(state->addr->mask)) == -1) return -1; - if (asprintf(&env[2], "%s%ssubnet_cidr=%d", + if (efprintf(fp, "%s%ssubnet_cidr=%d", prefix, pf, inet_ntocidr(state->addr->mask)) == -1) return -1; - if (asprintf(&env[3], "%s%sbroadcast_address=%s", + if (efprintf(fp, "%s%sbroadcast_address=%s", prefix, pf, inet_ntoa(state->addr->brd)) == -1) return -1; netnum.s_addr = state->addr->addr.s_addr & state->addr->mask.s_addr; - if (asprintf(&env[4], "%s%snetwork_number=%s", + if (efprintf(fp, "%s%snetwork_number=%s", prefix, pf, inet_ntoa(netnum)) == -1) return -1; return 5; } static void -ipv4ll_probed(struct arp_state *astate) +ipv4ll_announced_arp(struct arp_state *astate) +{ + struct ipv4ll_state *state = IPV4LL_STATE(astate->iface); + + state->conflicts = 0; +#ifdef KERNEL_RFC5227 + arp_free(astate); +#endif +} + +static void +ipv4ll_arpfree(struct arp_state *astate) { - struct interface *ifp; struct ipv4ll_state *state; - struct ipv4_addr *ia; - assert(astate != NULL); - assert(astate->iface != NULL); + state = IPV4LL_STATE(astate->iface); + if (state != NULL && state->arp == astate) + state->arp = NULL; +} + +static void +ipv4ll_not_found(struct interface *ifp) +{ + struct ipv4ll_state *state; + struct ipv4_addr *ia; +#ifdef KERNEL_RFC5227 + struct arp_state *astate; +#endif - ifp = astate->iface; state = IPV4LL_STATE(ifp); assert(state != NULL); - ia = ipv4_iffindaddr(ifp, &astate->addr, &inaddr_llmask); + ia = ipv4_iffindaddr(ifp, &state->pickedaddr, &inaddr_llmask); #ifdef IN_IFF_NOTREADY if (ia == NULL || ia->addr_flags & IN_IFF_NOTREADY) #endif loginfox("%s: using IPv4LL address %s", - ifp->name, inet_ntoa(astate->addr)); + ifp->name, inet_ntoa(state->pickedaddr)); if (ia == NULL) { if (ifp->ctx->options & DHCPCD_TEST) goto test; - ia = ipv4_addaddr(ifp, &astate->addr, - &inaddr_llmask, &inaddr_llbcast); + ia = ipv4_addaddr(ifp, &state->pickedaddr, + &inaddr_llmask, &inaddr_llbcast, + DHCP_INFINITE_LIFETIME, DHCP_INFINITE_LIFETIME); } if (ia == NULL) return; #ifdef IN_IFF_NOTREADY if (ia->addr_flags & IN_IFF_NOTREADY) return; - logdebugx("%s: DAD completed for %s", - ifp->name, inet_ntoa(astate->addr)); + logdebugx("%s: DAD completed for %s", ifp->name, ia->saddr); #endif test: state->addr = ia; + state->down = false; if (ifp->ctx->options & DHCPCD_TEST) { script_runreason(ifp, "TEST"); eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS); return; } - timespecclear(&state->defend); - if_initrt(ifp->ctx, AF_INET); rt_build(ifp->ctx, AF_INET); - arp_announce(astate); +#ifdef KERNEL_RFC5227 + astate = arp_new(ifp, &ia->addr); + if (astate != NULL) { + astate->announced_cb = ipv4ll_announced_arp; + astate->free_cb = ipv4ll_arpfree; + arp_announce(astate); + } +#else + arp_announce(state->arp); +#endif script_runreason(ifp, "IPV4LL"); dhcpcd_daemonise(ifp->ctx); } static void -ipv4ll_announced(struct arp_state *astate) +ipv4ll_startifp(void *arg) { - struct ipv4ll_state *state = IPV4LL_STATE(astate->iface); + struct interface *ifp = arg; + struct ipv4ll_state *state; - state->conflicts = 0; - /* Need to keep the arp state so we can defend our IP. */ + state = IPV4LL_STATE(ifp); + ipv4ll_start1(ifp, state->arp); } static void -ipv4ll_probe(void *arg) +ipv4ll_found(struct interface *ifp) { + struct ipv4ll_state *state = IPV4LL_STATE(ifp); -#ifdef IN_IFF_TENTATIVE - ipv4ll_probed(arg); -#else - arp_probe(arg); -#endif + arp_cancel(state->arp); + if (++state->conflicts == MAX_CONFLICTS) + logerr("%s: failed to acquire an IPv4LL address", + ifp->name); + eloop_timeout_add_sec(ifp->ctx->eloop, + state->conflicts >= MAX_CONFLICTS ? + RATE_LIMIT_INTERVAL : PROBE_WAIT, + ipv4ll_startifp, ifp); } static void -ipv4ll_conflicted(struct arp_state *astate, const struct arp_msg *amsg) +ipv4ll_defend_failed(struct interface *ifp) +{ + struct ipv4ll_state *state = IPV4LL_STATE(ifp); + + ipv4_deladdr(state->addr, 1); + state->down = true; + state->addr = NULL; + rt_build(ifp->ctx, AF_INET); + script_runreason(ifp, "IPV4LL"); + ipv4ll_start1(ifp, state->arp); +} + +#ifndef KERNEL_RFC5227 +static void +ipv4ll_not_found_arp(struct arp_state *astate) { struct interface *ifp; struct ipv4ll_state *state; -#ifdef IN_IFF_DUPLICATED - struct ipv4_addr *ia; -#endif assert(astate != NULL); assert(astate->iface != NULL); + ifp = astate->iface; state = IPV4LL_STATE(ifp); assert(state != NULL); + assert(state->arp == astate); + ipv4ll_not_found(ifp); +} - /* - * NULL amsg means kernel detected DAD. - * We always fail on matching sip. - * We only fail on matching tip and we haven't added that address yet. - */ - if (amsg == NULL || - amsg->sip.s_addr == astate->addr.s_addr || - (amsg->sip.s_addr == 0 && amsg->tip.s_addr == astate->addr.s_addr - && ipv4_iffindaddr(ifp, &amsg->tip, NULL) == NULL)) - astate->failed = astate->addr; - else - return; - - arp_report_conflicted(astate, amsg); - - if (state->addr != NULL && - astate->failed.s_addr == state->addr->addr.s_addr) - { -#ifdef KERNEL_RFC5227 - logwarnx("%s: IPv4LL defence failed for %s", - ifp->name, state->addr->saddr); -#else - struct timespec now, defend; - - /* RFC 3927 Section 2.5 says a defence should - * broadcast an ARP announcement. - * Because the kernel will also unicast a reply to the - * hardware address which requested the IP address - * the other IPv4LL client will receieve two ARP - * messages. - * If another conflict happens within DEFEND_INTERVAL - * then we must drop our address and negotiate a new one. */ - defend.tv_sec = state->defend.tv_sec + DEFEND_INTERVAL; - defend.tv_nsec = state->defend.tv_nsec; - clock_gettime(CLOCK_MONOTONIC, &now); - if (timespeccmp(&defend, &now, >)) - logwarnx("%s: IPv4LL %d second defence failed for %s", - ifp->name, DEFEND_INTERVAL, state->addr->saddr); - else if (arp_request(ifp, - state->addr->addr.s_addr, state->addr->addr.s_addr) == -1) - logerr(__func__); - else { - logdebugx("%s: defended IPv4LL address %s", - ifp->name, state->addr->saddr); - state->defend = now; - return; - } -#endif - ipv4_deladdr(state->addr, 1); - state->down = 1; - state->addr = NULL; - script_runreason(ifp, "IPV4LL"); - } - -#ifdef IN_IFF_DUPLICATED - ia = ipv4_iffindaddr(ifp, &astate->addr, NULL); - if (ia != NULL && ia->addr_flags & IN_IFF_DUPLICATED) - ipv4_deladdr(ia, 1); -#endif +static void +ipv4ll_found_arp(struct arp_state *astate, __unused const struct arp_msg *amsg) +{ + struct interface *ifp = astate->iface; + struct ipv4ll_state *state = IPV4LL_STATE(ifp); - arp_cancel(astate); - if (++state->conflicts == MAX_CONFLICTS) - logerr("%s: failed to acquire an IPv4LL address", - ifp->name); - astate->addr.s_addr = ipv4ll_pickaddr(astate); - eloop_timeout_add_sec(ifp->ctx->eloop, - state->conflicts >= MAX_CONFLICTS ? - RATE_LIMIT_INTERVAL : PROBE_WAIT, - ipv4ll_probe, astate); + assert(state->arp == astate); + ipv4ll_found(ifp); } static void -ipv4ll_arpfree(struct arp_state *astate) +ipv4ll_defend_failed_arp(struct arp_state *astate) { - struct ipv4ll_state *state; + struct ipv4ll_state *state = IPV4LL_STATE(astate->iface); - state = IPV4LL_STATE(astate->iface); - if (state->arp == astate) - state->arp = NULL; + assert(state->arp == astate); + ipv4ll_defend_failed(astate->iface); } +#endif -void -ipv4ll_start(void *arg) +static void +ipv4ll_start1(struct interface *ifp, struct arp_state *astate) { - struct interface *ifp; struct ipv4ll_state *state; - struct arp_state *astate; struct ipv4_addr *ia; + bool repick; - assert(arg != NULL); - ifp = arg; + assert(ifp != NULL); if ((state = IPV4LL_STATE(ifp)) == NULL) { ifp->if_data[IF_DATA_IPV4LL] = calloc(1, sizeof(*state)); if ((state = IPV4LL_STATE(ifp)) == NULL) { @@ -353,14 +337,11 @@ ipv4ll_start(void *arg) } } - if (state->arp != NULL) - return; - /* RFC 3927 Section 2.1 states that the random number generator * SHOULD be seeded with a value derived from persistent information * such as the IEEE 802 MAC address so that it usually picks * the same address without persistent storage. */ - if (state->conflicts == 0) { + if (!state->seeded) { unsigned int seed; char *orig; @@ -380,98 +361,161 @@ ipv4ll_start(void *arg) /* Set back the original state until we need the seeded one. */ setstate(ifp->ctx->randomstate); + state->seeded = true; } - if ((astate = arp_new(ifp, NULL)) == NULL) - return; +#ifndef KERNEL_RFC5227 + if (astate == NULL) { + if (state->arp != NULL) + return; + if ((astate = arp_new(ifp, NULL)) == NULL) + return; + astate->found_cb = ipv4ll_found_arp; + astate->not_found_cb = ipv4ll_not_found_arp; + astate->announced_cb = ipv4ll_announced_arp; + astate->defend_failed_cb = ipv4ll_defend_failed_arp; + astate->free_cb = ipv4ll_arpfree; + state->arp = astate; + } else + assert(state->arp == astate); +#else + UNUSED(astate); +#endif + + state->down = true; - state->arp = astate; - astate->probed_cb = ipv4ll_probed; - astate->announced_cb = ipv4ll_announced; - astate->conflicted_cb = ipv4ll_conflicted; - astate->free_cb = ipv4ll_arpfree; + /* Find the previosuly used address. */ + if (state->pickedaddr.s_addr != INADDR_ANY) + ia = ipv4_iffindaddr(ifp, &state->pickedaddr, NULL); + else + ia = NULL; /* Find an existing IPv4LL address and ensure we can work with it. */ - ia = ipv4_iffindlladdr(ifp); + if (ia == NULL) + ia = ipv4_iffindlladdr(ifp); -#ifdef IN_IFF_TENTATIVE + repick = false; +#ifdef IN_IFF_DUPLICATED if (ia != NULL && ia->addr_flags & IN_IFF_DUPLICATED) { + state->pickedaddr = ia->addr; /* So it's not picked again. */ + repick = true; ipv4_deladdr(ia, 0); ia = NULL; } #endif + state->addr = ia; if (ia != NULL) { + state->pickedaddr = ia->addr; +#ifndef KERNEL_RFC5227 astate->addr = ia->addr; +#endif #ifdef IN_IFF_TENTATIVE if (ia->addr_flags & (IN_IFF_TENTATIVE | IN_IFF_DETACHED)) { loginfox("%s: waiting for DAD to complete on %s", ifp->name, inet_ntoa(ia->addr)); return; } +#endif +#ifdef IN_IFF_DUPLICATED loginfox("%s: using IPv4LL address %s", ifp->name, ia->saddr); #endif - ipv4ll_probed(astate); + ipv4ll_not_found(ifp); return; } loginfox("%s: probing for an IPv4LL address", ifp->name); - astate->addr.s_addr = ipv4ll_pickaddr(astate); -#ifdef IN_IFF_TENTATIVE - ipv4ll_probed(astate); + if (repick || state->pickedaddr.s_addr == INADDR_ANY) + state->pickedaddr.s_addr = ipv4ll_pickaddr(ifp); +#ifndef KERNEL_RFC5227 + astate->addr = state->pickedaddr; +#endif +#ifdef IN_IFF_DUPLICATED + ipv4ll_not_found(ifp); #else arp_probe(astate); #endif } void -ipv4ll_freedrop(struct interface *ifp, int drop) +ipv4ll_start(void *arg) +{ + + ipv4ll_start1(arg, NULL); +} + +static void +ipv4ll_freearp(struct interface *ifp) { struct ipv4ll_state *state; - int dropped; - assert(ifp != NULL); state = IPV4LL_STATE(ifp); - dropped = 0; + if (state == NULL || state->arp == NULL) + return; - /* Free ARP state first because ipv4_deladdr might also ... */ - if (state && state->arp) { - eloop_timeout_delete(ifp->ctx->eloop, NULL, state->arp); - arp_free(state->arp); - state->arp = NULL; - } + eloop_timeout_delete(ifp->ctx->eloop, NULL, state->arp); + arp_free(state->arp); +} - if (drop && (ifp->options->options & DHCPCD_NODROP) != DHCPCD_NODROP) { - struct ipv4_state *istate; +void +ipv4ll_drop(struct interface *ifp) +{ + struct ipv4ll_state *state; + bool dropped = false; + struct ipv4_state *istate; - if (state && state->addr != NULL) { - ipv4_deladdr(state->addr, 1); - state->addr = NULL; - dropped = 1; - } + assert(ifp != NULL); - /* Free any other link local addresses that might exist. */ - if ((istate = IPV4_STATE(ifp)) != NULL) { - struct ipv4_addr *ia, *ian; + ipv4ll_freearp(ifp); - TAILQ_FOREACH_SAFE(ia, &istate->addrs, next, ian) { - if (IN_LINKLOCAL(ntohl(ia->addr.s_addr))) { - ipv4_deladdr(ia, 0); - dropped = 1; - } - } - } + if ((ifp->options->options & DHCPCD_NODROP) == DHCPCD_NODROP) + return; + + state = IPV4LL_STATE(ifp); + if (state && state->addr != NULL) { + ipv4_deladdr(state->addr, 1); + state->addr = NULL; + dropped = true; } - if (state) { - free(state); - ifp->if_data[IF_DATA_IPV4LL] = NULL; + /* Free any other link local addresses that might exist. */ + if ((istate = IPV4_STATE(ifp)) != NULL) { + struct ipv4_addr *ia, *ian; - if (dropped) { - rt_build(ifp->ctx, AF_INET); - script_runreason(ifp, "IPV4LL"); + TAILQ_FOREACH_SAFE(ia, &istate->addrs, next, ian) { + if (IN_LINKLOCAL(ntohl(ia->addr.s_addr))) { + ipv4_deladdr(ia, 0); + dropped = true; + } } } + + if (dropped) { + rt_build(ifp->ctx, AF_INET); + script_runreason(ifp, "IPV4LL"); + } +} + +void +ipv4ll_reset(struct interface *ifp) +{ + struct ipv4ll_state *state = IPV4LL_STATE(ifp); + + if (state == NULL) + return; + state->pickedaddr.s_addr = INADDR_ANY; + state->seeded = false; +} + +void +ipv4ll_free(struct interface *ifp) +{ + + assert(ifp != NULL); + + ipv4ll_freearp(ifp); + free(IPV4LL_STATE(ifp)); + ifp->if_data[IF_DATA_IPV4LL] = NULL; } /* This may cause issues in BSD systems, where running as a single dhcpcd @@ -483,10 +527,6 @@ ipv4ll_recvrt(__unused int cmd, const struct rt *rt) struct dhcpcd_ctx *ctx; struct interface *ifp; - /* Ignore route init. */ - if (rt->rt_dflags & RTDF_INIT) - return 0; - /* Only interested in default route changes. */ if (sa_is_unspecified(&rt->rt_dest)) return 0; @@ -495,7 +535,6 @@ ipv4ll_recvrt(__unused int cmd, const struct rt *rt) ctx = rt->rt_ifp->ctx; TAILQ_FOREACH(ifp, ctx->ifaces, next) { if (IPV4LL_STATE_RUNNING(ifp)) { - if_initrt(ctx, AF_INET); rt_build(ctx, AF_INET); break; } @@ -504,4 +543,39 @@ ipv4ll_recvrt(__unused int cmd, const struct rt *rt) return 0; } #endif + +void +ipv4ll_handleifa(int cmd, struct ipv4_addr *ia, pid_t pid) +{ + struct interface *ifp; + struct ipv4ll_state *state; + + ifp = ia->iface; + state = IPV4LL_STATE(ifp); + if (state == NULL) + return; + + if (cmd == RTM_DELADDR && + state->addr != NULL && + IN_ARE_ADDR_EQUAL(&state->addr->addr, &ia->addr)) + { + loginfox("%s: pid %d deleted IP address %s", + ifp->name, pid, ia->saddr); + ipv4ll_defend_failed(ifp); + return; + } + +#ifdef IN_IFF_DUPLICATED + if (cmd != RTM_NEWADDR) + return; + if (!IN_ARE_ADDR_EQUAL(&state->pickedaddr, &ia->addr)) + return; + if (!(ia->addr_flags & IN_IFF_NOTUSEABLE)) + ipv4ll_not_found(ifp); + else if (ia->addr_flags & IN_IFF_DUPLICATED) { + logerrx("%s: DAD detected %s", ifp->name, ia->saddr); + ipv4_deladdr(state->addr, 1); + ipv4ll_found(ifp); + } #endif +} diff --git a/contrib/dhcpcd/src/ipv4ll.h b/contrib/dhcpcd/src/ipv4ll.h index 6ce1bef2d2..dd68d30a1d 100644 --- a/contrib/dhcpcd/src/ipv4ll.h +++ b/contrib/dhcpcd/src/ipv4ll.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -40,12 +41,13 @@ #include "arp.h" struct ipv4ll_state { + struct in_addr pickedaddr; struct ipv4_addr *addr; struct arp_state *arp; - unsigned int conflicts; - struct timespec defend; char randomstate[128]; - uint8_t down; + bool seeded; + bool down; + size_t conflicts; }; #define IPV4LL_STATE(ifp) \ @@ -56,25 +58,20 @@ struct ipv4ll_state { (IPV4LL_CSTATE((ifp)) && !IPV4LL_CSTATE((ifp))->down && \ (IPV4LL_CSTATE((ifp))->addr != NULL)) -int ipv4ll_subnetroute(struct rt_head *, struct interface *); -int ipv4ll_defaultroute(struct rt_head *,struct interface *); -ssize_t ipv4ll_env(char **, const char *, const struct interface *); +int ipv4ll_subnetroute(rb_tree_t *, struct interface *); +int ipv4ll_defaultroute(rb_tree_t *,struct interface *); +ssize_t ipv4ll_env(FILE *, const char *, const struct interface *); void ipv4ll_start(void *); void ipv4ll_claimed(void *); void ipv4ll_handle_failure(void *); +void ipv4ll_handleifa(int, struct ipv4_addr *, pid_t pid); #ifdef HAVE_ROUTE_METRIC int ipv4ll_recvrt(int, const struct rt *); #endif -#define ipv4ll_free(ifp) ipv4ll_freedrop((ifp), 0); -#define ipv4ll_drop(ifp) ipv4ll_freedrop((ifp), 1); -void ipv4ll_freedrop(struct interface *, int); -#else -#define IPV4LL_STATE_RUNNING(ifp) (0) -#define ipv4ll_subnetroute(route, ifp) (0) -#define ipv4ll_defaultroute(route, ifp) (0) -#define ipv4ll_handlert(a, b, c) (0) -#define ipv4ll_free(a) {} +void ipv4ll_reset(struct interface *); +void ipv4ll_drop(struct interface *); +void ipv4ll_free(struct interface *); #endif #endif diff --git a/contrib/dhcpcd/src/ipv6.c b/contrib/dhcpcd/src/ipv6.c index 49de68607a..ca54dab91a 100644 --- a/contrib/dhcpcd/src/ipv6.c +++ b/contrib/dhcpcd/src/ipv6.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -30,6 +31,7 @@ #include #include +#include #include #include #include @@ -128,30 +130,17 @@ int ipv6_init(struct dhcpcd_ctx *ctx) { - if (ctx->sndhdr.msg_iovlen == 1) + if (ctx->ra_routers != NULL) return 0; - if (ctx->ra_routers == NULL) { - ctx->ra_routers = malloc(sizeof(*ctx->ra_routers)); - if (ctx->ra_routers == NULL) - return -1; - } + ctx->ra_routers = malloc(sizeof(*ctx->ra_routers)); + if (ctx->ra_routers == NULL) + return -1; TAILQ_INIT(ctx->ra_routers); - ctx->sndhdr.msg_namelen = sizeof(struct sockaddr_in6); - ctx->sndhdr.msg_iov = ctx->sndiov; - ctx->sndhdr.msg_iovlen = 1; - ctx->sndhdr.msg_control = ctx->sndbuf; - ctx->sndhdr.msg_controllen = sizeof(ctx->sndbuf); - ctx->rcvhdr.msg_name = &ctx->from; - ctx->rcvhdr.msg_namelen = sizeof(ctx->from); - ctx->rcvhdr.msg_iov = ctx->iov; - ctx->rcvhdr.msg_iovlen = 1; - ctx->rcvhdr.msg_control = ctx->ctlbuf; - // controllen is set at recieve - //ctx->rcvhdr.msg_controllen = sizeof(ctx->rcvbuf); - +#ifndef __sun ctx->nd_fd = -1; +#endif ctx->dhcp6_fd = -1; return 0; } @@ -626,31 +615,39 @@ ipv6_deleteaddr(struct ipv6_addr *ia) break; } } + +#ifdef ND6_ADVERTISE + /* Advertise the address if it exists on another interface. */ + ipv6nd_advertise(ia); +#endif } static int ipv6_addaddr1(struct ipv6_addr *ia, const struct timespec *now) { struct interface *ifp; - struct ipv6_state *state; - struct ipv6_addr *ia2; uint32_t pltime, vltime; __printflike(1, 2) void (*logfunc)(const char *, ...); +#ifdef ND6_ADVERTISE + bool vltime_was_zero = ia->prefix_vltime == 0; +#endif +#ifdef __sun + struct ipv6_state *state; + struct ipv6_addr *ia2; - /* Ensure no other interface has this address */ - TAILQ_FOREACH(ifp, ia->iface->ctx->ifaces, next) { - if (ifp == ia->iface) - continue; - state = IPV6_STATE(ifp); - if (state == NULL) - continue; - TAILQ_FOREACH(ia2, &state->addrs, next) { - if (IN6_ARE_ADDR_EQUAL(&ia2->addr, &ia->addr)) { - ipv6_deleteaddr(ia2); - break; - } - } + /* If we re-add then address on Solaris then the prefix + * route will be scrubbed and re-added. Something might + * be using it, so let's avoid it. */ + if (ia->flags & IPV6_AF_DADCOMPLETED) { + logdebugx("%s: IP address %s already exists", + ia->iface->name, ia->saddr); +#ifdef ND6_ADVERTISE + goto advertise; +#else + return 0; +#endif } +#endif /* Remember the interface of the address. */ ifp = ia->iface; @@ -714,7 +711,6 @@ ipv6_addaddr1(struct ipv6_addr *ia, const struct timespec *now) " seconds", ifp->name, ia->prefix_pltime, ia->prefix_vltime); - if (if_address6(RTM_NEWADDR, ia) == -1) { logerr(__func__); /* Restore real pltime and vltime */ @@ -778,17 +774,26 @@ ipv6_addaddr1(struct ipv6_addr *ia, const struct timespec *now) } #endif +#ifdef ND6_ADVERTISE +#ifdef __sun +advertise: +#endif + /* Re-advertise the preferred address to be safe. */ + if (!vltime_was_zero) + ipv6nd_advertise(ia); +#endif + return 0; } #ifdef ALIAS_ADDR -/* Find the next logical aliase address we can use. */ +/* Find the next logical alias address we can use. */ static int ipv6_aliasaddr(struct ipv6_addr *ia, struct ipv6_addr **repl) { struct ipv6_state *state; struct ipv6_addr *iap; - unsigned int unit; + unsigned int lun; char alias[IF_NAMESIZE]; if (ia->alias[0] != '\0') @@ -807,12 +812,14 @@ ipv6_aliasaddr(struct ipv6_addr *ia, struct ipv6_addr **repl) } } - unit = 0; + lun = 0; find_unit: - if (unit == 0) - strlcpy(alias, ia->iface->name, sizeof(alias)); - else - snprintf(alias, sizeof(alias), "%s:%u", ia->iface->name, unit); + if (if_makealias(alias, IF_NAMESIZE, ia->iface->name, lun) >= + IF_NAMESIZE) + { + errno = ENOMEM; + return -1; + } TAILQ_FOREACH(iap, &state->addrs, next) { if (iap->alias[0] == '\0') continue; @@ -828,11 +835,11 @@ find_unit: } if (iap != NULL) { - if (unit == UINT_MAX) { + if (lun == UINT_MAX) { errno = ERANGE; return -1; } - unit++; + lun++; goto find_unit; } @@ -891,10 +898,14 @@ ipv6_findaddrmatch(const struct ipv6_addr *addr, const struct in6_addr *match, struct ipv6_addr * ipv6_findaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr, unsigned int flags) { - struct ipv6_addr *dap, *nap; + struct ipv6_addr *nap; +#ifdef DHCP6 + struct ipv6_addr *dap; +#endif - dap = dhcp6_findaddr(ctx, addr, flags); nap = ipv6nd_findaddr(ctx, addr, flags); +#ifdef DHCP6 + dap = dhcp6_findaddr(ctx, addr, flags); if (!dap && !nap) return NULL; if (dap && !nap) @@ -904,12 +915,15 @@ ipv6_findaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr, unsigned int if (nap->iface->metric < dap->iface->metric) return nap; return dap; +#else + return nap; +#endif } ssize_t ipv6_addaddrs(struct ipv6_addrhead *addrs) { - struct ipv6_addr *ap, *apn, *apf; + struct ipv6_addr *ap, *apn; ssize_t i; struct timespec now; @@ -935,27 +949,6 @@ ipv6_addaddrs(struct ipv6_addrhead *addrs) } else if (!(ap->flags & IPV6_AF_STALE) && !IN6_IS_ADDR_UNSPECIFIED(&ap->addr)) { - apf = ipv6_findaddr(ap->iface->ctx, - &ap->addr, IPV6_AF_ADDED); - if (apf && apf->iface != ap->iface) { - if (apf->iface->metric <= ap->iface->metric) { - loginfox("%s: preferring %s on %s", - ap->iface->name, - ap->saddr, - apf->iface->name); - continue; - } - loginfox("%s: preferring %s on %s", - apf->iface->name, - ap->saddr, - ap->iface->name); - if (if_address6(RTM_DELADDR, apf) == -1 && - errno != EADDRNOTAVAIL && errno != ENXIO) - logerr(__func__); - apf->flags &= - ~(IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED); - } else if (apf) - apf->flags &= ~IPV6_AF_ADDED; if (ap->flags & IPV6_AF_NEW) i++; if (!timespecisset(&now)) @@ -989,6 +982,7 @@ ipv6_freeaddr(struct ipv6_addr *ia) } eloop_q_timeout_delete(ia->iface->ctx->eloop, 0, NULL, ia); + free(ia->na); free(ia); } @@ -1070,6 +1064,31 @@ ipv6_getstate(struct interface *ifp) return state; } +struct ipv6_addr * +ipv6_ifanyglobal(struct interface *ifp) +{ + struct ipv6_state *state; + struct ipv6_addr *ia; + + if (ifp->carrier == LINK_DOWN) + return NULL; + + state = IPV6_STATE(ifp); + if (state == NULL) + return NULL; + + TAILQ_FOREACH(ia, &state->addrs, next) { + if (IN6_IS_ADDR_LINKLOCAL(&ia->addr)) + continue; + /* Let's be optimistic. + * Any decent OS won't forward or accept traffic + * from/to tentative or detached addresses. */ + if (!(ia->addr_flags & IN6_IFF_DUPLICATED)) + break; + } + return ia; +} + void ipv6_handleifa(struct dhcpcd_ctx *ctx, int cmd, struct if_head *ifs, const char *ifname, @@ -1079,6 +1098,22 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx, struct ipv6_state *state; struct ipv6_addr *ia; struct ll_callback *cb; + bool anyglobal; + +#ifdef __sun + struct sockaddr_in6 subnet; + + /* Solaris on-link route is an unspecified address! */ + if (IN6_IS_ADDR_UNSPECIFIED(addr)) { + if (if_getsubnet(ctx, ifname, AF_INET6, + &subnet, sizeof(subnet)) == -1) + { + logerr(__func__); + return; + } + addr = &subnet.sin6_addr; + } +#endif #if 0 char dbuf[INET6_ADDRSTRLEN]; @@ -1086,8 +1121,8 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx, dbp = inet_ntop(AF_INET6, &addr->s6_addr, dbuf, INET6_ADDRSTRLEN); - loginfox("%s: cmd %d addr %s", - ifname, cmd, dbp); + loginfox("%s: cmd %d addr %s addrflags %d", + ifname, cmd, dbp, addrflags); #endif if (ifs == NULL) @@ -1098,6 +1133,7 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx, return; if ((state = ipv6_getstate(ifp)) == NULL) return; + anyglobal = ipv6_ifanyglobal(ifp) != NULL; TAILQ_FOREACH(ia, &state->addrs, next) { if (IN6_ARE_ADDR_EQUAL(&ia->addr, addr)) @@ -1108,6 +1144,11 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx, case RTM_DELADDR: if (ia != NULL) { TAILQ_REMOVE(&state->addrs, ia, next); +#ifdef ND6_ADVERTISE + /* Advertise the address if it exists on + * another interface. */ + ipv6nd_advertise(ia); +#endif /* We'll free it at the end of the function. */ } break; @@ -1192,6 +1233,7 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx, if (ia == NULL) return; + ctx->options &= ~DHCPCD_RTBUILD; ipv6nd_handleifa(cmd, ia, pid); #ifdef DHCP6 dhcp6_handleifa(cmd, ia, pid); @@ -1201,6 +1243,17 @@ out: /* Done with the ia now, so free it. */ if (cmd == RTM_DELADDR) ipv6_freeaddr(ia); + else if (!(ia->addr_flags & IN6_IFF_NOTUSEABLE)) + ia->flags |= IPV6_AF_DADCOMPLETED; + + /* If we've not already called rt_build via the IPv6ND + * or DHCP6 handlers and the existance of any useable + * global address on the interface has changed, + * call rt_build to add/remove the default route. */ + if (ifp->active && ifp->options->options & DHCPCD_IPV6 && + !(ctx->options & DHCPCD_RTBUILD) && + (ipv6_ifanyglobal(ifp) != NULL) != anyglobal) + rt_build(ctx, AF_INET6); } int @@ -1209,8 +1262,10 @@ ipv6_hasaddr(const struct interface *ifp) if (ipv6nd_iffindaddr(ifp, NULL, 0) != NULL) return 1; +#ifdef DHCP6 if (dhcp6_iffindaddr(ifp, NULL, 0) != NULL) return 1; +#endif return 0; } @@ -1477,8 +1532,10 @@ ipv6_newaddr(struct interface *ifp, const struct in6_addr *addr, goto err; ia->iface = ifp; - ia->flags = IPV6_AF_NEW | flags; ia->addr_flags = addr_flags; + ia->flags = IPV6_AF_NEW | flags; + if (!(ia->addr_flags & IN6_IFF_NOTUSEABLE)) + ia->flags |= IPV6_AF_DADCOMPLETED; ia->prefix_len = prefix_len; ia->dhcp6_fd = -1; @@ -1497,7 +1554,13 @@ ipv6_newaddr(struct interface *ifp, const struct in6_addr *addr, goto err; } else if (ia->flags & IPV6_AF_RAPFX) { ia->prefix = *addr; +#ifdef __sun + ia->addr = *addr; + cbp = inet_ntop(AF_INET6, &ia->addr, buf, sizeof(buf)); + goto paddr; +#else return ia; +#endif } else if (ia->flags & (IPV6_AF_REQUEST | IPV6_AF_DELEGATEDPFX) && prefix_len != 128) { @@ -1563,23 +1626,17 @@ ipv6_staticdadcallback(void *arg) } ssize_t -ipv6_env(char **env, const char *prefix, const struct interface *ifp) +ipv6_env(FILE *fp, const char *prefix, const struct interface *ifp) { - char **ep; - ssize_t n; struct ipv6_addr *ia; - ep = env; - n = 0; ia = ipv6_iffindaddr(UNCONST(ifp), &ifp->options->req_addr6, IN6_IFF_NOTUSEABLE); - if (ia) { - if (env) - addvar(&ep, prefix, "ip6_address", ia->saddr); - n++; - } - - return n; + if (ia == NULL) + return 0; + if (efprintf(fp, "%s_ip6_address=%s", prefix, ia->saddr) == -1) + return -1; + return 1; } int @@ -1635,7 +1692,6 @@ ipv6_startstatic(struct interface *ifp) ia->prefix_pltime = ND6_INFINITE_LIFETIME; ia->dadcallback = ipv6_staticdadcallback; ipv6_addaddr(ia, NULL); - if_initrt(ifp->ctx, AF_INET6); rt_build(ifp->ctx, AF_INET6); if (run_script) script_runreason(ifp, "STATIC6"); @@ -1677,8 +1733,6 @@ ipv6_start(struct interface *ifp) ipv6_regentempifid(ifp); } - /* Load existing routes */ - if_initrt(ifp->ctx, AF_INET6); return 0; } @@ -1702,10 +1756,8 @@ ipv6_freedrop(struct interface *ifp, int drop) ipv6_freedrop_addrs(&state->addrs, drop ? 2 : 0, NULL); if (drop) { - if (ifp->ctx->ra_routers != NULL) { - if_initrt(ifp->ctx, AF_INET6); + if (ifp->ctx->ra_routers != NULL) rt_build(ifp->ctx, AF_INET6); - } } else { /* Because we need to cache the addresses we don't control, * we only free the state on when NOT dropping addresses. */ @@ -2177,13 +2229,11 @@ inet6_makeprefix(struct interface *ifp, const struct ra *rap, } /* There is no point in trying to manage a /128 prefix, - * ones without a lifetime or ones not on link or delegated */ - if (addr->prefix_len == 128 || - addr->prefix_vltime == 0 || - !(addr->flags & (IPV6_AF_ONLINK | IPV6_AF_DELEGATEDPFX))) + * ones without a lifetime. */ + if (addr->prefix_len == 128 || addr->prefix_vltime == 0) return NULL; - /* Don't install a reject route when not creating bigger prefixes */ + /* Don't install a reject route when not creating bigger prefixes. */ if (addr->flags & IPV6_AF_NOREJECT) return NULL; @@ -2215,7 +2265,9 @@ inet6_makeprefix(struct interface *ifp, const struct ra *rap, #ifndef __linux__ sa_in6_init(&rt->rt_gateway, &in6addr_loopback); #endif - } else + } else if (!(addr->flags & IPV6_AF_ONLINK)) + sa_in6_init(&rt->rt_gateway, &rap->from); + else rt->rt_gateway.sa_family = AF_UNSPEC; sa_in6_init(&rt->rt_ifa, &addr->addr); return rt; @@ -2239,7 +2291,7 @@ inet6_makerouter(struct ra *rap) IN6_ARE_ADDR_EQUAL(&((rtp)->mask), &in6addr_any)) static int -inet6_staticroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx) +inet6_staticroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx) { struct interface *ifp; struct ipv6_state *state; @@ -2255,7 +2307,7 @@ inet6_staticroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx) { rt = inet6_makeprefix(ifp, NULL, ia); if (rt) - TAILQ_INSERT_TAIL(routes, rt, rt_next); + rt_proto_add(routes, rt); } } } @@ -2263,15 +2315,14 @@ inet6_staticroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx) } static int -inet6_raroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx, int expired, - bool *have_default) +inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx) { struct rt *rt; struct ra *rap; const struct ipv6_addr *addr; TAILQ_FOREACH(rap, ctx->ra_routers, next) { - if (rap->expired != expired) + if (rap->expired) continue; TAILQ_FOREACH(addr, &rap->addrs, next) { if (addr->prefix_vltime == 0) @@ -2279,24 +2330,25 @@ inet6_raroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx, int expired, rt = inet6_makeprefix(rap->iface, rap, addr); if (rt) { rt->rt_dflags |= RTDF_RA; - TAILQ_INSERT_TAIL(routes, rt, rt_next); - } - } - if (rap->lifetime) { - rt = inet6_makerouter(rap); - if (rt) { - rt->rt_dflags |= RTDF_RA; - TAILQ_INSERT_TAIL(routes, rt, rt_next); - if (have_default) - *have_default = true; + rt_proto_add(routes, rt); } } + if (rap->lifetime == 0) + continue; + if (ipv6_ifanyglobal(rap->iface) == NULL) + continue; + rt = inet6_makerouter(rap); + if (rt == NULL) + continue; + rt->rt_dflags |= RTDF_RA; + rt_proto_add(routes, rt); } return 0; } +#ifdef DHCP6 static int -inet6_dhcproutes(struct rt_head *routes, struct dhcpcd_ctx *ctx, +inet6_dhcproutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx, enum DH6S dstate) { struct interface *ifp; @@ -2309,52 +2361,37 @@ inet6_dhcproutes(struct rt_head *routes, struct dhcpcd_ctx *ctx, if (d6_state && d6_state->state == dstate) { TAILQ_FOREACH(addr, &d6_state->addrs, next) { rt = inet6_makeprefix(ifp, NULL, addr); - if (rt) { - rt->rt_dflags |= RTDF_DHCP; - TAILQ_INSERT_TAIL(routes, rt, rt_next); - } + if (rt == NULL) + continue; + rt->rt_dflags |= RTDF_DHCP; + rt_proto_add(routes, rt); } } } return 0; } +#endif bool -inet6_getroutes(struct dhcpcd_ctx *ctx, struct rt_head *routes) +inet6_getroutes(struct dhcpcd_ctx *ctx, rb_tree_t *routes) { - bool have_default; /* Should static take priority? */ if (inet6_staticroutes(routes, ctx) == -1) return false; /* First add reachable routers and their prefixes */ - have_default = false; - if (inet6_raroutes(routes, ctx, 0, &have_default) == -1) + if (inet6_raroutes(routes, ctx) == -1) return false; +#ifdef DHCP6 /* We have no way of knowing if prefixes added by DHCP are reachable * or not, so we have to assume they are. - * Add bound before delegated so we can prefer interfaces better */ + * Add bound before delegated so we can prefer interfaces better. */ if (inet6_dhcproutes(routes, ctx, DH6S_BOUND) == -1) return false; if (inet6_dhcproutes(routes, ctx, DH6S_DELEGATED) == -1) return false; - -#ifdef HAVE_ROUTE_METRIC - /* If we have an unreachable router, we really do need to remove the - * route to it beause it could be a lower metric than a reachable - * router. Of course, we should at least have some routers if all - * are unreachable. */ - if (!have_default) { -#endif - /* Add our non-reachable routers and prefixes - * Unsure if this is needed, but it's a close match to kernel - * behaviour */ - if (inet6_raroutes(routes, ctx, 1, NULL) == -1) - return false; -#ifdef HAVE_ROUTE_METRIC - } #endif return true; diff --git a/contrib/dhcpcd/src/ipv6.h b/contrib/dhcpcd/src/ipv6.h index ef740ba4a2..7dbf4fe692 100644 --- a/contrib/dhcpcd/src/ipv6.h +++ b/contrib/dhcpcd/src/ipv6.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -44,8 +45,6 @@ # endif #endif -#define ALLROUTERS "ff02::2" - #define EUI64_GBIT 0x01 #define EUI64_UBIT 0x02 #define EUI64_TO_IFID(in6) do {(in6)->s6_addr[8] ^= EUI64_UBIT; } while (0) @@ -76,6 +75,17 @@ (((d)->s6_addr32[3] ^ (a)->s6_addr32[3]) & (m)->s6_addr32[3]) == 0 ) #endif +#ifndef IN6ADDR_LINKLOCAL_ALLNODES_INIT +#define IN6ADDR_LINKLOCAL_ALLNODES_INIT \ + {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}} +#endif +#ifndef IN6ADDR_LINKLOCAL_ALLROUTERS_INIT +#define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \ + {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}} +#endif + /* * BSD kernels don't inform userland of DAD results. * See the discussion here: @@ -90,7 +100,8 @@ #endif /* This was fixed in NetBSD */ -#if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 699002000 +#if (defined(__DragonFly_version) && __DragonFly_version >= 500704) || \ + (defined(__NetBSD_Version__) && __NetBSD_Version__ >= 699002000) # undef IPV6_POLLADDRFLAG #endif @@ -99,9 +110,9 @@ * While it supports DaD, to seems to only expose IFF_DUPLICATE * so we have no way of knowing if it's tentative or not. * I don't even know if Solaris has any special treatment for tentative. */ -# define IN6_IFF_TENTATIVE 0 +# define IN6_IFF_TENTATIVE 0x02 # define IN6_IFF_DUPLICATED 0x04 -# define IN6_IFF_DETACHED 0 +# define IN6_IFF_DETACHED 0x00 #endif #define IN6_IFF_NOTUSEABLE \ @@ -139,6 +150,20 @@ # define IN6_IFF_DETACHED 0 #endif +/* + * ND6 Advertising is only used for IP address sharing to prefer + * the address on a specific interface. + * This just fails to work on OpenBSD and causes erroneous duplicate + * address messages on BSD's other then DragonFly and NetBSD. + */ +#if !defined(SMALL) && \ + ((defined(__DragonFly_version) && __DragonFly_version >= 500703) || \ + (defined(__NetBSD_Version__) && __NetBSD_Version__ >= 899002800) || \ + defined(__linux__) || defined(__sun)) +# define ND6_ADVERTISE +#endif + +#ifdef INET6 TAILQ_HEAD(ipv6_addrhead, ipv6_addr); struct ipv6_addr { TAILQ_ENTRY(ipv6_addr) next; @@ -169,6 +194,10 @@ struct ipv6_addr { void (*dadcallback)(void *); int dadcounter; + struct nd_neighbor_advert *na; + size_t na_len; + int na_count; + #ifdef ALIAS_ADDR char alias[IF_NAMESIZE]; #endif @@ -218,7 +247,6 @@ struct ipv6_state { ((const struct ipv6_state *)(ifp)->if_data[IF_DATA_IPV6]) #define IPV6_STATE_RUNNING(ifp) ipv6_staticdadcompleted((ifp)) -#ifdef INET6 int ipv6_init(struct dhcpcd_ctx *); int ipv6_makestableprivate(struct in6_addr *addr, @@ -245,6 +273,7 @@ int ipv6_handleifa_addrs(int, struct ipv6_addrhead *, const struct ipv6_addr *, struct ipv6_addr *ipv6_iffindaddr(struct interface *, const struct in6_addr *, int); int ipv6_hasaddr(const struct interface *); +struct ipv6_addr *ipv6_ifanyglobal(struct interface *); int ipv6_findaddrmatch(const struct ipv6_addr *, const struct in6_addr *, unsigned int); struct ipv6_addr *ipv6_findaddr(struct dhcpcd_ctx *, @@ -274,19 +303,9 @@ void ipv6_addtempaddrs(struct interface *, const struct timespec *); int ipv6_start(struct interface *); int ipv6_staticdadcompleted(const struct interface *); int ipv6_startstatic(struct interface *); -ssize_t ipv6_env(char **, const char *, const struct interface *); +ssize_t ipv6_env(FILE *, const char *, const struct interface *); void ipv6_ctxfree(struct dhcpcd_ctx *); -bool inet6_getroutes(struct dhcpcd_ctx *, struct rt_head *); - -#else -#define ipv6_start(a) (-1) -#define ipv6_startstatic(a) -#define ipv6_staticdadcompleted(a) (0) -#define ipv6_hasaddr(a) (0) -#define ipv6_free_ll_callbacks(a) {} -#define ipv6_free(a) {} -#define ipv6_ctxfree(a) {} -#define ipv6_gentempifid(a) {} -#endif +bool inet6_getroutes(struct dhcpcd_ctx *, rb_tree_t *); +#endif /* INET6 */ -#endif +#endif /* INET6_H */ diff --git a/contrib/dhcpcd/src/ipv6nd.c b/contrib/dhcpcd/src/ipv6nd.c index 5126d5abb1..affc911708 100644 --- a/contrib/dhcpcd/src/ipv6nd.c +++ b/contrib/dhcpcd/src/ipv6nd.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - IPv6 ND handling - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -34,6 +35,7 @@ #include #include +#include #include #include #include @@ -44,6 +46,7 @@ #define ELOOP_QUEUE 3 #include "common.h" #include "dhcpcd.h" +#include "dhcp-common.h" #include "dhcp6.h" #include "eloop.h" #include "if.h" @@ -103,6 +106,9 @@ __CTASSERT(sizeof(struct nd_opt_rdnss) == 8); #define RTPREF_RESERVED (-2) #define RTPREF_INVALID (-3) /* internal */ +#define EXPIRED_MAX 5 /* Remember 5 expired routers to avoid + logspam. */ + #define MIN_RANDOM_FACTOR 500 /* millisecs */ #define MAX_RANDOM_FACTOR 1500 /* millisecs */ #define MIN_RANDOM_FACTOR_U MIN_RANDOM_FACTOR * 1000 /* usecs */ @@ -188,54 +194,105 @@ ipv6nd_printoptions(const struct dhcpcd_ctx *ctx, } static int -ipv6nd_open(struct dhcpcd_ctx *ctx) +ipv6nd_open0(void) { - int on; + int s, on; struct icmp6_filter filt; - if (ctx->nd_fd != -1) - return ctx->nd_fd; #define SOCK_FLAGS SOCK_CLOEXEC | SOCK_NONBLOCK - ctx->nd_fd = xsocket(PF_INET6, SOCK_RAW | SOCK_FLAGS, IPPROTO_ICMPV6); + s = xsocket(PF_INET6, SOCK_RAW | SOCK_FLAGS, IPPROTO_ICMPV6); #undef SOCK_FLAGS - if (ctx->nd_fd == -1) + if (s == -1) return -1; /* RFC4861 4.1 */ on = 255; - if (setsockopt(ctx->nd_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, - &on, sizeof(on)) == -1) - goto eexit; - - on = 1; - if (setsockopt(ctx->nd_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, + if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &on, sizeof(on)) == -1) goto eexit; on = 1; - if (setsockopt(ctx->nd_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)) == -1) goto eexit; ICMP6_FILTER_SETBLOCKALL(&filt); - ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filt); ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); - if (setsockopt(ctx->nd_fd, IPPROTO_ICMPV6, ICMP6_FILTER, + if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, sizeof(filt)) == -1) goto eexit; - eloop_event_add(ctx->eloop, ctx->nd_fd, ipv6nd_handledata, ctx); - return ctx->nd_fd; + return s; eexit: - if (ctx->nd_fd != -1) { - eloop_event_delete(ctx->eloop, ctx->nd_fd); - close(ctx->nd_fd); - ctx->nd_fd = -1; - } + close(s); return -1; } +#ifdef __sun +static int +ipv6nd_open(struct interface *ifp) +{ + int s; + struct ipv6_mreq mreq = { + .ipv6mr_multiaddr = IN6ADDR_LINKLOCAL_ALLNODES_INIT, + .ipv6mr_interface = ifp->index + }; + struct rs_state *state = RS_STATE(ifp); + uint_t ifindex = ifp->index; + + if (state->nd_fd != -1) + return state->nd_fd; + + s = ipv6nd_open0(); + if (s == -1) + return -1; + + if (setsockopt(s, IPPROTO_IPV6, IPV6_BOUND_IF, + &ifindex, sizeof(ifindex)) == -1) + { + close(s); + return -1; + } + + if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq, sizeof(mreq)) == -1) + { + close(s); + return -1; + } + + state->nd_fd = s; + eloop_event_add(ifp->ctx->eloop, s, ipv6nd_handledata, ifp); + return s; +} +#else +static int +ipv6nd_open(struct dhcpcd_ctx *ctx) +{ + int s, on; + + if (ctx->nd_fd != -1) + return ctx->nd_fd; + + s = ipv6nd_open0(); + if (s == -1) + return -1; + + on = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, + &on, sizeof(on)) == -1) + { + close(s); + return -1; + } + + ctx->nd_fd = s; + eloop_event_add(ctx->eloop, s, ipv6nd_handledata, ctx); + return s; +} +#endif + static int ipv6nd_makersprobe(struct interface *ifp) { @@ -271,11 +328,22 @@ static void ipv6nd_sendrsprobe(void *arg) { struct interface *ifp = arg; - struct dhcpcd_ctx *ctx; - struct rs_state *state; - struct sockaddr_in6 dst; + struct rs_state *state = RS_STATE(ifp); + struct sockaddr_in6 dst = { + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT, + .sin6_scope_id = ifp->index, + }; + struct iovec iov = { .iov_base = state->rs, .iov_len = state->rslen }; + unsigned char ctl[CMSG_SPACE(sizeof(struct in6_pktinfo))] = { 0 }; + struct msghdr msg = { + .msg_name = &dst, .msg_namelen = sizeof(dst), + .msg_iov = &iov, .msg_iovlen = 1, + .msg_control = ctl, .msg_controllen = sizeof(ctl), + }; struct cmsghdr *cm; - struct in6_pktinfo pi; + struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index }; + int s; if (ipv6_linklocal(ifp) == NULL) { logdebugx("%s: delaying Router Solicitation for LL address", @@ -284,36 +352,26 @@ ipv6nd_sendrsprobe(void *arg) return; } - memset(&dst, 0, sizeof(dst)); - dst.sin6_family = AF_INET6; #ifdef HAVE_SA_LEN dst.sin6_len = sizeof(dst); #endif - dst.sin6_scope_id = ifp->index; - if (inet_pton(AF_INET6, ALLROUTERS, &dst.sin6_addr) != 1) { - logerr(__func__); - return; - } - - state = RS_STATE(ifp); - ctx = ifp->ctx; - ctx->sndhdr.msg_name = (void *)&dst; - ctx->sndhdr.msg_iov[0].iov_base = state->rs; - ctx->sndhdr.msg_iov[0].iov_len = state->rslen; /* Set the outbound interface */ - cm = CMSG_FIRSTHDR(&ctx->sndhdr); + cm = CMSG_FIRSTHDR(&msg); if (cm == NULL) /* unlikely */ return; cm->cmsg_level = IPPROTO_IPV6; cm->cmsg_type = IPV6_PKTINFO; cm->cmsg_len = CMSG_LEN(sizeof(pi)); - memset(&pi, 0, sizeof(pi)); - pi.ipi6_ifindex = ifp->index; memcpy(CMSG_DATA(cm), &pi, sizeof(pi)); logdebugx("%s: sending Router Solicitation", ifp->name); - if (sendmsg(ctx->nd_fd, &ctx->sndhdr, 0) == -1) { +#ifdef __sun + s = state->nd_fd; +#else + s = ifp->ctx->nd_fd; +#endif + if (sendmsg(s, &msg, 0) == -1) { logerr(__func__); /* Allow IPv6ND to continue .... at most a few errors * would be logged. @@ -327,91 +385,234 @@ ipv6nd_sendrsprobe(void *arg) else { logwarnx("%s: no IPv6 Routers available", ifp->name); ipv6nd_drop(ifp); - dhcp6_dropnondelegates(ifp); } } +#ifdef ND6_ADVERTISE +static void +ipv6nd_sendadvertisement(void *arg) +{ + struct ipv6_addr *ia = arg; + struct interface *ifp = ia->iface; + struct dhcpcd_ctx *ctx = ifp->ctx; + struct sockaddr_in6 dst = { + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_LINKLOCAL_ALLNODES_INIT, + .sin6_scope_id = ifp->index, + }; + struct iovec iov = { .iov_base = ia->na, .iov_len = ia->na_len }; + unsigned char ctl[CMSG_SPACE(sizeof(struct in6_pktinfo))] = { 0 }; + struct msghdr msg = { + .msg_name = &dst, .msg_namelen = sizeof(dst), + .msg_iov = &iov, .msg_iovlen = 1, + .msg_control = ctl, .msg_controllen = sizeof(ctl), + }; + struct cmsghdr *cm; + struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index }; + const struct rs_state *state = RS_CSTATE(ifp); + int s; + + if (state == NULL || ifp->carrier <= LINK_DOWN) + goto freeit; + +#ifdef SIN6_LEN + dst.sin6_len = sizeof(dst); +#endif + + /* Set the outbound interface. */ + cm = CMSG_FIRSTHDR(&msg); + assert(cm != NULL); + cm->cmsg_level = IPPROTO_IPV6; + cm->cmsg_type = IPV6_PKTINFO; + cm->cmsg_len = CMSG_LEN(sizeof(pi)); + memcpy(CMSG_DATA(cm), &pi, sizeof(pi)); + logdebugx("%s: sending NA for %s", ifp->name, ia->saddr); +#ifdef __sun + s = state->nd_fd; +#else + s = ctx->nd_fd; +#endif + if (sendmsg(s, &msg, 0) == -1) + logerr(__func__); + + if (++ia->na_count < MAX_NEIGHBOR_ADVERTISEMENT) { + eloop_timeout_add_sec(ctx->eloop, + state->retrans / 1000, ipv6nd_sendadvertisement, ia); + return; + } + +freeit: + free(ia->na); + ia->na = NULL; + ia->na_count = 0; +} + void -ipv6nd_expire(struct interface *ifp, uint32_t seconds) +ipv6nd_advertise(struct ipv6_addr *ia) +{ + struct dhcpcd_ctx *ctx; + struct interface *ifp; + struct ipv6_state *state; + struct ipv6_addr *iap, *iaf; + struct nd_neighbor_advert *na; + + if (IN6_IS_ADDR_MULTICAST(&ia->addr)) + return; + +#ifdef __sun + if (!(ia->flags & IPV6_AF_AUTOCONF) && ia->flags & IPV6_AF_RAPFX) + return; +#endif + + ctx = ia->iface->ctx; + /* Find the most preferred address to advertise. */ + iaf = NULL; + TAILQ_FOREACH(ifp, ctx->ifaces, next) { + state = IPV6_STATE(ifp); + if (state == NULL || ifp->carrier <= LINK_DOWN) + continue; + + TAILQ_FOREACH(iap, &state->addrs, next) { + if (!IN6_ARE_ADDR_EQUAL(&iap->addr, &ia->addr)) + continue; + + /* Cancel any current advertisement. */ + eloop_timeout_delete(ctx->eloop, + ipv6nd_sendadvertisement, iap); + + /* Don't advertise what we can't use. */ + if (iap->prefix_vltime == 0 || + iap->addr_flags & IN6_IFF_NOTUSEABLE) + continue; + + if (iaf == NULL || + iaf->iface->metric > iap->iface->metric) + iaf = iap; + } + } + if (iaf == NULL) + return; + + /* Make the packet. */ + ifp = iaf->iface; + iaf->na_len = sizeof(*na); + if (ifp->hwlen != 0) + iaf->na_len += (size_t)ROUNDUP8(ifp->hwlen + 2); + na = calloc(1, iaf->na_len); + if (na == NULL) { + logerr(__func__); + return; + } + + na->nd_na_type = ND_NEIGHBOR_ADVERT; + na->nd_na_flags_reserved = ND_NA_FLAG_OVERRIDE; + if (ip6_forwarding(ifp->name) == 1) + na->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER; + na->nd_na_target = ia->addr; + + if (ifp->hwlen != 0) { + struct nd_opt_hdr *opt; + + opt = (struct nd_opt_hdr *)(na + 1); + opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; + opt->nd_opt_len = (uint8_t)((ROUNDUP8(ifp->hwlen + 2)) >> 3); + memcpy(opt + 1, ifp->hwaddr, ifp->hwlen); + } + + iaf->na_count = 0; + free(iaf->na); + iaf->na = na; + eloop_timeout_delete(ctx->eloop, ipv6nd_sendadvertisement, iaf); + ipv6nd_sendadvertisement(iaf); +} +#elif !defined(SMALL) +#warning kernel does not support userland sending ND6 advertisements +#endif /* ND6_ADVERTISE */ + +static void +ipv6nd_expire(void *arg) { + struct interface *ifp = arg; struct ra *rap; - struct timespec now; - uint32_t vltime = seconds; - uint32_t pltime = seconds / 2; + struct ipv6_addr *ia; + struct timespec now = { .tv_sec = 1 }; if (ifp->ctx->ra_routers == NULL) return; - clock_gettime(CLOCK_MONOTONIC, &now); - TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) { - if (rap->iface == ifp) { - rap->acquired = now; - rap->expired = seconds ? 0 : 1; - if (seconds) { - struct ipv6_addr *ia; - - rap->lifetime = seconds; - TAILQ_FOREACH(ia, &rap->addrs, next) { - if (ia->prefix_pltime > pltime || - ia->prefix_vltime > vltime) - { - ia->acquired = now; - if (ia->prefix_pltime != 0) - ia->prefix_pltime = - pltime; - ia->prefix_vltime = vltime; - } - } - ipv6_addaddrs(&rap->addrs); - } + if (rap->iface == ifp) + continue; + rap->acquired = now; + TAILQ_FOREACH(ia, &rap->addrs, next) { + ia->acquired = now; } } - if (seconds) - ipv6nd_expirera(ifp); - else - rt_build(ifp->ctx, AF_INET6); + ipv6nd_expirera(ifp); } -static void -ipv6nd_reachable(struct ra *rap, int flags) +void +ipv6nd_startexpire(struct interface *ifp) +{ + + eloop_timeout_add_sec(ifp->ctx->eloop, RTR_CARRIER_EXPIRE, + ipv6nd_expire, ifp); +} + +/* + * Neighbour reachability. + * + * RFC 4681 6.2.5 says when a node is no longer a router it MUST + * send a RA with a zero lifetime. + * All OS's I know of set the NA router flag if they are a router + * or not and disregard that they are actively advertising or + * shutting down. If the interface is disabled, it cant't send a NA at all. + * + * As such we CANNOT rely on the NA Router flag and MUST use + * unreachability or receive a RA with a lifetime of zero to remove + * the node as a default router. + */ +void +ipv6nd_neighbour(struct dhcpcd_ctx *ctx, struct in6_addr *addr, bool reachable) { + struct ra *rap, *rapr; - if (rap->lifetime == 0) + if (ctx->ra_routers == NULL) + return; + + TAILQ_FOREACH(rap, ctx->ra_routers, next) { + if (IN6_ARE_ADDR_EQUAL(&rap->from, addr)) + break; + } + + if (rap == NULL || rap->expired) return; - if (flags & IPV6ND_REACHABLE) { - if (rap->expired == 0) + if (reachable) { + if (rap->isreachable) return; loginfox("%s: %s is reachable again", rap->iface->name, rap->sfrom); - rap->expired = 0; + rap->isreachable = true; + return; } else { - if (rap->expired != 0) + if (!rap->isreachable) return; - logwarnx("%s: %s is unreachable, expiring it", + logwarnx("%s: %s is unreachable", rap->iface->name, rap->sfrom); - rap->expired = 1; + rap->isreachable = false; } - rt_build(rap->iface->ctx, AF_INET6); - /* XXX Not really an RA */ - script_runreason(rap->iface, "ROUTERADVERT"); -} - -void -ipv6nd_neighbour(struct dhcpcd_ctx *ctx, struct in6_addr *addr, int flags) -{ - struct ra *rap; - - if (ctx->ra_routers) { - TAILQ_FOREACH(rap, ctx->ra_routers, next) { - if (IN6_ARE_ADDR_EQUAL(&rap->from, addr)) { - ipv6nd_reachable(rap, flags); - break; - } - } + /* If we have no reachable default routers, try and solicit one. */ + TAILQ_FOREACH(rapr, ctx->ra_routers, next) { + if (rap == rapr || rap->iface != rapr->iface) + continue; + if (rapr->isreachable && !rapr->expired && rapr->lifetime) + break; } + + if (rapr == NULL) + ipv6nd_startrs(rap->iface); } const struct ipv6_addr * @@ -486,6 +687,11 @@ ipv6nd_free(struct interface *ifp) if (state == NULL) return 0; + ctx = ifp->ctx; +#ifdef __sun + eloop_event_delete(ctx->eloop, state->nd_fd); + close(state->nd_fd); +#endif free(state->rs); free(state); ifp->if_data[IF_DATA_IPV6ND] = NULL; @@ -497,9 +703,9 @@ ipv6nd_free(struct interface *ifp) } } +#ifndef __sun /* If we don't have any more IPv6 enabled interfaces, * close the global socket and release resources */ - ctx = ifp->ctx; TAILQ_FOREACH(ifp, ctx->ifaces, next) { if (RS_STATE(ifp)) break; @@ -511,6 +717,7 @@ ipv6nd_free(struct interface *ifp) ctx->nd_fd = -1; } } +#endif return n; } @@ -718,13 +925,19 @@ try_script: return; } } +#ifdef ND6_ADVERTISE + ipv6nd_advertise(ia); +#endif } } #ifndef DHCP6 /* If DHCPv6 is compiled out, supply a shim to provide an error message * if IPv6RA requests DHCPv6. */ -#undef dhcp6_start +enum DH6S { + DH6S_REQUEST, + DH6S_INFORM, +}; static int dhcp6_start(__unused struct interface *ifp, __unused enum DH6S init_state) { @@ -735,8 +948,9 @@ dhcp6_start(__unused struct interface *ifp, __unused enum DH6S init_state) #endif static void -ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp, - struct icmp6_hdr *icp, size_t len, int hoplimit) +ipv6nd_handlera(struct dhcpcd_ctx *ctx, + const struct sockaddr_in6 *from, const char *sfrom, + struct interface *ifp, struct icmp6_hdr *icp, size_t len, int hoplimit) { size_t i, olen; struct nd_router_advert *nd_ra; @@ -749,7 +963,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp, struct in6_addr pi_prefix; struct ipv6_addr *ap; struct dhcp_opt *dho; - bool new_rap, new_data; + bool new_rap, new_data, has_address; uint32_t old_lifetime; __printflike(1, 2) void (*logfunc)(const char *, ...); #ifdef IPV6_MANAGETEMPADDR @@ -758,33 +972,29 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp, if (ifp == NULL) { #ifdef DEBUG_RS - logdebugx("RA for unexpected interface from %s", - ctx->sfrom); + logdebugx("RA for unexpected interface from %s", sfrom); #endif return; } if (len < sizeof(struct nd_router_advert)) { - logerrx("IPv6 RA packet too short from %s", ctx->sfrom); + logerrx("IPv6 RA packet too short from %s", sfrom); return; } /* RFC 4861 7.1.2 */ if (hoplimit != 255) { - logerrx("invalid hoplimit(%d) in RA from %s", - hoplimit, ctx->sfrom); + logerrx("invalid hoplimit(%d) in RA from %s", hoplimit, sfrom); return; } - - if (!IN6_IS_ADDR_LINKLOCAL(&ctx->from.sin6_addr)) { - logerrx("RA from non local address %s", ctx->sfrom); + if (!IN6_IS_ADDR_LINKLOCAL(&from->sin6_addr)) { + logerrx("RA from non local address %s", sfrom); return; } if (!(ifp->options->options & DHCPCD_IPV6RS)) { #ifdef DEBUG_RS - logerrx("%s: unexpected RA from %s", - ifp->name, ctx->sfrom); + logerrx("%s: unexpected RA from %s", ifp->name, sfrom); #endif return; } @@ -793,20 +1003,20 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp, if (ipv6_linklocal(ifp) == NULL) { #ifdef DEBUG_RS logdebugx("%s: received RA from %s (no link-local)", - ifp->name, ctx->sfrom); + ifp->name, sfrom); #endif return; } - if (ipv6_iffindaddr(ifp, &ctx->from.sin6_addr, IN6_IFF_TENTATIVE)) { + if (ipv6_iffindaddr(ifp, &from->sin6_addr, IN6_IFF_TENTATIVE)) { logdebugx("%s: ignoring RA from ourself %s", - ifp->name, ctx->sfrom); + ifp->name, sfrom); return; } TAILQ_FOREACH(rap, ctx->ra_routers, next) { if (ifp == rap->iface && - IN6_ARE_ADDR_EQUAL(&rap->from, &ctx->from.sin6_addr)) + IN6_ARE_ADDR_EQUAL(&rap->from, &from->sin6_addr)) break; } @@ -831,10 +1041,11 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp, return; } rap->iface = ifp; - rap->from = ctx->from.sin6_addr; - strlcpy(rap->sfrom, ctx->sfrom, sizeof(rap->sfrom)); + rap->from = from->sin6_addr; + strlcpy(rap->sfrom, sfrom, sizeof(rap->sfrom)); TAILQ_INIT(&rap->addrs); new_rap = true; + rap->isreachable = true; } else new_rap = false; if (rap->data_len == 0) { @@ -853,9 +1064,8 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp, * routers like to decrease the advertised valid and preferred times * in accordance with the own prefix times which would result in too * much needless log spam. */ - logfunc = new_rap ? loginfox : logdebugx, - logfunc("%s: Router Advertisement from %s", - ifp->name, ctx->sfrom); + logfunc = new_data || !rap->isreachable ? loginfox : logdebugx, + logfunc("%s: Router Advertisement from %s", ifp->name, rap->sfrom); clock_gettime(CLOCK_MONOTONIC, &rap->acquired); rap->flags = nd_ra->nd_ra_flags_reserved; @@ -869,11 +1079,15 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp, if (rap->reachable > MAX_REACHABLE_TIME) rap->reachable = 0; } - if (nd_ra->nd_ra_retransmit) - rap->retrans = ntohl(nd_ra->nd_ra_retransmit); - if (rap->lifetime) - rap->expired = 0; - rap->hasdns = 0; + if (nd_ra->nd_ra_retransmit) { + struct rs_state *state = RS_STATE(ifp); + + state->retrans = rap->retrans = ntohl(nd_ra->nd_ra_retransmit); + } + rap->expired = false; + rap->hasdns = false; + rap->isreachable = true; + has_address = false; #ifdef IPV6_AF_TEMPORARY ipv6_markaddrsstale(ifp, IPV6_AF_TEMPORARY); @@ -913,10 +1127,10 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp, } if (dho != NULL) logwarnx("%s: reject RA (option %s) from %s", - ifp->name, dho->var, ctx->sfrom); + ifp->name, dho->var, rap->sfrom); else logwarnx("%s: reject RA (option %d) from %s", - ifp->name, ndo.nd_opt_type, ctx->sfrom); + ifp->name, ndo.nd_opt_type, rap->sfrom); if (new_rap) ipv6nd_removefreedrop_ra(rap, 0, 0); else @@ -962,12 +1176,6 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp, if (ap == NULL) { unsigned int flags; - if (!(pi.nd_opt_pi_flags_reserved & - ND_OPT_PI_FLAG_AUTO) && - !(pi.nd_opt_pi_flags_reserved & - ND_OPT_PI_FLAG_ONLINK)) - continue; - flags = IPV6_AF_RAPFX; if (pi.nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO && @@ -980,7 +1188,8 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp, if (ap == NULL) break; ap->prefix = pi_prefix; - ap->dadcallback = ipv6nd_dadcallback; + if (flags & IPV6_AF_AUTOCONF) + ap->dadcallback = ipv6nd_dadcallback; ap->created = ap->acquired = rap->acquired; TAILQ_INSERT_TAIL(&rap->addrs, ap, next); @@ -990,7 +1199,8 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp, * temporary address also exists then * extend the existing one rather than * create a new one */ - if (ipv6_iffindaddr(ifp, &ap->addr, + if (flags & IPV6_AF_AUTOCONF && + ipv6_iffindaddr(ifp, &ap->addr, IN6_IFF_NOTUSEABLE) && ipv6_settemptime(ap, 0)) new_ap = 0; @@ -1011,6 +1221,9 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp, ntohl(pi.nd_opt_pi_valid_time); ap->prefix_pltime = ntohl(pi.nd_opt_pi_preferred_time); + if (ap->prefix_vltime != 0 && + ap->flags & IPV6_AF_AUTOCONF) + has_address = true; #ifdef IPV6_MANAGETEMPADDR /* RFC4941 Section 3.3.3 */ @@ -1031,6 +1244,10 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp, break; case ND_OPT_MTU: + if (len < sizeof(mtu)) { + logerrx("%s: short MTU option", ifp->name); + break; + } memcpy(&mtu, p, sizeof(mtu)); mtu.nd_opt_mtu_mtu = ntohl(mtu.nd_opt_mtu_mtu); if (mtu.nd_opt_mtu_mtu < IPV6_MMTU) { @@ -1042,6 +1259,10 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp, break; case ND_OPT_RDNSS: + if (len < sizeof(rdnss)) { + logerrx("%s: short RDNSS option", ifp->name); + break; + } memcpy(&rdnss, p, sizeof(rdnss)); if (rdnss.nd_opt_rdnss_lifetime && rdnss.nd_opt_rdnss_len > 1) @@ -1060,7 +1281,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp, dho->option)) { logwarnx("%s: reject RA (no option %s) from %s", - ifp->name, dho->var, ctx->sfrom); + ifp->name, dho->var, rap->sfrom); if (new_rap) ipv6nd_removefreedrop_ra(rap, 0, 0); else @@ -1069,6 +1290,10 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp, } } + if (new_data && !has_address && rap->lifetime && !ipv6_ifanyglobal(ifp)) + logwarnx("%s: no global addresses for default route", + ifp->name); + if (new_rap) add_router(ifp->ctx, rap); @@ -1081,11 +1306,6 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp, ipv6_addtempaddrs(ifp, &rap->acquired); #endif - /* Find any freshly added routes, such as the subnet route. - * We do this because we cannot rely on recieving the kernel - * notification right now via our link socket. */ - if_initrt(ifp->ctx, AF_INET6); - rt_build(ifp->ctx, AF_INET6); if (ipv6nd_scriptrun(rap)) return; @@ -1109,8 +1329,10 @@ handle_flag: if (new_data && dhcp6_start(ifp, DH6S_INFORM) == -1) LOG_DHCP6("dhcp6_start: %s", ifp->name); } else { +#ifdef DHCP6 if (new_data) logdebugx("%s: No DHCPv6 instruction in RA", ifp->name); +#endif nodhcp6: if (ifp->ctx->options & DHCPCD_TEST) { eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS); @@ -1122,20 +1344,21 @@ nodhcp6: ipv6nd_expirera(ifp); } -int -ipv6nd_hasra(const struct interface *ifp) +bool +ipv6nd_hasralifetime(const struct interface *ifp, bool lifetime) { const struct ra *rap; if (ifp->ctx->ra_routers) { TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) - if (rap->iface == ifp && !rap->expired) - return 1; + if (rap->iface == ifp && !rap->expired && + (!lifetime ||rap->lifetime)) + return true; } - return 0; + return false; } -int +bool ipv6nd_hasradhcp(const struct interface *ifp) { const struct ra *rap; @@ -1144,11 +1367,11 @@ ipv6nd_hasradhcp(const struct interface *ifp) TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) { if (rap->iface == ifp && !rap->expired && - (rap->flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER))) - return 1; + (rap->flags &(ND_RA_FLAG_MANAGED|ND_RA_FLAG_OTHER))) + return true; } } - return 0; + return false; } static const uint8_t * @@ -1191,11 +1414,11 @@ ipv6nd_getoption(struct dhcpcd_ctx *ctx, } ssize_t -ipv6nd_env(char **env, const char *prefix, const struct interface *ifp) +ipv6nd_env(FILE *fp, const struct interface *ifp) { size_t i, j, n, len, olen; struct ra *rap; - char ndprefix[32], abuf[24]; + char ndprefix[32]; struct dhcp_opt *opt; uint8_t *p; struct nd_opt_hdr ndo; @@ -1205,37 +1428,28 @@ ipv6nd_env(char **env, const char *prefix, const struct interface *ifp) clock_gettime(CLOCK_MONOTONIC, &now); i = n = 0; TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) { - if (rap->iface != ifp) + if (rap->iface != ifp || rap->expired) continue; i++; - if (prefix != NULL) - snprintf(ndprefix, sizeof(ndprefix), - "%s_nd%zu", prefix, i); - else - snprintf(ndprefix, sizeof(ndprefix), - "nd%zu", i); - if (env) - setvar(&env[n], ndprefix, "from", rap->sfrom); - n++; - if (env) - setvard(&env[n], ndprefix, "acquired", - (size_t)rap->acquired.tv_sec); - n++; - if (env) - setvard(&env[n], ndprefix, "now", (size_t)now.tv_sec); - n++; + snprintf(ndprefix, sizeof(ndprefix), "nd%zu", i); + if (efprintf(fp, "%s_from=%s", ndprefix, rap->sfrom) == -1) + return -1; + if (efprintf(fp, "%s_acquired=%lld", ndprefix, + (long long)rap->acquired.tv_sec) == -1) + return -1; + if (efprintf(fp, "%s_now=%lld", ndprefix, + (long long)now.tv_sec) == -1) + return -1; /* Zero our indexes */ - if (env) { - for (j = 0, opt = rap->iface->ctx->nd_opts; - j < rap->iface->ctx->nd_opts_len; - j++, opt++) - dhcp_zero_index(opt); - for (j = 0, opt = rap->iface->options->nd_override; - j < rap->iface->options->nd_override_len; - j++, opt++) - dhcp_zero_index(opt); - } + for (j = 0, opt = rap->iface->ctx->nd_opts; + j < rap->iface->ctx->nd_opts_len; + j++, opt++) + dhcp_zero_index(opt); + for (j = 0, opt = rap->iface->options->nd_override; + j < rap->iface->options->nd_override_len; + j++, opt++) + dhcp_zero_index(opt); /* Unlike DHCP, ND6 options *may* occur more than once. * There is also no provision for option concatenation @@ -1268,34 +1482,31 @@ ipv6nd_env(char **env, const char *prefix, const struct interface *ifp) if (j == rap->iface->ctx->nd_opts_len) opt = NULL; } - if (opt) { - n += dhcp_envoption(rap->iface->ctx, - env == NULL ? NULL : &env[n], - ndprefix, rap->iface->name, - opt, ipv6nd_getoption, - p + sizeof(ndo), olen - sizeof(ndo)); - } + if (opt == NULL) + continue; + dhcp_envoption(rap->iface->ctx, fp, + ndprefix, rap->iface->name, + opt, ipv6nd_getoption, + p + sizeof(ndo), olen - sizeof(ndo)); } /* We need to output the addresses we actually made * from the prefix information options as well. */ j = 0; TAILQ_FOREACH(ia, &rap->addrs, next) { - if (!(ia->flags & IPV6_AF_AUTOCONF) + if (!(ia->flags & IPV6_AF_AUTOCONF) || #ifdef IPV6_AF_TEMPORARY - || ia->flags & IPV6_AF_TEMPORARY + ia->flags & IPV6_AF_TEMPORARY || #endif - ) + !(ia->flags & IPV6_AF_ADDED) || + ia->prefix_vltime == 0) continue; - j++; - if (env) { - snprintf(abuf, sizeof(abuf), "addr%zu", j); - setvar(&env[n], ndprefix, abuf, ia->saddr); - } - n++; + if (efprintf(fp, "%s_addr%zu=%s", + ndprefix, j++, ia->saddr) == -1) + return -1; } } - return (ssize_t)n; + return 1; } void @@ -1321,33 +1532,41 @@ ipv6nd_expirera(void *arg) struct interface *ifp; struct ra *rap, *ran; struct timespec now, lt, expire, next; - uint8_t expired, anyvalid, valid, validone; + bool expired, valid; struct ipv6_addr *ia; + size_t len, olen; + uint8_t *p; + struct nd_opt_hdr ndo; +#if 0 + struct nd_opt_prefix_info pi; +#endif + struct nd_opt_dnssl dnssl; + struct nd_opt_rdnss rdnss; + uint32_t ltime; + size_t nexpired = 0; ifp = arg; clock_gettime(CLOCK_MONOTONIC, &now); - expired = 0; + expired = false; timespecclear(&next); - anyvalid = 0; TAILQ_FOREACH_SAFE(rap, ifp->ctx->ra_routers, next, ran) { - if (rap->iface != ifp) + if (rap->iface != ifp || rap->expired) continue; - valid = validone = 0; + valid = false; if (rap->lifetime) { lt.tv_sec = (time_t)rap->lifetime; lt.tv_nsec = 0; timespecadd(&rap->acquired, <, &expire); - if (rap->lifetime == 0 || timespeccmp(&now, &expire, >)) - { + if (timespeccmp(&now, &expire, >)) { if (!rap->expired) { logwarnx("%s: %s: router expired", ifp->name, rap->sfrom); - rap->expired = expired = 1; rap->lifetime = 0; + expired = true; } } else { - valid = 1; + valid = true; timespecsub(&expire, &now, <); if (!timespecisset(&next) || timespeccmp(&next, <, >)) @@ -1362,7 +1581,7 @@ ipv6nd_expirera(void *arg) if (ia->prefix_vltime == 0) continue; if (ia->prefix_vltime == ND6_INFINITE_LIFETIME) { - validone = 1; + valid = true; continue; } lt.tv_sec = (time_t)ia->prefix_vltime; @@ -1380,47 +1599,107 @@ ipv6nd_expirera(void *arg) ia->prefix_vltime = ia->prefix_pltime = 0; ia->flags &= ~(IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED); - expired = 1; + expired = true; } else { timespecsub(&expire, &now, <); if (!timespecisset(&next) || timespeccmp(&next, <, >)) next = lt; - validone = 1; + valid = true; } } - /* XXX FixMe! - * We need to extract the lifetime from each option and check - * if that has expired or not. - * If it has, zero the option out in the returned data. */ + /* Work out expiry for ND options */ + len = rap->data_len - sizeof(struct nd_router_advert); + for (p = rap->data + sizeof(struct nd_router_advert); + len >= sizeof(ndo); + p += olen, len -= olen) + { + memcpy(&ndo, p, sizeof(ndo)); + olen = (size_t)(ndo.nd_opt_len * 8); + if (olen > len) { + errno = EINVAL; + break; + } + + if (has_option_mask(rap->iface->options->nomasknd, + ndo.nd_opt_type)) + continue; - /* No valid lifetimes are left on the RA, so we might - * as well punt it. */ - if (!valid && !validone) + switch (ndo.nd_opt_type) { + /* Prefix info is already checked in the above loop. */ +#if 0 + case ND_OPT_PREFIX_INFORMATION: + if (len < sizeof(pi)) + break; + memcpy(&pi, p, sizeof(pi)); + ltime = pi.nd_opt_pi_valid_time; + break; +#endif + case ND_OPT_DNSSL: + if (len < sizeof(dnssl)) + continue; + memcpy(&dnssl, p, sizeof(dnssl)); + ltime = dnssl.nd_opt_dnssl_lifetime; + break; + case ND_OPT_RDNSS: + if (len < sizeof(rdnss)) + continue; + memcpy(&rdnss, p, sizeof(rdnss)); + ltime = rdnss.nd_opt_rdnss_lifetime; + break; + default: + continue; + } + + if (ltime == 0) + continue; + if (ltime == ND6_INFINITE_LIFETIME) { + valid = true; + continue; + } + + lt.tv_sec = (time_t)ntohl(ltime); + lt.tv_nsec = 0; + timespecadd(&rap->acquired, <, &expire); + if (timespeccmp(&now, &expire, >)) { + expired = true; + continue; + } + + timespecsub(&expire, &now, <); + if (!timespecisset(&next) || + timespeccmp(&next, <, >)) + { + next = lt; + valid = true; + } + } + + if (valid) + continue; + + /* Router has expired. Let's not keep a lot of them. */ + rap->expired = true; + if (++nexpired > EXPIRED_MAX) ipv6nd_free_ra(rap); - else - anyvalid = 1; } if (timespecisset(&next)) eloop_timeout_add_tv(ifp->ctx->eloop, &next, ipv6nd_expirera, ifp); if (expired) { + logwarnx("%s: part of Router Advertisement expired", ifp->name); rt_build(ifp->ctx, AF_INET6); script_runreason(ifp, "ROUTERADVERT"); } - - /* No valid routers? Kill any DHCPv6. */ - if (!anyvalid) - dhcp6_dropnondelegates(ifp); } void ipv6nd_drop(struct interface *ifp) { struct ra *rap, *ran; - uint8_t expired = 0; + bool expired = false; if (ifp->ctx->ra_routers == NULL) return; @@ -1428,7 +1707,7 @@ ipv6nd_drop(struct interface *ifp) eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); TAILQ_FOREACH_SAFE(rap, ifp->ctx->ra_routers, next, ran) { if (rap->iface == ifp) { - rap->expired = expired = 1; + rap->expired = expired = true; ipv6nd_drop_ra(rap); } } @@ -1440,162 +1719,77 @@ ipv6nd_drop(struct interface *ifp) } static void -ipv6nd_handlena(struct dhcpcd_ctx *ctx, struct interface *ifp, - struct icmp6_hdr *icp, size_t len, int hoplimit) -{ - struct nd_neighbor_advert *nd_na; - struct in6_addr nd_na_target; - struct ra *rap; - uint32_t is_router, is_solicited; - char buf[INET6_ADDRSTRLEN]; - const char *taddr; - - if (ifp == NULL) { -#ifdef DEBUG_NS - logdebugx("NA for unexpected interface from %s", - ctx->sfrom); -#endif - return; - } - - if ((size_t)len < sizeof(struct nd_neighbor_advert)) { - logerrx("%s: IPv6 NA too short from %s", - ifp->name, ctx->sfrom); - return; - } - - /* RFC 4861 7.1.2 */ - if (hoplimit != 255) { - logerrx("invalid hoplimit(%d) in NA from %s", - hoplimit, ctx->sfrom); - return; - } - - nd_na = (struct nd_neighbor_advert *)icp; - is_router = nd_na->nd_na_flags_reserved & ND_NA_FLAG_ROUTER; - is_solicited = nd_na->nd_na_flags_reserved & ND_NA_FLAG_SOLICITED; - taddr = inet_ntop(AF_INET6, &nd_na->nd_na_target, - buf, INET6_ADDRSTRLEN); - - /* nd_na->nd_na_target is not aligned. */ - memcpy(&nd_na_target, &nd_na->nd_na_target, sizeof(nd_na_target)); - if (IN6_IS_ADDR_MULTICAST(&nd_na_target)) { - logerrx("%s: NA multicast address %s (%s)", - ifp->name, taddr, ctx->sfrom); - return; - } - - TAILQ_FOREACH(rap, ctx->ra_routers, next) { - if (rap->iface == ifp && - IN6_ARE_ADDR_EQUAL(&rap->from, &nd_na_target)) - break; - } - if (rap == NULL) { -#ifdef DEBUG_NS - logdebugx("%s: unexpected NA from %s for %s", - ifp->name, ctx->sfrom, taddr); -#endif - return; - } - -#ifdef DEBUG_NS - logdebugx("%s: %sNA for %s from %s", - ifp->name, is_solicited ? "solicited " : "", taddr, ctx->sfrom); -#endif - - /* Node is no longer a router, so remove it from consideration */ - if (!is_router && !rap->expired) { - loginfox("%s: %s not a router (%s)", - ifp->name, taddr, ctx->sfrom); - rap->expired = 1; - rt_build(ifp->ctx, AF_INET6); - script_runreason(ifp, "ROUTERADVERT"); - return; - } - - if (is_solicited && is_router && rap->lifetime) - ipv6nd_reachable(rap, IPV6ND_REACHABLE); -} - -static void ipv6nd_handledata(void *arg) { struct dhcpcd_ctx *ctx; + int s; + struct sockaddr_in6 from; + unsigned char buf[64 * 1024]; /* Maximum ICMPv6 size */ + struct iovec iov = { + .iov_base = buf, + .iov_len = sizeof(buf), + }; + unsigned char ctl[CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int))] = { 0 }; + struct msghdr msg = { + .msg_name = &from, .msg_namelen = sizeof(from), + .msg_iov = &iov, .msg_iovlen = 1, + .msg_control = ctl, .msg_controllen = sizeof(ctl), + }; ssize_t len; - struct cmsghdr *cm; - int hoplimit; - struct in6_pktinfo pkt; + char sfrom[INET6_ADDRSTRLEN]; + int hoplimit = 0; struct icmp6_hdr *icp; struct interface *ifp; +#ifdef __sun + struct rs_state *state; + + ifp = arg; + state = RS_STATE(ifp); + ctx = ifp->ctx; + s = state->nd_fd; +#else ctx = arg; - ctx->rcvhdr.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + - CMSG_SPACE(sizeof(int)); - len = recvmsg_realloc(ctx->nd_fd, &ctx->rcvhdr, 0); + s = ctx->nd_fd; +#endif + len = recvmsg(s, &msg, 0); if (len == -1) { logerr(__func__); return; } - ctx->sfrom = inet_ntop(AF_INET6, &ctx->from.sin6_addr, - ctx->ntopbuf, INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &from.sin6_addr, sfrom, sizeof(sfrom)); if ((size_t)len < sizeof(struct icmp6_hdr)) { - logerrx("IPv6 ICMP packet too short from %s", ctx->sfrom); + logerrx("IPv6 ICMP packet too short from %s", sfrom); return; } - pkt.ipi6_ifindex = 0; - hoplimit = 0; - for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&ctx->rcvhdr); - cm; - cm = (struct cmsghdr *)CMSG_NXTHDR(&ctx->rcvhdr, cm)) - { - if (cm->cmsg_level != IPPROTO_IPV6) - continue; - switch(cm->cmsg_type) { - case IPV6_PKTINFO: - if (cm->cmsg_len == CMSG_LEN(sizeof(pkt))) - memcpy(&pkt, CMSG_DATA(cm), sizeof(pkt)); - break; - case IPV6_HOPLIMIT: - if (cm->cmsg_len == CMSG_LEN(sizeof(int))) - memcpy(&hoplimit, CMSG_DATA(cm), sizeof(int)); - break; - } - } - - if (pkt.ipi6_ifindex == 0) { - logerrx("IPv6 RA/NA did not contain index from %s", ctx->sfrom); +#ifdef __sun + if_findifpfromcmsg(ctx, &msg, &hoplimit); +#else + ifp = if_findifpfromcmsg(ctx, &msg, &hoplimit); + if (ifp == NULL) { + logerr(__func__); return; } - - /* Find the receiving interface */ - TAILQ_FOREACH(ifp, ctx->ifaces, next) { - if (ifp->index == (unsigned int)pkt.ipi6_ifindex) - break; - } +#endif /* Don't do anything if the user hasn't configured it. */ - if (ifp != NULL && - (ifp->active != IF_ACTIVE_USER || - !(ifp->options->options & DHCPCD_IPV6))) + if (ifp->active != IF_ACTIVE_USER || + !(ifp->options->options & DHCPCD_IPV6)) return; - icp = (struct icmp6_hdr *)ctx->rcvhdr.msg_iov[0].iov_base; + icp = (struct icmp6_hdr *)buf; if (icp->icmp6_code == 0) { switch(icp->icmp6_type) { - case ND_NEIGHBOR_ADVERT: - ipv6nd_handlena(ctx, ifp, icp, (size_t)len, - hoplimit); - return; case ND_ROUTER_ADVERT: - ipv6nd_handlera(ctx, ifp, icp, (size_t)len, - hoplimit); + ipv6nd_handlera(ctx, &from, sfrom, + ifp, icp, (size_t)len, hoplimit); return; } } logerrx("invalid IPv6 type %d or code %d from %s", - icp->icmp6_type, icp->icmp6_code, ctx->sfrom); + icp->icmp6_type, icp->icmp6_code, sfrom); } static void @@ -1605,11 +1799,6 @@ ipv6nd_startrs1(void *arg) struct rs_state *state; loginfox("%s: soliciting an IPv6 router", ifp->name); - if (ipv6nd_open(ifp->ctx) == -1) { - logerr(__func__); - return; - } - state = RS_STATE(ifp); if (state == NULL) { ifp->if_data[IF_DATA_IPV6ND] = calloc(1, sizeof(*state)); @@ -1618,8 +1807,23 @@ ipv6nd_startrs1(void *arg) logerr(__func__); return; } +#ifdef __sun + state->nd_fd = -1; +#endif } +#ifdef __sun + if (ipv6nd_open(ifp) == -1) { + logerr(__func__); + return; + } +#else + if (ipv6nd_open(ifp->ctx) == -1) { + logerr(__func__); + return; + } +#endif + /* Always make a new probe as the underlying hardware * address could have changed. */ ipv6nd_makersprobe(ifp); @@ -1628,6 +1832,7 @@ ipv6nd_startrs1(void *arg) return; } + state->retrans = RETRANS_TIMER; state->rsprobes = 0; ipv6nd_sendrsprobe(ifp); } diff --git a/contrib/dhcpcd/src/ipv6nd.h b/contrib/dhcpcd/src/ipv6nd.h index d40ed0341d..16c4dc7950 100644 --- a/contrib/dhcpcd/src/ipv6nd.h +++ b/contrib/dhcpcd/src/ipv6nd.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - IPv6 ND handling - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -28,6 +29,8 @@ #ifndef IPV6ND_H #define IPV6ND_H +#ifdef INET6 + #include #include "config.h" @@ -48,8 +51,9 @@ struct ra { uint32_t retrans; uint32_t mtu; struct ipv6_addrhead addrs; - uint8_t hasdns; - uint8_t expired; + bool hasdns; + bool expired; + bool isreachable; }; TAILQ_HEAD(ra_head, ra); @@ -58,9 +62,14 @@ struct rs_state { struct nd_router_solicit *rs; size_t rslen; int rsprobes; + uint32_t retrans; +#ifdef __sun + int nd_fd; +#endif }; #define RS_STATE(a) ((struct rs_state *)(ifp)->if_data[IF_DATA_IPV6ND]) +#define RS_CSTATE(a) ((const struct rs_state *)(ifp)->if_data[IF_DATA_IPV6ND]) #define RS_STATE_RUNNING(a) (ipv6nd_hasra((a)) && ipv6nd_dadcompleted((a))) #ifndef MAX_RTR_SOLICITATION_DELAY @@ -68,6 +77,7 @@ struct rs_state { #define MAX_UNICAST_SOLICIT 3 /* 3 transmissions */ #define RTR_SOLICITATION_INTERVAL 4 /* seconds */ #define MAX_RTR_SOLICITATIONS 3 /* times */ +#define MAX_NEIGHBOR_ADVERTISEMENT 3 /* 3 transmissions */ #endif /* On carrier up, expire known routers after RTR_CARRIER_EXPIRE seconds. */ @@ -81,33 +91,25 @@ struct rs_state { #define RETRANS_TIMER 1000 /* milliseconds */ #define DELAY_FIRST_PROBE_TIME 5 /* seconds */ -#define IPV6ND_REACHABLE (1 << 0) -#define IPV6ND_ROUTER (1 << 1) - -#ifdef INET6 void ipv6nd_printoptions(const struct dhcpcd_ctx *, const struct dhcp_opt *, size_t); void ipv6nd_startrs(struct interface *); -ssize_t ipv6nd_env(char **, const char *, const struct interface *); +ssize_t ipv6nd_env(FILE *, const struct interface *); const struct ipv6_addr *ipv6nd_iffindaddr(const struct interface *ifp, const struct in6_addr *addr, unsigned int flags); struct ipv6_addr *ipv6nd_findaddr(struct dhcpcd_ctx *, const struct in6_addr *, unsigned int); ssize_t ipv6nd_free(struct interface *); void ipv6nd_expirera(void *arg); -int ipv6nd_hasra(const struct interface *); -int ipv6nd_hasradhcp(const struct interface *); +bool ipv6nd_hasralifetime(const struct interface *, bool); +#define ipv6nd_hasra(i) ipv6nd_hasralifetime((i), false) +bool ipv6nd_hasradhcp(const struct interface *); void ipv6nd_handleifa(int, struct ipv6_addr *, pid_t); int ipv6nd_dadcompleted(const struct interface *); -void ipv6nd_expire(struct interface *, uint32_t); +void ipv6nd_advertise(struct ipv6_addr *); +void ipv6nd_startexpire(struct interface *); void ipv6nd_drop(struct interface *); -void ipv6nd_neighbour(struct dhcpcd_ctx *, struct in6_addr *, int); -#else -#define ipv6nd_startrs(a) {} -#define ipv6nd_free(a) {} -#define ipv6nd_hasra(a) (0) -#define ipv6nd_dadcompleted(a) (0) -#define ipv6nd_expire(a, b) {} -#endif +void ipv6nd_neighbour(struct dhcpcd_ctx *, struct in6_addr *, bool); +#endif /* INET6 */ -#endif +#endif /* IPV6ND_H */ diff --git a/contrib/dhcpcd/src/logerr.c b/contrib/dhcpcd/src/logerr.c index 6117c66c10..59f8cb6112 100644 --- a/contrib/dhcpcd/src/logerr.c +++ b/contrib/dhcpcd/src/logerr.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * logerr: errx with logging - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -91,7 +92,7 @@ getprogname(void) #ifndef SMALL /* Write the time, syslog style. month day time - */ -static void +static int logprintdate(FILE *stream) { struct timeval tv; @@ -100,19 +101,22 @@ logprintdate(FILE *stream) char buf[32]; if (gettimeofday(&tv, NULL) == -1) - return; + return -1; now = tv.tv_sec; tzset(); - localtime_r(&now, &tmnow); - strftime(buf, sizeof(buf), "%b %d %T ", &tmnow); - fprintf(stream, "%s", buf); + if (localtime_r(&now, &tmnow) == NULL) + return -1; + if (strftime(buf, sizeof(buf), "%b %d %T ", &tmnow) == 0) + return -1; + return fprintf(stream, "%s", buf); } #endif -__printflike(3, 0) static void +__printflike(3, 0) static int vlogprintf_r(struct logctx *ctx, FILE *stream, const char *fmt, va_list args) { + int len = 0, e; va_list a; #ifndef SMALL bool log_pid; @@ -122,7 +126,11 @@ vlogprintf_r(struct logctx *ctx, FILE *stream, const char *fmt, va_list args) if ((stream == stderr && ctx->log_opts & LOGERR_ERR_DATE) || (stream != stderr && ctx->log_opts & LOGERR_LOG_DATE)) - logprintdate(stream); + { + if ((e = logprintdate(stream)) == -1) + return -1; + len += e; + } #ifdef LOGERR_TAG log_tag = ((stream == stderr && ctx->log_opts & LOGERR_ERR_TAG) || @@ -130,29 +138,43 @@ vlogprintf_r(struct logctx *ctx, FILE *stream, const char *fmt, va_list args) if (log_tag) { if (ctx->log_tag == NULL) ctx->log_tag = getprogname(); - fprintf(stream, "%s", ctx->log_tag); + if ((e = fprintf(stream, "%s", ctx->log_tag)) == -1) + return -1; + len += e; } #endif log_pid = ((stream == stderr && ctx->log_opts & LOGERR_ERR_PID) || (stream != stderr && ctx->log_opts & LOGERR_LOG_PID)); - if (log_pid) - fprintf(stream, "[%d]", getpid()); + if (log_pid) { + if ((e = fprintf(stream, "[%d]", getpid())) == -1) + return -1; + len += e; + } #ifdef LOGERR_TAG if (log_tag || log_pid) #else if (log_pid) #endif - fprintf(stream, ": "); + { + if ((e = fprintf(stream, ": ")) == -1) + return -1; + len += e; + } #else UNUSED(ctx); #endif va_copy(a, args); - vfprintf(stream, fmt, a); - fputc('\n', stream); + e = vfprintf(stream, fmt, a); + if (fputc('\n', stream) == EOF) + e = -1; + else if (e != -1) + e++; va_end(a); + + return e == -1 ? -1 : len + e; } /* @@ -170,30 +192,32 @@ vlogprintf_r(struct logctx *ctx, FILE *stream, const char *fmt, va_list args) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmissing-format-attribute" #endif -__printflike(2, 0) static void +__printflike(2, 0) static int vlogmessage(int pri, const char *fmt, va_list args) { struct logctx *ctx = &_logctx; + int len = 0; if (ctx->log_opts & LOGERR_ERR && (pri <= LOG_ERR || (!(ctx->log_opts & LOGERR_QUIET) && pri <= LOG_INFO) || (ctx->log_opts & LOGERR_DEBUG && pri <= LOG_DEBUG))) - vlogprintf_r(ctx, stderr, fmt, args); + len = vlogprintf_r(ctx, stderr, fmt, args); if (!(ctx->log_opts & LOGERR_LOG)) - return; + return len; #ifdef SMALL vsyslog(pri, fmt, args); + return len; #else if (ctx->log_file == NULL) { vsyslog(pri, fmt, args); - return; + return len; } if (pri == LOG_DEBUG && !(ctx->log_opts & LOGERR_DEBUG)) - return; - vlogprintf_r(ctx, ctx->log_file, fmt, args); + return len; + return vlogprintf_r(ctx, ctx->log_file, fmt, args); #endif } #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5)) diff --git a/contrib/dhcpcd/src/logerr.h b/contrib/dhcpcd/src/logerr.h index d4afa77c39..cca1956ced 100644 --- a/contrib/dhcpcd/src/logerr.h +++ b/contrib/dhcpcd/src/logerr.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * logerr: errx with logging - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without diff --git a/contrib/dhcpcd/src/route.c b/contrib/dhcpcd/src/route.c index 8dc8639217..01bf8df9df 100644 --- a/contrib/dhcpcd/src/route.c +++ b/contrib/dhcpcd/src/route.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - route management - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -29,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +39,7 @@ #include "common.h" #include "dhcpcd.h" #include "if.h" +#include "if-options.h" #include "ipv4.h" #include "ipv4ll.h" #include "ipv6.h" @@ -44,6 +47,66 @@ #include "route.h" #include "sa.h" +/* Needed for NetBSD-6, 7 and 8. */ +#ifndef RB_TREE_FOREACH_SAFE +#ifndef RB_TREE_PREV +#define RB_TREE_NEXT(T, N) rb_tree_iterate((T), (N), RB_DIR_RIGHT) +#define RB_TREE_PREV(T, N) rb_tree_iterate((T), (N), RB_DIR_LEFT) +#endif +#define RB_TREE_FOREACH_SAFE(N, T, S) \ + for ((N) = RB_TREE_MIN(T); \ + (N) && ((S) = RB_TREE_NEXT((T), (N)), 1); \ + (N) = (S)) +#define RB_TREE_FOREACH_REVERSE_SAFE(N, T, S) \ + for ((N) = RB_TREE_MAX(T); \ + (N) && ((S) = RB_TREE_PREV((T), (N)), 1); \ + (N) = (S)) +#endif + +#ifdef RT_FREE_ROUTE_TABLE_STATS +static size_t croutes; +static size_t nroutes; +static size_t froutes; +static size_t mroutes; +#endif + +static void +rt_maskedaddr(struct sockaddr *dst, + const struct sockaddr *addr, const struct sockaddr *netmask) +{ + const char *addrp = addr->sa_data, *netmaskp = netmask->sa_data; + char *dstp = dst->sa_data; + const char *addre = (char *)dst + sa_len(addr); + const char *netmaske = (char *)dst + MIN(sa_len(addr), sa_len(netmask)); + + dst->sa_family = addr->sa_family; +#ifdef HAVE_SA_LEN + dst->sa_len = addr->sa_len; +#endif + + if (sa_is_unspecified(netmask)) { + if (addre > dstp) + memcpy(dstp, addrp, (size_t)(addre - dstp)); + return; + } + + while (dstp < netmaske) + *dstp++ = *addrp++ & *netmaskp++; + if (dstp < addre) + memset(dstp, 0, (size_t)(addre - dstp)); +} + +int +rt_cmp_dest(const struct rt *rt1, const struct rt *rt2) +{ + union sa_ss ma1 = { .sa.sa_family = AF_UNSPEC }; + union sa_ss ma2 = { .sa.sa_family = AF_UNSPEC }; + + rt_maskedaddr(&ma1.sa, &rt1->rt_dest, &rt1->rt_netmask); + rt_maskedaddr(&ma2.sa, &rt2->rt_dest, &rt2->rt_netmask); + return sa_cmp(&ma1.sa, &ma2.sa); +} + /* * On some systems, host routes have no need for a netmask. * However DHCP specifies host routes using an all-ones netmask. @@ -59,13 +122,114 @@ rt_cmp_netmask(const struct rt *rt1, const struct rt *rt2) return sa_cmp(&rt1->rt_netmask, &rt2->rt_netmask); } +static int +rt_compare_os(__unused void *context, const void *node1, const void *node2) +{ + const struct rt *rt1 = node1, *rt2 = node2; + int c; + + /* Sort by masked destination. */ + c = rt_cmp_dest(rt1, rt2); + if (c != 0) + return c; + +#ifdef HAVE_ROUTE_METRIC + c = (int)(rt1->rt_ifp->metric - rt2->rt_ifp->metric); +#endif + return c; +} + +static int +rt_compare_list(__unused void *context, const void *node1, const void *node2) +{ + const struct rt *rt1 = node1, *rt2 = node2; + + if (rt1->rt_order > rt2->rt_order) + return 1; + if (rt1->rt_order < rt2->rt_order) + return -1; + return 0; +} + +static int +rt_compare_proto(void *context, const void *node1, const void *node2) +{ + const struct rt *rt1 = node1, *rt2 = node2; + int c; + struct interface *ifp1, *ifp2; + + assert(rt1->rt_ifp != NULL); + assert(rt2->rt_ifp != NULL); + ifp1 = rt1->rt_ifp; + ifp2 = rt2->rt_ifp; + + /* Prefer interfaces with a carrier. */ + c = ifp1->carrier - ifp2->carrier; + if (c != 0) + return -c; + + /* Lower metric interfaces come first. */ + c = (int)(ifp1->metric - ifp2->metric); + if (c != 0) + return c; + + /* Finally the order in which the route was given to us. */ + return rt_compare_list(context, rt1, rt2); +} + +static const rb_tree_ops_t rt_compare_os_ops = { + .rbto_compare_nodes = rt_compare_os, + .rbto_compare_key = rt_compare_os, + .rbto_node_offset = offsetof(struct rt, rt_tree), + .rbto_context = NULL +}; + +const rb_tree_ops_t rt_compare_list_ops = { + .rbto_compare_nodes = rt_compare_list, + .rbto_compare_key = rt_compare_list, + .rbto_node_offset = offsetof(struct rt, rt_tree), + .rbto_context = NULL +}; + +const rb_tree_ops_t rt_compare_proto_ops = { + .rbto_compare_nodes = rt_compare_proto, + .rbto_compare_key = rt_compare_proto, + .rbto_node_offset = offsetof(struct rt, rt_tree), + .rbto_context = NULL +}; + +#ifdef RT_FREE_ROUTE_TABLE +static int +rt_compare_free(__unused void *context, const void *node1, const void *node2) +{ + + return node1 == node2 ? 0 : node1 < node2 ? -1 : 1; +} + +static const rb_tree_ops_t rt_compare_free_ops = { + .rbto_compare_nodes = rt_compare_free, + .rbto_compare_key = rt_compare_free, + .rbto_node_offset = offsetof(struct rt, rt_tree), + .rbto_context = NULL +}; +#endif + void rt_init(struct dhcpcd_ctx *ctx) { - TAILQ_INIT(&ctx->routes); - TAILQ_INIT(&ctx->kroutes); - TAILQ_INIT(&ctx->froutes); + rb_tree_init(&ctx->routes, &rt_compare_os_ops); +#ifdef RT_FREE_ROUTE_TABLE + rb_tree_init(&ctx->froutes, &rt_compare_free_ops); +#endif +} + +bool +rt_is_default(const struct rt *rt) +{ + + return sa_is_unspecified(&rt->rt_dest) && + sa_is_unspecified(&rt->rt_netmask); } static void @@ -92,9 +256,7 @@ rt_desc(const char *cmd, const struct rt *rt) else loginfox("%s: %s host route to %s via %s", ifname, cmd, dest, gateway); - } else if (sa_is_unspecified(&rt->rt_dest) && - sa_is_unspecified(&rt->rt_netmask)) - { + } else if (rt_is_default(rt)) { if (gateway_unspec) loginfox("%s: %s default route", ifname, cmd); @@ -114,42 +276,44 @@ rt_desc(const char *cmd, const struct rt *rt) } void -rt_headclear0(struct dhcpcd_ctx *ctx, struct rt_head *rts, int af) +rt_headclear0(struct dhcpcd_ctx *ctx, rb_tree_t *rts, int af) { struct rt *rt, *rtn; if (rts == NULL) return; assert(ctx != NULL); +#ifdef RT_FREE_ROUTE_TABLE assert(&ctx->froutes != rts); +#endif - TAILQ_FOREACH_SAFE(rt, rts, rt_next, rtn) { + RB_TREE_FOREACH_SAFE(rt, rts, rtn) { if (af != AF_UNSPEC && rt->rt_dest.sa_family != af && rt->rt_gateway.sa_family != af) continue; - TAILQ_REMOVE(rts, rt, rt_next); - TAILQ_INSERT_TAIL(&ctx->froutes, rt, rt_next); + rb_tree_remove_node(rts, rt); + rt_free(rt); } } void -rt_headclear(struct rt_head *rts, int af) +rt_headclear(rb_tree_t *rts, int af) { struct rt *rt; - if (rts == NULL || (rt = TAILQ_FIRST(rts)) == NULL) + if (rts == NULL || (rt = RB_TREE_MIN(rts)) == NULL) return; rt_headclear0(rt->rt_ifp->ctx, rts, af); } static void -rt_headfree(struct rt_head *rts) +rt_headfree(rb_tree_t *rts) { struct rt *rt; - while ((rt = TAILQ_FIRST(rts))) { - TAILQ_REMOVE(rts, rt, rt_next); + while ((rt = RB_TREE_MIN(rts)) != NULL) { + rb_tree_remove_node(rts, rt); free(rt); } } @@ -160,8 +324,14 @@ rt_dispose(struct dhcpcd_ctx *ctx) assert(ctx != NULL); rt_headfree(&ctx->routes); - rt_headfree(&ctx->kroutes); +#ifdef RT_FREE_ROUTE_TABLE rt_headfree(&ctx->froutes); +#ifdef RT_FREE_ROUTE_TABLE_STATS + logdebugx("free route list used %zu times", froutes); + logdebugx("new routes from route free list %zu", nroutes); + logdebugx("maximum route free list size %zu", mroutes); +#endif +#endif } struct rt * @@ -170,9 +340,16 @@ rt_new0(struct dhcpcd_ctx *ctx) struct rt *rt; assert(ctx != NULL); - if ((rt = TAILQ_FIRST(&ctx->froutes)) != NULL) - TAILQ_REMOVE(&ctx->froutes, rt, rt_next); - else if ((rt = malloc(sizeof(*rt))) == NULL) { +#ifdef RT_FREE_ROUTE_TABLE + if ((rt = RB_TREE_MIN(&ctx->froutes)) != NULL) { + rb_tree_remove_node(&ctx->froutes, rt); +#ifdef RT_FREE_ROUTE_TABLE_STATS + croutes--; + nroutes++; +#endif + } else +#endif + if ((rt = malloc(sizeof(*rt))) == NULL) { logerr(__func__); return NULL; } @@ -204,13 +381,49 @@ rt_new(struct interface *ifp) return rt; } +struct rt * +rt_proto_add_ctx(rb_tree_t *tree, struct rt *rt, struct dhcpcd_ctx *ctx) +{ + + rt->rt_order = ctx->rt_order++; + if (rb_tree_insert_node(tree, rt) == rt) + return rt; + + rt_free(rt); + return NULL; +} + +struct rt * +rt_proto_add(rb_tree_t *tree, struct rt *rt) +{ + + assert (rt->rt_ifp != NULL); + return rt_proto_add_ctx(tree, rt, rt->rt_ifp->ctx); +} + void rt_free(struct rt *rt) { +#ifdef RT_FREE_ROUTE_TABLE + struct dhcpcd_ctx *ctx; assert(rt != NULL); - assert(rt->rt_ifp->ctx != NULL); - TAILQ_INSERT_TAIL(&rt->rt_ifp->ctx->froutes, rt, rt_next); + if (rt->rt_ifp == NULL) { + free(rt); + return; + } + + ctx = rt->rt_ifp->ctx; + rb_tree_insert_node(&ctx->froutes, rt); +#ifdef RT_FREE_ROUTE_TABLE_STATS + croutes++; + froutes++; + if (croutes > mroutes) + mroutes = croutes; +#endif +#else + free(rt); +#endif } void @@ -222,98 +435,53 @@ rt_freeif(struct interface *ifp) if (ifp == NULL) return; ctx = ifp->ctx; - TAILQ_FOREACH_SAFE(rt, &ctx->routes, rt_next, rtn) { - if (rt->rt_ifp == ifp) { - TAILQ_REMOVE(&ctx->routes, rt, rt_next); - rt_free(rt); - } - } - TAILQ_FOREACH_SAFE(rt, &ctx->kroutes, rt_next, rtn) { + RB_TREE_FOREACH_SAFE(rt, &ctx->routes, rtn) { if (rt->rt_ifp == ifp) { - TAILQ_REMOVE(&ctx->kroutes, rt, rt_next); + rb_tree_remove_node(&ctx->routes, rt); rt_free(rt); } } } -struct rt * -rt_find(struct rt_head *rts, const struct rt *f) -{ - struct rt *rt; - - assert(rts != NULL); - assert(f != NULL); - TAILQ_FOREACH(rt, rts, rt_next) { - if (sa_cmp(&rt->rt_dest, &f->rt_dest) == 0 && -#ifdef HAVE_ROUTE_METRIC - (f->rt_ifp == NULL || - rt->rt_ifp->metric == f->rt_ifp->metric) && -#endif - rt_cmp_netmask(f, rt) == 0) - return rt; - } - return NULL; -} - -static void -rt_kfree(struct rt *rt) -{ - struct dhcpcd_ctx *ctx; - struct rt *f; - - assert(rt != NULL); - ctx = rt->rt_ifp->ctx; - if ((f = rt_find(&ctx->kroutes, rt)) != NULL) { - TAILQ_REMOVE(&ctx->kroutes, f, rt_next); - rt_free(f); - } -} - /* If something other than dhcpcd removes a route, * we need to remove it from our internal table. */ void -rt_recvrt(int cmd, const struct rt *rt) +rt_recvrt(int cmd, const struct rt *rt, pid_t pid) { struct dhcpcd_ctx *ctx; struct rt *f; assert(rt != NULL); + assert(rt->rt_ifp != NULL); + assert(rt->rt_ifp->ctx != NULL); + ctx = rt->rt_ifp->ctx; - f = rt_find(&ctx->kroutes, rt); switch(cmd) { case RTM_DELETE: + f = rb_tree_find_node(&ctx->routes, rt); if (f != NULL) { - TAILQ_REMOVE(&ctx->kroutes, f, rt_next); - rt_free(f); - } - if ((f = rt_find(&ctx->routes, rt)) != NULL) { - TAILQ_REMOVE(&ctx->routes, f, rt_next); - rt_desc("deleted", f); + char buf[32]; + + rb_tree_remove_node(&ctx->routes, f); + snprintf(buf, sizeof(buf), "pid %d deleted", pid); + rt_desc(buf, f); rt_free(f); } break; - case RTM_ADD: - if (f != NULL) - break; - if ((f = rt_new(rt->rt_ifp)) == NULL) - break; - memcpy(f, rt, sizeof(*f)); - TAILQ_INSERT_TAIL(&ctx->kroutes, f, rt_next); - break; } -#if defined(INET) && defined(HAVE_ROUTE_METRIC) +#if defined(IPV4LL) && defined(HAVE_ROUTE_METRIC) if (rt->rt_dest.sa_family == AF_INET) ipv4ll_recvrt(cmd, rt); #endif } static bool -rt_add(struct rt *nrt, struct rt *ort) +rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort) { struct dhcpcd_ctx *ctx; - bool change; + bool change, kroute, result; assert(nrt != NULL); ctx = nrt->rt_ifp->ctx; @@ -335,9 +503,9 @@ rt_add(struct rt *nrt, struct rt *ort) rt_desc(ort == NULL ? "adding" : "changing", nrt); - change = false; + change = kroute = result = false; if (ort == NULL) { - ort = rt_find(&ctx->kroutes, nrt); + ort = rb_tree_find_node(kroutes, nrt); if (ort != NULL && ((ort->rt_flags & RTF_REJECT && nrt->rt_flags & RTF_REJECT) || @@ -350,6 +518,7 @@ rt_add(struct rt *nrt, struct rt *ort) if (ort->rt_mtu == nrt->rt_mtu) return true; change = true; + kroute = true; } } else if (ort->rt_dflags & RTDF_FAKE && !(nrt->rt_dflags & RTDF_FAKE) && @@ -376,8 +545,10 @@ rt_add(struct rt *nrt, struct rt *ort) #endif if (change) { - if (if_route(RTM_CHANGE, nrt) != -1) - return true; + if (if_route(RTM_CHANGE, nrt) != -1) { + result = true; + goto out; + } if (errno != ESRCH) logerr("if_route (CHG)"); } @@ -389,9 +560,9 @@ rt_add(struct rt *nrt, struct rt *ort) if (ort != NULL) { if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH) logerr("if_route (DEL)"); - rt_kfree(ort); } - return true; + result = true; + goto out; } /* If the kernel claims the route exists we need to rip out the @@ -409,7 +580,7 @@ rt_add(struct rt *nrt, struct rt *ort) if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH) logerr("if_route (DEL)"); else - rt_kfree(ort); + kroute = false; } #ifdef ROUTE_PER_GATEWAY /* The OS allows many routes to the same dest with different gateways. @@ -422,13 +593,23 @@ rt_add(struct rt *nrt, struct rt *ort) } } #endif - if (if_route(RTM_ADD, nrt) != -1) - return true; + + if (if_route(RTM_ADD, nrt) != -1) { + result = true; + goto out; + } + #ifdef HAVE_ROUTE_METRIC logerr: #endif logerr("if_route (ADD)"); - return false; + +out: + if (kroute) { + rb_tree_remove_node(kroutes, ort); + rt_free(ort); + } + return result; } static bool @@ -440,10 +621,6 @@ rt_delete(struct rt *rt) retval = if_route(RTM_DELETE, rt) == -1 ? false : true; if (!retval && errno != ENOENT && errno != ESRCH) logerr(__func__); - /* Remove the route from our kernel table so we can add a - * IPv4LL default route if possible. */ - else - rt_kfree(rt); return retval; } @@ -459,14 +636,15 @@ rt_cmp(const struct rt *r1, const struct rt *r2) } static bool -rt_doroute(struct rt *rt) +rt_doroute(rb_tree_t *kroutes, struct rt *rt) { struct dhcpcd_ctx *ctx; struct rt *or; ctx = rt->rt_ifp->ctx; /* Do we already manage it? */ - if ((or = rt_find(&ctx->routes, rt))) { + or = rb_tree_find_node(&ctx->routes, rt); + if (or != NULL) { if (rt->rt_dflags & RTDF_FAKE) return true; if (or->rt_dflags & RTDF_FAKE || @@ -475,19 +653,20 @@ rt_doroute(struct rt *rt) sa_cmp(&or->rt_ifa, &rt->rt_ifa) != 0) || or->rt_mtu != rt->rt_mtu) { - if (!rt_add(rt, or)) + if (!rt_add(kroutes, rt, or)) return false; } - TAILQ_REMOVE(&ctx->routes, or, rt_next); + rb_tree_remove_node(&ctx->routes, or); rt_free(or); } else { if (rt->rt_dflags & RTDF_FAKE) { - if ((or = rt_find(&ctx->kroutes, rt)) == NULL) + or = rb_tree_find_node(kroutes, rt); + if (or == NULL) return false; if (!rt_cmp(rt, or)) return false; } else { - if (!rt_add(rt, NULL)) + if (!rt_add(kroutes, rt, NULL)) return false; } } @@ -498,16 +677,16 @@ rt_doroute(struct rt *rt) void rt_build(struct dhcpcd_ctx *ctx, int af) { - struct rt_head routes, added; + rb_tree_t routes, added, kroutes; struct rt *rt, *rtn; unsigned long long o; - /* We need to have the interfaces in the correct order to ensure - * our routes are managed correctly. */ - if_sortinterfaces(ctx); - - TAILQ_INIT(&routes); - TAILQ_INIT(&added); + rb_tree_init(&routes, &rt_compare_proto_ops); + rb_tree_init(&added, &rt_compare_os_ops); + rb_tree_init(&kroutes, &rt_compare_os_ops); + if_initrt(ctx, &kroutes, af); + ctx->rt_order = 0; + ctx->options |= DHCPCD_RTBUILD; switch (af) { #ifdef INET @@ -524,26 +703,34 @@ rt_build(struct dhcpcd_ctx *ctx, int af) #endif } - TAILQ_FOREACH_SAFE(rt, &routes, rt_next, rtn) { - if (rt->rt_dest.sa_family != af && - rt->rt_gateway.sa_family != af) + RB_TREE_FOREACH_SAFE(rt, &routes, rtn) { + if ((rt->rt_dest.sa_family != af && + rt->rt_dest.sa_family != AF_UNSPEC) || + (rt->rt_gateway.sa_family != af && + rt->rt_gateway.sa_family != AF_UNSPEC)) continue; /* Is this route already in our table? */ - if ((rt_find(&added, rt)) != NULL) + if (rb_tree_find_node(&added, rt) != NULL) continue; - if (rt_doroute(rt)) { - TAILQ_REMOVE(&routes, rt, rt_next); - TAILQ_INSERT_TAIL(&added, rt, rt_next); + if (rt_doroute(&kroutes, rt)) { + rb_tree_remove_node(&routes, rt); + if (rb_tree_insert_node(&added, rt) != rt) { + errno = EEXIST; + logerr(__func__); + rt_free(rt); + } } } /* Remove old routes we used to manage. */ - TAILQ_FOREACH_SAFE(rt, &ctx->routes, rt_next, rtn) { - if (rt->rt_dest.sa_family != af && - rt->rt_gateway.sa_family != af) + RB_TREE_FOREACH_REVERSE_SAFE(rt, &ctx->routes, rtn) { + if ((rt->rt_dest.sa_family != af && + rt->rt_dest.sa_family != AF_UNSPEC) || + (rt->rt_gateway.sa_family != af && + rt->rt_gateway.sa_family != AF_UNSPEC)) continue; - TAILQ_REMOVE(&ctx->routes, rt, rt_next); - if (rt_find(&added, rt) == NULL) { + rb_tree_remove_node(&ctx->routes, rt); + if (rb_tree_find_node(&added, rt) == NULL) { o = rt->rt_ifp->options ? rt->rt_ifp->options->options : ctx->options; @@ -552,12 +739,21 @@ rt_build(struct dhcpcd_ctx *ctx, int af) (DHCPCD_EXITING | DHCPCD_PERSISTENT)) rt_delete(rt); } - TAILQ_INSERT_TAIL(&ctx->froutes, rt, rt_next); + rt_free(rt); + } + + /* XXX This needs to be optimised. */ + while ((rt = RB_TREE_MIN(&added)) != NULL) { + rb_tree_remove_node(&added, rt); + if (rb_tree_insert_node(&ctx->routes, rt) != rt) { + errno = EEXIST; + logerr(__func__); + rt_free(rt); + } } - rt_headclear(&ctx->routes, af); - TAILQ_CONCAT(&ctx->routes, &added, rt_next); getfail: rt_headclear(&routes, AF_UNSPEC); + rt_headclear(&kroutes, AF_UNSPEC); } diff --git a/contrib/dhcpcd/src/route.h b/contrib/dhcpcd/src/route.h index 204b20c0b2..74570fa794 100644 --- a/contrib/dhcpcd/src/route.h +++ b/contrib/dhcpcd/src/route.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - route management - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * rEDISTRIBUTION AND USE IN SOURCE AND BINARY FORMS, WITH OR WITHOUT @@ -28,6 +29,10 @@ #ifndef ROUTE_H #define ROUTE_H +#ifdef HAVE_SYS_RBTREE_H +#include +#endif + #include #include @@ -36,6 +41,17 @@ #include "dhcpcd.h" #include "sa.h" +/* + * Enable the route free list by default as + * memory usage is still reported as low/unchanged even + * when dealing with millions of routes. + */ +#if !defined(RT_FREE_ROUTE_TABLE) +#define RT_FREE_ROUTE_TABLE 1 +#elif RT_FREE_ROUTE_TABLE == 0 +#undef RT_FREE_ROUTE_TABLE +#endif + /* Some systems have route metrics. * OpenBSD route priority is not this. */ #ifndef HAVE_ROUTE_METRIC @@ -56,7 +72,6 @@ #endif struct rt { - TAILQ_ENTRY(rt) rt_next; union sa_ss rt_ss_dest; #define rt_dest rt_ss_dest.sa union sa_ss rt_ss_netmask; @@ -72,27 +87,34 @@ struct rt { unsigned int rt_metric; #endif unsigned int rt_dflags; -#define RTDF_INIT 0x01 /* Generated by if_initrt() */ #define RTDF_IFA_ROUTE 0x02 /* Address generated route */ #define RTDF_FAKE 0x04 /* Maybe us on lease reboot */ #define RTDF_RA 0x08 /* Router Advertisement */ #define RTDF_DHCP 0x10 /* DHCP route */ #define RTDF_STATIC 0x20 /* Configured in dhcpcd */ +#define RTDF_GATELINK 0x40 /* Gateway is on link */ + size_t rt_order; + rb_node_t rt_tree; }; -TAILQ_HEAD(rt_head, rt); + +extern const rb_tree_ops_t rt_compare_list_ops; +extern const rb_tree_ops_t rt_compare_proto_ops; void rt_init(struct dhcpcd_ctx *); void rt_dispose(struct dhcpcd_ctx *); -struct rt * rt_find(struct rt_head *, const struct rt *); void rt_free(struct rt *); void rt_freeif(struct interface *); -void rt_headclear0(struct dhcpcd_ctx *, struct rt_head *, int); -void rt_headclear(struct rt_head *, int); -void rt_headfreeif(struct rt_head *); +bool rt_is_default(const struct rt *); +void rt_headclear0(struct dhcpcd_ctx *, rb_tree_t *, int); +void rt_headclear(rb_tree_t *, int); +void rt_headfreeif(rb_tree_t *); struct rt * rt_new0(struct dhcpcd_ctx *); void rt_setif(struct rt *, struct interface *); struct rt * rt_new(struct interface *); -void rt_recvrt(int, const struct rt *); +struct rt * rt_proto_add_ctx(rb_tree_t *, struct rt *, struct dhcpcd_ctx *); +struct rt * rt_proto_add(rb_tree_t *, struct rt *); +int rt_cmp_dest(const struct rt *, const struct rt *); +void rt_recvrt(int, const struct rt *, pid_t); void rt_build(struct dhcpcd_ctx *, int); #endif diff --git a/contrib/dhcpcd/src/sa.c b/contrib/dhcpcd/src/sa.c index dc9c241546..7f4d21f04a 100644 --- a/contrib/dhcpcd/src/sa.c +++ b/contrib/dhcpcd/src/sa.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * Socket Address handling for dhcpcd - * Copyright (c) 2015-2018 Roy Marples + * Copyright (c) 2015-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -37,6 +38,7 @@ #include #include +#include #include #include #include @@ -92,6 +94,30 @@ sa_addrlen(const struct sockaddr *sa) } } +#ifndef HAVE_SA_LEN +socklen_t +sa_len(const struct sockaddr *sa) +{ + + switch (sa->sa_family) { +#ifdef AF_LINK + case AF_LINK: + return sizeof(struct sockaddr_dl); +#endif +#ifdef AF_PACKET + case AF_PACKET: + return sizeof(struct sockaddr_ll); +#endif + case AF_INET: + return sizeof(struct sockaddr_in); + case AF_INET6: + return sizeof(struct sockaddr_in6); + default: + return sizeof(struct sockaddr); + } +} +#endif + bool sa_is_unspecified(const struct sockaddr *sa) { @@ -262,11 +288,9 @@ sa_toprefix(const struct sockaddr *sa) #ifndef NDEBUG /* Ensure the calculation is correct */ if (!sa_inprefix) { - union sa_ss ss; + union sa_ss ss = { .sa.sa_family = sa->sa_family }; sa_inprefix = true; - memset(&ss, 0, sizeof(ss)); - ss.sa.sa_family = sa->sa_family; sa_fromprefix(&ss.sa, prefix); assert(sa_cmp(sa, &ss.sa) == 0); sa_inprefix = false; @@ -339,6 +363,13 @@ sa_addrtop(const struct sockaddr *sa, char *buf, socklen_t len) const void *addr; assert(buf != NULL); + assert(len > 0); + + if (sa->sa_family == 0) { + *buf = '\0'; + return NULL; + } + #ifdef AF_LINK #ifndef CLLADDR #define CLLADDR(sdl) (const void *)((sdl)->sdl_data + (sdl)->sdl_nlen) diff --git a/contrib/dhcpcd/src/sa.h b/contrib/dhcpcd/src/sa.h index 26c741cca2..b2cb639b6e 100644 --- a/contrib/dhcpcd/src/sa.h +++ b/contrib/dhcpcd/src/sa.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * Socket Address handling for dhcpcd - * Copyright (c) 2015-2018 Roy Marples + * Copyright (c) 2015-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -29,6 +30,7 @@ #define SA_H #include +#include union sa_ss { struct sockaddr sa; @@ -54,6 +56,11 @@ union sa_ss { socklen_t sa_addroffset(const struct sockaddr *sa); socklen_t sa_addrlen(const struct sockaddr *sa); +#ifdef HAVE_SA_LEN +#define sa_len(sa) ((sa)->sa_len) +#else +socklen_t sa_len(const struct sockaddr *sa); +#endif bool sa_is_unspecified(const struct sockaddr *); bool sa_is_allones(const struct sockaddr *); bool sa_is_loopback(const struct sockaddr *); diff --git a/contrib/dhcpcd/src/script.c b/contrib/dhcpcd/src/script.c index 7c55f8abdd..3cb33b6e70 100644 --- a/contrib/dhcpcd/src/script.c +++ b/contrib/dhcpcd/src/script.c @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -32,10 +33,12 @@ #include #include +#include #include #include #include #include +#include #include #include #include @@ -56,7 +59,7 @@ #define RC_SVCNAME "RC_SVCNAME" #endif -#define DEFAULT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin" +#define DEFAULT_PATH "/usr/bin:/usr/sbin:/bin:/sbin" static const char * const if_params[] = { "interface", @@ -120,103 +123,23 @@ exec_script(const struct dhcpcd_ctx *ctx, char *const *argv, char *const *env) } #ifdef INET -static char * -make_var(const char *prefix, const char *var) -{ - size_t len; - char *v; - - len = strlen(prefix) + strlen(var) + 2; - if ((v = malloc(len)) == NULL) { - logerr(__func__); - return NULL; - } - snprintf(v, len, "%s_%s", prefix, var); - return v; -} - - static int -append_config(char ***env, size_t *len, - const char *prefix, const char *const *config) +append_config(FILE *fp, const char *prefix, const char *const *config) { - size_t i, j, e1; - char **ne, *eq, **nep, *p; - int ret; + size_t i; if (config == NULL) return 0; - ne = *env; - ret = 0; + /* Do we need to replace existing config rather than append? */ for (i = 0; config[i] != NULL; i++) { - eq = strchr(config[i], '='); - e1 = (size_t)(eq - config[i] + 1); - for (j = 0; j < *len; j++) { - if (strncmp(ne[j], prefix, strlen(prefix)) == 0 && - ne[j][strlen(prefix)] == '_' && - strncmp(ne[j] + strlen(prefix) + 1, - config[i], e1) == 0) - { - p = make_var(prefix, config[i]); - if (p == NULL) { - ret = -1; - break; - } - free(ne[j]); - ne[j] = p; - break; - } - } - if (j == *len) { - j++; - p = make_var(prefix, config[i]); - if (p == NULL) { - ret = -1; - break; - } - nep = realloc(ne, sizeof(char *) * (j + 1)); - if (nep == NULL) { - logerr(__func__); - free(p); - ret = -1; - break; - } - ne = nep; - ne[j - 1] = p; - *len = j; - } + if (efprintf(fp, "%s_%s", prefix, config[i]) == -1) + return -1; } - *env = ne; - return ret; + return 1; } -#endif - -static ssize_t -arraytostr(const char *const *argv, char **s) -{ - const char *const *ap; - char *p; - size_t len, l; - if (*argv == NULL) - return 0; - len = 0; - ap = argv; - while (*ap) - len += strlen(*ap++) + 1; - *s = p = malloc(len); - if (p == NULL) - return -1; - ap = argv; - while (*ap) { - l = strlen(*ap) + 1; - memcpy(p, *ap, l); - p += l; - ap++; - } - return (ssize_t)len; -} +#endif #define PROTO_LINK 0 #define PROTO_DHCP 1 @@ -233,15 +156,32 @@ static const char *protocols[] = { "static6" }; -static ssize_t -make_env(const struct interface *ifp, const char *reason, char ***argv) +int +efprintf(FILE *fp, const char *fmt, ...) { - int protocol, r; - char **env, **nenv, *p; - size_t e, elen, l; -#if defined(INET) || defined(INET6) - ssize_t n; -#endif + va_list args; + int r; + + va_start(args, fmt); + r = vfprintf(fp, fmt, args); + va_end(args); + if (r == -1) + return -1; + /* Write a trailing NULL so we can easily create env strings. */ + if (fputc('\0', fp) == EOF) + return -1; + return r; +} + +static long +make_env(const struct interface *ifp, const char *reason) +{ + struct dhcpcd_ctx *ctx = ifp->ctx; + FILE *fp; + char **env, **envp, *bufp, *endp, *path; + size_t nenv; + long buf_pos, i; + int protocol = PROTO_LINK; const struct if_options *ifo = ifp->options; const struct interface *ifp2; int af; @@ -251,24 +191,52 @@ make_env(const struct interface *ifp, const char *reason, char ***argv) const struct ipv4ll_state *istate; #endif #endif -#ifdef INET6 +#ifdef DHCP6 const struct dhcp6_state *d6_state; #endif +#ifdef HAVE_OPEN_MEMSTREAM + if (ctx->script_fp == NULL) { + fp = open_memstream(&ctx->script_buf, &ctx->script_buflen); + if (fp == NULL) + goto eexit; + ctx->script_fp = fp; + } else { + fp = ctx->script_fp; + rewind(fp); + } +#else + char tmpfile[] = "/tmp/dhcpcd-script-env-XXXXXX"; + int tmpfd; + + fp = NULL; + tmpfd = mkstemp(tmpfile); + if (tmpfd == -1) + goto eexit; + unlink(tmpfile); + fp = fdopen(tmpfd, "w+"); + if (fp == NULL) { + close(tmpfd); + goto eexit; + } +#endif + #ifdef INET state = D_STATE(ifp); #ifdef IPV4LL istate = IPV4LL_CSTATE(ifp); #endif #endif -#ifdef INET6 +#ifdef DHCP6 d6_state = D6_CSTATE(ifp); #endif if (strcmp(reason, "TEST") == 0) { if (1 == 2) {} #ifdef INET6 +#ifdef DHCP6 else if (d6_state && d6_state->new) protocol = PROTO_DHCP6; +#endif else if (ipv6nd_hasra(ifp)) protocol = PROTO_RA; #endif @@ -284,8 +252,10 @@ make_env(const struct interface *ifp, const char *reason, char ***argv) #ifdef INET6 else if (strcmp(reason, "STATIC6") == 0) protocol = PROTO_STATIC6; +#ifdef DHCP6 else if (reason[strlen(reason) - 1] == '6') protocol = PROTO_DHCP6; +#endif else if (strcmp(reason, "ROUTERADVERT") == 0) protocol = PROTO_RA; #endif @@ -305,71 +275,60 @@ make_env(const struct interface *ifp, const char *reason, char ***argv) protocol = PROTO_DHCP; #endif - /* When dumping the lease, we only want to report interface and - reason - the other interface variables are meaningless */ - if (ifp->ctx->options & DHCPCD_DUMPLEASE) - elen = 2; - else - elen = 11; + /* Needed for scripts */ + path = getenv("PATH"); + if (efprintf(fp, "PATH=%s", path == NULL ? DEFAULT_PATH:path) == -1) + goto eexit; -#define EMALLOC(i, l) if ((env[(i)] = malloc((l))) == NULL) goto eexit; - /* Make our env + space for profile, wireless and debug */ - env = calloc(1, sizeof(char *) * (elen + 5 + 1)); - if (env == NULL) + if (efprintf(fp, "interface=%s", ifp->name) == -1) + goto eexit; + if (efprintf(fp, "reason=%s", reason) == -1) goto eexit; - e = strlen("interface") + strlen(ifp->name) + 2; - EMALLOC(0, e); - snprintf(env[0], e, "interface=%s", ifp->name); - e = strlen("reason") + strlen(reason) + 2; - EMALLOC(1, e); - snprintf(env[1], e, "reason=%s", reason); if (ifp->ctx->options & DHCPCD_DUMPLEASE) goto dumplease; - e = 20; - EMALLOC(2, e); - snprintf(env[2], e, "pid=%d", getpid()); - EMALLOC(3, e); - snprintf(env[3], e, "ifcarrier=%s", + if (efprintf(fp, "pid=%d", getpid()) == -1) + goto eexit; + if (efprintf(fp, "ifcarrier=%s", ifp->carrier == LINK_UNKNOWN ? "unknown" : - ifp->carrier == LINK_UP ? "up" : "down"); - EMALLOC(4, e); - snprintf(env[4], e, "ifmetric=%d", ifp->metric); - EMALLOC(5, e); - snprintf(env[5], e, "ifwireless=%d", ifp->wireless); - EMALLOC(6, e); - snprintf(env[6], e, "ifflags=%u", ifp->flags); - EMALLOC(7, e); - snprintf(env[7], e, "ifmtu=%d", if_getmtu(ifp)); - l = e = strlen("interface_order="); - TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { - e += strlen(ifp2->name) + 1; - } - EMALLOC(8, e); - p = env[8]; - strlcpy(p, "interface_order=", e); - e -= l; - p += l; + ifp->carrier == LINK_UP ? "up" : "down") == -1) + goto eexit; + if (efprintf(fp, "ifmetric=%d", ifp->metric) == -1) + goto eexit; + if (efprintf(fp, "ifwireless=%d", ifp->wireless) == -1) + goto eexit; + if (efprintf(fp, "ifflags=%u", ifp->flags) == -1) + goto eexit; + if (efprintf(fp, "ifmtu=%d", if_getmtu(ifp)) == -1) + goto eexit; + + if (fprintf(fp, "interface_order=") == -1) + goto eexit; TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { - l = strlcpy(p, ifp2->name, e); - p += l; - e -= l; - *p++ = ' '; - e--; + if (ifp2 != TAILQ_FIRST(ifp->ctx->ifaces)) { + if (fputc(' ', fp) == EOF) + return -1; + } + if (fprintf(fp, "%s", ifp2->name) == -1) + return -1; } - *--p = '\0'; + if (fputc('\0', fp) == EOF) + return -1; + if (strcmp(reason, "STOPPED") == 0) { - env[9] = strdup("if_up=false"); - if (ifo->options & DHCPCD_RELEASE) - env[10] = strdup("if_down=true"); - else - env[10] = strdup("if_down=false"); + if (efprintf(fp, "if_up=false") == -1) + goto eexit; + if (efprintf(fp, "if_down=%s", + ifo->options & DHCPCD_RELEASE ? "true" : "false") == -1) + goto eexit; } else if (strcmp(reason, "TEST") == 0 || strcmp(reason, "PREINIT") == 0 || strcmp(reason, "CARRIER") == 0 || strcmp(reason, "UNKNOWN") == 0) { - env[9] = strdup("if_up=false"); - env[10] = strdup("if_down=false"); + if (efprintf(fp, "if_up=false") == -1) + goto eexit; + if (efprintf(fp, "if_down=false") == -1) + goto eexit; } else if (1 == 2 /* appease ifdefs */ #ifdef INET || (protocol == PROTO_DHCP && state && state->new) @@ -379,29 +338,30 @@ make_env(const struct interface *ifp, const char *reason, char ***argv) #endif #ifdef INET6 || (protocol == PROTO_STATIC6 && IPV6_STATE_RUNNING(ifp)) +#ifdef DHCP6 || (protocol == PROTO_DHCP6 && d6_state && d6_state->new) +#endif || (protocol == PROTO_RA && ipv6nd_hasra(ifp)) #endif ) { - env[9] = strdup("if_up=true"); - env[10] = strdup("if_down=false"); + if (efprintf(fp, "if_up=true") == -1) + goto eexit; + if (efprintf(fp, "if_down=false") == -1) + goto eexit; } else { - env[9] = strdup("if_up=false"); - env[10] = strdup("if_down=true"); + if (efprintf(fp, "if_up=false") == -1) + goto eexit; + if (efprintf(fp, "if_down=true") == -1) + goto eexit; } - if (env[9] == NULL || env[10] == NULL) - goto eexit; if (protocols[protocol] != NULL) { - r = asprintf(&env[elen], "protocol=%s", protocols[protocol]); - if (r == -1) + if (efprintf(fp, "protocol=%s", protocols[protocol]) == -1) goto eexit; - elen++; } if ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) { - e = 20; - EMALLOC(elen, e); - snprintf(env[elen++], e, "if_afwaiting=%d", af); + if (efprintf(fp, "if_afwaiting=%d", af) == -1) + goto eexit; } if ((af = dhcpcd_afwaiting(ifp->ctx)) != AF_MAX) { TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { @@ -410,75 +370,42 @@ make_env(const struct interface *ifp, const char *reason, char ***argv) } } if (af != AF_MAX) { - e = 20; - EMALLOC(elen, e); - snprintf(env[elen++], e, "af_waiting=%d", af); + if (efprintf(fp, "af_waiting=%d", af) == -1) + goto eexit; } if (ifo->options & DHCPCD_DEBUG) { - e = strlen("syslog_debug=true") + 1; - EMALLOC(elen, e); - snprintf(env[elen++], e, "syslog_debug=true"); + if (efprintf(fp, "syslog_debug=true") == -1) + goto eexit; } if (*ifp->profile) { - e = strlen("profile=") + strlen(ifp->profile) + 1; - EMALLOC(elen, e); - snprintf(env[elen++], e, "profile=%s", ifp->profile); + if (efprintf(fp, "profile=%s", ifp->profile) == -1) + goto eexit; } if (ifp->wireless) { - static const char *pfx = "ifssid="; - size_t pfx_len; - ssize_t psl; - - pfx_len = strlen(pfx); - psl = print_string(NULL, 0, OT_ESCSTRING, - (const uint8_t *)ifp->ssid, ifp->ssid_len); - if (psl != -1) { - EMALLOC(elen, pfx_len + (size_t)psl + 1); - memcpy(env[elen], pfx, pfx_len); - print_string(env[elen] + pfx_len, (size_t)psl + 1, - OT_ESCSTRING, - (const uint8_t *)ifp->ssid, ifp->ssid_len); - elen++; + char pssid[IF_SSIDLEN * 4]; + + if (print_string(pssid, sizeof(pssid), OT_ESCSTRING, + ifp->ssid, ifp->ssid_len) != -1) + { + if (efprintf(fp, "ifssid=%s", pssid) == -1) + goto eexit; } } #ifdef INET if (protocol == PROTO_DHCP && state && state->old) { - n = dhcp_env(NULL, NULL, state->old, state->old_len, ifp); - if (n == -1) + if (dhcp_env(fp, "old", ifp, + state->old, state->old_len) == -1) goto eexit; - if (n > 0) { - nenv = realloc(env, sizeof(char *) * - (elen + (size_t)n + 1)); - if (nenv == NULL) - goto eexit; - env = nenv; - n = dhcp_env(env + elen, "old", - state->old, state->old_len, ifp); - if (n == -1) - goto eexit; - elen += (size_t)n; - } - if (append_config(&env, &elen, "old", + if (append_config(fp, "old", (const char *const *)ifo->config) == -1) goto eexit; } #endif -#ifdef INET6 +#ifdef DHCP6 if (protocol == PROTO_DHCP6 && d6_state && d6_state->old) { - n = dhcp6_env(NULL, NULL, ifp, - d6_state->old, d6_state->old_len); - if (n > 0) { - nenv = realloc(env, sizeof(char *) * - (elen + (size_t)n + 1)); - if (nenv == NULL) - goto eexit; - env = nenv; - n = dhcp6_env(env + elen, "old", ifp, - d6_state->old, d6_state->old_len); - if (n == -1) - goto eexit; - elen += (size_t)n; - } + if (dhcp6_env(fp, "old", ifp, + d6_state->old, d6_state->old_len) == -1) + goto eexit; } #endif @@ -486,142 +413,124 @@ dumplease: #ifdef INET #ifdef IPV4LL if (protocol == PROTO_IPV4LL) { - n = ipv4ll_env(NULL, NULL, ifp); - if (n > 0) { - nenv = realloc(env, sizeof(char *) * - (elen + (size_t)n + 1)); - if (nenv == NULL) - goto eexit; - env = nenv; - if ((n = ipv4ll_env(env + elen, - istate->down ? "old" : "new", ifp)) == -1) - goto eexit; - elen += (size_t)n; - } + if (ipv4ll_env(fp, istate->down ? "old" : "new", ifp) == -1) + goto eexit; } #endif if (protocol == PROTO_DHCP && state && state->new) { - n = dhcp_env(NULL, NULL, state->new, state->new_len, ifp); - if (n > 0) { - nenv = realloc(env, sizeof(char *) * - (elen + (size_t)n + 1)); - if (nenv == NULL) - goto eexit; - env = nenv; - n = dhcp_env(env + elen, "new", - state->new, state->new_len, ifp); - if (n == -1) - goto eexit; - elen += (size_t)n; - } - if (append_config(&env, &elen, "new", + if (dhcp_env(fp, "new", ifp, + state->new, state->new_len) == -1) + goto eexit; + if (append_config(fp, "new", (const char *const *)ifo->config) == -1) goto eexit; } #endif #ifdef INET6 if (protocol == PROTO_STATIC6) { - n = ipv6_env(NULL, NULL, ifp); - if (n > 0) { - nenv = realloc(env, sizeof(char *) * - (elen + (size_t)n + 1)); - if (nenv == NULL) - goto eexit; - env = nenv; - n = ipv6_env(env + elen, "new", ifp); - if (n == -1) - goto eexit; - elen += (size_t)n; - } + if (ipv6_env(fp, "new", ifp) == -1) + goto eexit; } +#ifdef DHCP6 if (protocol == PROTO_DHCP6 && D6_STATE_RUNNING(ifp)) { - n = dhcp6_env(NULL, NULL, ifp, - d6_state->new, d6_state->new_len); - if (n > 0) { - nenv = realloc(env, sizeof(char *) * - (elen + (size_t)n + 1)); - if (nenv == NULL) - goto eexit; - env = nenv; - n = dhcp6_env(env + elen, "new", ifp, - d6_state->new, d6_state->new_len); - if (n == -1) - goto eexit; - elen += (size_t)n; - } + if (dhcp6_env(fp, "new", ifp, + d6_state->new, d6_state->new_len) == -1) + goto eexit; } +#endif if (protocol == PROTO_RA) { - n = ipv6nd_env(NULL, NULL, ifp); - if (n > 0) { - nenv = realloc(env, sizeof(char *) * - (elen + (size_t)n + 1)); - if (nenv == NULL) - goto eexit; - env = nenv; - n = ipv6nd_env(env + elen, NULL, ifp); - if (n == -1) - goto eexit; - elen += (size_t)n; - } + if (ipv6nd_env(fp, ifp) == -1) + goto eexit; } #endif /* Add our base environment */ if (ifo->environ) { - e = 0; - while (ifo->environ[e++]) - ; - nenv = realloc(env, sizeof(char *) * (elen + e + 1)); - if (nenv == NULL) - goto eexit; - env = nenv; - e = 0; - while (ifo->environ[e]) { - env[elen + e] = strdup(ifo->environ[e]); - if (env[elen + e] == NULL) + for (i = 0; ifo->environ[i] != NULL; i++) + if (efprintf(fp, "%s", ifo->environ[i]) == -1) goto eexit; - e++; + } + + /* Convert buffer to argv */ + fflush(fp); + + buf_pos = ftell(fp); + if (buf_pos == -1) { + logerr(__func__); + goto eexit; + } + +#ifndef HAVE_OPEN_MEMSTREAM + size_t buf_len = (size_t)buf_pos; + if (ctx->script_buflen < buf_len) { + char *buf = realloc(ctx->script_buf, buf_len); + if (buf == NULL) + goto eexit; + ctx->script_buf = buf; + ctx->script_buflen = buf_len; + } + rewind(fp); + if (fread(ctx->script_buf, sizeof(char), buf_len, fp) != buf_len) + goto eexit; + fclose(fp); + fp = NULL; +#endif + + /* Count the terminated env strings. + * Assert that the terminations are correct. */ + nenv = 0; + endp = ctx->script_buf + buf_pos; + for (bufp = ctx->script_buf; bufp < endp; bufp++) { + if (*bufp == '\0') { +#ifndef NDEBUG + if (bufp + 1 < endp) + assert(*(bufp + 1) != '\0'); +#endif + nenv++; } - elen += e; } - env[elen] = NULL; + assert(*(bufp - 1) == '\0'); - *argv = env; - return (ssize_t)elen; + if (ctx->script_envlen < nenv) { + env = reallocarray(ctx->script_env, nenv + 1, sizeof(*env)); + if (env == NULL) + goto eexit; + ctx->script_env = env; + ctx->script_envlen = nenv; + } + + bufp = ctx->script_buf; + envp = ctx->script_env; + *envp++ = bufp++; + endp--; /* Avoid setting the last \0 to an invalid pointer */ + for (; bufp < endp; bufp++) { + if (*bufp == '\0') + *envp++ = bufp + 1; + } + *envp = NULL; + + return buf_pos - 1; eexit: logerr(__func__); - if (env) { - nenv = env; - while (*nenv) - free(*nenv++); - free(env); - } +#ifndef HAVE_OPEN_MEMSTREAM + if (fp != NULL) + fclose(fp); +#endif return -1; } static int -send_interface1(struct fd_list *fd, const struct interface *iface, +send_interface1(struct fd_list *fd, const struct interface *ifp, const char *reason) { - char **env, **ep, *s; - size_t elen; - int retval; + struct dhcpcd_ctx *ctx = ifp->ctx; + long len; - if (make_env(iface, reason, &env) == -1) + len = make_env(ifp, reason); + if (len == -1) return -1; - s = NULL; - elen = (size_t)arraytostr((const char *const *)env, &s); - if ((ssize_t)elen == -1) { - free(s); - retval = -1; - } else - retval = control_queue(fd, s, elen, 1); - ep = env; - while (*ep) - free(*ep++); - free(env); - return retval; + return control_queue(fd, ctx->script_buf, (size_t)len, 1); } int @@ -632,7 +541,7 @@ send_interface(struct fd_list *fd, const struct interface *ifp) #ifdef INET const struct dhcp_state *d; #endif -#ifdef INET6 +#ifdef DHCP6 const struct dhcp6_state *d6; #endif @@ -641,6 +550,7 @@ send_interface(struct fd_list *fd, const struct interface *ifp) reason = "CARRIER"; break; case LINK_DOWN: + case LINK_DOWN_IFFUP: reason = "NOCARRIER"; break; default: @@ -672,12 +582,14 @@ send_interface(struct fd_list *fd, const struct interface *ifp) if (send_interface1(fd, ifp, "ROUTERADVERT") == -1) retval = -1; } +#ifdef DHCP6 if (D6_STATE_RUNNING(ifp)) { d6 = D6_CSTATE(ifp); if (send_interface1(fd, ifp, d6->reason) == -1) retval = -1; } #endif +#endif return retval; } @@ -685,73 +597,30 @@ send_interface(struct fd_list *fd, const struct interface *ifp) int script_runreason(const struct interface *ifp, const char *reason) { + struct dhcpcd_ctx *ctx = ifp->ctx; char *argv[2]; - char **env = NULL, **ep; - char *svcname, *path, *bigenv; - size_t e, elen = 0; pid_t pid; int status = 0; struct fd_list *fd; - if (ifp->options->script && - (ifp->options->script[0] == '\0' || - strcmp(ifp->options->script, "/dev/null") == 0) && + if (ifp->options->script == NULL && TAILQ_FIRST(&ifp->ctx->control_fds) == NULL) return 0; /* Make our env */ - elen = (size_t)make_env(ifp, reason, &env); - if (elen == (size_t)-1) { + if (make_env(ifp, reason) == -1) { logerr(__func__); return -1; } - if (ifp->options->script && - (ifp->options->script[0] == '\0' || - strcmp(ifp->options->script, "/dev/null") == 0)) + if (ifp->options->script == NULL) goto send_listeners; - argv[0] = ifp->options->script ? ifp->options->script : UNCONST(SCRIPT); + argv[0] = ifp->options->script; argv[1] = NULL; logdebugx("%s: executing `%s' %s", ifp->name, argv[0], reason); - /* Resize for PATH and RC_SVCNAME */ - svcname = getenv(RC_SVCNAME); - ep = reallocarray(env, elen + 2 + (svcname ? 1 : 0), sizeof(char *)); - if (ep == NULL) { - elen = 0; - goto out; - } - env = ep; - /* Add path to it */ - path = getenv("PATH"); - if (path) { - e = strlen("PATH") + strlen(path) + 2; - env[elen] = malloc(e); - if (env[elen] == NULL) { - elen = 0; - goto out; - } - snprintf(env[elen], e, "PATH=%s", path); - } else { - env[elen] = strdup(DEFAULT_PATH); - if (env[elen] == NULL) { - elen = 0; - goto out; - } - } - if (svcname) { - e = strlen(RC_SVCNAME) + strlen(svcname) + 2; - env[++elen] = malloc(e); - if (env[elen] == NULL) { - elen = 0; - goto out; - } - snprintf(env[elen], e, "%s=%s", RC_SVCNAME, svcname); - } - env[++elen] = NULL; - - pid = exec_script(ifp->ctx, argv, env); + pid = exec_script(ctx, argv, ctx->script_env); if (pid == -1) logerr("%s: %s", __func__, argv[0]); else if (pid != 0) { @@ -774,36 +643,16 @@ script_runreason(const struct interface *ifp, const char *reason) send_listeners: /* Send to our listeners */ - bigenv = NULL; status = 0; - TAILQ_FOREACH(fd, &ifp->ctx->control_fds, next) { + TAILQ_FOREACH(fd, &ctx->control_fds, next) { if (!(fd->flags & FD_LISTEN)) continue; - if (bigenv == NULL) { - elen = (size_t)arraytostr((const char *const *)env, - &bigenv); - if ((ssize_t)elen == -1) { - logerr("%s: arraytostr", ifp->name); - break; - } - } - if (control_queue(fd, bigenv, elen, 1) == -1) + if (control_queue(fd, ctx->script_buf, ctx->script_buflen, + true) == -1) logerr("%s: control_queue", __func__); else status = 1; } - if (!status) - free(bigenv); -out: - /* Cleanup */ - ep = env; - while (*ep) - free(*ep++); - free(env); - if (elen == 0) { - logerr(__func__); - return -1; - } return WEXITSTATUS(status); } diff --git a/contrib/dhcpcd/src/script.h b/contrib/dhcpcd/src/script.h index 0b9d6991ce..d02e54cac9 100644 --- a/contrib/dhcpcd/src/script.h +++ b/contrib/dhcpcd/src/script.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2018 Roy Marples + * Copyright (c) 2006-2019 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -30,6 +31,7 @@ #include "control.h" +__printflike(2, 3) int efprintf(FILE *, const char *, ...); void if_printoptions(void); int send_interface(struct fd_list *, const struct interface *); int script_runreason(const struct interface *, const char *); -- 2.11.4.GIT