Import dhcpcd-8.0.4 to vendor branch.
authorRoy Marples <roy@marples.name>
Wed, 4 Sep 2019 16:55:23 +0000 (17:55 +0100)
committerRoy Marples <roy@marples.name>
Wed, 4 Sep 2019 16:55:23 +0000 (17:55 +0100)
72 files changed:
contrib/dhcpcd/BUILDING.md [deleted file]
contrib/dhcpcd/LICENSE
contrib/dhcpcd/README.DELETED [deleted file]
contrib/dhcpcd/README.DRAGONFLY [deleted file]
contrib/dhcpcd/compat/consttime_memequal.h [new file with mode: 0644]
contrib/dhcpcd/compat/rb.c [new file with mode: 0644]
contrib/dhcpcd/compat/rbtree.h [new file with mode: 0644]
contrib/dhcpcd/compat/reallocarray.c [deleted file]
contrib/dhcpcd/compat/reallocarray.h [deleted file]
contrib/dhcpcd/hooks/10-wpa_supplicant
contrib/dhcpcd/hooks/20-resolv.conf
contrib/dhcpcd/hooks/29-lookup-hostname
contrib/dhcpcd/hooks/30-hostname
contrib/dhcpcd/hooks/50-dhcpcd-compat [deleted file]
contrib/dhcpcd/hooks/50-ntp.conf
contrib/dhcpcd/hooks/50-yp.conf [deleted file]
contrib/dhcpcd/hooks/50-ypbind.in
contrib/dhcpcd/hooks/dhcpcd-run-hooks.in
contrib/dhcpcd/src/arp.c
contrib/dhcpcd/src/arp.h
contrib/dhcpcd/src/auth.c
contrib/dhcpcd/src/auth.h
contrib/dhcpcd/src/bpf.c
contrib/dhcpcd/src/bpf.h
contrib/dhcpcd/src/common.c
contrib/dhcpcd/src/common.h
contrib/dhcpcd/src/control.c
contrib/dhcpcd/src/control.h
contrib/dhcpcd/src/defs.h
contrib/dhcpcd/src/dev.c [deleted file]
contrib/dhcpcd/src/dev.h
contrib/dhcpcd/src/dhcp-common.c
contrib/dhcpcd/src/dhcp-common.h
contrib/dhcpcd/src/dhcp.c
contrib/dhcpcd/src/dhcp.h
contrib/dhcpcd/src/dhcp6.c
contrib/dhcpcd/src/dhcp6.h
contrib/dhcpcd/src/dhcpcd-definitions.conf [deleted file]
contrib/dhcpcd/src/dhcpcd-embedded.c [new file with mode: 0644]
contrib/dhcpcd/src/dhcpcd-embedded.c.in [deleted file]
contrib/dhcpcd/src/dhcpcd-embedded.h [moved from contrib/dhcpcd/src/dhcpcd-embedded.h.in with 89% similarity]
contrib/dhcpcd/src/dhcpcd.8.in
contrib/dhcpcd/src/dhcpcd.c
contrib/dhcpcd/src/dhcpcd.conf.5.in
contrib/dhcpcd/src/dhcpcd.h
contrib/dhcpcd/src/duid.c
contrib/dhcpcd/src/duid.h
contrib/dhcpcd/src/eloop.c
contrib/dhcpcd/src/eloop.h
contrib/dhcpcd/src/genembedc [deleted file]
contrib/dhcpcd/src/genembedh [deleted file]
contrib/dhcpcd/src/if-bsd.c
contrib/dhcpcd/src/if-options.c
contrib/dhcpcd/src/if-options.h
contrib/dhcpcd/src/if.c
contrib/dhcpcd/src/if.h
contrib/dhcpcd/src/ipv4.c
contrib/dhcpcd/src/ipv4.h
contrib/dhcpcd/src/ipv4ll.c
contrib/dhcpcd/src/ipv4ll.h
contrib/dhcpcd/src/ipv6.c
contrib/dhcpcd/src/ipv6.h
contrib/dhcpcd/src/ipv6nd.c
contrib/dhcpcd/src/ipv6nd.h
contrib/dhcpcd/src/logerr.c
contrib/dhcpcd/src/logerr.h
contrib/dhcpcd/src/route.c
contrib/dhcpcd/src/route.h
contrib/dhcpcd/src/sa.c
contrib/dhcpcd/src/sa.h
contrib/dhcpcd/src/script.c
contrib/dhcpcd/src/script.h

diff --git a/contrib/dhcpcd/BUILDING.md b/contrib/dhcpcd/BUILDING.md
deleted file mode 100644 (file)
index f157424..0000000
+++ /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.
-
index b0ca15b..74bcc86 100644 (file)
@@ -1,4 +1,4 @@
-Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
 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 (file)
index d7d6cca..0000000
+++ /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 (file)
index 669803d..0000000
+++ /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 (file)
index 0000000..9830648
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Written by Matthias Drochner <drochner@NetBSD.org>.
+ * 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 (file)
index 0000000..3c0bed5
--- /dev/null
@@ -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 <matt@3am-software.com>.
+ *
+ * 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 <sys/types.h>
+#include <stddef.h>
+#include <assert.h>
+#include <stdbool.h>
+#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 <lib/libkern/libkern.h>
+__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 <sys/rbtree.h>
+#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 (file)
index 0000000..656da22
--- /dev/null
@@ -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 <matt@3am-software.com>.
+ *
+ * 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 <sys/types.h>
+#else
+#include <stdbool.h>
+#include <inttypes.h>
+#endif
+#ifdef HAVE_SYS_QUEUE_H
+#include <sys/queue.h>
+#else
+#include "queue.h"
+#endif
+#if !defined(__linux__) && !defined(__QNX__) && !defined(__sun)
+#include <sys/endian.h>
+#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 (file)
index 9ff9c20..0000000
+++ /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 <joerg@NetBSD.org>.
- * 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 <errno.h>
-#include <limits.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-/*
- * 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 (file)
index d855e7b..0000000
+++ /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 <joerg@NetBSD.org>.
- * 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
index 6249385..04acce0 100644 (file)
@@ -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
index b03ab57..422d815 100644 (file)
@@ -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
index d0b1032..811c7a9 100644 (file)
@@ -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
 }
index 5f7d26d..53ae6b1 100644 (file)
@@ -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 (file)
index 0d6256e..0000000
+++ /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"
index 1217868..4b68f77 100644 (file)
@@ -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 (file)
index cc63d13..0000000
+++ /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
index eeb3f71..5bd5eac 100644 (file)
@@ -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
index 6e506da..049bd1b 100644 (file)
@@ -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
index f8bff87..49393f5 100644 (file)
@@ -1,6 +1,7 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - ARP handler
- * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
@@ -36,6 +37,7 @@
 
 #include <errno.h>
 #include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 
@@ -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);
@@ -105,6 +111,77 @@ eexit:
        return -1;
 }
 
+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)
 {
@@ -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 */
 }
index 6b9d5a6..0065e0c 100644 (file)
@@ -1,6 +1,7 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
  * 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 */
index a272e93..8a83530 100644 (file)
@@ -1,6 +1,7 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
@@ -30,6 +31,7 @@
 #include <fcntl.h>
 #include <inttypes.h>
 #include <stddef.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
@@ -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;
        }
index f65d62c..3f6b5a5 100644 (file)
@@ -1,6 +1,7 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
index 537a7a6..06bbf3f 100644 (file)
@@ -1,6 +1,7 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd: BPF arp and bootp filtering
- * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
  * 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++;
index 2515978..e886f9c 100644 (file)
@@ -1,6 +1,7 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd: BPF arp and bootp filtering
- * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
 #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);
index d4b606d..31998c0 100644 (file)
@@ -1,6 +1,7 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
 /* 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;
-}
index 3031fab..301c886 100644 (file)
@@ -1,6 +1,7 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
 
 #include <sys/param.h>
 #include <sys/time.h>
+#include <stdint.h>
 #include <stdio.h>
 
-#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
 # 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
index d9bf949..4ae2279 100644 (file)
@@ -1,6 +1,7 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
             (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
 #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)
 {
@@ -79,14 +59,20 @@ control_queue_free(struct fd_list *fd)
 
        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;
index 93ac01f..e52343d 100644 (file)
@@ -1,6 +1,7 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
 
 #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
index c68a98c..9bf8736 100644 (file)
@@ -1,6 +1,7 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
  *
  * 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 (file)
index 0d38917..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
- *
- * 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 <dirent.h>
-#include <dlfcn.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#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;
-}
index daf4d29..b2cd804 100644 (file)
@@ -1,6 +1,7 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
index eed3dbf..dab20a2 100644 (file)
@@ -1,6 +1,7 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
@@ -36,8 +37,6 @@
 #include <string.h>
 #include <unistd.h>
 
-#include <arpa/nameser.h> /* after normal includes for sunos */
-
 #include "config.h"
 
 #include "common.h"
 #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) {
index aea3d85..2732823 100644 (file)
@@ -1,6 +1,7 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
 
 #include <stdint.h>
 
+#include <arpa/nameser.h> /* 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 **),
index 1e9fe18..eafd6e7 100644 (file)
@@ -1,6 +1,7 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
@@ -47,6 +48,7 @@
 #include <inttypes.h>
 #include <stdbool.h>
 #include <stddef.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -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,74 +3228,107 @@ 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)
 {
@@ -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;
index fb27958..4b6311a 100644 (file)
@@ -1,6 +1,7 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
  * 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 */
index 155bcb0..3c22005 100644 (file)
@@ -1,6 +1,7 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
 /*
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
  * 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;
-