Move slaacd to /sbin
authorflorian <florian@openbsd.org>
Sat, 3 Jun 2017 10:00:29 +0000 (10:00 +0000)
committerflorian <florian@openbsd.org>
Sat, 3 Jun 2017 10:00:29 +0000 (10:00 +0000)
jca points out that all the other interface configuration tools live
there (like ifconfig or dhclient). Furthermore it starts so early in
the boot process that /usr might not be mounted yet if it's a nfs
filesystem.
sthen and deraadt agree

28 files changed:
etc/rc.d/slaacd
sbin/Makefile
sbin/slaacd/Makefile [new file with mode: 0644]
sbin/slaacd/control.c [new file with mode: 0644]
sbin/slaacd/control.h [new file with mode: 0644]
sbin/slaacd/engine.c [new file with mode: 0644]
sbin/slaacd/engine.h [new file with mode: 0644]
sbin/slaacd/frontend.c [new file with mode: 0644]
sbin/slaacd/frontend.h [new file with mode: 0644]
sbin/slaacd/log.c [new file with mode: 0644]
sbin/slaacd/log.h [new file with mode: 0644]
sbin/slaacd/slaacd.8 [new file with mode: 0644]
sbin/slaacd/slaacd.c [new file with mode: 0644]
sbin/slaacd/slaacd.h [new file with mode: 0644]
usr.sbin/Makefile
usr.sbin/slaacctl/Makefile
usr.sbin/slaacd/Makefile [deleted file]
usr.sbin/slaacd/control.c [deleted file]
usr.sbin/slaacd/control.h [deleted file]
usr.sbin/slaacd/engine.c [deleted file]
usr.sbin/slaacd/engine.h [deleted file]
usr.sbin/slaacd/frontend.c [deleted file]
usr.sbin/slaacd/frontend.h [deleted file]
usr.sbin/slaacd/log.c [deleted file]
usr.sbin/slaacd/log.h [deleted file]
usr.sbin/slaacd/slaacd.8 [deleted file]
usr.sbin/slaacd/slaacd.c [deleted file]
usr.sbin/slaacd/slaacd.h [deleted file]

index 7790863..fbb4749 100644 (file)
@@ -1,8 +1,8 @@
 #!/bin/sh
 #
-# $OpenBSD: slaacd,v 1.1 2017/05/29 10:24:06 florian Exp $
+# $OpenBSD: slaacd,v 1.2 2017/06/03 10:00:29 florian Exp $
 
-daemon="/usr/sbin/slaacd"
+daemon="/sbin/slaacd"
 
 . /etc/rc.d/rc.subr
 
index 8100ac5..f93db6a 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: Makefile,v 1.105 2016/09/17 18:37:01 florian Exp $
+#      $OpenBSD: Makefile,v 1.106 2017/06/03 10:00:29 florian Exp $
 
 SUBDIR=        atactl badsect bioctl clri dhclient \
        disklabel dmesg dump dumpfs fdisk fsck fsck_ext2fs fsck_ffs  \
@@ -9,7 +9,7 @@ SUBDIR= atactl badsect bioctl clri dhclient \
        mount_vnd mountd ncheck_ffs newfs newfs_ext2fs newfs_msdos \
        nfsd nologin pdisk pfctl pflogd ping quotacheck \
        reboot restore route savecore scan_ffs \
-       scsi shutdown swapctl sysctl ttyflags tunefs \
+       scsi slaacd shutdown swapctl sysctl ttyflags tunefs \
        umount wsconsctl
 
 .include <bsd.subdir.mk>
diff --git a/sbin/slaacd/Makefile b/sbin/slaacd/Makefile
new file mode 100644 (file)
index 0000000..989f432
--- /dev/null
@@ -0,0 +1,21 @@
+#      $OpenBSD: Makefile,v 1.1 2017/06/03 10:00:29 florian Exp $
+
+PROG=  slaacd
+SRCS=  control.c engine.c frontend.c log.c slaacd.c
+
+MAN=   slaacd.8
+
+#DEBUG=        -g -DDEBUG=3 -O0
+
+CFLAGS+= -DSKIP_PROPOSAL
+
+CFLAGS+= -Wall -I${.CURDIR}
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+CFLAGS+= -Wsign-compare
+YFLAGS=
+LDADD+=        -levent -lutil
+DPADD+= ${LIBEVENT} ${LIBUTIL}
+
+.include <bsd.prog.mk>
diff --git a/sbin/slaacd/control.c b/sbin/slaacd/control.c
new file mode 100644 (file)
index 0000000..76b0f3b
--- /dev/null
@@ -0,0 +1,301 @@
+/*     $OpenBSD: control.c,v 1.1 2017/06/03 10:00:29 florian Exp $     */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <errno.h>
+#include <event.h>
+#include <imsg.h>
+#include <md5.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "slaacd.h"
+#include "control.h"
+#include "frontend.h"
+
+#define        CONTROL_BACKLOG 5
+
+struct ctl_conn        *control_connbyfd(int);
+struct ctl_conn        *control_connbypid(pid_t);
+void            control_close(int);
+
+int
+control_init(char *path)
+{
+       struct sockaddr_un       sun;
+       int                      fd;
+       mode_t                   old_umask;
+
+       if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+           0)) == -1) {
+               log_warn("%s: socket", __func__);
+               return (-1);
+       }
+
+       memset(&sun, 0, sizeof(sun));
+       sun.sun_family = AF_UNIX;
+       strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
+
+       if (unlink(path) == -1)
+               if (errno != ENOENT) {
+                       log_warn("%s: unlink %s", __func__, path);
+                       close(fd);
+                       return (-1);
+               }
+
+       old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
+       if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
+               log_warn("%s: bind: %s", __func__, path);
+               close(fd);
+               umask(old_umask);
+               return (-1);
+       }
+       umask(old_umask);
+
+       if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
+               log_warn("%s: chmod", __func__);
+               close(fd);
+               (void)unlink(path);
+               return (-1);
+       }
+
+       control_state.fd = fd;
+
+       return (0);
+}
+
+int
+control_listen(void)
+{
+
+       if (listen(control_state.fd, CONTROL_BACKLOG) == -1) {
+               log_warn("%s: listen", __func__);
+               return (-1);
+       }
+
+       event_set(&control_state.ev, control_state.fd, EV_READ,
+           control_accept, NULL);
+       event_add(&control_state.ev, NULL);
+       evtimer_set(&control_state.evt, control_accept, NULL);
+
+       return (0);
+}
+
+void
+control_cleanup(char *path)
+{
+       if (path == NULL)
+               return;
+       event_del(&control_state.ev);
+       event_del(&control_state.evt);
+       unlink(path);
+}
+
+void
+control_accept(int listenfd, short event, void *bula)
+{
+       int                      connfd;
+       socklen_t                len;
+       struct sockaddr_un       sun;
+       struct ctl_conn         *c;
+
+       event_add(&control_state.ev, NULL);
+       if ((event & EV_TIMEOUT))
+               return;
+
+       len = sizeof(sun);
+       if ((connfd = accept4(listenfd, (struct sockaddr *)&sun, &len,
+           SOCK_CLOEXEC | SOCK_NONBLOCK)) == -1) {
+               /*
+                * Pause accept if we are out of file descriptors, or
+                * libevent will haunt us here too.
+                */
+               if (errno == ENFILE || errno == EMFILE) {
+                       struct timeval evtpause = { 1, 0 };
+
+                       event_del(&control_state.ev);
+                       evtimer_add(&control_state.evt, &evtpause);
+               } else if (errno != EWOULDBLOCK && errno != EINTR &&
+                   errno != ECONNABORTED)
+                       log_warn("%s: accept4", __func__);
+               return;
+       }
+
+       if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
+               log_warn("%s: calloc", __func__);
+               close(connfd);
+               return;
+       }
+
+       imsg_init(&c->iev.ibuf, connfd);
+       c->iev.handler = control_dispatch_imsg;
+       c->iev.events = EV_READ;
+       event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
+           c->iev.handler, &c->iev);
+       event_add(&c->iev.ev, NULL);
+
+       TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
+}
+
+struct ctl_conn *
+control_connbyfd(int fd)
+{
+       struct ctl_conn *c;
+
+       TAILQ_FOREACH(c, &ctl_conns, entry) {
+               if (c->iev.ibuf.fd == fd)
+                       break;
+       }
+
+       return (c);
+}
+
+struct ctl_conn *
+control_connbypid(pid_t pid)
+{
+       struct ctl_conn *c;
+
+       TAILQ_FOREACH(c, &ctl_conns, entry) {
+               if (c->iev.ibuf.pid == pid)
+                       break;
+       }
+
+       return (c);
+}
+
+void
+control_close(int fd)
+{
+       struct ctl_conn *c;
+
+       if ((c = control_connbyfd(fd)) == NULL) {
+               log_warnx("%s: fd %d: not found", __func__, fd);
+               return;
+       }
+
+       msgbuf_clear(&c->iev.ibuf.w);
+       TAILQ_REMOVE(&ctl_conns, c, entry);
+
+       event_del(&c->iev.ev);
+       close(c->iev.ibuf.fd);
+
+       /* Some file descriptors are available again. */
+       if (evtimer_pending(&control_state.evt, NULL)) {
+               evtimer_del(&control_state.evt);
+               event_add(&control_state.ev, NULL);
+       }
+
+       free(c);
+}
+
+void
+control_dispatch_imsg(int fd, short event, void *bula)
+{
+       struct ctl_conn *c;
+       struct imsg      imsg;
+       ssize_t          n;
+       int              verbose;
+
+       if ((c = control_connbyfd(fd)) == NULL) {
+               log_warnx("%s: fd %d: not found", __func__, fd);
+               return;
+       }
+
+       if (event & EV_READ) {
+               if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) ||
+                   n == 0) {
+                       control_close(fd);
+                       return;
+               }
+       }
+       if (event & EV_WRITE) {
+               if (msgbuf_write(&c->iev.ibuf.w) <= 0 && errno != EAGAIN) {
+                       control_close(fd);
+                       return;
+               }
+       }
+
+       for (;;) {
+               if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
+                       control_close(fd);
+                       return;
+               }
+               if (n == 0)
+                       break;
+
+               switch (imsg.hdr.type) {
+               case IMSG_CTL_LOG_VERBOSE:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE +
+                           sizeof(verbose))
+                               break;
+
+                       /* Forward to all other processes. */
+                       frontend_imsg_compose_main(imsg.hdr.type, imsg.hdr.pid,
+                           imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
+                       frontend_imsg_compose_engine(imsg.hdr.type, 0,
+                           imsg.hdr.pid, imsg.data,
+                           imsg.hdr.len - IMSG_HEADER_SIZE);
+
+                       memcpy(&verbose, imsg.data, sizeof(verbose));
+                       log_setverbose(verbose);
+                       break;
+               case IMSG_CTL_SHOW_INTERFACE_INFO:
+                       c->iev.ibuf.pid = imsg.hdr.pid;
+                       frontend_imsg_compose_engine(imsg.hdr.type, 0,
+                           imsg.hdr.pid, imsg.data, imsg.hdr.len -
+                           IMSG_HEADER_SIZE);
+                       break;
+               case IMSG_CTL_SEND_SOLICITATION:
+                       c->iev.ibuf.pid = imsg.hdr.pid;
+                       frontend_imsg_compose_engine(imsg.hdr.type, 0,
+                           imsg.hdr.pid, imsg.data, imsg.hdr.len -
+                           IMSG_HEADER_SIZE);
+                       break;
+               default:
+                       log_debug("%s: error handling imsg %d", __func__,
+                           imsg.hdr.type);
+                       break;
+               }
+               imsg_free(&imsg);
+       }
+
+       imsg_event_add(&c->iev);
+}
+
+int
+control_imsg_relay(struct imsg *imsg)
+{
+       struct ctl_conn *c;
+
+       if ((c = control_connbypid(imsg->hdr.pid)) == NULL)
+               return (0);
+
+       return (imsg_compose_event(&c->iev, imsg->hdr.type, 0, imsg->hdr.pid,
+           -1, imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE));
+}
diff --git a/sbin/slaacd/control.h b/sbin/slaacd/control.h
new file mode 100644 (file)
index 0000000..49d6bd8
--- /dev/null
@@ -0,0 +1,35 @@
+/*     $OpenBSD: control.h,v 1.1 2017/06/03 10:00:29 florian Exp $     */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+struct {
+       struct event    ev;
+       struct event    evt;
+       int             fd;
+} control_state;
+
+struct ctl_conn {
+       TAILQ_ENTRY(ctl_conn)   entry;
+       struct imsgev           iev;
+};
+
+int    control_init(char *);
+int    control_listen(void);
+void   control_accept(int, short, void *);
+void   control_dispatch_imsg(int, short, void *);
+int    control_imsg_relay(struct imsg *);
+void   control_cleanup(char *);
diff --git a/sbin/slaacd/engine.c b/sbin/slaacd/engine.c
new file mode 100644 (file)
index 0000000..3231599
--- /dev/null
@@ -0,0 +1,2177 @@
+/*     $OpenBSD: engine.c,v 1.1 2017/06/03 10:00:29 florian Exp $      */
+
+/*
+ * Copyright (c) 2017 Florian Obser <florian@openbsd.org>
+ * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * 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.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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 <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/uio.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+#include <netinet/icmp6.h>
+
+#include <errno.h>
+#include <event.h>
+#include <imsg.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "slaacd.h"
+#include "engine.h"
+
+#define        MAX_RTR_SOLICITATION_DELAY      1
+#define        MAX_RTR_SOLICITATION_DELAY_USEC MAX_RTR_SOLICITATION_DELAY * 1000000
+#define        RTR_SOLICITATION_INTERVAL       4
+#define        MAX_RTR_SOLICITATIONS           3
+
+enum if_state {
+       IF_DOWN,
+       IF_DELAY,
+       IF_PROBE,
+       IF_IDLE,
+};
+
+const char* if_state_name[] = {
+       "IF_DOWN",
+       "IF_DELAY",
+       "IF_PROBE",
+       "IF_IDLE",
+};
+
+enum proposal_state {
+       PROPOSAL_NOT_CONFIGURED,
+       PROPOSAL_SENT,
+       PROPOSAL_CONFIGURED,
+       PROPOSAL_NEARLY_EXPIRED,
+       PROPOSAL_WITHDRAWN,
+};
+
+const char* proposal_state_name[] = {
+       "NOT_CONFIGURED",
+       "SENT",
+       "CONFIGURED",
+       "NEARLY_EXPIRED",
+       "WITHDRAWN",
+};
+
+const char* rpref_name[] = {
+       "Low",
+       "Medium",
+       "High",
+};
+
+struct radv_prefix {
+       LIST_ENTRY(radv_prefix) entries;
+       struct in6_addr         prefix;
+       uint8_t                 prefix_len; /*XXX int */
+       int                     onlink;
+       int                     autonomous;
+       uint32_t                vltime;
+       uint32_t                pltime;
+};
+
+struct radv_rdns {
+       LIST_ENTRY(radv_rdns)   entries;
+       struct in6_addr         rdns;
+};
+
+struct radv_dnssl {
+       LIST_ENTRY(radv_dnssl)  entries;
+       char                    dnssl[SLAACD_MAX_DNSSL];
+};
+
+struct radv {
+       LIST_ENTRY(radv)                 entries;
+       struct sockaddr_in6              from;
+       struct timespec                  when;
+       struct timespec                  uptime;
+       struct event                     timer;
+       uint32_t                         min_lifetime;
+       uint8_t                          curhoplimit;
+       int                              managed;
+       int                              other;
+       enum rpref                       rpref;
+       uint16_t                         router_lifetime; /* in seconds */
+       uint32_t                         reachable_time; /* in milliseconds */
+       uint32_t                         retrans_time; /* in milliseconds */
+       LIST_HEAD(, radv_prefix)         prefixes;
+       uint32_t                         rdns_lifetime;
+       LIST_HEAD(, radv_rdns)           rdns_servers;
+       uint32_t                         dnssl_lifetime;
+       LIST_HEAD(, radv_dnssl)          dnssls;
+};
+
+struct address_proposal {
+       LIST_ENTRY(address_proposal)     entries;
+       struct event                     timer;
+       int64_t                          id;
+       enum proposal_state              state;
+       int                              next_timeout;
+       int                              timeout_count;
+       struct timespec                  when;
+       struct timespec                  uptime;
+       uint32_t                         if_index;
+       struct ether_addr                hw_address;
+       struct sockaddr_in6              addr;
+       struct in6_addr                  mask;
+       struct in6_addr                  prefix;
+       int                              privacy;
+       uint8_t                          prefix_len;
+       uint32_t                         vltime;
+       uint32_t                         pltime;
+};
+
+struct dfr_proposal {
+       LIST_ENTRY(dfr_proposal)         entries;
+       struct event                     timer;
+       int64_t                          id;
+       enum proposal_state              state;
+       int                              next_timeout;
+       int                              timeout_count;
+       struct timespec                  when;
+       struct timespec                  uptime;
+       uint32_t                         if_index;
+       struct sockaddr_in6              addr;
+       uint32_t                         router_lifetime;
+       enum rpref                       rpref;
+};
+
+struct slaacd_iface {
+       LIST_ENTRY(slaacd_iface)         entries;
+       enum if_state                    state;
+       struct event                     timer;
+       int                              probes;
+       uint32_t                         if_index;
+       int                              running;
+       int                              autoconfprivacy;
+       struct ether_addr                hw_address;
+       struct sockaddr_in6              ll_address;
+       LIST_HEAD(, radv)                radvs;
+       LIST_HEAD(, address_proposal)    addr_proposals;
+       LIST_HEAD(, dfr_proposal)        dfr_proposals;
+};
+
+LIST_HEAD(, slaacd_iface) slaacd_interfaces;
+
+__dead void             engine_shutdown(void);
+void                    engine_sig_handler(int sig, short, void *);
+void                    engine_dispatch_frontend(int, short, void *);
+void                    engine_dispatch_main(int, short, void *);
+void                    send_interface_info(struct slaacd_iface *, pid_t);
+void                    engine_showinfo_ctl(struct imsg *, uint32_t);
+struct slaacd_iface    *get_slaacd_iface_by_id(uint32_t);
+void                    remove_slaacd_iface(uint32_t);
+void                    free_ra(struct radv *);
+void                    parse_ra(struct slaacd_iface *, struct imsg_ra *);
+void                    gen_addr(struct slaacd_iface *, struct radv_prefix *,
+                            struct address_proposal *, int);
+void                    gen_address_proposal(struct slaacd_iface *, struct
+                            radv *, struct radv_prefix *, int);
+void                    configure_address(struct address_proposal *);
+void                    in6_prefixlen2mask(struct in6_addr *, int len);
+void                    gen_dfr_proposal(struct slaacd_iface *, struct
+                            radv *);
+void                    configure_dfr(struct dfr_proposal *);
+void                    free_dfr_proposal(struct dfr_proposal *);
+void                    withdraw_dfr(struct dfr_proposal *);
+void                    debug_log_ra(struct imsg_ra *);
+char                   *parse_dnssl(char *, int);
+void                    update_iface_ra(struct slaacd_iface *, struct radv *);
+void                    send_proposal(struct imsg_proposal *);
+void                    start_probe(struct slaacd_iface *);
+void                    address_proposal_timeout(int, short, void *);
+void                    dfr_proposal_timeout(int, short, void *);
+void                    iface_timeout(int, short, void *);
+struct radv            *find_ra(struct slaacd_iface *, struct sockaddr_in6 *);
+struct address_proposal        *find_address_proposal_by_id(struct slaacd_iface *,
+                            int64_t);
+struct address_proposal        *find_address_proposal_by_addr(struct slaacd_iface *,
+                            struct sockaddr_in6 *);
+struct dfr_proposal    *find_dfr_proposal_by_id(struct slaacd_iface *,
+                            int64_t);
+void                    find_prefix(struct slaacd_iface *, struct
+                            address_proposal *, struct radv **, struct
+                            radv_prefix **);
+int                     engine_imsg_compose_main(int, pid_t, void *, uint16_t);
+uint32_t                real_lifetime(struct timespec *, uint32_t);
+
+struct imsgev          *iev_frontend;
+struct imsgev          *iev_main;
+int64_t                         proposal_id;
+
+void
+engine_sig_handler(int sig, short event, void *arg)
+{
+       /*
+        * Normal signal handler rules don't apply because libevent
+        * decouples for us.
+        */
+
+       switch (sig) {
+       case SIGINT:
+       case SIGTERM:
+               engine_shutdown();
+       default:
+               fatalx("unexpected signal");
+       }
+}
+
+void
+engine(int debug, int verbose)
+{
+       struct event             ev_sigint, ev_sigterm;
+       struct passwd           *pw;
+
+       log_init(debug, LOG_DAEMON);
+       log_setverbose(verbose);
+
+       if ((pw = getpwnam(SLAACD_USER)) == NULL)
+               fatal("getpwnam");
+
+       if (chroot(pw->pw_dir) == -1)
+               fatal("chroot");
+       if (chdir("/") == -1)
+               fatal("chdir(\"/\")");
+
+       slaacd_process = PROC_ENGINE;
+       setproctitle("%s", log_procnames[slaacd_process]);
+       log_procinit(log_procnames[slaacd_process]);
+
+       if (setgroups(1, &pw->pw_gid) ||
+           setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+           setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+               fatal("can't drop privileges");
+
+       if (pledge("stdio recvfd", NULL) == -1)
+               fatal("pledge");
+
+       event_init();
+
+       /* Setup signal handler(s). */
+       signal_set(&ev_sigint, SIGINT, engine_sig_handler, NULL);
+       signal_set(&ev_sigterm, SIGTERM, engine_sig_handler, NULL);
+       signal_add(&ev_sigint, NULL);
+       signal_add(&ev_sigterm, NULL);
+       signal(SIGPIPE, SIG_IGN);
+       signal(SIGHUP, SIG_IGN);
+
+       /* Setup pipe and event handler to the main process. */
+       if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
+               fatal(NULL);
+
+       imsg_init(&iev_main->ibuf, 3);
+       iev_main->handler = engine_dispatch_main;
+
+       /* Setup event handlers. */
+       iev_main->events = EV_READ;
+       event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
+           iev_main->handler, iev_main);
+       event_add(&iev_main->ev, NULL);
+
+       LIST_INIT(&slaacd_interfaces);
+
+       event_dispatch();
+
+       engine_shutdown();
+}
+
+__dead void
+engine_shutdown(void)
+{
+       /* Close pipes. */
+       msgbuf_clear(&iev_frontend->ibuf.w);
+       close(iev_frontend->ibuf.fd);
+       msgbuf_clear(&iev_main->ibuf.w);
+       close(iev_main->ibuf.fd);
+
+       free(iev_frontend);
+       free(iev_main);
+
+       log_info("engine exiting");
+       exit(0);
+}
+
+int
+engine_imsg_compose_frontend(int type, pid_t pid, void *data,
+    uint16_t datalen)
+{
+       return (imsg_compose_event(iev_frontend, type, 0, pid, -1,
+           data, datalen));
+}
+
+int
+engine_imsg_compose_main(int type, pid_t pid, void *data,
+    uint16_t datalen)
+{
+       return (imsg_compose_event(iev_main, type, 0, pid, -1,
+           data, datalen));
+}
+
+void
+engine_dispatch_frontend(int fd, short event, void *bula)
+{
+       struct imsgev                   *iev = bula;
+       struct imsgbuf                  *ibuf = &iev->ibuf;
+       struct imsg                      imsg;
+       struct slaacd_iface             *iface;
+       struct imsg_ra                   ra;
+       struct imsg_ifinfo               imsg_ifinfo;
+       struct imsg_proposal_ack         proposal_ack;
+       struct address_proposal         *addr_proposal = NULL;
+       struct dfr_proposal             *dfr_proposal = NULL;
+       struct imsg_del_addr             del_addr;
+       ssize_t                          n;
+       int                              shut = 0, verbose;
+       uint32_t                         if_index;
+
+       if (event & EV_READ) {
+               if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+                       fatal("imsg_read error");
+               if (n == 0)     /* Connection closed. */
+                       shut = 1;
+       }
+       if (event & EV_WRITE) {
+               if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+                       fatal("msgbuf_write");
+               if (n == 0)     /* Connection closed. */
+                       shut = 1;
+       }
+
+       for (;;) {
+               if ((n = imsg_get(ibuf, &imsg)) == -1)
+                       fatal("%s: imsg_get error", __func__);
+               if (n == 0)     /* No more messages. */
+                       break;
+
+               switch (imsg.hdr.type) {
+               case IMSG_CTL_LOG_VERBOSE:
+                       /* Already checked by frontend. */
+                       memcpy(&verbose, imsg.data, sizeof(verbose));
+                       log_setverbose(verbose);
+                       break;
+               case IMSG_CTL_SHOW_INTERFACE_INFO:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(if_index))
+                               fatal("%s: IMSG_CTL_SHOW_INTERFACE_INFO wrong "
+                                   "length: %d", __func__, imsg.hdr.len);
+                       memcpy(&if_index, imsg.data, sizeof(if_index));
+                       engine_showinfo_ctl(&imsg, if_index);
+                       break;
+               case IMSG_UPDATE_IF:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE +
+                           sizeof(imsg_ifinfo))
+                               fatal("%s: IMSG_UPDATE_IF wrong length: %d",
+                                   __func__, imsg.hdr.len);
+                       memcpy(&imsg_ifinfo, imsg.data, sizeof(imsg_ifinfo));
+
+                       iface = get_slaacd_iface_by_id(imsg_ifinfo.if_index);
+                       if (iface == NULL) {
+                               if ((iface = calloc(1, sizeof(*iface))) == NULL)
+                                       fatal("calloc");
+                               evtimer_set(&iface->timer, iface_timeout,
+                                   iface);
+                               iface->if_index = imsg_ifinfo.if_index;
+                               iface->running = imsg_ifinfo.running;
+                               if (iface->running)
+                                       start_probe(iface);
+                               else
+                                       iface->state = IF_DOWN;
+                               iface->autoconfprivacy =
+                                   imsg_ifinfo.autoconfprivacy;
+                               memcpy(&iface->hw_address,
+                                   &imsg_ifinfo.hw_address,
+                                   sizeof(struct ether_addr));
+                               memcpy(&iface->ll_address,
+                                   &imsg_ifinfo.ll_address,
+                                   sizeof(struct sockaddr_in6));
+                               LIST_INIT(&iface->radvs);
+                               LIST_INSERT_HEAD(&slaacd_interfaces,
+                                   iface, entries);
+                               LIST_INIT(&iface->addr_proposals);
+                               LIST_INIT(&iface->dfr_proposals);
+                       } else {
+                               int need_refresh = 0;
+
+                               if (iface->autoconfprivacy !=
+                                   imsg_ifinfo.autoconfprivacy) {
+                                       iface->autoconfprivacy =
+                                           imsg_ifinfo.autoconfprivacy;
+                                       need_refresh = 1;
+                               }
+                               if (memcmp(&iface->hw_address,
+                                           &imsg_ifinfo.hw_address,
+                                           sizeof(struct ether_addr)) != 0) {
+                                       memcpy(&iface->hw_address,
+                                           &imsg_ifinfo.hw_address,
+                                           sizeof(struct ether_addr));
+                                       need_refresh = 1;
+                               }
+
+                               if (iface->state != IF_DOWN &&
+                                   imsg_ifinfo.running && need_refresh)
+                                       start_probe(iface);
+
+                               iface->running = imsg_ifinfo.running;
+                               if (!iface->running) {
+                                       iface->state = IF_DOWN;
+                                       if (evtimer_pending(&iface->timer,
+                                           NULL))
+                                               evtimer_del(&iface->timer);
+                               }
+
+                               memcpy(&iface->ll_address,
+                                   &imsg_ifinfo.ll_address,
+                                   sizeof(struct sockaddr_in6));
+                       }
+                       break;
+               case IMSG_REMOVE_IF:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(if_index))
+                               fatal("%s: IMSG_REMOVE_IF wrong length: %d",
+                                   __func__, imsg.hdr.len);
+                       memcpy(&if_index, imsg.data, sizeof(if_index));
+                       remove_slaacd_iface(if_index);
+                       break;
+               case IMSG_RA:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(ra))
+                               fatal("%s: IMSG_RA wrong length: %d",
+                                   __func__, imsg.hdr.len);
+                       memcpy(&ra, imsg.data, sizeof(ra));
+                       iface = get_slaacd_iface_by_id(ra.if_index);
+                       if (iface != NULL)
+                               parse_ra(iface, &ra);
+                       break;
+               case IMSG_CTL_SEND_SOLICITATION:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(if_index))
+                               fatal("%s: IMSG_CTL_SEND_SOLICITATION wrong "
+                                   "length: %d", __func__, imsg.hdr.len);
+                       memcpy(&if_index, imsg.data, sizeof(if_index));
+                       iface = get_slaacd_iface_by_id(if_index);
+                       if (iface == NULL)
+                               log_warn("requested to send solicitation on "
+                                   "non-autoconf interface: %u", if_index);
+                       else
+                               engine_imsg_compose_frontend(
+                                   IMSG_CTL_SEND_SOLICITATION, imsg.hdr.pid,
+                                   &iface->if_index, sizeof(iface->if_index));
+                       break;
+               case IMSG_PROPOSAL_ACK:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE +
+                           sizeof(proposal_ack))
+                               fatal("%s: IMSG_PROPOSAL_ACK wrong length: %d",
+                                   __func__, imsg.hdr.len);
+                       memcpy(&proposal_ack, imsg.data, sizeof(proposal_ack));
+                       log_debug("%s: IMSG_PROPOSAL_ACK: %lld - %d", __func__,
+                           proposal_ack.id, proposal_ack.pid);
+                       if (proposal_ack.pid != getpid()) {
+                               log_debug("IMSG_PROPOSAL_ACK: wrong pid, "
+                                   "ignoring");
+                               break;
+                       }
+
+                       iface = get_slaacd_iface_by_id(proposal_ack.if_index);
+                       if (iface == NULL) {
+                               log_debug("IMSG_PROPOSAL_ACK: unknown interface"
+                                   ", ignoring");
+                               break;
+                       }
+
+                       addr_proposal = find_address_proposal_by_id(iface,
+                           proposal_ack.id);
+                       if (addr_proposal == NULL) {
+                               dfr_proposal = find_dfr_proposal_by_id(iface,
+                                   proposal_ack.id);
+                               if (dfr_proposal == NULL) {
+                                       log_debug("IMSG_PROPOSAL_ACK: cannot "
+                                           "find proposal, ignoring");
+                                       break;
+                               }
+                       }
+                       if (addr_proposal != NULL)
+                               configure_address(addr_proposal);
+                       else if (dfr_proposal != NULL)
+                               configure_dfr(dfr_proposal);
+
+                       break;
+               case IMSG_DEL_ADDRESS:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE +
+                           sizeof(del_addr))
+                               fatal("%s: IMSG_DEL_ADDRESS wrong length: %d",
+                                   __func__, imsg.hdr.len);
+                       memcpy(&del_addr, imsg.data, sizeof(del_addr));
+                       iface = get_slaacd_iface_by_id(del_addr.if_index);
+                       if (iface == NULL) {
+                               log_debug("IMSG_DEL_ADDRESS: unknown interface"
+                                   ", ignoring");
+                               break;
+                       }
+
+                       addr_proposal = find_address_proposal_by_addr(iface,
+                           &del_addr.addr);
+
+                       if (addr_proposal) {
+                               /* XXX should we inform netcfgd? */
+                               LIST_REMOVE(addr_proposal, entries);
+                               evtimer_del(&addr_proposal->timer);
+                               free(addr_proposal);
+                       }
+
+                       break;
+               default:
+                       log_debug("%s: unexpected imsg %d", __func__,
+                           imsg.hdr.type);
+                       break;
+               }
+               imsg_free(&imsg);
+       }
+       if (!shut)
+               imsg_event_add(iev);
+       else {
+               /* This pipe is dead. Remove its event handler. */
+               event_del(&iev->ev);
+               event_loopexit(NULL);
+       }
+}
+
+void
+engine_dispatch_main(int fd, short event, void *bula)
+{
+       struct imsg              imsg;
+       struct imsgev           *iev = bula;
+       struct imsgbuf          *ibuf = &iev->ibuf;
+       ssize_t                  n;
+       int                      shut = 0;
+
+       if (event & EV_READ) {
+               if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+                       fatal("imsg_read error");
+               if (n == 0)     /* Connection closed. */
+                       shut = 1;
+       }
+       if (event & EV_WRITE) {
+               if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+                       fatal("msgbuf_write");
+               if (n == 0)     /* Connection closed. */
+                       shut = 1;
+       }
+
+       for (;;) {
+               if ((n = imsg_get(ibuf, &imsg)) == -1)
+                       fatal("%s: imsg_get error", __func__);
+               if (n == 0)     /* No more messages. */
+                       break;
+
+               switch (imsg.hdr.type) {
+               case IMSG_SOCKET_IPC:
+                       /*
+                        * Setup pipe and event handler to the frontend
+                        * process.
+                        */
+                       if (iev_frontend) {
+                               log_warnx("%s: received unexpected imsg fd "
+                                   "to engine", __func__);
+                               break;
+                       }
+                       if ((fd = imsg.fd) == -1) {
+                               log_warnx("%s: expected to receive imsg fd to "
+                                  "engine but didn't receive any", __func__);
+                               break;
+                       }
+
+                       iev_frontend = malloc(sizeof(struct imsgev));
+                       if (iev_frontend == NULL)
+                               fatal(NULL);
+
+                       imsg_init(&iev_frontend->ibuf, fd);
+                       iev_frontend->handler = engine_dispatch_frontend;
+                       iev_frontend->events = EV_READ;
+
+                       event_set(&iev_frontend->ev, iev_frontend->ibuf.fd,
+                       iev_frontend->events, iev_frontend->handler,
+                           iev_frontend);
+                       event_add(&iev_frontend->ev, NULL);
+
+                       if (pledge("stdio", NULL) == -1)
+                               fatal("pledge");
+                       break;
+               default:
+                       log_debug("%s: unexpected imsg %d", __func__,
+                           imsg.hdr.type);
+                       break;
+               }
+               imsg_free(&imsg);
+       }
+       if (!shut)
+               imsg_event_add(iev);
+       else {
+               /* This pipe is dead. Remove its event handler. */
+               event_del(&iev->ev);
+               event_loopexit(NULL);
+       }
+}
+
+void
+send_interface_info(struct slaacd_iface *iface, pid_t pid)
+{
+       struct ctl_engine_info                   cei;
+       struct ctl_engine_info_ra                cei_ra;
+       struct ctl_engine_info_ra_prefix         cei_ra_prefix;
+       struct ctl_engine_info_ra_rdns           cei_ra_rdns;
+       struct ctl_engine_info_ra_dnssl          cei_ra_dnssl;
+       struct ctl_engine_info_address_proposal  cei_addr_proposal;
+       struct ctl_engine_info_dfr_proposal      cei_dfr_proposal;
+       struct radv                             *ra;
+       struct radv_prefix                      *prefix;
+       struct radv_rdns                        *rdns;
+       struct radv_dnssl                       *dnssl;
+       struct address_proposal                 *addr_proposal;
+       struct dfr_proposal                     *dfr_proposal;
+
+       memset(&cei, 0, sizeof(cei));
+       cei.if_index = iface->if_index;
+       cei.running = iface->running;
+       cei.autoconfprivacy = iface->autoconfprivacy;
+       memcpy(&cei.hw_address, &iface->hw_address, sizeof(struct ether_addr));
+       memcpy(&cei.ll_address, &iface->ll_address,
+           sizeof(struct sockaddr_in6));
+       engine_imsg_compose_frontend(IMSG_CTL_SHOW_INTERFACE_INFO, pid, &cei,
+           sizeof(cei));
+       LIST_FOREACH(ra, &iface->radvs, entries) {
+               memset(&cei_ra, 0, sizeof(cei_ra));
+               memcpy(&cei_ra.from, &ra->from, sizeof(cei_ra.from));
+               memcpy(&cei_ra.when, &ra->when, sizeof(cei_ra.when));
+               memcpy(&cei_ra.uptime, &ra->uptime, sizeof(cei_ra.uptime));
+               cei_ra.curhoplimit = ra->curhoplimit;
+               cei_ra.managed = ra->managed;
+               cei_ra.other = ra->other;
+               if (strlcpy(cei_ra.rpref, rpref_name[ra->rpref], sizeof(
+                   cei_ra.rpref)) >= sizeof(cei_ra.rpref))
+                       log_warn("truncated router preference");
+               cei_ra.router_lifetime = ra->router_lifetime;
+               cei_ra.reachable_time = ra->reachable_time;
+               cei_ra.retrans_time = ra->retrans_time;
+               engine_imsg_compose_frontend(IMSG_CTL_SHOW_INTERFACE_INFO_RA,
+                   pid, &cei_ra, sizeof(cei_ra));
+
+               LIST_FOREACH(prefix, &ra->prefixes, entries) {
+                       memset(&cei_ra_prefix, 0, sizeof(cei_ra_prefix));
+
+                       cei_ra_prefix.prefix = prefix->prefix;
+                       cei_ra_prefix.prefix_len = prefix->prefix_len;
+                       cei_ra_prefix.onlink = prefix->onlink;
+                       cei_ra_prefix.autonomous = prefix->autonomous;
+                       cei_ra_prefix.vltime = prefix->vltime;
+                       cei_ra_prefix.pltime = prefix->pltime;
+                       engine_imsg_compose_frontend(
+                           IMSG_CTL_SHOW_INTERFACE_INFO_RA_PREFIX, pid,
+                           &cei_ra_prefix, sizeof(cei_ra_prefix));
+               }
+
+               LIST_FOREACH(rdns, &ra->rdns_servers, entries) {
+                       memset(&cei_ra_rdns, 0, sizeof(cei_ra_rdns));
+                       memcpy(&cei_ra_rdns.rdns, &rdns->rdns,
+                           sizeof(cei_ra_rdns.rdns));
+                       cei_ra_rdns.lifetime = ra->rdns_lifetime;
+                       engine_imsg_compose_frontend(
+                           IMSG_CTL_SHOW_INTERFACE_INFO_RA_RDNS, pid,
+                           &cei_ra_rdns, sizeof(cei_ra_rdns));
+               }
+
+               LIST_FOREACH(dnssl, &ra->dnssls, entries) {
+                       memset(&cei_ra_dnssl, 0, sizeof(cei_ra_dnssl));
+                       memcpy(&cei_ra_dnssl.dnssl, &dnssl->dnssl,
+                           sizeof(cei_ra_dnssl.dnssl));
+                       cei_ra_dnssl.lifetime = ra->dnssl_lifetime;
+                       engine_imsg_compose_frontend(
+                           IMSG_CTL_SHOW_INTERFACE_INFO_RA_DNSSL, pid,
+                           &cei_ra_dnssl, sizeof(cei_ra_dnssl));
+               }
+       }
+
+       if (!LIST_EMPTY(&iface->addr_proposals))
+               engine_imsg_compose_frontend(
+                   IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSALS, pid, NULL, 0);
+
+       LIST_FOREACH(addr_proposal, &iface->addr_proposals, entries) {
+               memset(&cei_addr_proposal, 0, sizeof(cei_addr_proposal));
+               cei_addr_proposal.id = addr_proposal->id;
+               if(strlcpy(cei_addr_proposal.state,
+                   proposal_state_name[addr_proposal->state],
+                   sizeof(cei_addr_proposal.state)) >=
+                   sizeof(cei_addr_proposal.state))
+                       log_warn("truncated state name");
+               cei_addr_proposal.next_timeout = addr_proposal->next_timeout;
+               cei_addr_proposal.timeout_count = addr_proposal->timeout_count;
+               cei_addr_proposal.when = addr_proposal->when;
+               cei_addr_proposal.uptime = addr_proposal->uptime;
+               memcpy(&cei_addr_proposal.addr, &addr_proposal->addr, sizeof(
+                   cei_addr_proposal.addr));
+               memcpy(&cei_addr_proposal.prefix, &addr_proposal->prefix,
+                   sizeof(cei_addr_proposal.prefix));
+               cei_addr_proposal.prefix_len = addr_proposal->prefix_len;
+               cei_addr_proposal.privacy = addr_proposal->privacy;
+               cei_addr_proposal.vltime = addr_proposal->vltime;
+               cei_addr_proposal.pltime = addr_proposal->pltime;
+
+               engine_imsg_compose_frontend(
+                   IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSAL, pid,
+                           &cei_addr_proposal, sizeof(cei_addr_proposal));
+       }
+
+       if (!LIST_EMPTY(&iface->dfr_proposals))
+               engine_imsg_compose_frontend(
+                   IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSALS, pid, NULL, 0);
+
+       LIST_FOREACH(dfr_proposal, &iface->dfr_proposals, entries) {
+               memset(&cei_dfr_proposal, 0, sizeof(cei_dfr_proposal));
+               cei_dfr_proposal.id = dfr_proposal->id;
+               if(strlcpy(cei_dfr_proposal.state,
+                   proposal_state_name[dfr_proposal->state],
+                   sizeof(cei_dfr_proposal.state)) >=
+                   sizeof(cei_dfr_proposal.state))
+                       log_warn("truncated state name");
+               cei_dfr_proposal.next_timeout = dfr_proposal->next_timeout;
+               cei_dfr_proposal.timeout_count = dfr_proposal->timeout_count;
+               cei_dfr_proposal.when = dfr_proposal->when;
+               cei_dfr_proposal.uptime = dfr_proposal->uptime;
+               memcpy(&cei_dfr_proposal.addr, &dfr_proposal->addr, sizeof(
+                   cei_dfr_proposal.addr));
+               cei_dfr_proposal.router_lifetime =
+                   dfr_proposal->router_lifetime;
+               if(strlcpy(cei_dfr_proposal.rpref,
+                   rpref_name[dfr_proposal->rpref],
+                   sizeof(cei_dfr_proposal.rpref)) >=
+                   sizeof(cei_dfr_proposal.rpref))
+                       log_warn("truncated router preference");
+               engine_imsg_compose_frontend(
+                   IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSAL, pid,
+                           &cei_dfr_proposal, sizeof(cei_dfr_proposal));
+       }
+}
+
+void
+engine_showinfo_ctl(struct imsg *imsg, uint32_t if_index)
+{
+       struct slaacd_iface                     *iface;
+
+       switch (imsg->hdr.type) {
+       case IMSG_CTL_SHOW_INTERFACE_INFO:
+               if (if_index == 0) {
+                       LIST_FOREACH (iface, &slaacd_interfaces, entries)
+                               send_interface_info(iface, imsg->hdr.pid);
+               } else {
+                       if ((iface = get_slaacd_iface_by_id(if_index)) != NULL)
+                               send_interface_info(iface, imsg->hdr.pid);
+               }
+               engine_imsg_compose_frontend(IMSG_CTL_END, imsg->hdr.pid, NULL,
+                   0);
+               break;
+       default:
+               log_debug("%s: error handling imsg", __func__);
+               break;
+       }
+}
+
+struct slaacd_iface*
+get_slaacd_iface_by_id(uint32_t if_index)
+{
+       struct slaacd_iface     *iface;
+       LIST_FOREACH (iface, &slaacd_interfaces, entries) {
+               if (iface->if_index == if_index)
+                       return (iface);
+       }
+
+       return (NULL);
+}
+
+void
+remove_slaacd_iface(uint32_t if_index)
+{
+       struct slaacd_iface     *iface, *tiface;
+       struct radv             *ra;
+       struct address_proposal *addr_proposal;
+       struct dfr_proposal     *dfr_proposal;
+
+       LIST_FOREACH_SAFE (iface, &slaacd_interfaces, entries, tiface) {
+               if (iface->if_index == if_index) {
+                       LIST_REMOVE(iface, entries);
+                       while(!LIST_EMPTY(&iface->radvs)) {
+                               ra = LIST_FIRST(&iface->radvs);
+                               LIST_REMOVE(ra, entries);
+                               free_ra(ra);
+                       }
+                       /* XXX inform netcfgd? */
+                       while(!LIST_EMPTY(&iface->addr_proposals)) {
+                               addr_proposal =
+                                   LIST_FIRST(&iface->addr_proposals);
+                               LIST_REMOVE(addr_proposal, entries);
+                               free(addr_proposal);
+                       }
+                       while(!LIST_EMPTY(&iface->dfr_proposals)) {
+                               dfr_proposal =
+                                   LIST_FIRST(&iface->dfr_proposals);
+                               free_dfr_proposal(dfr_proposal);
+                       }
+                       free(iface);
+                       break;
+               }
+       }
+}
+
+void
+free_ra(struct radv *ra)
+{
+       struct radv_prefix      *prefix;
+       struct radv_rdns        *rdns;
+       struct radv_dnssl       *dnssl;
+
+       if (ra == NULL)
+               return;
+
+       evtimer_del(&ra->timer);
+
+       while (!LIST_EMPTY(&ra->prefixes)) {
+               prefix = LIST_FIRST(&ra->prefixes);
+               LIST_REMOVE(prefix, entries);
+               free(prefix);
+       }
+
+       while (!LIST_EMPTY(&ra->rdns_servers)) {
+               rdns = LIST_FIRST(&ra->rdns_servers);
+               LIST_REMOVE(rdns, entries);
+               free(rdns);
+       }
+
+       while (!LIST_EMPTY(&ra->dnssls)) {
+               dnssl = LIST_FIRST(&ra->dnssls);
+               LIST_REMOVE(dnssl, entries);
+               free(dnssl);
+       }
+
+       free(ra);
+}
+
+void
+parse_ra(struct slaacd_iface *iface, struct imsg_ra *ra)
+{
+       struct nd_router_advert *nd_ra;
+       struct radv             *radv;
+       struct radv_prefix      *prefix;
+       struct radv_rdns        *rdns;
+       struct radv_dnssl       *ra_dnssl;
+       ssize_t                  len = ra->len;
+       char                     hbuf[NI_MAXHOST];
+       uint8_t                 *p;
+
+#if 0
+       debug_log_ra(ra);
+#endif
+
+       if (getnameinfo((struct sockaddr *)&ra->from, ra->from.sin6_len, hbuf,
+           sizeof(hbuf), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV)) {
+               log_warn("cannot get router IP");
+               strlcpy(hbuf, "unknown", sizeof(hbuf));
+       }
+
+       if (!IN6_IS_ADDR_LINKLOCAL(&ra->from.sin6_addr)) {
+               log_warn("RA from non link local address %s", hbuf);
+               return;
+       }
+
+       if ((size_t)len < sizeof(struct nd_router_advert)) {
+               log_warn("received too short message (%ld) from %s", len, hbuf);
+               return;
+       }
+
+       if ((radv = calloc(1, sizeof(*radv))) == NULL)
+               fatal("calloc");
+
+       LIST_INIT(&radv->prefixes);
+       LIST_INIT(&radv->rdns_servers);
+       LIST_INIT(&radv->dnssls);
+
+       radv->min_lifetime = UINT32_MAX;
+
+       p = ra->packet;
+       nd_ra = (struct nd_router_advert *)p;
+       len -= sizeof(struct nd_router_advert);
+       p += sizeof(struct nd_router_advert);
+
+       log_debug("ICMPv6 type(%d), code(%d) from %s of length %ld",
+           nd_ra->nd_ra_type, nd_ra->nd_ra_code, hbuf, len);
+
+       if (nd_ra->nd_ra_type != ND_ROUTER_ADVERT) {
+               log_warnx("invalid ICMPv6 type (%d) from %s", nd_ra->nd_ra_type,
+                   hbuf);
+               goto err;
+       }
+
+       if (nd_ra->nd_ra_code != 0) {
+               log_warn("invalid ICMPv6 code (%d) from %s", nd_ra->nd_ra_code,
+                   hbuf);
+               goto err;
+       }
+
+       memcpy(&radv->from, &ra->from, sizeof(ra->from));
+
+       if (clock_gettime(CLOCK_REALTIME, &radv->when))
+               fatal("clock_gettime");
+       if (clock_gettime(CLOCK_MONOTONIC, &radv->uptime))
+               fatal("clock_gettime");
+
+       radv->curhoplimit = nd_ra->nd_ra_curhoplimit;
+       radv->managed = nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED;
+       radv->other = nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER;
+
+       switch (nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_RTPREF_MASK) {
+       case ND_RA_FLAG_RTPREF_HIGH:
+               radv->rpref=HIGH;
+               break;
+       case ND_RA_FLAG_RTPREF_LOW:
+               radv->rpref=LOW;
+               break;
+       case ND_RA_FLAG_RTPREF_MEDIUM:
+               /* fallthrough */
+       default:
+               radv->rpref=MEDIUM;
+               break;
+       }
+       radv->router_lifetime = ntohs(nd_ra->nd_ra_router_lifetime);
+       if (radv->router_lifetime != 0)
+               radv->min_lifetime = radv->router_lifetime;
+       radv->reachable_time = ntohl(nd_ra->nd_ra_reachable);
+       radv->retrans_time = ntohl(nd_ra->nd_ra_retransmit);
+
+       while ((size_t)len >= sizeof(struct nd_opt_hdr)) {
+               struct nd_opt_hdr *nd_opt_hdr = (struct nd_opt_hdr *)p;
+               struct nd_opt_prefix_info *prf;
+               struct nd_opt_rdnss *rdnss;
+               struct nd_opt_dnssl *dnssl;
+               struct in6_addr *in6;
+               int i;
+               char *nssl;
+
+               len -= sizeof(struct nd_opt_hdr);
+               p += sizeof(struct nd_opt_hdr);
+
+               if (nd_opt_hdr->nd_opt_len * 8 - 2 > len) {
+                       log_warnx("invalid option len: %u > %ld",
+                           nd_opt_hdr->nd_opt_len, len);
+                       goto err;
+               }
+
+               switch (nd_opt_hdr->nd_opt_type) {
+               case ND_OPT_PREFIX_INFORMATION:
+                       if (nd_opt_hdr->nd_opt_len != 4) {
+                               log_warn("invalid ND_OPT_PREFIX_INFORMATION: "
+                                  "len != 4");
+                               goto err;
+                       }
+
+                       if ((prefix = calloc(1, sizeof(*prefix))) == NULL)
+                               fatal("calloc");
+
+                       prf = (struct nd_opt_prefix_info*) nd_opt_hdr;
+                       prefix->prefix = prf->nd_opt_pi_prefix;
+                       prefix->prefix_len = prf->nd_opt_pi_prefix_len;
+                       prefix->onlink = prf->nd_opt_pi_flags_reserved &
+                           ND_OPT_PI_FLAG_ONLINK;
+                       prefix->autonomous = prf->nd_opt_pi_flags_reserved &
+                           ND_OPT_PI_FLAG_AUTO;
+                       prefix->vltime = ntohl(prf->nd_opt_pi_valid_time);
+                       prefix->pltime = ntohl(prf->nd_opt_pi_preferred_time);
+                       if (radv->min_lifetime > prefix->pltime)
+                               radv->min_lifetime = prefix->pltime;
+
+                       LIST_INSERT_HEAD(&radv->prefixes, prefix, entries);
+
+                       break;
+
+               case ND_OPT_RDNSS:
+                       if (nd_opt_hdr->nd_opt_len  < 3) {
+                               log_warnx("invalid ND_OPT_RDNSS: len < 24");
+                               goto err;
+                       }
+
+                       if ((nd_opt_hdr->nd_opt_len - 1) % 2 != 0) {
+                               log_warnx("invalid ND_OPT_RDNSS: length with"
+                                   "out header is not multiply of 16: %d",
+                                   (nd_opt_hdr->nd_opt_len - 1) * 8);
+                               goto err;
+                       }
+
+                       rdnss = (struct nd_opt_rdnss*) nd_opt_hdr;
+
+                       radv->rdns_lifetime = ntohl(
+                           rdnss->nd_opt_rdnss_lifetime);
+                       if (radv->min_lifetime > radv->rdns_lifetime)
+                               radv->min_lifetime = radv->rdns_lifetime;
+
+                       in6 = (struct in6_addr*) (p + 6);
+                       for (i=0; i < (nd_opt_hdr->nd_opt_len - 1)/2; i++,
+                           in6++) {
+                               if((rdns = calloc(1, sizeof(*rdns))) == NULL)
+                                       fatal("calloc");
+                               memcpy(&rdns->rdns, in6, sizeof(rdns->rdns));
+                               LIST_INSERT_HEAD(&radv->rdns_servers, rdns,
+                                   entries);
+                       }
+                       break;
+               case ND_OPT_DNSSL:
+                       if (nd_opt_hdr->nd_opt_len  < 2) {
+                               log_warnx("invalid ND_OPT_DNSSL: len < 16");
+                               goto err;
+                       }
+
+                       dnssl = (struct nd_opt_dnssl*) nd_opt_hdr;
+
+                       if ((nssl = parse_dnssl(p + 6,
+                           (nd_opt_hdr->nd_opt_len - 1) * 8)) == NULL)
+                               goto err; /* error logging in parse_dnssl */
+
+                       if((ra_dnssl = calloc(1, sizeof(*ra_dnssl))) == NULL)
+                               fatal("calloc");
+
+                       radv->dnssl_lifetime = ntohl(
+                           dnssl->nd_opt_dnssl_lifetime);
+                       if (radv->min_lifetime > radv->dnssl_lifetime)
+                               radv->min_lifetime = radv->dnssl_lifetime;
+
+                       if (strlcpy(ra_dnssl->dnssl, nssl,
+                           sizeof(ra_dnssl->dnssl)) >=
+                           sizeof(ra_dnssl->dnssl)) {
+                               log_warn("dnssl too long");
+                               goto err;
+                       }
+                       free(nssl);
+
+                       LIST_INSERT_HEAD(&radv->dnssls, ra_dnssl, entries);
+
+                       break;
+               case ND_OPT_REDIRECTED_HEADER:
+               case ND_OPT_SOURCE_LINKADDR:
+               case ND_OPT_TARGET_LINKADDR:
+               case ND_OPT_MTU:
+               case ND_OPT_ROUTE_INFO:
+#if 0
+                       log_debug("\tOption: %u (len: %u) not implemented",
+                           nd_opt_hdr->nd_opt_type, nd_opt_hdr->nd_opt_len *
+                           8);
+#endif
+                       break;
+               default:
+                       log_debug("\t\tUNKNOWN: %d", nd_opt_hdr->nd_opt_type);
+                       break;
+
+               }
+               len -= nd_opt_hdr->nd_opt_len * 8 - 2;
+               p += nd_opt_hdr->nd_opt_len * 8 - 2;
+       }
+       update_iface_ra(iface, radv);
+       iface->state = IF_IDLE;
+       return;
+
+err:
+       free_ra(radv);
+}
+
+void
+gen_addr(struct slaacd_iface *iface, struct radv_prefix *prefix, struct
+    address_proposal *addr_proposal, int privacy)
+{
+       struct in6_addr priv_in6;
+
+       /* from in6_ifadd() in nd6_rtr.c */
+       /* XXX from in6.h, guarded by #ifdef _KERNEL   XXX nonstandard */
+#define s6_addr32 __u6_addr.__u6_addr32
+
+       /* XXX from in6_ifattach.c */
+#define EUI64_GBIT     0x01
+#define EUI64_UBIT     0x02
+
+       if (privacy) {
+               arc4random_buf(&priv_in6.s6_addr32[2], 8);
+               priv_in6.s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */
+               priv_in6.s6_addr[8] |= EUI64_UBIT;  /* u bit to "local" */
+               /* convert EUI64 into IPv6 interface identifier */
+               priv_in6.s6_addr[8] ^= EUI64_UBIT;
+       }
+
+       in6_prefixlen2mask(&addr_proposal->mask, addr_proposal->prefix_len);
+
+       memset(&addr_proposal->addr, 0, sizeof(addr_proposal->addr));
+
+       addr_proposal->addr.sin6_family = AF_INET6;
+       addr_proposal->addr.sin6_len = sizeof(addr_proposal->addr);
+
+       memcpy(&addr_proposal->addr.sin6_addr, &prefix->prefix,
+           sizeof(addr_proposal->addr.sin6_addr));
+
+       addr_proposal->addr.sin6_addr.s6_addr32[0] &=
+           addr_proposal->mask.s6_addr32[0];
+       addr_proposal->addr.sin6_addr.s6_addr32[1] &=
+           addr_proposal->mask.s6_addr32[1];
+       addr_proposal->addr.sin6_addr.s6_addr32[2] &=
+           addr_proposal->mask.s6_addr32[2];
+       addr_proposal->addr.sin6_addr.s6_addr32[3] &=
+           addr_proposal->mask.s6_addr32[3];
+
+       if (privacy) {
+               addr_proposal->addr.sin6_addr.s6_addr32[0] |=
+                   (priv_in6.s6_addr32[0] & ~addr_proposal->mask.s6_addr32[0]);
+               addr_proposal->addr.sin6_addr.s6_addr32[1] |=
+                   (priv_in6.s6_addr32[1] & ~addr_proposal->mask.s6_addr32[1]);
+               addr_proposal->addr.sin6_addr.s6_addr32[2] |=
+                   (priv_in6.s6_addr32[2] & ~addr_proposal->mask.s6_addr32[2]);
+               addr_proposal->addr.sin6_addr.s6_addr32[3] |=
+                   (priv_in6.s6_addr32[3] & ~addr_proposal->mask.s6_addr32[3]);
+       } else {
+               addr_proposal->addr.sin6_addr.s6_addr32[0] |=
+                   (iface->ll_address.sin6_addr.s6_addr32[0] &
+                   ~addr_proposal->mask.s6_addr32[0]);
+               addr_proposal->addr.sin6_addr.s6_addr32[1] |=
+                   (iface->ll_address.sin6_addr.s6_addr32[1] &
+                   ~addr_proposal->mask.s6_addr32[1]);
+               addr_proposal->addr.sin6_addr.s6_addr32[2] |=
+                   (iface->ll_address.sin6_addr.s6_addr32[2] &
+                   ~addr_proposal->mask.s6_addr32[2]);
+               addr_proposal->addr.sin6_addr.s6_addr32[3] |=
+                   (iface->ll_address.sin6_addr.s6_addr32[3] &
+                   ~addr_proposal->mask.s6_addr32[3]);
+       }
+
+#undef s6_addr32
+}
+
+/* from sys/netinet6/in6.c */
+void
+in6_prefixlen2mask(struct in6_addr *maskp, int len)
+{
+       u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff};
+       int bytelen, bitlen, i;
+
+       if (0 > len || len > 128)
+               fatal("%s: invalid prefix length(%d)\n", __func__, len);
+
+       bzero(maskp, sizeof(*maskp));
+       bytelen = len / 8;
+       bitlen = len % 8;
+       for (i = 0; i < bytelen; i++)
+               maskp->s6_addr[i] = 0xff;
+       /* len == 128 is ok because bitlen == 0 then */
+       if (bitlen)
+               maskp->s6_addr[bytelen] = maskarray[bitlen - 1];
+}
+
+void
+debug_log_ra(struct imsg_ra *ra)
+{
+       struct nd_router_advert *nd_ra;
+       ssize_t                  len = ra->len;
+       char                     hbuf[NI_MAXHOST], ntopbuf[INET6_ADDRSTRLEN];
+       uint8_t                 *p;
+
+       if (getnameinfo((struct sockaddr *)&ra->from, ra->from.sin6_len, hbuf,
+           sizeof(hbuf), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV)) {
+               log_warn("cannot get router IP");
+               strlcpy(hbuf, "unknown", sizeof(hbuf));
+       }
+
+       if (!IN6_IS_ADDR_LINKLOCAL(&ra->from.sin6_addr)) {
+               log_warn("RA from non link local address %s", hbuf);
+               return;
+       }
+
+       if ((size_t)len < sizeof(struct nd_router_advert)) {
+               log_warn("received too short message (%ld) from %s", len, hbuf);
+               return;
+       }
+
+       p = ra->packet;
+       nd_ra = (struct nd_router_advert *)p;
+       len -= sizeof(struct nd_router_advert);
+       p += sizeof(struct nd_router_advert);
+
+       log_debug("ICMPv6 type(%d), code(%d) from %s of length %ld",
+           nd_ra->nd_ra_type, nd_ra->nd_ra_code, hbuf, len);
+
+       if (nd_ra->nd_ra_type != ND_ROUTER_ADVERT) {
+               log_warnx("invalid ICMPv6 type (%d) from %s", nd_ra->nd_ra_type,
+                   hbuf);
+               return;
+       }
+
+       if (nd_ra->nd_ra_code != 0) {
+               log_warn("invalid ICMPv6 code (%d) from %s", nd_ra->nd_ra_code,
+                   hbuf);
+               return;
+       }
+
+       log_debug("---");
+       log_debug("RA from %s", hbuf);
+       log_debug("\tCur Hop Limit: %u", nd_ra->nd_ra_curhoplimit);
+       log_debug("\tManaged address configuration: %d",
+           (nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) ? 1 : 0);
+       log_debug("\tOther configuration: %d",
+           (nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) ? 1 : 0);
+       switch (nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_RTPREF_MASK) {
+       case ND_RA_FLAG_RTPREF_HIGH:
+               log_debug("\tRouter Preference: high");
+               break;
+       case ND_RA_FLAG_RTPREF_MEDIUM:
+               log_debug("\tRouter Preference: medium");
+               break;
+       case ND_RA_FLAG_RTPREF_LOW:
+               log_debug("\tRouter Preference: low");
+               break;
+       case ND_RA_FLAG_RTPREF_RSV:
+               log_debug("\tRouter Preference: reserved");
+               break;
+       }
+       log_debug("\tRouter Lifetime: %hds",
+           ntohs(nd_ra->nd_ra_router_lifetime));
+       log_debug("\tReachable Time: %ums", ntohl(nd_ra->nd_ra_reachable));
+       log_debug("\tRetrans Timer: %ums", ntohl(nd_ra->nd_ra_retransmit));
+
+       while ((size_t)len >= sizeof(struct nd_opt_hdr)) {
+               struct nd_opt_hdr *nd_opt_hdr = (struct nd_opt_hdr *)p;
+               struct nd_opt_mtu *mtu;
+               struct nd_opt_prefix_info *prf;
+               struct nd_opt_rdnss *rdnss;
+               struct nd_opt_dnssl *dnssl;
+               struct in6_addr *in6;
+               int i;
+               char *nssl;
+
+               len -= sizeof(struct nd_opt_hdr);
+               p += sizeof(struct nd_opt_hdr);
+               if (nd_opt_hdr->nd_opt_len * 8 - 2 > len) {
+                       log_warnx("invalid option len: %u > %ld",
+                           nd_opt_hdr->nd_opt_len, len);
+                       return;
+               }
+               log_debug("\tOption: %u (len: %u)", nd_opt_hdr->nd_opt_type,
+                   nd_opt_hdr->nd_opt_len * 8);
+               switch (nd_opt_hdr->nd_opt_type) {
+               case ND_OPT_SOURCE_LINKADDR:
+                       if (nd_opt_hdr->nd_opt_len == 1)
+                               log_debug("\t\tND_OPT_SOURCE_LINKADDR: "
+                                   "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+                                   p[0], p[1], p[2], p[3], p[4], p[5], p[6],
+                                   p[7]);
+                       else
+                               log_debug("\t\tND_OPT_SOURCE_LINKADDR");
+                       break;
+               case ND_OPT_TARGET_LINKADDR:
+                       if (nd_opt_hdr->nd_opt_len == 1)
+                               log_debug("\t\tND_OPT_TARGET_LINKADDR: "
+                                   "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+                                   p[0], p[1], p[2], p[3], p[4], p[5], p[6],
+                                   p[7]);
+                       else
+                               log_debug("\t\tND_OPT_TARGET_LINKADDR");
+                       break;
+               case ND_OPT_PREFIX_INFORMATION:
+                       if (nd_opt_hdr->nd_opt_len != 4) {
+                               log_warn("invalid ND_OPT_PREFIX_INFORMATION: "
+                                  "len != 4");
+                               return;
+                       }
+                       prf = (struct nd_opt_prefix_info*) nd_opt_hdr;
+
+                       log_debug("\t\tND_OPT_PREFIX_INFORMATION: %s/%u",
+                           inet_ntop(AF_INET6, &prf->nd_opt_pi_prefix,
+                           ntopbuf, INET6_ADDRSTRLEN),
+                           prf->nd_opt_pi_prefix_len);
+                       log_debug("\t\t\tOn-link: %d",
+                           prf->nd_opt_pi_flags_reserved &
+                           ND_OPT_PI_FLAG_ONLINK ? 1:0);
+                       log_debug("\t\t\tAutonomous address-configuration: %d",
+                           prf->nd_opt_pi_flags_reserved &
+                           ND_OPT_PI_FLAG_AUTO ? 1 : 0);
+                       log_debug("\t\t\tvltime: %u",
+                           ntohl(prf->nd_opt_pi_valid_time));
+                       log_debug("\t\t\tpltime: %u",
+                           ntohl(prf->nd_opt_pi_preferred_time));
+                       break;
+               case ND_OPT_REDIRECTED_HEADER:
+                       log_debug("\t\tND_OPT_REDIRECTED_HEADER");
+                       break;
+               case ND_OPT_MTU:
+                       if (nd_opt_hdr->nd_opt_len != 1) {
+                               log_warn("invalid ND_OPT_MTU: len != 1");
+                               return;
+                       }
+                       mtu = (struct nd_opt_mtu*) nd_opt_hdr;
+                       log_debug("\t\tND_OPT_MTU: %u",
+                           ntohl(mtu->nd_opt_mtu_mtu));
+                       break;
+               case ND_OPT_ROUTE_INFO:
+                       log_debug("\t\tND_OPT_ROUTE_INFO");
+                       break;
+               case ND_OPT_RDNSS:
+                       if (nd_opt_hdr->nd_opt_len  < 3) {
+                               log_warnx("invalid ND_OPT_RDNSS: len < 24");
+                               return;
+                       }
+                       if ((nd_opt_hdr->nd_opt_len - 1) % 2 != 0) {
+                               log_warnx("invalid ND_OPT_RDNSS: length with"
+                                   "out header is not multiply of 16: %d",
+                                   (nd_opt_hdr->nd_opt_len - 1) * 8);
+                               return;
+                       }
+                       rdnss = (struct nd_opt_rdnss*) nd_opt_hdr;
+                       log_debug("\t\tND_OPT_RDNSS: lifetime: %u", ntohl(
+                           rdnss->nd_opt_rdnss_lifetime));
+                       in6 = (struct in6_addr*) (p + 6);
+                       for (i=0; i < (nd_opt_hdr->nd_opt_len - 1)/2; i++,
+                           in6++) {
+                               log_debug("\t\t\t%s", inet_ntop(AF_INET6, in6,
+                                   ntopbuf, INET6_ADDRSTRLEN));
+                       }
+                       break;
+               case ND_OPT_DNSSL:
+                       if (nd_opt_hdr->nd_opt_len  < 2) {
+                               log_warnx("invalid ND_OPT_DNSSL: len < 16");
+                               return;
+                       }
+                       dnssl = (struct nd_opt_dnssl*) nd_opt_hdr;
+                       nssl = parse_dnssl(p + 6, (nd_opt_hdr->nd_opt_len - 1)
+                           * 8);
+
+                       if (nssl == NULL)
+                               return;
+
+                       log_debug("\t\tND_OPT_DNSSL: lifetime: %u", ntohl(
+                           dnssl->nd_opt_dnssl_lifetime));
+                       log_debug("\t\t\tsearch: %s", nssl);
+
+                       free(nssl);
+                       break;
+               default:
+                       log_debug("\t\tUNKNOWN: %d", nd_opt_hdr->nd_opt_type);
+                       break;
+
+               }
+               len -= nd_opt_hdr->nd_opt_len * 8 - 2;
+               p += nd_opt_hdr->nd_opt_len * 8 - 2;
+       }
+}
+
+char*
+parse_dnssl(char* data, int datalen)
+{
+       int len, pos;
+       char *nssl, *nsslp;
+
+       if((nssl = calloc(1, datalen + 1)) == NULL) {
+               log_warn("malloc");
+               return NULL;
+       }
+       nsslp = nssl;
+
+       pos = 0;
+
+       do {
+               len = data[pos];
+               if (len > 63 || len + pos + 1 > datalen) {
+                       free(nssl);
+                       log_warnx("invalid label in DNSSL");
+                       return NULL;
+               }
+               if (len == 0) {
+                       if (pos < datalen && data[pos + 1] != 0)
+                               *nsslp++ = ' '; /* seperator for next domain */
+                       else
+                               break;
+               } else {
+                       if (pos != 0 && data[pos - 1] != 0) /* no . at front */
+                               *nsslp++ = '.';
+                       memcpy(nsslp, data + pos + 1, len);
+                       nsslp += len;
+               }
+               pos += len + 1;
+       } while(pos < datalen);
+       if (len != 0) {
+               free(nssl);
+               log_warnx("invalid label in DNSSL");
+               return NULL;
+       }
+       return nssl;
+}
+
+void update_iface_ra(struct slaacd_iface *iface, struct radv *ra)
+{
+       struct radv             *old_ra;
+       struct radv_prefix      *prefix;
+       struct address_proposal *addr_proposal;
+       struct dfr_proposal     *dfr_proposal, *tmp;
+       int                      found, found_privacy;
+       char                     hbuf[NI_MAXHOST];
+
+       if ((old_ra = find_ra(iface, &ra->from)) == NULL)
+               LIST_INSERT_HEAD(&iface->radvs, ra, entries);
+       else {
+               LIST_REPLACE(old_ra, ra, entries);
+               free_ra(old_ra);
+       }
+       if (ra->router_lifetime == 0) {
+               LIST_FOREACH_SAFE(dfr_proposal, &iface->dfr_proposals, entries,
+                   tmp) {
+                       if (memcmp(&dfr_proposal->addr,
+                           &ra->from, sizeof(struct sockaddr_in6)) ==
+                           0) {
+                               free_dfr_proposal(dfr_proposal);
+                       }
+               }
+       } else {
+               found = 0;
+               LIST_FOREACH(dfr_proposal, &iface->dfr_proposals, entries) {
+                       if (memcmp(&dfr_proposal->addr,
+                           &ra->from, sizeof(struct sockaddr_in6)) ==
+                           0) {
+                               found = 1;
+                               if (real_lifetime(&dfr_proposal->uptime,
+                                   dfr_proposal->router_lifetime) >=
+                                   ra->router_lifetime)
+                                       log_warn("ignoring router advertisement"
+                                           " that lowers router lifetime");
+                               else {
+                                       dfr_proposal->when = ra->when;
+                                       dfr_proposal->uptime = ra->uptime;
+                                       dfr_proposal->router_lifetime =
+                                           ra->router_lifetime;
+
+                                       log_debug("%s, dfr state: %s, rl: %d",
+                                           __func__, proposal_state_name[
+                                           dfr_proposal->state],
+                                           real_lifetime(&dfr_proposal->uptime,
+                                           dfr_proposal->router_lifetime));
+
+                                       switch (dfr_proposal->state) {
+                                       case PROPOSAL_CONFIGURED:
+                                       case PROPOSAL_NEARLY_EXPIRED:
+                                               log_debug("updating dfr");
+                                               configure_dfr(dfr_proposal);
+                                               break;
+                                       default:
+                                               if (getnameinfo((struct
+                                                   sockaddr *)
+                                                   &dfr_proposal->addr,
+                                                   dfr_proposal->
+                                                   addr.sin6_len, hbuf,
+                                                   sizeof(hbuf), NULL, 0,
+                                                   NI_NUMERICHOST |
+                                                   NI_NUMERICSERV)) {
+                                                       log_warn("cannot get "
+                                                           "proposal IP");
+                                                       strlcpy(hbuf, "unknown",
+                                                           sizeof(hbuf));
+                                               }
+                                               log_debug("%s: iface %d: %s",
+                                                   __func__, iface->if_index,
+                                                   hbuf);
+                                               break;
+                                       }
+                               }
+
+                               break;
+                       }
+               }
+               if (!found)
+                       /* new proposal */
+                       gen_dfr_proposal(iface, ra);
+
+               LIST_FOREACH(prefix, &ra->prefixes, entries) {
+                       found = 0;
+                       found_privacy = 0;
+                       LIST_FOREACH(addr_proposal, &iface->addr_proposals,
+                           entries) {
+                               if (prefix->prefix_len ==
+                                   addr_proposal-> prefix_len &&
+                                   memcmp(&prefix->prefix,
+                                   &addr_proposal->prefix,
+                                   sizeof(struct in6_addr)) != 0)
+                                       continue;
+
+                               if (memcmp(&addr_proposal->hw_address,
+                                   &iface->hw_address,
+                                   sizeof(addr_proposal->hw_address)) != 0)
+                                       continue;
+
+                               if (addr_proposal->privacy) {
+                                       /*
+                                        * create new privacy address if old
+                                        * expires
+                                        */
+                                       if (addr_proposal->state !=
+                                           PROPOSAL_NEARLY_EXPIRED)
+                                               found_privacy = 1;
+
+                                       if (!iface->autoconfprivacy)
+                                               log_debug("%s XXX need to "
+                                                   "remove privacy address",
+                                                   __func__);
+
+                                       log_debug("%s, privacy addr state: %s",
+                                           __func__, proposal_state_name[
+                                           addr_proposal->state]);
+
+                                       /* privacy addresses just expire */
+                                       continue;
+                               }
+
+                               found = 1;
+
+                               if (real_lifetime(&addr_proposal->uptime,
+                                   addr_proposal->vltime) >= prefix->vltime) {
+                                       log_warn("ignoring router advertisement"
+                                           " that lowers vltime");
+                                       continue;
+                               }
+
+                               addr_proposal->when = ra->when;
+                               addr_proposal->uptime = ra->uptime;
+                               addr_proposal->vltime = prefix->vltime;
+                               addr_proposal->pltime = prefix->pltime;
+
+                               log_debug("%s, addr state: %s", __func__,
+                                   proposal_state_name[addr_proposal->state]);
+
+                               switch (addr_proposal->state) {
+                               case PROPOSAL_CONFIGURED:
+                               case PROPOSAL_NEARLY_EXPIRED:
+                                       log_debug("updating address");
+                                       configure_address(addr_proposal);
+                                       break;
+                               default:
+                                       if (getnameinfo((struct sockaddr *)
+                                           &addr_proposal->addr,
+                                           addr_proposal->addr.sin6_len, hbuf,
+                                           sizeof(hbuf), NULL, 0,
+                                           NI_NUMERICHOST | NI_NUMERICSERV)) {
+                                               log_warn("cannot get proposal "
+                                                   "IP");
+                                               strlcpy(hbuf, "unknown",
+                                                   sizeof(hbuf));
+                                       }
+                                       log_debug("%s: iface %d: %s", __func__,
+                                           iface->if_index, hbuf);
+                                       break;
+                               }
+                       }
+
+                       if (!found)
+                               /* new proposal */
+                               gen_address_proposal(iface, ra, prefix, 0);
+
+                       if (!found_privacy && iface->autoconfprivacy)
+                               /* new privacy proposal */
+                               gen_address_proposal(iface, ra, prefix, 1);
+               }
+       }
+}
+
+void
+configure_address(struct address_proposal *addr_proposal)
+{
+       struct imsg_configure_address    address;
+       struct timeval                   tv;
+
+       addr_proposal->next_timeout = addr_proposal->pltime -
+           MAX_RTR_SOLICITATIONS * (RTR_SOLICITATION_INTERVAL + 1);
+
+       tv.tv_sec = addr_proposal->next_timeout;
+       tv.tv_usec = arc4random_uniform(1000000);
+       evtimer_add(&addr_proposal->timer, &tv);
+
+       addr_proposal->state = PROPOSAL_CONFIGURED;
+
+       log_debug("%s: %d", __func__, addr_proposal->if_index);
+
+       address.if_index = addr_proposal->if_index;
+       memcpy(&address.addr, &addr_proposal->addr, sizeof(address.addr));
+       memcpy(&address.mask, &addr_proposal->mask, sizeof(address.mask));
+       address.vltime = addr_proposal->vltime;
+       address.pltime = addr_proposal->pltime;
+       address.privacy = addr_proposal->privacy;
+
+       engine_imsg_compose_main(IMSG_CONFIGURE_ADDRESS, 0, &address,
+           sizeof(address));
+}
+
+void
+gen_address_proposal(struct slaacd_iface *iface, struct radv *ra, struct
+    radv_prefix *prefix, int privacy)
+{
+       struct address_proposal *addr_proposal;
+       struct timeval           tv;
+       char                     hbuf[NI_MAXHOST];
+
+       if ((addr_proposal = calloc(1, sizeof(*addr_proposal))) == NULL)
+               fatal("calloc");
+       evtimer_set(&addr_proposal->timer, address_proposal_timeout,
+           addr_proposal);
+       addr_proposal->next_timeout = 1;
+       addr_proposal->timeout_count = 0;
+       addr_proposal->state = PROPOSAL_NOT_CONFIGURED;
+       addr_proposal->when = ra->when;
+       addr_proposal->uptime = ra->uptime;
+       addr_proposal->if_index = iface->if_index;
+       memcpy(&addr_proposal->hw_address, &iface->hw_address,
+           sizeof(addr_proposal->hw_address));
+       addr_proposal->privacy = privacy;
+       memcpy(&addr_proposal->prefix, &prefix->prefix,
+           sizeof(addr_proposal->prefix));
+       addr_proposal->prefix_len = prefix->prefix_len;
+
+       if (privacy) {
+               if (prefix->vltime > ND6_PRIV_VALID_LIFETIME)
+                       addr_proposal->vltime = ND6_PRIV_VALID_LIFETIME;
+               else
+                       addr_proposal->vltime = prefix->vltime;
+
+               if (prefix->pltime > ND6_PRIV_PREFERRED_LIFETIME)
+                       addr_proposal->pltime = ND6_PRIV_PREFERRED_LIFETIME;
+               else
+                       addr_proposal->pltime = prefix->pltime;
+       } else {
+               addr_proposal->vltime = prefix->vltime;
+               addr_proposal->pltime = prefix->pltime;
+       }
+
+       gen_addr(iface, prefix, addr_proposal, privacy);
+
+       tv.tv_sec = 0;
+       tv.tv_usec = 0;
+       evtimer_add(&addr_proposal->timer, &tv);
+
+       LIST_INSERT_HEAD(&iface->addr_proposals, addr_proposal, entries);
+
+       if (getnameinfo((struct sockaddr *)&addr_proposal->addr,
+           addr_proposal->addr.sin6_len, hbuf, sizeof(hbuf), NULL, 0,
+           NI_NUMERICHOST | NI_NUMERICSERV)) {
+               log_warn("cannot get router IP");
+               strlcpy(hbuf, "unknown", sizeof(hbuf));
+       }
+       log_debug("%s: iface %d: %s: %lld s", __func__,
+           iface->if_index, hbuf, tv.tv_sec);
+}
+
+void
+gen_dfr_proposal(struct slaacd_iface *iface, struct radv *ra)
+{
+       struct dfr_proposal     *dfr_proposal;
+       struct timeval           tv;
+       char                     hbuf[NI_MAXHOST];
+
+       if ((dfr_proposal = calloc(1, sizeof(*dfr_proposal))) == NULL)
+               fatal("calloc");
+       evtimer_set(&dfr_proposal->timer, dfr_proposal_timeout,
+           dfr_proposal);
+       dfr_proposal->next_timeout = 1;
+       dfr_proposal->timeout_count = 0;
+       dfr_proposal->state = PROPOSAL_NOT_CONFIGURED;
+       dfr_proposal->when = ra->when;
+       dfr_proposal->uptime = ra->uptime;
+       dfr_proposal->if_index = iface->if_index;
+       memcpy(&dfr_proposal->addr, &ra->from,
+           sizeof(dfr_proposal->addr));
+       dfr_proposal->router_lifetime = ra->router_lifetime;
+       dfr_proposal->rpref = ra->rpref;
+
+       tv.tv_sec = 0;
+       tv.tv_usec = 0;
+       evtimer_add(&dfr_proposal->timer, &tv);
+
+       LIST_INSERT_HEAD(&iface->dfr_proposals, dfr_proposal, entries);
+
+       if (getnameinfo((struct sockaddr *)&dfr_proposal->addr,
+           dfr_proposal->addr.sin6_len, hbuf, sizeof(hbuf), NULL, 0,
+           NI_NUMERICHOST | NI_NUMERICSERV)) {
+               log_warn("cannot get router IP");
+               strlcpy(hbuf, "unknown", sizeof(hbuf));
+       }
+       log_debug("%s: iface %d: %s: %lld s", __func__,
+           iface->if_index, hbuf, tv.tv_sec);
+}
+
+void
+configure_dfr(struct dfr_proposal *dfr_proposal)
+{
+       struct imsg_configure_dfr        dfr;
+       struct timeval                   tv;
+       enum proposal_state              prev_state;
+
+       dfr_proposal->next_timeout = dfr_proposal->router_lifetime -
+           MAX_RTR_SOLICITATIONS * (RTR_SOLICITATION_INTERVAL + 1);
+
+       tv.tv_sec = dfr_proposal->next_timeout;
+       tv.tv_usec = arc4random_uniform(1000000);
+       evtimer_add(&dfr_proposal->timer, &tv);
+
+       prev_state = dfr_proposal->state;
+
+       dfr_proposal->state = PROPOSAL_CONFIGURED;
+
+       log_debug("%s: %d", __func__, dfr_proposal->if_index);
+
+       if (prev_state == PROPOSAL_CONFIGURED || prev_state ==
+           PROPOSAL_NEARLY_EXPIRED) {
+               /*
+                * nothing to do here, routes do not expire in the kernel
+                * XXX check if the route got deleted and re-add it?
+                */
+               return;
+       }
+
+       dfr.if_index = dfr_proposal->if_index;
+       memcpy(&dfr.addr, &dfr_proposal->addr, sizeof(dfr.addr));
+       dfr.router_lifetime = dfr_proposal->router_lifetime;
+
+       engine_imsg_compose_main(IMSG_CONFIGURE_DFR, 0, &dfr, sizeof(dfr));
+}
+
+void
+withdraw_dfr(struct dfr_proposal *dfr_proposal)
+{
+       struct imsg_configure_dfr        dfr;
+
+       log_debug("%s: %d", __func__, dfr_proposal->if_index);
+
+       dfr.if_index = dfr_proposal->if_index;
+       memcpy(&dfr.addr, &dfr_proposal->addr, sizeof(dfr.addr));
+       dfr.router_lifetime = dfr_proposal->router_lifetime;
+
+       engine_imsg_compose_main(IMSG_WITHDRAW_DFR, 0, &dfr, sizeof(dfr));
+}
+
+void
+free_dfr_proposal(struct dfr_proposal *dfr_proposal)
+{
+
+       LIST_REMOVE(dfr_proposal, entries);
+       evtimer_del(&dfr_proposal->timer);
+       switch (dfr_proposal->state) {
+       case PROPOSAL_CONFIGURED:
+       case PROPOSAL_NEARLY_EXPIRED:
+               withdraw_dfr(dfr_proposal);
+               break;
+       default:
+               break;
+       }
+       free(dfr_proposal);
+}
+
+void
+send_proposal(struct imsg_proposal *proposal)
+{
+#ifndef SKIP_PROPOSAL
+       engine_imsg_compose_main(IMSG_PROPOSAL, 0, proposal, sizeof(*proposal));
+#else
+       struct imsg_proposal_ack        ack;
+       ack.id = proposal->id;
+       ack.pid = proposal->pid;
+       ack.if_index = proposal->if_index;
+       engine_imsg_compose_frontend(IMSG_FAKE_ACK, 0, &ack, sizeof(ack));
+#endif
+}
+
+void
+start_probe(struct slaacd_iface *iface)
+{
+       struct timeval  tv;
+
+       iface->state = IF_DELAY;
+       iface->probes = 0;
+
+       tv.tv_sec = 0;
+       tv.tv_usec = arc4random_uniform(MAX_RTR_SOLICITATION_DELAY_USEC);
+
+       log_debug("%s: iface %d: sleeping for %ldusec", __func__,
+           iface->if_index, tv.tv_usec);
+
+       evtimer_add(&iface->timer, &tv);
+}
+
+void
+address_proposal_timeout(int fd, short events, void *arg)
+{
+       struct address_proposal *addr_proposal;
+       struct imsg_proposal     proposal;
+       struct timeval           tv;
+       char                     hbuf[NI_MAXHOST];
+
+       addr_proposal = (struct address_proposal *)arg;
+
+       if (getnameinfo((struct sockaddr *)&addr_proposal->addr,
+           addr_proposal->addr.sin6_len, hbuf, sizeof(hbuf), NULL, 0,
+           NI_NUMERICHOST | NI_NUMERICSERV)) {
+               log_warn("cannot get router IP");
+               strlcpy(hbuf, "unknown", sizeof(hbuf));
+       }
+       log_debug("%s: iface %d: %s [%s], priv: %s", __func__,
+           addr_proposal->if_index, hbuf,
+           proposal_state_name[addr_proposal->state],
+           addr_proposal->privacy ? "y" : "n");
+
+       switch (addr_proposal->state) {
+       case PROPOSAL_NOT_CONFIGURED:
+       case PROPOSAL_SENT:
+               if (addr_proposal->timeout_count++ < 6) {
+                       addr_proposal->id = ++proposal_id;
+
+                       memset(&proposal, 0, sizeof(proposal));
+                       proposal.if_index = addr_proposal->if_index;
+                       proposal.pid = getpid();
+                       proposal.id = addr_proposal->id;
+                       memcpy(&proposal.addr, &addr_proposal->addr,
+                           sizeof(proposal.addr));
+                       memcpy(&proposal.mask, &addr_proposal->mask,
+                           sizeof(proposal.mask));
+
+                       proposal.rtm_addrs = RTA_NETMASK | RTA_IFA;
+
+                       addr_proposal->state = PROPOSAL_SENT;
+
+                       send_proposal(&proposal);
+
+                       tv.tv_sec = addr_proposal->next_timeout;
+                       tv.tv_usec = arc4random_uniform(1000000);
+                       addr_proposal->next_timeout *= 2;
+                       evtimer_add(&addr_proposal->timer, &tv);
+                       log_debug("%s: scheduling new timeout in %llds.%06ld",
+                           __func__, tv.tv_sec, tv.tv_usec);
+               } else {
+                       log_debug("%s: giving up, no response to proposal",
+                           __func__);
+                       LIST_REMOVE(addr_proposal, entries);
+                       evtimer_del(&addr_proposal->timer);
+                       free(addr_proposal);
+               }
+               break;
+       case PROPOSAL_CONFIGURED:
+               log_debug("PROPOSAL_CONFIGURED timeout: id: %lld, privacy: %s",
+                   addr_proposal->id, addr_proposal->privacy ? "y" : "n");
+
+               addr_proposal->next_timeout = 1;
+               addr_proposal->timeout_count = 0;
+               addr_proposal->state = PROPOSAL_NEARLY_EXPIRED;
+
+               tv.tv_sec = 0;
+               tv.tv_usec = 0;
+               evtimer_add(&addr_proposal->timer, &tv);
+
+               break;
+       case PROPOSAL_NEARLY_EXPIRED:
+               log_debug("%s: rl: %d", __func__,
+                   real_lifetime(&addr_proposal->uptime,
+                   addr_proposal->vltime));
+               /*
+                * we should have gotten a RTM_DELADDR from the kernel,
+                * in case we missed it, delete to not waste memory
+                */
+               if (real_lifetime(&addr_proposal->uptime,
+                   addr_proposal->vltime) == 0) {
+                       evtimer_del(&addr_proposal->timer);
+                       LIST_REMOVE(addr_proposal, entries);
+                       free(addr_proposal);
+                       log_debug("%s: removing address proposal", __func__);
+                       break;
+               }
+               if (addr_proposal->privacy)
+                       break; /* just let it expire */
+
+               engine_imsg_compose_frontend(IMSG_CTL_SEND_SOLICITATION,
+                   0, &addr_proposal->if_index,
+                   sizeof(addr_proposal->if_index));
+               tv.tv_sec = addr_proposal->next_timeout;
+               tv.tv_usec = arc4random_uniform(1000000);
+               addr_proposal->next_timeout *= 2;
+               evtimer_add(&addr_proposal->timer, &tv);
+               log_debug("%s: scheduling new timeout in %llds.%06ld",
+                   __func__, tv.tv_sec, tv.tv_usec);
+               break;
+       default:
+               log_debug("%s: unhandled state: %s", __func__,
+                   proposal_state_name[addr_proposal->state]);
+       }
+}
+
+void
+dfr_proposal_timeout(int fd, short events, void *arg)
+{
+       struct dfr_proposal     *dfr_proposal;
+       struct imsg_proposal     proposal;
+       struct timeval           tv;
+       char                     hbuf[NI_MAXHOST];
+
+       dfr_proposal = (struct dfr_proposal *)arg;
+
+       if (getnameinfo((struct sockaddr *)&dfr_proposal->addr,
+           dfr_proposal->addr.sin6_len, hbuf, sizeof(hbuf), NULL, 0,
+           NI_NUMERICHOST | NI_NUMERICSERV)) {
+               log_warn("cannot get router IP");
+               strlcpy(hbuf, "unknown", sizeof(hbuf));
+       }
+       log_debug("%s: iface %d: %s [%s]", __func__, dfr_proposal->if_index,
+           hbuf, proposal_state_name[dfr_proposal->state]);
+
+       switch (dfr_proposal->state) {
+       case PROPOSAL_NOT_CONFIGURED:
+       case PROPOSAL_SENT:
+               if (dfr_proposal->timeout_count++ < 6) {
+                       dfr_proposal->id = ++proposal_id;
+
+                       memset(&proposal, 0, sizeof(proposal));
+                       proposal.if_index = dfr_proposal->if_index;
+                       proposal.pid = getpid();
+                       proposal.id = dfr_proposal->id;
+                       memcpy(&proposal.addr, &dfr_proposal->addr,
+                           sizeof(proposal.addr));
+
+                       proposal.rtm_addrs = RTA_GATEWAY;
+
+                       dfr_proposal->state = PROPOSAL_SENT;
+
+                       send_proposal(&proposal);
+
+                       tv.tv_sec = dfr_proposal->next_timeout;
+                       tv.tv_usec = arc4random_uniform(1000000);
+                       dfr_proposal->next_timeout *= 2;
+                       evtimer_add(&dfr_proposal->timer, &tv);
+                       log_debug("%s: scheduling new timeout in %llds.%06ld",
+                           __func__, tv.tv_sec, tv.tv_usec);
+               } else {
+                       log_debug("%s: giving up, no response to proposal",
+                           __func__);
+                       free_dfr_proposal(dfr_proposal);
+               }
+               break;
+       case PROPOSAL_CONFIGURED:
+               log_debug("PROPOSAL_CONFIGURED timeout: id: %lld",
+                   dfr_proposal->id);
+
+               dfr_proposal->next_timeout = 1;
+               dfr_proposal->timeout_count = 0;
+               dfr_proposal->state = PROPOSAL_NEARLY_EXPIRED;
+
+               tv.tv_sec = 0;
+               tv.tv_usec = 0;
+               evtimer_add(&dfr_proposal->timer, &tv);
+
+               break;
+       case PROPOSAL_NEARLY_EXPIRED:
+               if (real_lifetime(&dfr_proposal->uptime,
+                   dfr_proposal->router_lifetime) == 0) {
+                       free_dfr_proposal(dfr_proposal);
+                       log_debug("%s: removing dfr proposal", __func__);
+                       break;
+               }
+               engine_imsg_compose_frontend(IMSG_CTL_SEND_SOLICITATION,
+                   0, &dfr_proposal->if_index,
+                   sizeof(dfr_proposal->if_index));
+               tv.tv_sec = dfr_proposal->next_timeout;
+               tv.tv_usec = arc4random_uniform(1000000);
+               dfr_proposal->next_timeout *= 2;
+               evtimer_add(&dfr_proposal->timer, &tv);
+               log_debug("%s: scheduling new timeout in %llds.%06ld",
+                   __func__, tv.tv_sec, tv.tv_usec);
+               break;
+       default:
+               log_debug("%s: unhandled state: %s", __func__,
+                   proposal_state_name[dfr_proposal->state]);
+       }
+}
+
+void
+iface_timeout(int fd, short events, void *arg)
+{
+       struct slaacd_iface     *iface = (struct slaacd_iface *)arg;
+       struct timeval           tv;
+
+       log_debug("%s[%d]: %s", __func__, iface->if_index,
+           if_state_name[iface->state]);
+
+       switch (iface->state) {
+               case IF_DELAY:
+               case IF_PROBE:
+                       iface->state = IF_PROBE;
+                       engine_imsg_compose_frontend(
+                           IMSG_CTL_SEND_SOLICITATION, 0, &iface->if_index,
+                           sizeof(iface->if_index));
+                       if (++iface->probes >= MAX_RTR_SOLICITATIONS)
+                               iface->state = IF_IDLE;
+                       else {
+                               tv.tv_sec = RTR_SOLICITATION_INTERVAL;
+                               tv.tv_usec = arc4random_uniform(1000000);
+                               evtimer_add(&iface->timer, &tv);
+                       }
+                       break;
+               case IF_DOWN:
+               case IF_IDLE:
+               default:
+                       break;
+       }
+}
+
+struct radv*
+find_ra(struct slaacd_iface *iface, struct sockaddr_in6 *from)
+{
+       struct radv     *ra;
+
+       LIST_FOREACH (ra, &iface->radvs, entries) {
+               if (memcmp(&ra->from.sin6_addr, &from->sin6_addr,
+                   sizeof(from->sin6_addr)) == 0)
+                       return (ra);
+       }
+
+       return (NULL);
+}
+
+struct address_proposal*
+find_address_proposal_by_id(struct slaacd_iface *iface, int64_t id)
+{
+       struct address_proposal *addr_proposal;
+
+       LIST_FOREACH (addr_proposal, &iface->addr_proposals, entries) {
+               if (addr_proposal->id == id)
+                       return (addr_proposal);
+       }
+
+       return (NULL);
+}
+
+struct address_proposal*
+find_address_proposal_by_addr(struct slaacd_iface *iface, struct sockaddr_in6
+    *addr)
+{
+       struct address_proposal *addr_proposal;
+
+       LIST_FOREACH (addr_proposal, &iface->addr_proposals, entries) {
+               if (memcmp(&addr_proposal->addr, addr, sizeof(*addr)) == 0)
+                       return (addr_proposal);
+       }
+
+       return (NULL);
+}
+
+struct dfr_proposal*
+find_dfr_proposal_by_id(struct slaacd_iface *iface, int64_t id)
+{
+       struct dfr_proposal     *dfr_proposal;
+
+       LIST_FOREACH (dfr_proposal, &iface->dfr_proposals, entries) {
+               if (dfr_proposal->id == id)
+                       return (dfr_proposal);
+       }
+
+       return (NULL);
+}
+
+
+/* XXX currently unused */
+void
+find_prefix(struct slaacd_iface *iface, struct address_proposal *addr_proposal,
+    struct radv **result_ra, struct radv_prefix **result_prefix)
+{
+       struct radv             *ra;
+       struct radv_prefix      *prefix;
+       uint32_t                 lifetime, max_lifetime = 0;
+
+       *result_ra = NULL;
+       *result_prefix = NULL;
+
+       LIST_FOREACH(ra, &iface->radvs, entries) {
+               LIST_FOREACH(prefix, &ra->prefixes, entries) {
+                       if (memcmp(&prefix->prefix, &addr_proposal->prefix,
+                           sizeof(addr_proposal->prefix)) != 0)
+                               continue;
+                       lifetime = real_lifetime(&ra->uptime,
+                           prefix->vltime);
+                       if (lifetime > max_lifetime) {
+                               max_lifetime = lifetime;
+                               *result_ra = ra;
+                               *result_prefix = prefix;
+                       }
+               }
+       }
+}
+
+uint32_t
+real_lifetime(struct timespec *received_uptime, uint32_t ltime)
+{
+       struct timespec  now, diff;
+       int64_t          remaining;
+
+       if (clock_gettime(CLOCK_MONOTONIC, &now))
+               fatal("clock_gettime");
+
+       timespecsub(&now, received_uptime, &diff);
+
+       remaining = ((int64_t)ltime) - diff.tv_sec;
+
+       if (remaining < 0)
+               remaining = 0;
+
+       return (remaining);
+}
diff --git a/sbin/slaacd/engine.h b/sbin/slaacd/engine.h
new file mode 100644 (file)
index 0000000..e7f520f
--- /dev/null
@@ -0,0 +1,47 @@
+/*     $OpenBSD: engine.h,v 1.1 2017/06/03 10:00:29 florian Exp $      */
+
+/*
+ * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+struct imsg_proposal {
+       uint32_t                        if_index;
+       pid_t                           pid;
+       int64_t                         id;
+       struct sockaddr_in6             addr;
+       struct in6_addr                 mask;
+       struct sockaddr_in6             gateway;
+       struct sockaddr_rtdns           rdns;
+       struct sockaddr_rtsearch        dnssl;
+       int                             rtm_addrs;
+};
+
+struct imsg_configure_address {
+       uint32_t                 if_index;
+       struct sockaddr_in6      addr;
+       struct in6_addr          mask;
+       uint32_t                 vltime;
+       uint32_t                 pltime;
+       int                      privacy;
+};
+
+struct imsg_configure_dfr {
+       uint32_t                 if_index;
+       struct sockaddr_in6      addr;
+       uint32_t                 router_lifetime;
+};
+
+void            engine(int, int);
+int             engine_imsg_compose_frontend(int, pid_t, void *, uint16_t);
diff --git a/sbin/slaacd/frontend.c b/sbin/slaacd/frontend.c
new file mode 100644 (file)
index 0000000..18fb1af
--- /dev/null
@@ -0,0 +1,826 @@
+/*     $OpenBSD: frontend.c,v 1.1 2017/06/03 10:00:29 florian Exp $    */
+
+/*
+ * Copyright (c) 2017 Florian Obser <florian@openbsd.org>
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/uio.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <arpa/inet.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+
+#include <errno.h>
+#include <event.h>
+#include <ifaddrs.h>
+#include <imsg.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "slaacd.h"
+#include "frontend.h"
+#include "control.h"
+
+#define        ROUTE_SOCKET_BUF_SIZE   16384
+#define        ALLROUTER               "ff02::2"
+
+__dead void     frontend_shutdown(void);
+void            frontend_sig_handler(int, short, void *);
+void            update_iface(uint32_t, char*);
+void            frontend_startup(void);
+void            route_receive(int, short, void *);
+void            handle_route_message(struct rt_msghdr *, struct sockaddr **);
+void            get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
+void            icmp6_receive(int, short, void *);
+int             get_flags(char *);
+int             get_xflags(char *);
+void            get_lladdr(char *, struct ether_addr *, struct sockaddr_in6 *);
+void            send_solicitation(uint32_t);
+
+struct imsgev                  *iev_main;
+struct imsgev                  *iev_engine;
+struct event                    ev_route;
+struct msghdr                   sndmhdr;
+struct iovec                    sndiov[4];
+struct nd_router_solicit        rs;
+struct nd_opt_hdr               nd_opt_hdr;
+struct ether_addr               nd_opt_source_link_addr;
+struct sockaddr_in6             dst;
+int             icmp6sock, routesock, xflagssock;
+
+struct icmp6_ev {
+       struct event             ev;
+       uint8_t                  answer[1500];
+       struct msghdr            rcvmhdr;
+       struct iovec             rcviov[1];
+       struct sockaddr_in6      from;
+} icmp6ev;
+
+void
+frontend_sig_handler(int sig, short event, void *bula)
+{
+       /*
+        * Normal signal handler rules don't apply because libevent
+        * decouples for us.
+        */
+
+       switch (sig) {
+       case SIGINT:
+       case SIGTERM:
+               frontend_shutdown();
+       default:
+               fatalx("unexpected signal");
+       }
+}
+
+void
+frontend(int debug, int verbose, char *sockname)
+{
+       struct event             ev_sigint, ev_sigterm;
+       struct passwd           *pw;
+       struct icmp6_filter      filt;
+       struct in6_pktinfo      *pi;
+       struct cmsghdr          *cm;
+       size_t                   rcvcmsglen, sndcmsglen;
+       int                      hoplimit = 255, on = 1, rtfilter;
+       uint8_t                 *rcvcmsgbuf, *sndcmsgbuf;
+
+       log_init(debug, LOG_DAEMON);
+       log_setverbose(verbose);
+
+       /* Create slaacd control socket outside chroot. */
+       if (control_init(sockname) == -1)
+               fatalx("control socket setup failed");
+
+       if ((pw = getpwnam(SLAACD_USER)) == NULL)
+               fatal("getpwnam");
+
+       if (chroot(pw->pw_dir) == -1)
+               fatal("chroot");
+       if (chdir("/") == -1)
+               fatal("chdir(\"/\")");
+
+       slaacd_process = PROC_FRONTEND;
+       setproctitle("%s", log_procnames[slaacd_process]);
+       log_procinit(log_procnames[slaacd_process]);
+
+       if ((icmp6sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0)
+               fatal("ICMPv6 socket");
+
+       if ((routesock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0)
+               fatal("route socket");
+
+       if ((xflagssock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+               fatal("socket");
+
+       if (setgroups(1, &pw->pw_gid) ||
+           setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+           setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+               fatal("can't drop privileges");
+
+       if (setsockopt(icmp6sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
+           sizeof(on)) < 0)
+               fatal("IPV6_RECVPKTINFO");
+
+       if (setsockopt(icmp6sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
+           sizeof(on)) < 0)
+               fatal("IPV6_RECVHOPLIMIT");
+
+       /* only router advertisements */
+       ICMP6_FILTER_SETBLOCKALL(&filt);
+       ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
+       if (setsockopt(icmp6sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+           sizeof(filt)) == -1)
+               fatal("ICMP6_FILTER");
+
+       rtfilter = ROUTE_FILTER(RTM_IFINFO) | ROUTE_FILTER(RTM_NEWADDR) |
+           ROUTE_FILTER(RTM_DELADDR) | ROUTE_FILTER(RTM_PROPOSAL);
+       if (setsockopt(routesock, PF_ROUTE, ROUTE_MSGFILTER,
+           &rtfilter, sizeof(rtfilter)) < 0)
+               fatal("setsockopt(ROUTE_MSGFILTER)");
+
+       if (pledge("stdio inet recvfd route", NULL) == -1)
+               fatal("pledge");
+
+       event_init();
+
+       /* Setup signal handler. */
+       signal_set(&ev_sigint, SIGINT, frontend_sig_handler, NULL);
+       signal_set(&ev_sigterm, SIGTERM, frontend_sig_handler, NULL);
+       signal_add(&ev_sigint, NULL);
+       signal_add(&ev_sigterm, NULL);
+       signal(SIGPIPE, SIG_IGN);
+       signal(SIGHUP, SIG_IGN);
+
+       /* Setup pipe and event handler to the parent process. */
+       if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
+               fatal(NULL);
+       imsg_init(&iev_main->ibuf, 3);
+       iev_main->handler = frontend_dispatch_main;
+       iev_main->events = EV_READ;
+       event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
+           iev_main->handler, iev_main);
+       event_add(&iev_main->ev, NULL);
+
+       /* Listen on control socket. */
+       TAILQ_INIT(&ctl_conns);
+       control_listen();
+
+       event_set(&ev_route, routesock, EV_READ | EV_PERSIST, route_receive,
+           NULL);
+
+       event_set(&icmp6ev.ev, icmp6sock, EV_READ | EV_PERSIST, icmp6_receive,
+           NULL);
+
+       rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+           CMSG_SPACE(sizeof(int));
+       if((rcvcmsgbuf = malloc(rcvcmsglen)) == NULL)
+               fatal("malloc");
+
+       icmp6ev.rcviov[0].iov_base = (caddr_t)icmp6ev.answer;
+       icmp6ev.rcviov[0].iov_len = sizeof(icmp6ev.answer);
+       icmp6ev.rcvmhdr.msg_name = (caddr_t)&icmp6ev.from;
+       icmp6ev.rcvmhdr.msg_namelen = sizeof(icmp6ev.from);
+       icmp6ev.rcvmhdr.msg_iov = icmp6ev.rcviov;
+       icmp6ev.rcvmhdr.msg_iovlen = 1;
+       icmp6ev.rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf;
+       icmp6ev.rcvmhdr.msg_controllen = rcvcmsglen;
+
+       sndcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+           CMSG_SPACE(sizeof(int));
+
+       if ((sndcmsgbuf = malloc(sndcmsglen)) == NULL)
+               fatal("malloc");
+
+       rs.nd_rs_type = ND_ROUTER_SOLICIT;
+       rs.nd_rs_code = 0;
+       rs.nd_rs_cksum = 0;
+       rs.nd_rs_reserved = 0;
+
+       nd_opt_hdr.nd_opt_type = ND_OPT_SOURCE_LINKADDR;
+       nd_opt_hdr.nd_opt_len = 1;
+
+       memset(&dst, 0, sizeof(dst));
+       dst.sin6_family = AF_INET6;
+       if (inet_pton(AF_INET6, ALLROUTER, &dst.sin6_addr.s6_addr) != 1)
+               fatal("inet_pton");
+
+       sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
+       sndmhdr.msg_iov = sndiov;
+       sndmhdr.msg_iovlen = 3;
+       sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
+       sndmhdr.msg_controllen = sndcmsglen;
+
+       sndmhdr.msg_name = (caddr_t)&dst;
+       sndmhdr.msg_iov[0].iov_base = (caddr_t)&rs;
+       sndmhdr.msg_iov[0].iov_len = sizeof(rs);
+       sndmhdr.msg_iov[1].iov_base = (caddr_t)&nd_opt_hdr;
+       sndmhdr.msg_iov[1].iov_len = sizeof(nd_opt_hdr);
+       sndmhdr.msg_iov[2].iov_base = (caddr_t)&nd_opt_source_link_addr;
+       sndmhdr.msg_iov[2].iov_len = sizeof(nd_opt_source_link_addr);
+
+       cm = CMSG_FIRSTHDR(&sndmhdr);
+
+       cm->cmsg_level = IPPROTO_IPV6;
+       cm->cmsg_type = IPV6_PKTINFO;
+       cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+       pi = (struct in6_pktinfo *)CMSG_DATA(cm);
+       memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr));
+       pi->ipi6_ifindex = 0;
+
+       cm = CMSG_NXTHDR(&sndmhdr, cm);
+       cm->cmsg_level = IPPROTO_IPV6;
+       cm->cmsg_type = IPV6_HOPLIMIT;
+       cm->cmsg_len = CMSG_LEN(sizeof(int));
+       memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
+
+       event_dispatch();
+
+       frontend_shutdown();
+}
+
+__dead void
+frontend_shutdown(void)
+{
+       /* Close pipes. */
+       msgbuf_write(&iev_engine->ibuf.w);
+       msgbuf_clear(&iev_engine->ibuf.w);
+       close(iev_engine->ibuf.fd);
+       msgbuf_write(&iev_main->ibuf.w);
+       msgbuf_clear(&iev_main->ibuf.w);
+       close(iev_main->ibuf.fd);
+
+       free(iev_engine);
+       free(iev_main);
+
+       log_info("frontend exiting");
+       exit(0);
+}
+
+int
+frontend_imsg_compose_main(int type, pid_t pid, void *data,
+    uint16_t datalen)
+{
+       return (imsg_compose_event(iev_main, type, 0, pid, -1, data,
+           datalen));
+}
+
+int
+frontend_imsg_compose_engine(int type, uint32_t peerid, pid_t pid,
+    void *data, uint16_t datalen)
+{
+       return (imsg_compose_event(iev_engine, type, peerid, pid, -1,
+           data, datalen));
+}
+
+void
+frontend_dispatch_main(int fd, short event, void *bula)
+{
+       struct imsg              imsg;
+       struct imsgev           *iev = bula;
+       struct imsgbuf          *ibuf = &iev->ibuf;
+       ssize_t                  n;
+       int                      shut = 0;
+
+       if (event & EV_READ) {
+               if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+                       fatal("imsg_read error");
+               if (n == 0)     /* Connection closed. */
+                       shut = 1;
+       }
+       if (event & EV_WRITE) {
+               if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+                       fatal("msgbuf_write");
+               if (n == 0)     /* Connection closed. */
+                       shut = 1;
+       }
+
+       for (;;) {
+               if ((n = imsg_get(ibuf, &imsg)) == -1)
+                       fatal("%s: imsg_get error", __func__);
+               if (n == 0)     /* No more messages. */
+                       break;
+
+               switch (imsg.hdr.type) {
+               case IMSG_SOCKET_IPC:
+                       /*
+                        * Setup pipe and event handler to the engine
+                        * process.
+                        */
+                       if (iev_engine) {
+                               log_warnx("%s: received unexpected imsg fd "
+                                   "to frontend", __func__);
+                               break;
+                       }
+                       if ((fd = imsg.fd) == -1) {
+                               log_warnx("%s: expected to receive imsg fd to "
+                                  "frontend but didn't receive any",
+                                  __func__);
+                               break;
+                       }
+
+                       iev_engine = malloc(sizeof(struct imsgev));
+                       if (iev_engine == NULL)
+                               fatal(NULL);
+
+                       imsg_init(&iev_engine->ibuf, fd);
+                       iev_engine->handler = frontend_dispatch_engine;
+                       iev_engine->events = EV_READ;
+
+                       event_set(&iev_engine->ev, iev_engine->ibuf.fd,
+                       iev_engine->events, iev_engine->handler, iev_engine);
+                       event_add(&iev_engine->ev, NULL);
+
+                       if (pledge("stdio inet route", NULL) == -1)
+                               fatal("pledge");
+
+                       break;
+               case IMSG_STARTUP:
+                       frontend_startup();
+                       break;
+               case IMSG_CTL_END:
+                       control_imsg_relay(&imsg);
+                       break;
+               default:
+                       log_debug("%s: error handling imsg %d", __func__,
+                           imsg.hdr.type);
+                       break;
+               }
+               imsg_free(&imsg);
+       }
+       if (!shut)
+               imsg_event_add(iev);
+       else {
+               /* This pipe is dead. Remove its event handler. */
+               event_del(&iev->ev);
+               event_loopexit(NULL);
+       }
+}
+
+void
+frontend_dispatch_engine(int fd, short event, void *bula)
+{
+       struct imsgev           *iev = bula;
+       struct imsgbuf          *ibuf = &iev->ibuf;
+       struct imsg              imsg;
+       ssize_t                  n;
+       int                      shut = 0;
+       uint32_t                 if_index;
+
+       if (event & EV_READ) {
+               if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+                       fatal("imsg_read error");
+               if (n == 0)     /* Connection closed. */
+                       shut = 1;
+       }
+       if (event & EV_WRITE) {
+               if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+                       fatal("msgbuf_write");
+               if (n == 0)     /* Connection closed. */
+                       shut = 1;
+       }
+
+       for (;;) {
+               if ((n = imsg_get(ibuf, &imsg)) == -1)
+                       fatal("%s: imsg_get error", __func__);
+               if (n == 0)     /* No more messages. */
+                       break;
+
+               switch (imsg.hdr.type) {
+               case IMSG_CTL_END:
+               case IMSG_CTL_SHOW_INTERFACE_INFO:
+               case IMSG_CTL_SHOW_INTERFACE_INFO_RA:
+               case IMSG_CTL_SHOW_INTERFACE_INFO_RA_PREFIX:
+               case IMSG_CTL_SHOW_INTERFACE_INFO_RA_RDNS:
+               case IMSG_CTL_SHOW_INTERFACE_INFO_RA_DNSSL:
+               case IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSALS:
+               case IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSAL:
+               case IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSALS:
+               case IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSAL:
+                       control_imsg_relay(&imsg);
+                       break;
+               case IMSG_CTL_SEND_SOLICITATION:
+                       if_index = *((uint32_t *)imsg.data);
+                       send_solicitation(if_index);
+                       break;
+               case IMSG_FAKE_ACK:
+                       frontend_imsg_compose_engine(IMSG_PROPOSAL_ACK,
+                          0, 0, imsg.data, sizeof(struct imsg_proposal_ack));
+                       break;
+               default:
+                       log_debug("%s: error handling imsg %d", __func__,
+                           imsg.hdr.type);
+                       break;
+               }
+               imsg_free(&imsg);
+       }
+       if (!shut)
+               imsg_event_add(iev);
+       else {
+               /* This pipe is dead. Remove its event handler. */
+               event_del(&iev->ev);
+               event_loopexit(NULL);
+       }
+}
+
+int
+get_flags(char *if_name)
+{
+       struct ifreq             ifr;
+
+       (void) strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
+       if (ioctl(xflagssock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0)
+               fatal("SIOCGIFFLAGS");
+       return ifr.ifr_flags;
+}
+
+int
+get_xflags(char *if_name)
+{
+       struct ifreq             ifr;
+
+       (void) strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
+       if (ioctl(xflagssock, SIOCGIFXFLAGS, (caddr_t)&ifr) < 0)
+               fatal("SIOCGIFXFLAGS");
+       return ifr.ifr_flags;
+}
+
+void
+update_iface(uint32_t if_index, char* if_name)
+{
+       struct imsg_ifinfo       imsg_ifinfo;
+       int                      flags, xflags;
+
+       flags = get_flags(if_name);
+       xflags = get_xflags(if_name);
+
+       if (!(xflags & IFXF_AUTOCONF6))
+               return;
+
+       memset(&imsg_ifinfo, 0, sizeof(imsg_ifinfo));
+
+       imsg_ifinfo.if_index = if_index;
+       imsg_ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP |
+           IFF_RUNNING);
+       imsg_ifinfo.autoconfprivacy = !(xflags & IFXF_INET6_NOPRIVACY);
+       get_lladdr(if_name, &imsg_ifinfo.hw_address, &imsg_ifinfo.ll_address);
+
+       memcpy(&nd_opt_source_link_addr, &imsg_ifinfo.hw_address,
+           sizeof(nd_opt_source_link_addr));
+
+       frontend_imsg_compose_engine(IMSG_UPDATE_IF, 0, 0, &imsg_ifinfo,
+           sizeof(imsg_ifinfo));
+}
+
+void
+frontend_startup(void)
+{
+       struct if_nameindex     *ifnidxp, *ifnidx;
+
+       event_add(&ev_route, NULL);
+       event_add(&icmp6ev.ev, NULL);
+
+       if ((ifnidxp = if_nameindex()) == NULL)
+               fatalx("if_nameindex");
+
+       for(ifnidx = ifnidxp; ifnidx->if_index !=0 && ifnidx->if_name != NULL;
+           ifnidx++)
+               update_iface(ifnidx->if_index, ifnidx->if_name);
+
+       if_freenameindex(ifnidxp);
+}
+
+void
+route_receive(int fd, short events, void *arg)
+{
+       static uint8_t                   buf[ROUTE_SOCKET_BUF_SIZE];
+
+       struct rt_msghdr                *rtm;
+       struct sockaddr                 *sa, *rti_info[RTAX_MAX];
+       size_t                           len, offset;
+       ssize_t                          n;
+       char                            *next;
+
+       if ((n = read(fd, &buf, sizeof(buf))) == -1) {
+               if (errno == EAGAIN || errno == EINTR)
+                       return;
+               log_warn("dispatch_rtmsg: read error");
+               return;
+       }
+
+       if (n == 0) {
+               log_warnx("routing socket closed");
+               return;
+       }
+
+       len = n;
+       for (offset = 0; offset < len; offset += rtm->rtm_msglen) {
+               next = buf + offset;
+               rtm = (struct rt_msghdr *)next;
+               if (len < offset + sizeof(u_short) ||
+                   len < offset + rtm->rtm_msglen)
+                       fatalx("rtmsg_process: partial rtm in buffer");
+               if (rtm->rtm_version != RTM_VERSION)
+                       continue;
+
+               sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
+               get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+
+               handle_route_message(rtm, rti_info);
+       }
+}
+
+void
+handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info)
+{
+       struct if_msghdr                *ifm;
+       struct imsg_proposal_ack         proposal_ack;
+       struct imsg_del_addr             del_addr;
+       struct sockaddr_rtlabel         *rl;
+       int64_t                          id, pid;
+       int                              flags, xflags, if_index;
+       char                             ifnamebuf[IFNAMSIZ];
+       char                            *if_name;
+       char                            **ap, *argv[4], *p;
+       const char                      *errstr;
+
+       switch (rtm->rtm_type) {
+       case RTM_IFINFO:
+               ifm = (struct if_msghdr *)rtm;
+               if_name = if_indextoname(ifm->ifm_index, ifnamebuf);
+               if (if_name == NULL) {
+                       log_debug("RTM_IFINFO: lost if %d", ifm->ifm_index);
+                       if_index = ifm->ifm_index;
+                       frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
+                           &if_index, sizeof(if_index));
+               } else {
+                       xflags = get_xflags(if_name);
+                       flags = get_flags(if_name);
+                       if (!(xflags & IFXF_AUTOCONF6)) {
+                               log_debug("RTM_IFINFO: %s(%d) no(longer) "
+                                  "autoconf6", if_name, ifm->ifm_index);
+                               if_index = ifm->ifm_index;
+                               frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0,
+                                   0, &if_index, sizeof(if_index));
+                       } else
+                               update_iface(ifm->ifm_index, if_name);
+               }
+               break;
+       case RTM_NEWADDR:
+               ifm = (struct if_msghdr *)rtm;
+               if_name = if_indextoname(ifm->ifm_index, ifnamebuf);
+               log_debug("RTM_NEWADDR: %s[%u]", if_name, ifm->ifm_index);
+               update_iface(ifm->ifm_index, if_name);
+               break;
+       case RTM_DELADDR:
+               ifm = (struct if_msghdr *)rtm;
+               if_name = if_indextoname(ifm->ifm_index, ifnamebuf);
+               if (rtm->rtm_addrs & RTA_IFA && rti_info[RTAX_IFA]->sa_family
+                   == AF_INET6) {
+                       del_addr.if_index = ifm->ifm_index;
+                       memcpy(&del_addr.addr, rti_info[RTAX_IFA], sizeof(
+                           del_addr.addr));
+                       frontend_imsg_compose_engine(IMSG_DEL_ADDRESS,
+                                   0, 0, &del_addr, sizeof(del_addr));
+                       log_debug("RTM_DELADDR: %s[%u]", if_name,
+                           ifm->ifm_index);
+               }
+               break;
+       case RTM_PROPOSAL:
+               ifm = (struct if_msghdr *)rtm;
+               if_name = if_indextoname(ifm->ifm_index, ifnamebuf);
+
+               if ((rtm->rtm_flags & (RTF_DONE | RTF_PROTO1)) ==
+                   (RTF_DONE | RTF_PROTO1) && rtm->rtm_addrs == RTA_LABEL) {
+                       rl = (struct sockaddr_rtlabel *)rti_info[RTAX_LABEL];
+                       /* XXX validate rl */
+
+                       p = rl->sr_label;
+
+                       for (ap = argv; ap < &argv[3] && (*ap =
+                           strsep(&p, " ")) != NULL;) {
+                               if (**ap != '\0')
+                                       ap++;
+                       }
+                       *ap = NULL;
+
+                       if (argv[0] != NULL && strncmp(argv[0], "slaacd:",
+                           strlen("slaacd:")) == 0 && argv[1] != NULL &&
+                           argv[2] != NULL && argv[3] == NULL) {
+                               id = strtonum(argv[1], 0, INT64_MAX, &errstr);
+                               if (errstr != NULL) {
+                                       log_warn("%s: proposal seq is %s: %s",
+                                           __func__, errstr, argv[1]);
+                                       break;
+                               }
+                               pid = strtonum(argv[2], 0, INT32_MAX, &errstr);
+                               if (errstr != NULL) {
+                                       log_warn("%s: pid is %s: %s",
+                                           __func__, errstr, argv[2]);
+                                       break;
+                               }
+                               proposal_ack.id = id;
+                               proposal_ack.pid = pid;
+                               proposal_ack.if_index = ifm->ifm_index;
+
+                               frontend_imsg_compose_engine(IMSG_PROPOSAL_ACK,
+                                   0, 0, &proposal_ack, sizeof(proposal_ack));
+                       } else {
+                               log_debug("cannot parse: %s", rl->sr_label);
+                       }
+               } else {
+#if 0
+                       log_debug("%s: got flags %x, expcted %x", __func__,
+                           rtm->rtm_flags, (RTF_DONE | RTF_PROTO1));
+#endif
+               }
+
+               break;
+       default:
+               log_debug("unexpected RTM: %d", rtm->rtm_type);
+               break;
+       }
+
+}
+
+#define        ROUNDUP(a)      \
+    (((a) & (sizeof(long) - 1)) ? (1 + ((a) | (sizeof(long) - 1))) : (a))
+
+void
+get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
+{
+       int     i;
+
+       for (i = 0; i < RTAX_MAX; i++) {
+               if (addrs & (1 << i)) {
+                       rti_info[i] = sa;
+                       sa = (struct sockaddr *)((char *)(sa) +
+                           ROUNDUP(sa->sa_len));
+               } else
+                       rti_info[i] = NULL;
+       }
+}
+
+void
+get_lladdr(char *if_name, struct ether_addr *mac, struct sockaddr_in6 *ll)
+{
+       struct ifaddrs          *ifap, *ifa;
+       struct sockaddr_dl      *sdl;
+       struct sockaddr_in6     *sin6;
+
+       if (getifaddrs(&ifap) != 0)
+               fatal("getifaddrs");
+
+       memset(mac, 0, sizeof(*mac));
+       memset(ll, 0, sizeof(*ll));
+
+       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+               if (strcmp(if_name, ifa->ifa_name) != 0)
+                       continue;
+
+               switch(ifa->ifa_addr->sa_family) {
+               case AF_LINK:
+                       sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+                       if (sdl->sdl_type != IFT_ETHER ||
+                           sdl->sdl_alen != ETHER_ADDR_LEN)
+                               continue;
+
+                       memcpy(mac->ether_addr_octet, LLADDR(sdl),
+                           ETHER_ADDR_LEN);
+                       break;
+               case AF_INET6:
+                       sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+                       if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+                               sin6->sin6_scope_id = ntohs(*(u_int16_t *)
+                                   &sin6->sin6_addr.s6_addr[2]);
+                               sin6->sin6_addr.s6_addr[2] =
+                                   sin6->sin6_addr.s6_addr[3] = 0;
+                               memcpy(ll, sin6, sizeof(*ll));
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+       freeifaddrs(ifap);
+}
+
+void
+icmp6_receive(int fd, short events, void *arg)
+{
+       struct imsg_ra           ra;
+
+       struct in6_pktinfo      *pi = NULL;
+       struct cmsghdr          *cm;
+       ssize_t                  len;
+       int                      if_index = 0, *hlimp = NULL;
+       char                     ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+       uint8_t                 *p;
+
+       p = icmp6ev.answer;
+
+       if ((len = recvmsg(fd, &icmp6ev.rcvmhdr, 0)) < 0) {
+               log_warn("recvmsg");
+               return;
+       }
+
+       /* extract optional information via Advanced API */
+       for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&icmp6ev.rcvmhdr); cm;
+           cm = (struct cmsghdr *)CMSG_NXTHDR(&icmp6ev.rcvmhdr, cm)) {
+               if (cm->cmsg_level == IPPROTO_IPV6 &&
+                   cm->cmsg_type == IPV6_PKTINFO &&
+                   cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
+                       pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
+                       if_index = pi->ipi6_ifindex;
+               }
+               if (cm->cmsg_level == IPPROTO_IPV6 &&
+                   cm->cmsg_type == IPV6_HOPLIMIT &&
+                   cm->cmsg_len == CMSG_LEN(sizeof(int)))
+                       hlimp = (int *)CMSG_DATA(cm);
+       }
+
+       if (if_index == 0) {
+               log_warnx("failed to get receiving interface");
+               return;
+       }
+
+       if (hlimp == NULL) {
+               log_warnx("failed to get receiving hop limit");
+               return;
+       }
+
+       if (*hlimp != 255) {
+               log_warn("invalid RA with hop limit of %d from %s on %s",
+                   *hlimp, inet_ntop(AF_INET6, &icmp6ev.from.sin6_addr,
+                   ntopbuf, INET6_ADDRSTRLEN), if_indextoname(if_index,
+                   ifnamebuf));
+               return;
+       }
+
+       if ((size_t)len > sizeof(ra.packet)) {
+               log_warn("invalid RA with size %ld from %s on %s",
+                   len, inet_ntop(AF_INET6, &icmp6ev.from.sin6_addr,
+                   ntopbuf, INET6_ADDRSTRLEN), if_indextoname(if_index,
+                   ifnamebuf));
+               return;
+       }
+
+       ra.if_index = if_index;
+       memcpy(&ra.from,  &icmp6ev.from, sizeof(ra.from));
+       ra.len = len;
+       memcpy(ra.packet, icmp6ev.answer, len);
+
+       frontend_imsg_compose_engine(IMSG_RA, 0, 0, &ra, sizeof(ra));
+}
+
+void
+send_solicitation(uint32_t if_index)
+{
+       struct in6_pktinfo              *pi;
+       struct cmsghdr                  *cm;
+
+       log_debug("%s(%u)", __func__, if_index);
+
+       dst.sin6_scope_id = if_index;
+
+       cm = CMSG_FIRSTHDR(&sndmhdr);
+       pi = (struct in6_pktinfo *)CMSG_DATA(cm);
+       pi->ipi6_ifindex = if_index;
+
+       if (sendmsg(icmp6sock, &sndmhdr, 0) != sizeof(rs) +
+           sizeof(nd_opt_hdr) + sizeof(nd_opt_source_link_addr))
+               log_warn("sendmsg");
+}
diff --git a/sbin/slaacd/frontend.h b/sbin/slaacd/frontend.h
new file mode 100644 (file)
index 0000000..ced99d3
--- /dev/null
@@ -0,0 +1,26 @@
+/*     $OpenBSD: frontend.h,v 1.1 2017/06/03 10:00:29 florian Exp $    */
+
+/*
+ * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+TAILQ_HEAD(ctl_conns, ctl_conn)        ctl_conns;
+
+void            frontend(int, int, char *);
+void            frontend_dispatch_main(int, short, void *);
+void            frontend_dispatch_engine(int, short, void *);
+int             frontend_imsg_compose_main(int, pid_t, void *, uint16_t);
+int             frontend_imsg_compose_engine(int, uint32_t, pid_t, void *,
+                    uint16_t);
diff --git a/sbin/slaacd/log.c b/sbin/slaacd/log.c
new file mode 100644 (file)
index 0000000..fa77182
--- /dev/null
@@ -0,0 +1,199 @@
+/*     $OpenBSD: log.c,v 1.1 2017/06/03 10:00:29 florian Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <syslog.h>
+#include <errno.h>
+#include <time.h>
+
+#include "log.h"
+
+static int              debug;
+static int              verbose;
+static const char      *log_procname;
+
+void
+log_init(int n_debug, int facility)
+{
+       extern char     *__progname;
+
+       debug = n_debug;
+       verbose = n_debug;
+       log_procinit(__progname);
+
+       if (!debug)
+               openlog(__progname, LOG_PID | LOG_NDELAY, facility);
+
+       tzset();
+}
+
+void
+log_procinit(const char *procname)
+{
+       if (procname != NULL)
+               log_procname = procname;
+}
+
+void
+log_setverbose(int v)
+{
+       verbose = v;
+}
+
+int
+log_getverbose(void)
+{
+       return (verbose);
+}
+
+void
+logit(int pri, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       vlog(pri, fmt, ap);
+       va_end(ap);
+}
+
+void
+vlog(int pri, const char *fmt, va_list ap)
+{
+       char    *nfmt;
+       int      saved_errno = errno;
+
+       if (debug) {
+               /* best effort in out of mem situations */
+               if (asprintf(&nfmt, "%s\n", fmt) == -1) {
+                       vfprintf(stderr, fmt, ap);
+                       fprintf(stderr, "\n");
+               } else {
+                       vfprintf(stderr, nfmt, ap);
+                       free(nfmt);
+               }
+               fflush(stderr);
+       } else
+               vsyslog(pri, fmt, ap);
+
+       errno = saved_errno;
+}
+
+void
+log_warn(const char *emsg, ...)
+{
+       char            *nfmt;
+       va_list          ap;
+       int              saved_errno = errno;
+
+       /* best effort to even work in out of memory situations */
+       if (emsg == NULL)
+               logit(LOG_ERR, "%s", strerror(saved_errno));
+       else {
+               va_start(ap, emsg);
+
+               if (asprintf(&nfmt, "%s: %s", emsg,
+                   strerror(saved_errno)) == -1) {
+                       /* we tried it... */
+                       vlog(LOG_ERR, emsg, ap);
+                       logit(LOG_ERR, "%s", strerror(saved_errno));
+               } else {
+                       vlog(LOG_ERR, nfmt, ap);
+                       free(nfmt);
+               }
+               va_end(ap);
+       }
+
+       errno = saved_errno;
+}
+
+void
+log_warnx(const char *emsg, ...)
+{
+       va_list  ap;
+
+       va_start(ap, emsg);
+       vlog(LOG_ERR, emsg, ap);
+       va_end(ap);
+}
+
+void
+log_info(const char *emsg, ...)
+{
+       va_list  ap;
+
+       va_start(ap, emsg);
+       vlog(LOG_INFO, emsg, ap);
+       va_end(ap);
+}
+
+void
+log_debug(const char *emsg, ...)
+{
+       va_list  ap;
+
+       if (verbose) {
+               va_start(ap, emsg);
+               vlog(LOG_DEBUG, emsg, ap);
+               va_end(ap);
+       }
+}
+
+static void
+vfatalc(int code, const char *emsg, va_list ap)
+{
+       static char     s[BUFSIZ];
+       const char      *sep;
+
+       if (emsg != NULL) {
+               (void)vsnprintf(s, sizeof(s), emsg, ap);
+               sep = ": ";
+       } else {
+               s[0] = '\0';
+               sep = "";
+       }
+       if (code)
+               logit(LOG_CRIT, "fatal in %s: %s%s%s",
+                   log_procname, s, sep, strerror(code));
+       else
+               logit(LOG_CRIT, "fatal in %s%s%s", log_procname, sep, s);
+}
+
+void
+fatal(const char *emsg, ...)
+{
+       va_list ap;
+
+       va_start(ap, emsg);
+       vfatalc(errno, emsg, ap);
+       va_end(ap);
+       exit(1);
+}
+
+void
+fatalx(const char *emsg, ...)
+{
+       va_list ap;
+
+       va_start(ap, emsg);
+       vfatalc(0, emsg, ap);
+       va_end(ap);
+       exit(1);
+}
diff --git a/sbin/slaacd/log.h b/sbin/slaacd/log.h
new file mode 100644 (file)
index 0000000..8653cff
--- /dev/null
@@ -0,0 +1,46 @@
+/*     $OpenBSD: log.h,v 1.1 2017/06/03 10:00:29 florian Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef LOG_H
+#define LOG_H
+
+#include <stdarg.h>
+#include <sys/cdefs.h>
+
+void   log_init(int, int);
+void   log_procinit(const char *);
+void   log_setverbose(int);
+int    log_getverbose(void);
+void   log_warn(const char *, ...)
+           __attribute__((__format__ (printf, 1, 2)));
+void   log_warnx(const char *, ...)
+           __attribute__((__format__ (printf, 1, 2)));
+void   log_info(const char *, ...)
+           __attribute__((__format__ (printf, 1, 2)));
+void   log_debug(const char *, ...)
+           __attribute__((__format__ (printf, 1, 2)));
+void   logit(int, const char *, ...)
+           __attribute__((__format__ (printf, 2, 3)));
+void   vlog(int, const char *, va_list)
+           __attribute__((__format__ (printf, 2, 0)));
+__dead void fatal(const char *, ...)
+           __attribute__((__format__ (printf, 1, 2)));
+__dead void fatalx(const char *, ...)
+           __attribute__((__format__ (printf, 1, 2)));
+
+#endif /* LOG_H */
diff --git a/sbin/slaacd/slaacd.8 b/sbin/slaacd/slaacd.8
new file mode 100644 (file)
index 0000000..b753403
--- /dev/null
@@ -0,0 +1,116 @@
+.\"    $OpenBSD: slaacd.8,v 1.1 2017/06/03 10:00:29 florian Exp $
+.\"
+.\" Copyright (c) 2017 Florian Obser <florian@openbsd.org>
+.\" Copyright (c) 2016 Kenneth R Westerback <kwesterback@gmail.com>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: June 3 2017 $
+.Dt SLAACD 8
+.Os
+.Sh NAME
+.Nm slaacd
+.Nd a stateless address autoconfiguration daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl dv
+.Op Fl s Ar socket
+.Sh DESCRIPTION
+.Nm
+is a stateless address autoconfiguration (SLAAC) daemon.
+It listens for IPv6 router advertisement messages on interfaces with the
+.Sy AUTOCONF6
+flag set.
+.Nm
+derives IPv6 addresses and default routes from received router
+advertisements and installs them in the kernel.
+See
+.Xr hostname.if 5
+and
+.Xr ifconfig 8
+on how to enable auto configuration on an interface.
+.Pp
+.Nm
+monitors network interface states (interface going up or down,
+auto configuration enabled or disabled etc.) and sends router solicitations
+when necessary.
+.Pp
+A running
+.Nm
+can be controlled with the
+.Xr slaacctl 8
+utility.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Do not daemonize.
+If this option is specified,
+.Nm
+will run in the foreground and log to
+.Em stderr .
+.It Fl s Ar socket
+Use an alternate location for the default control socket.
+.It Fl v
+Produce more verbose output.
+.El
+.Sh FILES
+.Bl -tag -width "/var/run/slaacd.sockXX" -compact
+.It Pa /var/run/slaacd.sock
+.Ux Ns -domain
+socket used for communication with
+.Xr slaacctl 8 .
+.El
+.Sh SEE ALSO
+.Xr hostname.if 5 ,
+.Xr ifconfig 8 ,
+.Xr slaacctl 8
+.Sh STANDARDS
+.Rs
+.%A T. Narten
+.%A E. Nordmark
+.%A W. Simpson
+.%A H. Soliman
+.%D September 2007
+.%R RFC 4861
+.%T Neighbor Discovery for IP version 6 (IPv6)
+.Re
+.Pp
+.Rs
+.%A J. Jeong
+.%A S. Park
+.%A L. Beloeil
+.%A S. Madanapalli
+.%D November 2010
+.%R RFC 6106
+.%T IPv6 Router Advertisement Options for DNS Configuration
+.Re
+.Pp
+.Rs
+.%A R. Draves
+.%A D. Thaler
+.%D November 2005
+.%R RFC 4191
+.%T Default Router Preferences and More-Specific Routes
+.Re
+.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Ox 6.2 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+program was written by
+.An Florian Obser Aq Mt florian@openbsd.org .
diff --git a/sbin/slaacd/slaacd.c b/sbin/slaacd/slaacd.c
new file mode 100644 (file)
index 0000000..04ae074
--- /dev/null
@@ -0,0 +1,768 @@
+/*     $OpenBSD: slaacd.c,v 1.1 2017/06/03 10:00:29 florian Exp $      */
+
+/*
+ * Copyright (c) 2017 Florian Obser <florian@openbsd.org>
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet6/in6_var.h>
+
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <imsg.h>
+#include <pwd.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "slaacd.h"
+#include "frontend.h"
+#include "engine.h"
+#include "control.h"
+
+const char* imsg_type_name[] = {
+       "IMSG_NONE",
+       "IMSG_CTL_LOG_VERBOSE",
+       "IMSG_CTL_SHOW_INTERFACE_INFO",
+       "IMSG_CTL_SHOW_INTERFACE_INFO_RA",
+       "IMSG_CTL_SHOW_INTERFACE_INFO_RA_PREFIX",
+       "IMSG_CTL_SHOW_INTERFACE_INFO_RA_RDNS",
+       "IMSG_CTL_SHOW_INTERFACE_INFO_RA_DNSSL",
+       "IMSG_CTL_SHOW_FRONTEND_INFO",
+       "IMSG_CTL_SHOW_MAIN_INFO",
+       "IMSG_CTL_END",
+       "IMSG_SOCKET_IPC",
+       "IMSG_STARTUP",
+       "IMSG_UPDATE_IF",
+       "IMSG_REMOVE_IF",
+       "IMSG_RA",
+       "IMSG_CTL_SEND_SOLICITATION",
+       "IMSG_PROPOSAL",
+       "IMSG_PROPOSAL_ACK",
+       "IMSG_CONFIGURE_ADDRESS",
+       "IMSG_DEL_ADDRESS",
+       "IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSAL",
+       "IMSG_FAKE_ACK",
+       "IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSALS",
+       "IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSAL",
+       "IMSG_CONFIGURE_DFR",
+       "IMSG_WITHDRAW_DFR",
+};
+
+__dead void    usage(void);
+__dead void    main_shutdown(void);
+
+void   main_sig_handler(int, short, void *);
+
+static pid_t   start_child(int, char *, int, int, int, char *);
+
+void   main_dispatch_frontend(int, short, void *);
+void   main_dispatch_engine(int, short, void *);
+void   handle_proposal(struct imsg_proposal *);
+void   configure_interface(struct imsg_configure_address *);
+void   configure_gateway(struct imsg_configure_dfr *, uint8_t);
+void   add_gateway(struct imsg_configure_dfr *);
+void   delete_gateway(struct imsg_configure_dfr *);
+
+static int     main_imsg_send_ipc_sockets(struct imsgbuf *, struct imsgbuf *);
+
+struct imsgev          *iev_frontend;
+struct imsgev          *iev_engine;
+
+pid_t   frontend_pid;
+pid_t   engine_pid;
+
+uint32_t cmd_opts;
+
+int     routesock, ioctl_sock;
+
+char   *csock;
+
+int     rtm_seq = 0;
+
+void
+main_sig_handler(int sig, short event, void *arg)
+{
+       /*
+        * Normal signal handler rules don't apply because libevent
+        * decouples for us.
+        */
+
+       switch (sig) {
+       case SIGTERM:
+       case SIGINT:
+               main_shutdown();
+       case SIGHUP:
+               log_debug("sighub received");
+               break;
+       default:
+               fatalx("unexpected signal");
+       }
+}
+
+__dead void
+usage(void)
+{
+       extern char *__progname;
+
+       fprintf(stderr, "usage: %s [-dv] [-s socket]\n",
+           __progname);
+       exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+       struct event     ev_sigint, ev_sigterm, ev_sighup;
+       int              ch;
+       int              debug = 0, engine_flag = 0, frontend_flag = 0;
+       char            *saved_argv0;
+       int              pipe_main2frontend[2];
+       int              pipe_main2engine[2];
+
+       csock = SLAACD_SOCKET;
+
+       log_init(1, LOG_DAEMON);        /* Log to stderr until daemonized. */
+       log_setverbose(1);
+
+       saved_argv0 = argv[0];
+       if (saved_argv0 == NULL)
+               saved_argv0 = "slaacd";
+
+       while ((ch = getopt(argc, argv, "dEFs:v")) != -1) {
+               switch (ch) {
+               case 'd':
+                       debug = 1;
+                       break;
+               case 'E':
+                       engine_flag = 1;
+                       break;
+               case 'F':
+                       frontend_flag = 1;
+                       break;
+               case 's':
+                       csock = optarg;
+                       break;
+               case 'v':
+                       if (cmd_opts & OPT_VERBOSE)
+                               cmd_opts |= OPT_VERBOSE2;
+                       cmd_opts |= OPT_VERBOSE;
+                       break;
+               default:
+                       usage();
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       if (argc > 0 || (engine_flag && frontend_flag))
+               usage();
+
+       if (engine_flag)
+               engine(debug, cmd_opts & OPT_VERBOSE);
+       else if (frontend_flag)
+               frontend(debug, cmd_opts & OPT_VERBOSE, csock);
+
+       /* Check for root privileges. */
+       if (geteuid())
+               errx(1, "need root privileges");
+
+       /* Check for assigned daemon user */
+       if (getpwnam(SLAACD_USER) == NULL)
+               errx(1, "unknown user %s", SLAACD_USER);
+
+       log_init(debug, LOG_DAEMON);
+       log_setverbose(cmd_opts & OPT_VERBOSE);
+
+       if (!debug)
+               daemon(1, 0);
+
+       log_info("startup");
+
+       if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+           PF_UNSPEC, pipe_main2frontend) == -1)
+               fatal("main2frontend socketpair");
+       if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+           PF_UNSPEC, pipe_main2engine) == -1)
+               fatal("main2engine socketpair");
+
+       /* Start children. */
+       engine_pid = start_child(PROC_ENGINE, saved_argv0, pipe_main2engine[1],
+           debug, cmd_opts & OPT_VERBOSE, NULL);
+       frontend_pid = start_child(PROC_FRONTEND, saved_argv0,
+           pipe_main2frontend[1], debug, cmd_opts & OPT_VERBOSE, csock);
+
+       slaacd_process = PROC_MAIN;
+
+       log_procinit(log_procnames[slaacd_process]);
+
+       if ((routesock = socket(PF_ROUTE, SOCK_RAW | SOCK_CLOEXEC |
+           SOCK_NONBLOCK, 0)) < 0)
+               fatal("route socket");
+
+       event_init();
+
+       /* Setup signal handler. */
+       signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL);
+       signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL);
+       signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL);
+       signal_add(&ev_sigint, NULL);
+       signal_add(&ev_sigterm, NULL);
+       signal_add(&ev_sighup, NULL);
+       signal(SIGPIPE, SIG_IGN);
+
+       /* Setup pipes to children. */
+
+       if ((iev_frontend = malloc(sizeof(struct imsgev))) == NULL ||
+           (iev_engine = malloc(sizeof(struct imsgev))) == NULL)
+               fatal(NULL);
+       imsg_init(&iev_frontend->ibuf, pipe_main2frontend[0]);
+       iev_frontend->handler = main_dispatch_frontend;
+       imsg_init(&iev_engine->ibuf, pipe_main2engine[0]);
+       iev_engine->handler = main_dispatch_engine;
+
+       /* Setup event handlers for pipes to engine & frontend. */
+       iev_frontend->events = EV_READ;
+       event_set(&iev_frontend->ev, iev_frontend->ibuf.fd,
+           iev_frontend->events, iev_frontend->handler, iev_frontend);
+       event_add(&iev_frontend->ev, NULL);
+
+       iev_engine->events = EV_READ;
+       event_set(&iev_engine->ev, iev_engine->ibuf.fd, iev_engine->events,
+           iev_engine->handler, iev_engine);
+       event_add(&iev_engine->ev, NULL);
+
+       if (main_imsg_send_ipc_sockets(&iev_frontend->ibuf, &iev_engine->ibuf))
+               fatal("could not establish imsg links");
+
+       if ((ioctl_sock = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0)
+               fatal("socket");
+
+#if 0
+       /* XXX ioctl SIOCAIFADDR_IN6 */
+       if (pledge("rpath stdio sendfd cpath", NULL) == -1)
+               fatal("pledge");
+#endif
+
+       main_imsg_compose_frontend(IMSG_STARTUP, 0, NULL, 0);
+
+       event_dispatch();
+
+       main_shutdown();
+       return (0);
+}
+
+__dead void
+main_shutdown(void)
+{
+       pid_t    pid;
+       int      status;
+
+       /* Close pipes. */
+       msgbuf_clear(&iev_frontend->ibuf.w);
+       close(iev_frontend->ibuf.fd);
+       msgbuf_clear(&iev_engine->ibuf.w);
+       close(iev_engine->ibuf.fd);
+
+       log_debug("waiting for children to terminate");
+       do {
+               pid = wait(&status);
+               if (pid == -1) {
+                       if (errno != EINTR && errno != ECHILD)
+                               fatal("wait");
+               } else if (WIFSIGNALED(status))
+                       log_warnx("%s terminated; signal %d",
+                           (pid == engine_pid) ? "engine" :
+                           "frontend", WTERMSIG(status));
+       } while (pid != -1 || (pid == -1 && errno == EINTR));
+
+       free(iev_frontend);
+       free(iev_engine);
+
+       control_cleanup(csock);
+
+       log_info("terminating");
+       exit(0);
+}
+
+static pid_t
+start_child(int p, char *argv0, int fd, int debug, int verbose, char *sockname)
+{
+       char    *argv[7];
+       int      argc = 0;
+       pid_t    pid;
+
+       switch (pid = fork()) {
+       case -1:
+               fatal("cannot fork");
+       case 0:
+               break;
+       default:
+               close(fd);
+               return (pid);
+       }
+
+       if (dup2(fd, 3) == -1)
+               fatal("cannot setup imsg fd");
+
+       argv[argc++] = argv0;
+       switch (p) {
+       case PROC_MAIN:
+               fatalx("Can not start main process");
+       case PROC_ENGINE:
+               argv[argc++] = "-E";
+               break;
+       case PROC_FRONTEND:
+               argv[argc++] = "-F";
+               break;
+       }
+       if (debug)
+               argv[argc++] = "-d";
+       if (verbose)
+               argv[argc++] = "-v";
+       if (sockname) {
+               argv[argc++] = "-s";
+               argv[argc++] = sockname;
+       }
+       argv[argc++] = NULL;
+
+       execvp(argv0, argv);
+       fatal("execvp");
+}
+
+void
+main_dispatch_frontend(int fd, short event, void *bula)
+{
+       struct imsgev           *iev = bula;
+       struct imsgbuf          *ibuf;
+       struct imsg              imsg;
+       ssize_t                  n;
+       int                      shut = 0, verbose;
+
+       ibuf = &iev->ibuf;
+
+       if (event & EV_READ) {
+               if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+                       fatal("imsg_read error");
+               if (n == 0)     /* Connection closed. */
+                       shut = 1;
+       }
+       if (event & EV_WRITE) {
+               if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+                       fatal("msgbuf_write");
+               if (n == 0)     /* Connection closed. */
+                       shut = 1;
+       }
+
+       for (;;) {
+               if ((n = imsg_get(ibuf, &imsg)) == -1)
+                       fatal("imsg_get");
+               if (n == 0)     /* No more messages. */
+                       break;
+
+               switch (imsg.hdr.type) {
+               case IMSG_CTL_LOG_VERBOSE:
+                       /* Already checked by frontend. */
+                       memcpy(&verbose, imsg.data, sizeof(verbose));
+                       log_setverbose(verbose);
+                       break;
+               default:
+                       log_debug("%s: error handling imsg %d", __func__,
+                           imsg.hdr.type);
+                       break;
+               }
+               imsg_free(&imsg);
+       }
+       if (!shut)
+               imsg_event_add(iev);
+       else {
+               /* This pipe is dead. Remove its event handler */
+               event_del(&iev->ev);
+               event_loopexit(NULL);
+       }
+}
+
+void
+main_dispatch_engine(int fd, short event, void *bula)
+{
+       struct imsgev                   *iev = bula;
+       struct imsgbuf                  *ibuf;
+       struct imsg                      imsg;
+       struct imsg_proposal             proposal;
+       struct imsg_configure_address    address;
+       struct imsg_configure_dfr        dfr;
+       ssize_t                          n;
+       int                              shut = 0;
+
+       ibuf = &iev->ibuf;
+
+       if (event & EV_READ) {
+               if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
+                       fatal("imsg_read error");
+               if (n == 0)     /* Connection closed. */
+                       shut = 1;
+       }
+       if (event & EV_WRITE) {
+               if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+                       fatal("msgbuf_write");
+               if (n == 0)     /* Connection closed. */
+                       shut = 1;
+       }
+
+       for (;;) {
+               if ((n = imsg_get(ibuf, &imsg)) == -1)
+                       fatal("imsg_get");
+               if (n == 0)     /* No more messages. */
+                       break;
+
+               switch (imsg.hdr.type) {
+               case IMSG_PROPOSAL:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(proposal))
+                               fatal("%s: IMSG_PROPOSAL wrong "
+                                   "length: %d", __func__, imsg.hdr.len);
+                       memcpy(&proposal, imsg.data, sizeof(proposal));
+                       handle_proposal(&proposal);
+                       break;
+               case IMSG_CONFIGURE_ADDRESS:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(address))
+                               fatal("%s: IMSG_CONFIGURE_ADDRESS wrong "
+                                   "length: %d", __func__, imsg.hdr.len);
+                       memcpy(&address, imsg.data, sizeof(address));
+                       configure_interface(&address);
+                       break;
+               case IMSG_CONFIGURE_DFR:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(dfr))
+                               fatal("%s: IMSG_CONFIGURE_DFR wrong "
+                                   "length: %d", __func__, imsg.hdr.len);
+                       memcpy(&dfr, imsg.data, sizeof(dfr));
+                       add_gateway(&dfr);
+                       break;
+               case IMSG_WITHDRAW_DFR:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(dfr))
+                               fatal("%s: IMSG_CONFIGURE_DFR wrong "
+                                   "length: %d", __func__, imsg.hdr.len);
+                       memcpy(&dfr, imsg.data, sizeof(dfr));
+                       delete_gateway(&dfr);
+                       break;
+               default:
+                       log_debug("%s: error handling imsg %d", __func__,
+                           imsg.hdr.type);
+                       break;
+               }
+               imsg_free(&imsg);
+       }
+       if (!shut)
+               imsg_event_add(iev);
+       else {
+               /* This pipe is dead. Remove its event handler. */
+               event_del(&iev->ev);
+               event_loopexit(NULL);
+       }
+}
+
+int
+main_imsg_compose_frontend(int type, pid_t pid, void *data, uint16_t datalen)
+{
+       if (iev_frontend)
+               return (imsg_compose_event(iev_frontend, type, 0, pid, -1, data,
+                   datalen));
+       else
+               return (-1);
+}
+
+int
+main_imsg_compose_engine(int type, pid_t pid, void *data, uint16_t datalen)
+{
+       if (iev_engine)
+               return(imsg_compose_event(iev_engine, type, 0, pid, -1, data,
+                   datalen));
+       else
+               return (-1);
+}
+
+void
+imsg_event_add(struct imsgev *iev)
+{
+       iev->events = EV_READ;
+       if (iev->ibuf.w.queued)
+               iev->events |= EV_WRITE;
+
+       event_del(&iev->ev);
+       event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev);
+       event_add(&iev->ev, NULL);
+}
+
+int
+imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
+    pid_t pid, int fd, void *data, uint16_t datalen)
+{
+       int     ret;
+
+       if ((ret = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data,
+           datalen)) != -1)
+               imsg_event_add(iev);
+
+       return (ret);
+}
+
+static int
+main_imsg_send_ipc_sockets(struct imsgbuf *frontend_buf,
+    struct imsgbuf *engine_buf)
+{
+       int pipe_frontend2engine[2];
+
+       if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+           PF_UNSPEC, pipe_frontend2engine) == -1)
+               return (-1);
+
+       if (imsg_compose(frontend_buf, IMSG_SOCKET_IPC, 0, 0,
+           pipe_frontend2engine[0], NULL, 0) == -1)
+               return (-1);
+       imsg_flush(frontend_buf);
+       if (imsg_compose(engine_buf, IMSG_SOCKET_IPC, 0, 0,
+           pipe_frontend2engine[1], NULL, 0) == -1)
+               return (-1);
+       imsg_flush(engine_buf);
+       return (0);
+}
+
+#define        ROUNDUP(a)      \
+    (((a) & (sizeof(long) - 1)) ? (1 + ((a) | (sizeof(long) - 1))) : (a))
+
+void
+handle_proposal(struct imsg_proposal *proposal)
+{
+       struct rt_msghdr                 rtm;
+       struct sockaddr_in6              ifa, mask;
+       struct sockaddr_rtlabel          rl;
+       struct iovec                     iov[13];
+       long                             pad = 0;
+       int                              iovcnt = 0, padlen;
+
+       memset(&rtm, 0, sizeof(rtm));
+
+       rtm.rtm_version = RTM_VERSION;
+       rtm.rtm_type = RTM_PROPOSAL;
+       rtm.rtm_msglen = sizeof(rtm);
+       rtm.rtm_tableid = 0; /* XXX imsg->rdomain; */
+       rtm.rtm_index = proposal->if_index;
+       rtm.rtm_seq = ++rtm_seq;
+       rtm.rtm_priority = RTP_PROPOSAL_SLAAC;
+       rtm.rtm_addrs = (proposal->rtm_addrs & (RTA_NETMASK | RTA_IFA)) |
+           RTA_LABEL;
+       rtm.rtm_flags = RTF_UP;
+
+       iov[iovcnt].iov_base = &rtm;
+       iov[iovcnt++].iov_len = sizeof(rtm);
+
+       if (rtm.rtm_addrs & RTA_NETMASK) {
+               memset(&mask, 0, sizeof(mask));
+               mask.sin6_family = AF_INET6;
+               mask.sin6_len = sizeof(struct sockaddr_in6);
+               mask.sin6_addr = proposal->mask;
+
+               iov[iovcnt].iov_base = &mask;
+               iov[iovcnt++].iov_len = sizeof(mask);
+               rtm.rtm_msglen += sizeof(mask);
+               padlen = ROUNDUP(sizeof(mask)) - sizeof(mask);
+               if (padlen > 0) {
+                       iov[iovcnt].iov_base = &pad;
+                       iov[iovcnt++].iov_len = padlen;
+                       rtm.rtm_msglen += padlen;
+               }
+       }
+
+       if (rtm.rtm_addrs & RTA_IFA) {
+               memcpy(&ifa, &proposal->addr, sizeof(ifa));
+
+               if (ifa.sin6_family != AF_INET6 || ifa.sin6_len !=
+                   sizeof(struct sockaddr_in6)) {
+                       log_warnx("%s: invalid address", __func__);
+                       return;
+               }
+
+               iov[iovcnt].iov_base = &ifa;
+               iov[iovcnt++].iov_len = sizeof(ifa);
+               rtm.rtm_msglen += sizeof(ifa);
+               padlen = ROUNDUP(sizeof(ifa)) - sizeof(ifa);
+               if (padlen > 0) {
+                       iov[iovcnt].iov_base = &pad;
+                       iov[iovcnt++].iov_len = padlen;
+                       rtm.rtm_msglen += padlen;
+               }
+       }
+
+       rl.sr_len = sizeof(rl);
+       rl.sr_family = AF_UNSPEC;
+       if (snprintf(rl.sr_label, sizeof(rl.sr_label), "%s: %lld %d", "slaacd",
+           proposal->id, (int32_t)proposal->pid) >=
+           (ssize_t)sizeof(rl.sr_label))
+               log_warnx("route label truncated");
+
+       iov[iovcnt].iov_base = &rl;
+       iov[iovcnt++].iov_len = sizeof(rl);
+       rtm.rtm_msglen += sizeof(rl);
+       padlen = ROUNDUP(sizeof(rl)) - sizeof(rl);
+       if (padlen > 0) {
+               iov[iovcnt].iov_base = &pad;
+               iov[iovcnt++].iov_len = padlen;
+               rtm.rtm_msglen += padlen;
+       }
+
+       if (writev(routesock, iov, iovcnt) == -1)
+               log_warn("failed to send proposal");
+}
+
+void
+configure_interface(struct imsg_configure_address *address)
+{
+
+       struct in6_aliasreq      in6_addreq;
+       time_t                   t;
+       char                    *if_name;
+
+       memset(&in6_addreq, 0, sizeof(in6_addreq));
+
+       if_name = if_indextoname(address->if_index, in6_addreq.ifra_name);
+       if (if_name == NULL) {
+               log_warn("%s: cannot find interface %d", __func__,
+                   address->if_index);
+               return;
+       }
+
+       memcpy(&in6_addreq.ifra_addr, &address->addr,
+           sizeof(in6_addreq.ifra_addr));
+       memcpy(&in6_addreq.ifra_prefixmask.sin6_addr, &address->mask,
+           sizeof(in6_addreq.ifra_prefixmask.sin6_addr));
+       in6_addreq.ifra_prefixmask.sin6_family = AF_INET6;
+       in6_addreq.ifra_prefixmask.sin6_len =
+           sizeof(in6_addreq.ifra_prefixmask);
+
+       t = time(NULL);
+
+       in6_addreq.ifra_lifetime.ia6t_expire = t + address->vltime;
+       in6_addreq.ifra_lifetime.ia6t_vltime = address->vltime;
+
+       in6_addreq.ifra_lifetime.ia6t_preferred = t + address->pltime;
+       in6_addreq.ifra_lifetime.ia6t_pltime = address->pltime;
+
+       in6_addreq.ifra_flags |= IN6_IFF_AUTOCONF;
+
+       if (address->privacy)
+               in6_addreq.ifra_flags |= IN6_IFF_PRIVACY;
+
+       log_debug("%s: %s", __func__, if_name);
+
+       if (ioctl(ioctl_sock, SIOCAIFADDR_IN6, &in6_addreq) < 0)
+               fatal("SIOCAIFADDR_IN6");
+}
+
+void
+configure_gateway(struct imsg_configure_dfr *dfr, uint8_t rtm_type)
+{
+       struct rt_msghdr                 rtm;
+       struct sockaddr_in6              dst, gw, mask;
+       struct iovec                     iov[8];
+       long                             pad = 0;
+       int                              iovcnt = 0, padlen;
+
+       memset(&rtm, 0, sizeof(rtm));
+
+       rtm.rtm_version = RTM_VERSION;
+       rtm.rtm_type = rtm_type;
+       rtm.rtm_msglen = sizeof(rtm);
+       rtm.rtm_tableid = 0; /* XXX imsg->rdomain; */
+       rtm.rtm_index = dfr->if_index;
+       rtm.rtm_seq = ++rtm_seq;
+       rtm.rtm_priority = RTP_DEFAULT;
+       rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+       rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
+
+       iov[iovcnt].iov_base = &rtm;
+       iov[iovcnt++].iov_len = sizeof(rtm);
+
+       memset(&dst, 0, sizeof(mask));
+       dst.sin6_family = AF_INET6;
+       dst.sin6_len = sizeof(struct sockaddr_in6);
+
+       iov[iovcnt].iov_base = &dst;
+       iov[iovcnt++].iov_len = sizeof(dst);
+       rtm.rtm_msglen += sizeof(dst);
+       padlen = ROUNDUP(sizeof(dst)) - sizeof(dst);
+       if (padlen > 0) {
+               iov[iovcnt].iov_base = &pad;
+               iov[iovcnt++].iov_len = padlen;
+               rtm.rtm_msglen += padlen;
+       }
+
+       memcpy(&gw, &dfr->addr, sizeof(gw));
+       *(u_int16_t *)& gw.sin6_addr.s6_addr[2] = htons(gw.sin6_scope_id);
+       /* gw.sin6_scope_id = 0; XXX route(8) does this*/
+       iov[iovcnt].iov_base = &gw;
+       iov[iovcnt++].iov_len = sizeof(gw);
+       rtm.rtm_msglen += sizeof(gw);
+       padlen = ROUNDUP(sizeof(gw)) - sizeof(gw);
+       if (padlen > 0) {
+               iov[iovcnt].iov_base = &pad;
+               iov[iovcnt++].iov_len = padlen;
+               rtm.rtm_msglen += padlen;
+       }
+
+       memset(&mask, 0, sizeof(mask));
+       mask.sin6_family = AF_INET6;
+       mask.sin6_len = 0;//sizeof(struct sockaddr_in6);
+       iov[iovcnt].iov_base = &mask;
+       iov[iovcnt++].iov_len = sizeof(mask);
+       rtm.rtm_msglen += sizeof(mask);
+       padlen = ROUNDUP(sizeof(mask)) - sizeof(mask);
+       if (padlen > 0) {
+               iov[iovcnt].iov_base = &pad;
+               iov[iovcnt++].iov_len = padlen;
+               rtm.rtm_msglen += padlen;
+       }
+
+       if (writev(routesock, iov, iovcnt) == -1)
+               log_warn("failed to send route message");
+}
+
+void
+add_gateway(struct imsg_configure_dfr *dfr)
+{
+       configure_gateway(dfr, RTM_ADD);
+}
+
+void
+delete_gateway(struct imsg_configure_dfr *dfr)
+{
+       configure_gateway(dfr, RTM_DELETE);
+}
diff --git a/sbin/slaacd/slaacd.h b/sbin/slaacd/slaacd.h
new file mode 100644 (file)
index 0000000..632ed46
--- /dev/null
@@ -0,0 +1,188 @@
+/*     $OpenBSD: slaacd.h,v 1.1 2017/06/03 10:00:29 florian Exp $      */
+
+/*
+ * Copyright (c) 2017 Florian Obser <florian@openbsd.org>
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define        SLAACD_SOCKET           "/var/run/slaacd.sock"
+#define SLAACD_USER            "_slaacd"
+
+#define OPT_VERBOSE    0x00000001
+#define OPT_VERBOSE2   0x00000002
+
+#define SLAACD_MAXTEXT         256
+#define SLAACD_MAXGROUPNAME    16
+
+/* MAXDNAME from arpa/namesr.h */
+#define SLAACD_MAX_DNSSL       1025
+
+static const char * const log_procnames[] = {
+       "main",
+       "frontend",
+       "engine"
+};
+
+struct imsgev {
+       struct imsgbuf   ibuf;
+       void            (*handler)(int, short, void *);
+       struct event     ev;
+       short            events;
+};
+
+enum imsg_type {
+       IMSG_NONE,
+       IMSG_CTL_LOG_VERBOSE,
+       IMSG_CTL_SHOW_INTERFACE_INFO,
+       IMSG_CTL_SHOW_INTERFACE_INFO_RA,
+       IMSG_CTL_SHOW_INTERFACE_INFO_RA_PREFIX,
+       IMSG_CTL_SHOW_INTERFACE_INFO_RA_RDNS,
+       IMSG_CTL_SHOW_INTERFACE_INFO_RA_DNSSL,
+       IMSG_CTL_END,
+       IMSG_SOCKET_IPC,
+       IMSG_STARTUP,
+       IMSG_UPDATE_IF,
+       IMSG_REMOVE_IF,
+       IMSG_RA,
+       IMSG_CTL_SEND_SOLICITATION,
+       IMSG_PROPOSAL,
+       IMSG_PROPOSAL_ACK,
+       IMSG_CONFIGURE_ADDRESS,
+       IMSG_DEL_ADDRESS,
+       IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSALS,
+       IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSAL,
+       IMSG_FAKE_ACK,
+       IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSALS,
+       IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSAL,
+       IMSG_CONFIGURE_DFR,
+       IMSG_WITHDRAW_DFR,
+};
+
+extern const char* imsg_type_name[];
+
+enum {
+       PROC_MAIN,
+       PROC_ENGINE,
+       PROC_FRONTEND
+} slaacd_process;
+
+struct ctl_engine_info {
+       uint32_t                if_index;
+       int                     running;
+       int                     autoconfprivacy;
+       struct ether_addr       hw_address;
+       struct sockaddr_in6     ll_address;
+};
+
+enum rpref {
+       LOW,
+       MEDIUM,
+       HIGH,
+};
+
+struct ctl_engine_info_ra {
+       struct sockaddr_in6      from;
+       struct timespec          when;
+       struct timespec          uptime;
+       uint8_t                  curhoplimit;
+       int                      managed;
+       int                      other;
+       char                     rpref[sizeof("MEDIUM")];
+       uint16_t                 router_lifetime;       /* in seconds */
+       uint32_t                 reachable_time;        /* in milliseconds */
+       uint32_t                 retrans_time;          /* in milliseconds */
+};
+
+struct ctl_engine_info_ra_prefix {
+       struct in6_addr         prefix;
+       uint8_t                 prefix_len;
+       int                     onlink;
+       int                     autonomous;
+       uint32_t                vltime;
+       uint32_t                pltime;
+};
+
+struct ctl_engine_info_ra_rdns {
+       uint32_t                lifetime;
+       struct in6_addr         rdns;
+};
+
+struct ctl_engine_info_ra_dnssl {
+       uint32_t                lifetime;
+       char                    dnssl[SLAACD_MAX_DNSSL];
+};
+
+struct ctl_engine_info_address_proposal {
+       int64_t                  id;
+       char                     state[sizeof("PROPOSAL_NEARLY_EXPIRED")];
+       int                      next_timeout;
+       int                      timeout_count;
+       struct timespec          when;
+       struct timespec          uptime;
+       struct sockaddr_in6      addr;
+       struct in6_addr          prefix;
+       int                      privacy;
+       uint8_t                  prefix_len;
+       uint32_t                 vltime;
+       uint32_t                 pltime;
+};
+
+struct ctl_engine_info_dfr_proposal {
+       int64_t                  id;
+       char                     state[sizeof("PROPOSAL_NEARLY_EXPIRED")];
+       int                      next_timeout;
+       int                      timeout_count;
+       struct timespec          when;
+       struct timespec          uptime;
+       struct sockaddr_in6      addr;
+       uint32_t                 router_lifetime;
+       char                     rpref[sizeof("MEDIUM")];
+};
+
+struct imsg_ifinfo {
+       uint32_t                if_index;
+       int                     running;
+       int                     autoconfprivacy;
+       struct ether_addr       hw_address;
+       struct sockaddr_in6     ll_address;
+};
+
+struct imsg_del_addr {
+       uint32_t                if_index;
+       struct sockaddr_in6     addr;
+};
+
+struct imsg_proposal_ack {
+       int64_t          id;
+       pid_t            pid;
+       uint32_t         if_index;
+};
+
+struct imsg_ra {
+       uint32_t                if_index;
+       struct sockaddr_in6     from;
+       ssize_t                 len;
+       uint8_t                 packet[1500];
+};
+
+extern uint32_t         cmd_opts;
+
+/* slaacd.c */
+int    main_imsg_compose_frontend(int, pid_t, void *, uint16_t);
+int    main_imsg_compose_engine(int, pid_t, void *, uint16_t);
+void   imsg_event_add(struct imsgev *);
+int    imsg_compose_event(struct imsgev *, uint16_t, uint32_t, pid_t,
+           int, void *, uint16_t);
index be56b7b..c142fab 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: Makefile,v 1.200 2017/05/29 12:51:56 florian Exp $
+#      $OpenBSD: Makefile,v 1.201 2017/06/03 10:00:29 florian Exp $
 
 .include <bsd.own.mk>
 
@@ -15,7 +15,7 @@ SUBDIR=       ac accton acme-client acpidump adduser amd apm apmd arp \
        quot quotaon radiusctl radiusd rarpd rbootd \
        rcctl rdate rebound relayctl relayd repquota ripctl ripd \
        rmt route6d rpc.bootparamd rpc.lockd rpc.statd rtadvd \
-       sa sasyncd sensorsd slaacctl slaacd slowcgi smtpd \
+       sa sasyncd sensorsd slaacctl slowcgi smtpd \
        snmpctl snmpd spamdb switchctl switchd syslogc syslogd sysmerge \
        syspatch tcpdrop tcpdump tftp-proxy tftpd tokenadm tokeninit \
        traceroute trpt unbound usbdevs user vmd vmctl vipw watchdogd \
index 487819d..9bc1d6d 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: Makefile,v 1.1 2017/04/10 13:35:42 florian Exp $
+#      $OpenBSD: Makefile,v 1.2 2017/06/03 10:00:29 florian Exp $
 
 PROG=  slaacctl
 SRCS=  slaacctl.c parser.c
@@ -10,7 +10,7 @@ CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
 CFLAGS+= -Wmissing-declarations
 CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
 CFLAGS+= -Wsign-compare
-CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../slaacd
+CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../../sbin/slaacd
 LDADD= -lutil
 DPADD= ${LIBUTIL}
 
diff --git a/usr.sbin/slaacd/Makefile b/usr.sbin/slaacd/Makefile
deleted file mode 100644 (file)
index 665b14c..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#      $OpenBSD: Makefile,v 1.2 2017/05/27 16:16:49 florian Exp $
-
-PROG=  slaacd
-SRCS=  control.c engine.c frontend.c log.c slaacd.c
-
-MAN=   slaacd.8
-
-#DEBUG=        -g -DDEBUG=3 -O0
-
-CFLAGS+= -DSKIP_PROPOSAL
-
-CFLAGS+= -Wall -I${.CURDIR}
-CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
-CFLAGS+= -Wmissing-declarations
-CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
-CFLAGS+= -Wsign-compare
-YFLAGS=
-LDADD+=        -levent -lutil
-DPADD+= ${LIBEVENT} ${LIBUTIL}
-
-.include <bsd.prog.mk>
diff --git a/usr.sbin/slaacd/control.c b/usr.sbin/slaacd/control.c
deleted file mode 100644 (file)
index a1226d4..0000000
+++ /dev/null
@@ -1,301 +0,0 @@
-/*     $OpenBSD: control.c,v 1.1 2017/03/18 17:33:13 florian Exp $     */
-
-/*
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-#include <sys/un.h>
-
-#include <net/if.h>
-#include <netinet/in.h>
-#include <netinet/if_ether.h>
-
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <md5.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "log.h"
-#include "slaacd.h"
-#include "control.h"
-#include "frontend.h"
-
-#define        CONTROL_BACKLOG 5
-
-struct ctl_conn        *control_connbyfd(int);
-struct ctl_conn        *control_connbypid(pid_t);
-void            control_close(int);
-
-int
-control_init(char *path)
-{
-       struct sockaddr_un       sun;
-       int                      fd;
-       mode_t                   old_umask;
-
-       if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
-           0)) == -1) {
-               log_warn("%s: socket", __func__);
-               return (-1);
-       }
-
-       memset(&sun, 0, sizeof(sun));
-       sun.sun_family = AF_UNIX;
-       strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
-
-       if (unlink(path) == -1)
-               if (errno != ENOENT) {
-                       log_warn("%s: unlink %s", __func__, path);
-                       close(fd);
-                       return (-1);
-               }
-
-       old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
-       if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
-               log_warn("%s: bind: %s", __func__, path);
-               close(fd);
-               umask(old_umask);
-               return (-1);
-       }
-       umask(old_umask);
-
-       if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
-               log_warn("%s: chmod", __func__);
-               close(fd);
-               (void)unlink(path);
-               return (-1);
-       }
-
-       control_state.fd = fd;
-
-       return (0);
-}
-
-int
-control_listen(void)
-{
-
-       if (listen(control_state.fd, CONTROL_BACKLOG) == -1) {
-               log_warn("%s: listen", __func__);
-               return (-1);
-       }
-
-       event_set(&control_state.ev, control_state.fd, EV_READ,
-           control_accept, NULL);
-       event_add(&control_state.ev, NULL);
-       evtimer_set(&control_state.evt, control_accept, NULL);
-
-       return (0);
-}
-
-void
-control_cleanup(char *path)
-{
-       if (path == NULL)
-               return;
-       event_del(&control_state.ev);
-       event_del(&control_state.evt);
-       unlink(path);
-}
-
-void
-control_accept(int listenfd, short event, void *bula)
-{
-       int                      connfd;
-       socklen_t                len;
-       struct sockaddr_un       sun;
-       struct ctl_conn         *c;
-
-       event_add(&control_state.ev, NULL);
-       if ((event & EV_TIMEOUT))
-               return;
-
-       len = sizeof(sun);
-       if ((connfd = accept4(listenfd, (struct sockaddr *)&sun, &len,
-           SOCK_CLOEXEC | SOCK_NONBLOCK)) == -1) {
-               /*
-                * Pause accept if we are out of file descriptors, or
-                * libevent will haunt us here too.
-                */
-               if (errno == ENFILE || errno == EMFILE) {
-                       struct timeval evtpause = { 1, 0 };
-
-                       event_del(&control_state.ev);
-                       evtimer_add(&control_state.evt, &evtpause);
-               } else if (errno != EWOULDBLOCK && errno != EINTR &&
-                   errno != ECONNABORTED)
-                       log_warn("%s: accept4", __func__);
-               return;
-       }
-
-       if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
-               log_warn("%s: calloc", __func__);
-               close(connfd);
-               return;
-       }
-
-       imsg_init(&c->iev.ibuf, connfd);
-       c->iev.handler = control_dispatch_imsg;
-       c->iev.events = EV_READ;
-       event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
-           c->iev.handler, &c->iev);
-       event_add(&c->iev.ev, NULL);
-
-       TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
-}
-
-struct ctl_conn *
-control_connbyfd(int fd)
-{
-       struct ctl_conn *c;
-
-       TAILQ_FOREACH(c, &ctl_conns, entry) {
-               if (c->iev.ibuf.fd == fd)
-                       break;
-       }
-
-       return (c);
-}
-
-struct ctl_conn *
-control_connbypid(pid_t pid)
-{
-       struct ctl_conn *c;
-
-       TAILQ_FOREACH(c, &ctl_conns, entry) {
-               if (c->iev.ibuf.pid == pid)
-                       break;
-       }
-
-       return (c);
-}
-
-void
-control_close(int fd)
-{
-       struct ctl_conn *c;
-
-       if ((c = control_connbyfd(fd)) == NULL) {
-               log_warnx("%s: fd %d: not found", __func__, fd);
-               return;
-       }
-
-       msgbuf_clear(&c->iev.ibuf.w);
-       TAILQ_REMOVE(&ctl_conns, c, entry);
-
-       event_del(&c->iev.ev);
-       close(c->iev.ibuf.fd);
-
-       /* Some file descriptors are available again. */
-       if (evtimer_pending(&control_state.evt, NULL)) {
-               evtimer_del(&control_state.evt);
-               event_add(&control_state.ev, NULL);
-       }
-
-       free(c);
-}
-
-void
-control_dispatch_imsg(int fd, short event, void *bula)
-{
-       struct ctl_conn *c;
-       struct imsg      imsg;
-       ssize_t          n;
-       int              verbose;
-
-       if ((c = control_connbyfd(fd)) == NULL) {
-               log_warnx("%s: fd %d: not found", __func__, fd);
-               return;
-       }
-
-       if (event & EV_READ) {
-               if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) ||
-                   n == 0) {
-                       control_close(fd);
-                       return;
-               }
-       }
-       if (event & EV_WRITE) {
-               if (msgbuf_write(&c->iev.ibuf.w) <= 0 && errno != EAGAIN) {
-                       control_close(fd);
-                       return;
-               }
-       }
-
-       for (;;) {
-               if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
-                       control_close(fd);
-                       return;
-               }
-               if (n == 0)
-                       break;
-
-               switch (imsg.hdr.type) {
-               case IMSG_CTL_LOG_VERBOSE:
-                       if (imsg.hdr.len != IMSG_HEADER_SIZE +
-                           sizeof(verbose))
-                               break;
-
-                       /* Forward to all other processes. */
-                       frontend_imsg_compose_main(imsg.hdr.type, imsg.hdr.pid,
-                           imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
-                       frontend_imsg_compose_engine(imsg.hdr.type, 0,
-                           imsg.hdr.pid, imsg.data,
-                           imsg.hdr.len - IMSG_HEADER_SIZE);
-
-                       memcpy(&verbose, imsg.data, sizeof(verbose));
-                       log_setverbose(verbose);
-                       break;
-               case IMSG_CTL_SHOW_INTERFACE_INFO:
-                       c->iev.ibuf.pid = imsg.hdr.pid;
-                       frontend_imsg_compose_engine(imsg.hdr.type, 0,
-                           imsg.hdr.pid, imsg.data, imsg.hdr.len -
-                           IMSG_HEADER_SIZE);
-                       break;
-               case IMSG_CTL_SEND_SOLICITATION:
-                       c->iev.ibuf.pid = imsg.hdr.pid;
-                       frontend_imsg_compose_engine(imsg.hdr.type, 0,
-                           imsg.hdr.pid, imsg.data, imsg.hdr.len -
-                           IMSG_HEADER_SIZE);
-                       break;
-               default:
-                       log_debug("%s: error handling imsg %d", __func__,
-                           imsg.hdr.type);
-                       break;
-               }
-               imsg_free(&imsg);
-       }
-
-       imsg_event_add(&c->iev);
-}
-
-int
-control_imsg_relay(struct imsg *imsg)
-{
-       struct ctl_conn *c;
-
-       if ((c = control_connbypid(imsg->hdr.pid)) == NULL)
-               return (0);
-
-       return (imsg_compose_event(&c->iev, imsg->hdr.type, 0, imsg->hdr.pid,
-           -1, imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE));
-}
diff --git a/usr.sbin/slaacd/control.h b/usr.sbin/slaacd/control.h
deleted file mode 100644 (file)
index 69eb083..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*     $OpenBSD: control.h,v 1.1 2017/03/18 17:33:13 florian Exp $     */
-
-/*
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-struct {
-       struct event    ev;
-       struct event    evt;
-       int             fd;
-} control_state;
-
-struct ctl_conn {
-       TAILQ_ENTRY(ctl_conn)   entry;
-       struct imsgev           iev;
-};
-
-int    control_init(char *);
-int    control_listen(void);
-void   control_accept(int, short, void *);
-void   control_dispatch_imsg(int, short, void *);
-int    control_imsg_relay(struct imsg *);
-void   control_cleanup(char *);
diff --git a/usr.sbin/slaacd/engine.c b/usr.sbin/slaacd/engine.c
deleted file mode 100644 (file)
index 4debc8b..0000000
+++ /dev/null
@@ -1,2177 +0,0 @@
-/*     $OpenBSD: engine.c,v 1.38 2017/05/31 09:39:03 florian Exp $     */
-
-/*
- * Copyright (c) 2017 Florian Obser <florian@openbsd.org>
- * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org>
- * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-/*
- * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
- * 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.
- * 3. Neither the name of the project nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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 <sys/types.h>
-#include <sys/queue.h>
-#include <sys/socket.h>
-#include <sys/syslog.h>
-#include <sys/uio.h>
-
-#include <net/if.h>
-#include <net/route.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <netinet/if_ether.h>
-#include <netinet/ip6.h>
-#include <netinet6/ip6_var.h>
-#include <netinet6/nd6.h>
-#include <netinet/icmp6.h>
-
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <netdb.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "log.h"
-#include "slaacd.h"
-#include "engine.h"
-
-#define        MAX_RTR_SOLICITATION_DELAY      1
-#define        MAX_RTR_SOLICITATION_DELAY_USEC MAX_RTR_SOLICITATION_DELAY * 1000000
-#define        RTR_SOLICITATION_INTERVAL       4
-#define        MAX_RTR_SOLICITATIONS           3
-
-enum if_state {
-       IF_DOWN,
-       IF_DELAY,
-       IF_PROBE,
-       IF_IDLE,
-};
-
-const char* if_state_name[] = {
-       "IF_DOWN",
-       "IF_DELAY",
-       "IF_PROBE",
-       "IF_IDLE",
-};
-
-enum proposal_state {
-       PROPOSAL_NOT_CONFIGURED,
-       PROPOSAL_SENT,
-       PROPOSAL_CONFIGURED,
-       PROPOSAL_NEARLY_EXPIRED,
-       PROPOSAL_WITHDRAWN,
-};
-
-const char* proposal_state_name[] = {
-       "NOT_CONFIGURED",
-       "SENT",
-       "CONFIGURED",
-       "NEARLY_EXPIRED",
-       "WITHDRAWN",
-};
-
-const char* rpref_name[] = {
-       "Low",
-       "Medium",
-       "High",
-};
-
-struct radv_prefix {
-       LIST_ENTRY(radv_prefix) entries;
-       struct in6_addr         prefix;
-       uint8_t                 prefix_len; /*XXX int */
-       int                     onlink;
-       int                     autonomous;
-       uint32_t                vltime;
-       uint32_t                pltime;
-};
-
-struct radv_rdns {
-       LIST_ENTRY(radv_rdns)   entries;
-       struct in6_addr         rdns;
-};
-
-struct radv_dnssl {
-       LIST_ENTRY(radv_dnssl)  entries;
-       char                    dnssl[SLAACD_MAX_DNSSL];
-};
-
-struct radv {
-       LIST_ENTRY(radv)                 entries;
-       struct sockaddr_in6              from;
-       struct timespec                  when;
-       struct timespec                  uptime;
-       struct event                     timer;
-       uint32_t                         min_lifetime;
-       uint8_t                          curhoplimit;
-       int                              managed;
-       int                              other;
-       enum rpref                       rpref;
-       uint16_t                         router_lifetime; /* in seconds */
-       uint32_t                         reachable_time; /* in milliseconds */
-       uint32_t                         retrans_time; /* in milliseconds */
-       LIST_HEAD(, radv_prefix)         prefixes;
-       uint32_t                         rdns_lifetime;
-       LIST_HEAD(, radv_rdns)           rdns_servers;
-       uint32_t                         dnssl_lifetime;
-       LIST_HEAD(, radv_dnssl)          dnssls;
-};
-
-struct address_proposal {
-       LIST_ENTRY(address_proposal)     entries;
-       struct event                     timer;
-       int64_t                          id;
-       enum proposal_state              state;
-       int                              next_timeout;
-       int                              timeout_count;
-       struct timespec                  when;
-       struct timespec                  uptime;
-       uint32_t                         if_index;
-       struct ether_addr                hw_address;
-       struct sockaddr_in6              addr;
-       struct in6_addr                  mask;
-       struct in6_addr                  prefix;
-       int                              privacy;
-       uint8_t                          prefix_len;
-       uint32_t                         vltime;
-       uint32_t                         pltime;
-};
-
-struct dfr_proposal {
-       LIST_ENTRY(dfr_proposal)         entries;
-       struct event                     timer;
-       int64_t                          id;
-       enum proposal_state              state;
-       int                              next_timeout;
-       int                              timeout_count;
-       struct timespec                  when;
-       struct timespec                  uptime;
-       uint32_t                         if_index;
-       struct sockaddr_in6              addr;
-       uint32_t                         router_lifetime;
-       enum rpref                       rpref;
-};
-
-struct slaacd_iface {
-       LIST_ENTRY(slaacd_iface)         entries;
-       enum if_state                    state;
-       struct event                     timer;
-       int                              probes;
-       uint32_t                         if_index;
-       int                              running;
-       int                              autoconfprivacy;
-       struct ether_addr                hw_address;
-       struct sockaddr_in6              ll_address;
-       LIST_HEAD(, radv)                radvs;
-       LIST_HEAD(, address_proposal)    addr_proposals;
-       LIST_HEAD(, dfr_proposal)        dfr_proposals;
-};
-
-LIST_HEAD(, slaacd_iface) slaacd_interfaces;
-
-__dead void             engine_shutdown(void);
-void                    engine_sig_handler(int sig, short, void *);
-void                    engine_dispatch_frontend(int, short, void *);
-void                    engine_dispatch_main(int, short, void *);
-void                    send_interface_info(struct slaacd_iface *, pid_t);
-void                    engine_showinfo_ctl(struct imsg *, uint32_t);
-struct slaacd_iface    *get_slaacd_iface_by_id(uint32_t);
-void                    remove_slaacd_iface(uint32_t);
-void                    free_ra(struct radv *);
-void                    parse_ra(struct slaacd_iface *, struct imsg_ra *);
-void                    gen_addr(struct slaacd_iface *, struct radv_prefix *,
-                            struct address_proposal *, int);
-void                    gen_address_proposal(struct slaacd_iface *, struct
-                            radv *, struct radv_prefix *, int);
-void                    configure_address(struct address_proposal *);
-void                    in6_prefixlen2mask(struct in6_addr *, int len);
-void                    gen_dfr_proposal(struct slaacd_iface *, struct
-                            radv *);
-void                    configure_dfr(struct dfr_proposal *);
-void                    free_dfr_proposal(struct dfr_proposal *);
-void                    withdraw_dfr(struct dfr_proposal *);
-void                    debug_log_ra(struct imsg_ra *);
-char                   *parse_dnssl(char *, int);
-void                    update_iface_ra(struct slaacd_iface *, struct radv *);
-void                    send_proposal(struct imsg_proposal *);
-void                    start_probe(struct slaacd_iface *);
-void                    address_proposal_timeout(int, short, void *);
-void                    dfr_proposal_timeout(int, short, void *);
-void                    iface_timeout(int, short, void *);
-struct radv            *find_ra(struct slaacd_iface *, struct sockaddr_in6 *);
-struct address_proposal        *find_address_proposal_by_id(struct slaacd_iface *,
-                            int64_t);
-struct address_proposal        *find_address_proposal_by_addr(struct slaacd_iface *,
-                            struct sockaddr_in6 *);
-struct dfr_proposal    *find_dfr_proposal_by_id(struct slaacd_iface *,
-                            int64_t);
-void                    find_prefix(struct slaacd_iface *, struct
-                            address_proposal *, struct radv **, struct
-                            radv_prefix **);
-int                     engine_imsg_compose_main(int, pid_t, void *, uint16_t);
-uint32_t                real_lifetime(struct timespec *, uint32_t);
-
-struct imsgev          *iev_frontend;
-struct imsgev          *iev_main;
-int64_t                         proposal_id;
-
-void
-engine_sig_handler(int sig, short event, void *arg)
-{
-       /*
-        * Normal signal handler rules don't apply because libevent
-        * decouples for us.
-        */
-
-       switch (sig) {
-       case SIGINT:
-       case SIGTERM:
-               engine_shutdown();
-       default:
-               fatalx("unexpected signal");
-       }
-}
-
-void
-engine(int debug, int verbose)
-{
-       struct event             ev_sigint, ev_sigterm;
-       struct passwd           *pw;
-
-       log_init(debug, LOG_DAEMON);
-       log_setverbose(verbose);
-
-       if ((pw = getpwnam(SLAACD_USER)) == NULL)
-               fatal("getpwnam");
-
-       if (chroot(pw->pw_dir) == -1)
-               fatal("chroot");
-       if (chdir("/") == -1)
-               fatal("chdir(\"/\")");
-
-       slaacd_process = PROC_ENGINE;
-       setproctitle("%s", log_procnames[slaacd_process]);
-       log_procinit(log_procnames[slaacd_process]);
-
-       if (setgroups(1, &pw->pw_gid) ||
-           setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
-           setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
-               fatal("can't drop privileges");
-
-       if (pledge("stdio recvfd", NULL) == -1)
-               fatal("pledge");
-
-       event_init();
-
-       /* Setup signal handler(s). */
-       signal_set(&ev_sigint, SIGINT, engine_sig_handler, NULL);
-       signal_set(&ev_sigterm, SIGTERM, engine_sig_handler, NULL);
-       signal_add(&ev_sigint, NULL);
-       signal_add(&ev_sigterm, NULL);
-       signal(SIGPIPE, SIG_IGN);
-       signal(SIGHUP, SIG_IGN);
-
-       /* Setup pipe and event handler to the main process. */
-       if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
-               fatal(NULL);
-
-       imsg_init(&iev_main->ibuf, 3);
-       iev_main->handler = engine_dispatch_main;
-
-       /* Setup event handlers. */
-       iev_main->events = EV_READ;
-       event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
-           iev_main->handler, iev_main);
-       event_add(&iev_main->ev, NULL);
-
-       LIST_INIT(&slaacd_interfaces);
-
-       event_dispatch();
-
-       engine_shutdown();
-}
-
-__dead void
-engine_shutdown(void)
-{
-       /* Close pipes. */
-       msgbuf_clear(&iev_frontend->ibuf.w);
-       close(iev_frontend->ibuf.fd);
-       msgbuf_clear(&iev_main->ibuf.w);
-       close(iev_main->ibuf.fd);
-
-       free(iev_frontend);
-       free(iev_main);
-
-       log_info("engine exiting");
-       exit(0);
-}
-
-int
-engine_imsg_compose_frontend(int type, pid_t pid, void *data,
-    uint16_t datalen)
-{
-       return (imsg_compose_event(iev_frontend, type, 0, pid, -1,
-           data, datalen));
-}
-
-int
-engine_imsg_compose_main(int type, pid_t pid, void *data,
-    uint16_t datalen)
-{
-       return (imsg_compose_event(iev_main, type, 0, pid, -1,
-           data, datalen));
-}
-
-void
-engine_dispatch_frontend(int fd, short event, void *bula)
-{
-       struct imsgev                   *iev = bula;
-       struct imsgbuf                  *ibuf = &iev->ibuf;
-       struct imsg                      imsg;
-       struct slaacd_iface             *iface;
-       struct imsg_ra                   ra;
-       struct imsg_ifinfo               imsg_ifinfo;
-       struct imsg_proposal_ack         proposal_ack;
-       struct address_proposal         *addr_proposal = NULL;
-       struct dfr_proposal             *dfr_proposal = NULL;
-       struct imsg_del_addr             del_addr;
-       ssize_t                          n;
-       int                              shut = 0, verbose;
-       uint32_t                         if_index;
-
-       if (event & EV_READ) {
-               if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
-                       fatal("imsg_read error");
-               if (n == 0)     /* Connection closed. */
-                       shut = 1;
-       }
-       if (event & EV_WRITE) {
-               if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
-                       fatal("msgbuf_write");
-               if (n == 0)     /* Connection closed. */
-                       shut = 1;
-       }
-
-       for (;;) {
-               if ((n = imsg_get(ibuf, &imsg)) == -1)
-                       fatal("%s: imsg_get error", __func__);
-               if (n == 0)     /* No more messages. */
-                       break;
-
-               switch (imsg.hdr.type) {
-               case IMSG_CTL_LOG_VERBOSE:
-                       /* Already checked by frontend. */
-                       memcpy(&verbose, imsg.data, sizeof(verbose));
-                       log_setverbose(verbose);
-                       break;
-               case IMSG_CTL_SHOW_INTERFACE_INFO:
-                       if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(if_index))
-                               fatal("%s: IMSG_CTL_SHOW_INTERFACE_INFO wrong "
-                                   "length: %d", __func__, imsg.hdr.len);
-                       memcpy(&if_index, imsg.data, sizeof(if_index));
-                       engine_showinfo_ctl(&imsg, if_index);
-                       break;
-               case IMSG_UPDATE_IF:
-                       if (imsg.hdr.len != IMSG_HEADER_SIZE +
-                           sizeof(imsg_ifinfo))
-                               fatal("%s: IMSG_UPDATE_IF wrong length: %d",
-                                   __func__, imsg.hdr.len);
-                       memcpy(&imsg_ifinfo, imsg.data, sizeof(imsg_ifinfo));
-
-                       iface = get_slaacd_iface_by_id(imsg_ifinfo.if_index);
-                       if (iface == NULL) {
-                               if ((iface = calloc(1, sizeof(*iface))) == NULL)
-                                       fatal("calloc");
-                               evtimer_set(&iface->timer, iface_timeout,
-                                   iface);
-                               iface->if_index = imsg_ifinfo.if_index;
-                               iface->running = imsg_ifinfo.running;
-                               if (iface->running)
-                                       start_probe(iface);
-                               else
-                                       iface->state = IF_DOWN;
-                               iface->autoconfprivacy =
-                                   imsg_ifinfo.autoconfprivacy;
-                               memcpy(&iface->hw_address,
-                                   &imsg_ifinfo.hw_address,
-                                   sizeof(struct ether_addr));
-                               memcpy(&iface->ll_address,
-                                   &imsg_ifinfo.ll_address,
-                                   sizeof(struct sockaddr_in6));
-                               LIST_INIT(&iface->radvs);
-                               LIST_INSERT_HEAD(&slaacd_interfaces,
-                                   iface, entries);
-                               LIST_INIT(&iface->addr_proposals);
-                               LIST_INIT(&iface->dfr_proposals);
-                       } else {
-                               int need_refresh = 0;
-
-                               if (iface->autoconfprivacy !=
-                                   imsg_ifinfo.autoconfprivacy) {
-                                       iface->autoconfprivacy =
-                                           imsg_ifinfo.autoconfprivacy;
-                                       need_refresh = 1;
-                               }
-                               if (memcmp(&iface->hw_address,
-                                           &imsg_ifinfo.hw_address,
-                                           sizeof(struct ether_addr)) != 0) {
-                                       memcpy(&iface->hw_address,
-                                           &imsg_ifinfo.hw_address,
-                                           sizeof(struct ether_addr));
-                                       need_refresh = 1;
-                               }
-
-                               if (iface->state != IF_DOWN &&
-                                   imsg_ifinfo.running && need_refresh)
-                                       start_probe(iface);
-
-                               iface->running = imsg_ifinfo.running;
-                               if (!iface->running) {
-                                       iface->state = IF_DOWN;
-                                       if (evtimer_pending(&iface->timer,
-                                           NULL))
-                                               evtimer_del(&iface->timer);
-                               }
-
-                               memcpy(&iface->ll_address,
-                                   &imsg_ifinfo.ll_address,
-                                   sizeof(struct sockaddr_in6));
-                       }
-                       break;
-               case IMSG_REMOVE_IF:
-                       if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(if_index))
-                               fatal("%s: IMSG_REMOVE_IF wrong length: %d",
-                                   __func__, imsg.hdr.len);
-                       memcpy(&if_index, imsg.data, sizeof(if_index));
-                       remove_slaacd_iface(if_index);
-                       break;
-               case IMSG_RA:
-                       if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(ra))
-                               fatal("%s: IMSG_RA wrong length: %d",
-                                   __func__, imsg.hdr.len);
-                       memcpy(&ra, imsg.data, sizeof(ra));
-                       iface = get_slaacd_iface_by_id(ra.if_index);
-                       if (iface != NULL)
-                               parse_ra(iface, &ra);
-                       break;
-               case IMSG_CTL_SEND_SOLICITATION:
-                       if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(if_index))
-                               fatal("%s: IMSG_CTL_SEND_SOLICITATION wrong "
-                                   "length: %d", __func__, imsg.hdr.len);
-                       memcpy(&if_index, imsg.data, sizeof(if_index));
-                       iface = get_slaacd_iface_by_id(if_index);
-                       if (iface == NULL)
-                               log_warn("requested to send solicitation on "
-                                   "non-autoconf interface: %u", if_index);
-                       else
-                               engine_imsg_compose_frontend(
-                                   IMSG_CTL_SEND_SOLICITATION, imsg.hdr.pid,
-                                   &iface->if_index, sizeof(iface->if_index));
-                       break;
-               case IMSG_PROPOSAL_ACK:
-                       if (imsg.hdr.len != IMSG_HEADER_SIZE +
-                           sizeof(proposal_ack))
-                               fatal("%s: IMSG_PROPOSAL_ACK wrong length: %d",
-                                   __func__, imsg.hdr.len);
-                       memcpy(&proposal_ack, imsg.data, sizeof(proposal_ack));
-                       log_debug("%s: IMSG_PROPOSAL_ACK: %lld - %d", __func__,
-                           proposal_ack.id, proposal_ack.pid);
-                       if (proposal_ack.pid != getpid()) {
-                               log_debug("IMSG_PROPOSAL_ACK: wrong pid, "
-                                   "ignoring");
-                               break;
-                       }
-
-                       iface = get_slaacd_iface_by_id(proposal_ack.if_index);
-                       if (iface == NULL) {
-                               log_debug("IMSG_PROPOSAL_ACK: unknown interface"
-                                   ", ignoring");
-                               break;
-                       }
-
-                       addr_proposal = find_address_proposal_by_id(iface,
-                           proposal_ack.id);
-                       if (addr_proposal == NULL) {
-                               dfr_proposal = find_dfr_proposal_by_id(iface,
-                                   proposal_ack.id);
-                               if (dfr_proposal == NULL) {
-                                       log_debug("IMSG_PROPOSAL_ACK: cannot "
-                                           "find proposal, ignoring");
-                                       break;
-                               }
-                       }
-                       if (addr_proposal != NULL)
-                               configure_address(addr_proposal);
-                       else if (dfr_proposal != NULL)
-                               configure_dfr(dfr_proposal);
-
-                       break;
-               case IMSG_DEL_ADDRESS:
-                       if (imsg.hdr.len != IMSG_HEADER_SIZE +
-                           sizeof(del_addr))
-                               fatal("%s: IMSG_DEL_ADDRESS wrong length: %d",
-                                   __func__, imsg.hdr.len);
-                       memcpy(&del_addr, imsg.data, sizeof(del_addr));
-                       iface = get_slaacd_iface_by_id(del_addr.if_index);
-                       if (iface == NULL) {
-                               log_debug("IMSG_DEL_ADDRESS: unknown interface"
-                                   ", ignoring");
-                               break;
-                       }
-
-                       addr_proposal = find_address_proposal_by_addr(iface,
-                           &del_addr.addr);
-
-                       if (addr_proposal) {
-                               /* XXX should we inform netcfgd? */
-                               LIST_REMOVE(addr_proposal, entries);
-                               evtimer_del(&addr_proposal->timer);
-                               free(addr_proposal);
-                       }
-
-                       break;
-               default:
-                       log_debug("%s: unexpected imsg %d", __func__,
-                           imsg.hdr.type);
-                       break;
-               }
-               imsg_free(&imsg);
-       }
-       if (!shut)
-               imsg_event_add(iev);
-       else {
-               /* This pipe is dead. Remove its event handler. */
-               event_del(&iev->ev);
-               event_loopexit(NULL);
-       }
-}
-
-void
-engine_dispatch_main(int fd, short event, void *bula)
-{
-       struct imsg              imsg;
-       struct imsgev           *iev = bula;
-       struct imsgbuf          *ibuf = &iev->ibuf;
-       ssize_t                  n;
-       int                      shut = 0;
-
-       if (event & EV_READ) {
-               if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
-                       fatal("imsg_read error");
-               if (n == 0)     /* Connection closed. */
-                       shut = 1;
-       }
-       if (event & EV_WRITE) {
-               if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
-                       fatal("msgbuf_write");
-               if (n == 0)     /* Connection closed. */
-                       shut = 1;
-       }
-
-       for (;;) {
-               if ((n = imsg_get(ibuf, &imsg)) == -1)
-                       fatal("%s: imsg_get error", __func__);
-               if (n == 0)     /* No more messages. */
-                       break;
-
-               switch (imsg.hdr.type) {
-               case IMSG_SOCKET_IPC:
-                       /*
-                        * Setup pipe and event handler to the frontend
-                        * process.
-                        */
-                       if (iev_frontend) {
-                               log_warnx("%s: received unexpected imsg fd "
-                                   "to engine", __func__);
-                               break;
-                       }
-                       if ((fd = imsg.fd) == -1) {
-                               log_warnx("%s: expected to receive imsg fd to "
-                                  "engine but didn't receive any", __func__);
-                               break;
-                       }
-
-                       iev_frontend = malloc(sizeof(struct imsgev));
-                       if (iev_frontend == NULL)
-                               fatal(NULL);
-
-                       imsg_init(&iev_frontend->ibuf, fd);
-                       iev_frontend->handler = engine_dispatch_frontend;
-                       iev_frontend->events = EV_READ;
-
-                       event_set(&iev_frontend->ev, iev_frontend->ibuf.fd,
-                       iev_frontend->events, iev_frontend->handler,
-                           iev_frontend);
-                       event_add(&iev_frontend->ev, NULL);
-
-                       if (pledge("stdio", NULL) == -1)
-                               fatal("pledge");
-                       break;
-               default:
-                       log_debug("%s: unexpected imsg %d", __func__,
-                           imsg.hdr.type);
-                       break;
-               }
-               imsg_free(&imsg);
-       }
-       if (!shut)
-               imsg_event_add(iev);
-       else {
-               /* This pipe is dead. Remove its event handler. */
-               event_del(&iev->ev);
-               event_loopexit(NULL);
-       }
-}
-
-void
-send_interface_info(struct slaacd_iface *iface, pid_t pid)
-{
-       struct ctl_engine_info                   cei;
-       struct ctl_engine_info_ra                cei_ra;
-       struct ctl_engine_info_ra_prefix         cei_ra_prefix;
-       struct ctl_engine_info_ra_rdns           cei_ra_rdns;
-       struct ctl_engine_info_ra_dnssl          cei_ra_dnssl;
-       struct ctl_engine_info_address_proposal  cei_addr_proposal;
-       struct ctl_engine_info_dfr_proposal      cei_dfr_proposal;
-       struct radv                             *ra;
-       struct radv_prefix                      *prefix;
-       struct radv_rdns                        *rdns;
-       struct radv_dnssl                       *dnssl;
-       struct address_proposal                 *addr_proposal;
-       struct dfr_proposal                     *dfr_proposal;
-
-       memset(&cei, 0, sizeof(cei));
-       cei.if_index = iface->if_index;
-       cei.running = iface->running;
-       cei.autoconfprivacy = iface->autoconfprivacy;
-       memcpy(&cei.hw_address, &iface->hw_address, sizeof(struct ether_addr));
-       memcpy(&cei.ll_address, &iface->ll_address,
-           sizeof(struct sockaddr_in6));
-       engine_imsg_compose_frontend(IMSG_CTL_SHOW_INTERFACE_INFO, pid, &cei,
-           sizeof(cei));
-       LIST_FOREACH(ra, &iface->radvs, entries) {
-               memset(&cei_ra, 0, sizeof(cei_ra));
-               memcpy(&cei_ra.from, &ra->from, sizeof(cei_ra.from));
-               memcpy(&cei_ra.when, &ra->when, sizeof(cei_ra.when));
-               memcpy(&cei_ra.uptime, &ra->uptime, sizeof(cei_ra.uptime));
-               cei_ra.curhoplimit = ra->curhoplimit;
-               cei_ra.managed = ra->managed;
-               cei_ra.other = ra->other;
-               if (strlcpy(cei_ra.rpref, rpref_name[ra->rpref], sizeof(
-                   cei_ra.rpref)) >= sizeof(cei_ra.rpref))
-                       log_warn("truncated router preference");
-               cei_ra.router_lifetime = ra->router_lifetime;
-               cei_ra.reachable_time = ra->reachable_time;
-               cei_ra.retrans_time = ra->retrans_time;
-               engine_imsg_compose_frontend(IMSG_CTL_SHOW_INTERFACE_INFO_RA,
-                   pid, &cei_ra, sizeof(cei_ra));
-
-               LIST_FOREACH(prefix, &ra->prefixes, entries) {
-                       memset(&cei_ra_prefix, 0, sizeof(cei_ra_prefix));
-
-                       cei_ra_prefix.prefix = prefix->prefix;
-                       cei_ra_prefix.prefix_len = prefix->prefix_len;
-                       cei_ra_prefix.onlink = prefix->onlink;
-                       cei_ra_prefix.autonomous = prefix->autonomous;
-                       cei_ra_prefix.vltime = prefix->vltime;
-                       cei_ra_prefix.pltime = prefix->pltime;
-                       engine_imsg_compose_frontend(
-                           IMSG_CTL_SHOW_INTERFACE_INFO_RA_PREFIX, pid,
-                           &cei_ra_prefix, sizeof(cei_ra_prefix));
-               }
-
-               LIST_FOREACH(rdns, &ra->rdns_servers, entries) {
-                       memset(&cei_ra_rdns, 0, sizeof(cei_ra_rdns));
-                       memcpy(&cei_ra_rdns.rdns, &rdns->rdns,
-                           sizeof(cei_ra_rdns.rdns));
-                       cei_ra_rdns.lifetime = ra->rdns_lifetime;
-                       engine_imsg_compose_frontend(
-                           IMSG_CTL_SHOW_INTERFACE_INFO_RA_RDNS, pid,
-                           &cei_ra_rdns, sizeof(cei_ra_rdns));
-               }
-
-               LIST_FOREACH(dnssl, &ra->dnssls, entries) {
-                       memset(&cei_ra_dnssl, 0, sizeof(cei_ra_dnssl));
-                       memcpy(&cei_ra_dnssl.dnssl, &dnssl->dnssl,
-                           sizeof(cei_ra_dnssl.dnssl));
-                       cei_ra_dnssl.lifetime = ra->dnssl_lifetime;
-                       engine_imsg_compose_frontend(
-                           IMSG_CTL_SHOW_INTERFACE_INFO_RA_DNSSL, pid,
-                           &cei_ra_dnssl, sizeof(cei_ra_dnssl));
-               }
-       }
-
-       if (!LIST_EMPTY(&iface->addr_proposals))
-               engine_imsg_compose_frontend(
-                   IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSALS, pid, NULL, 0);
-
-       LIST_FOREACH(addr_proposal, &iface->addr_proposals, entries) {
-               memset(&cei_addr_proposal, 0, sizeof(cei_addr_proposal));
-               cei_addr_proposal.id = addr_proposal->id;
-               if(strlcpy(cei_addr_proposal.state,
-                   proposal_state_name[addr_proposal->state],
-                   sizeof(cei_addr_proposal.state)) >=
-                   sizeof(cei_addr_proposal.state))
-                       log_warn("truncated state name");
-               cei_addr_proposal.next_timeout = addr_proposal->next_timeout;
-               cei_addr_proposal.timeout_count = addr_proposal->timeout_count;
-               cei_addr_proposal.when = addr_proposal->when;
-               cei_addr_proposal.uptime = addr_proposal->uptime;
-               memcpy(&cei_addr_proposal.addr, &addr_proposal->addr, sizeof(
-                   cei_addr_proposal.addr));
-               memcpy(&cei_addr_proposal.prefix, &addr_proposal->prefix,
-                   sizeof(cei_addr_proposal.prefix));
-               cei_addr_proposal.prefix_len = addr_proposal->prefix_len;
-               cei_addr_proposal.privacy = addr_proposal->privacy;
-               cei_addr_proposal.vltime = addr_proposal->vltime;
-               cei_addr_proposal.pltime = addr_proposal->pltime;
-
-               engine_imsg_compose_frontend(
-                   IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSAL, pid,
-                           &cei_addr_proposal, sizeof(cei_addr_proposal));
-       }
-
-       if (!LIST_EMPTY(&iface->dfr_proposals))
-               engine_imsg_compose_frontend(
-                   IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSALS, pid, NULL, 0);
-
-       LIST_FOREACH(dfr_proposal, &iface->dfr_proposals, entries) {
-               memset(&cei_dfr_proposal, 0, sizeof(cei_dfr_proposal));
-               cei_dfr_proposal.id = dfr_proposal->id;
-               if(strlcpy(cei_dfr_proposal.state,
-                   proposal_state_name[dfr_proposal->state],
-                   sizeof(cei_dfr_proposal.state)) >=
-                   sizeof(cei_dfr_proposal.state))
-                       log_warn("truncated state name");
-               cei_dfr_proposal.next_timeout = dfr_proposal->next_timeout;
-               cei_dfr_proposal.timeout_count = dfr_proposal->timeout_count;
-               cei_dfr_proposal.when = dfr_proposal->when;
-               cei_dfr_proposal.uptime = dfr_proposal->uptime;
-               memcpy(&cei_dfr_proposal.addr, &dfr_proposal->addr, sizeof(
-                   cei_dfr_proposal.addr));
-               cei_dfr_proposal.router_lifetime =
-                   dfr_proposal->router_lifetime;
-               if(strlcpy(cei_dfr_proposal.rpref,
-                   rpref_name[dfr_proposal->rpref],
-                   sizeof(cei_dfr_proposal.rpref)) >=
-                   sizeof(cei_dfr_proposal.rpref))
-                       log_warn("truncated router preference");
-               engine_imsg_compose_frontend(
-                   IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSAL, pid,
-                           &cei_dfr_proposal, sizeof(cei_dfr_proposal));
-       }
-}
-
-void
-engine_showinfo_ctl(struct imsg *imsg, uint32_t if_index)
-{
-       struct slaacd_iface                     *iface;
-
-       switch (imsg->hdr.type) {
-       case IMSG_CTL_SHOW_INTERFACE_INFO:
-               if (if_index == 0) {
-                       LIST_FOREACH (iface, &slaacd_interfaces, entries)
-                               send_interface_info(iface, imsg->hdr.pid);
-               } else {
-                       if ((iface = get_slaacd_iface_by_id(if_index)) != NULL)
-                               send_interface_info(iface, imsg->hdr.pid);
-               }
-               engine_imsg_compose_frontend(IMSG_CTL_END, imsg->hdr.pid, NULL,
-                   0);
-               break;
-       default:
-               log_debug("%s: error handling imsg", __func__);
-               break;
-       }
-}
-
-struct slaacd_iface*
-get_slaacd_iface_by_id(uint32_t if_index)
-{
-       struct slaacd_iface     *iface;
-       LIST_FOREACH (iface, &slaacd_interfaces, entries) {
-               if (iface->if_index == if_index)
-                       return (iface);
-       }
-
-       return (NULL);
-}
-
-void
-remove_slaacd_iface(uint32_t if_index)
-{
-       struct slaacd_iface     *iface, *tiface;
-       struct radv             *ra;
-       struct address_proposal *addr_proposal;
-       struct dfr_proposal     *dfr_proposal;
-
-       LIST_FOREACH_SAFE (iface, &slaacd_interfaces, entries, tiface) {
-               if (iface->if_index == if_index) {
-                       LIST_REMOVE(iface, entries);
-                       while(!LIST_EMPTY(&iface->radvs)) {
-                               ra = LIST_FIRST(&iface->radvs);
-                               LIST_REMOVE(ra, entries);
-                               free_ra(ra);
-                       }
-                       /* XXX inform netcfgd? */
-                       while(!LIST_EMPTY(&iface->addr_proposals)) {
-                               addr_proposal =
-                                   LIST_FIRST(&iface->addr_proposals);
-                               LIST_REMOVE(addr_proposal, entries);
-                               free(addr_proposal);
-                       }
-                       while(!LIST_EMPTY(&iface->dfr_proposals)) {
-                               dfr_proposal =
-                                   LIST_FIRST(&iface->dfr_proposals);
-                               free_dfr_proposal(dfr_proposal);
-                       }
-                       free(iface);
-                       break;
-               }
-       }
-}
-
-void
-free_ra(struct radv *ra)
-{
-       struct radv_prefix      *prefix;
-       struct radv_rdns        *rdns;
-       struct radv_dnssl       *dnssl;
-
-       if (ra == NULL)
-               return;
-
-       evtimer_del(&ra->timer);
-
-       while (!LIST_EMPTY(&ra->prefixes)) {
-               prefix = LIST_FIRST(&ra->prefixes);
-               LIST_REMOVE(prefix, entries);
-               free(prefix);
-       }
-
-       while (!LIST_EMPTY(&ra->rdns_servers)) {
-               rdns = LIST_FIRST(&ra->rdns_servers);
-               LIST_REMOVE(rdns, entries);
-               free(rdns);
-       }
-
-       while (!LIST_EMPTY(&ra->dnssls)) {
-               dnssl = LIST_FIRST(&ra->dnssls);
-               LIST_REMOVE(dnssl, entries);
-               free(dnssl);
-       }
-
-       free(ra);
-}
-
-void
-parse_ra(struct slaacd_iface *iface, struct imsg_ra *ra)
-{
-       struct nd_router_advert *nd_ra;
-       struct radv             *radv;
-       struct radv_prefix      *prefix;
-       struct radv_rdns        *rdns;
-       struct radv_dnssl       *ra_dnssl;
-       ssize_t                  len = ra->len;
-       char                     hbuf[NI_MAXHOST];
-       uint8_t                 *p;
-
-#if 0
-       debug_log_ra(ra);
-#endif
-
-       if (getnameinfo((struct sockaddr *)&ra->from, ra->from.sin6_len, hbuf,
-           sizeof(hbuf), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV)) {
-               log_warn("cannot get router IP");
-               strlcpy(hbuf, "unknown", sizeof(hbuf));
-       }
-
-       if (!IN6_IS_ADDR_LINKLOCAL(&ra->from.sin6_addr)) {
-               log_warn("RA from non link local address %s", hbuf);
-               return;
-       }
-
-       if ((size_t)len < sizeof(struct nd_router_advert)) {
-               log_warn("received too short message (%ld) from %s", len, hbuf);
-               return;
-       }
-
-       if ((radv = calloc(1, sizeof(*radv))) == NULL)
-               fatal("calloc");
-
-       LIST_INIT(&radv->prefixes);
-       LIST_INIT(&radv->rdns_servers);
-       LIST_INIT(&radv->dnssls);
-
-       radv->min_lifetime = UINT32_MAX;
-
-       p = ra->packet;
-       nd_ra = (struct nd_router_advert *)p;
-       len -= sizeof(struct nd_router_advert);
-       p += sizeof(struct nd_router_advert);
-
-       log_debug("ICMPv6 type(%d), code(%d) from %s of length %ld",
-           nd_ra->nd_ra_type, nd_ra->nd_ra_code, hbuf, len);
-
-       if (nd_ra->nd_ra_type != ND_ROUTER_ADVERT) {
-               log_warnx("invalid ICMPv6 type (%d) from %s", nd_ra->nd_ra_type,
-                   hbuf);
-               goto err;
-       }
-
-       if (nd_ra->nd_ra_code != 0) {
-               log_warn("invalid ICMPv6 code (%d) from %s", nd_ra->nd_ra_code,
-                   hbuf);
-               goto err;
-       }
-
-       memcpy(&radv->from, &ra->from, sizeof(ra->from));
-
-       if (clock_gettime(CLOCK_REALTIME, &radv->when))
-               fatal("clock_gettime");
-       if (clock_gettime(CLOCK_MONOTONIC, &radv->uptime))
-               fatal("clock_gettime");
-
-       radv->curhoplimit = nd_ra->nd_ra_curhoplimit;
-       radv->managed = nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED;
-       radv->other = nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER;
-
-       switch (nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_RTPREF_MASK) {
-       case ND_RA_FLAG_RTPREF_HIGH:
-               radv->rpref=HIGH;
-               break;
-       case ND_RA_FLAG_RTPREF_LOW:
-               radv->rpref=LOW;
-               break;
-       case ND_RA_FLAG_RTPREF_MEDIUM:
-               /* fallthrough */
-       default:
-               radv->rpref=MEDIUM;
-               break;
-       }
-       radv->router_lifetime = ntohs(nd_ra->nd_ra_router_lifetime);
-       if (radv->router_lifetime != 0)
-               radv->min_lifetime = radv->router_lifetime;
-       radv->reachable_time = ntohl(nd_ra->nd_ra_reachable);
-       radv->retrans_time = ntohl(nd_ra->nd_ra_retransmit);
-
-       while ((size_t)len >= sizeof(struct nd_opt_hdr)) {
-               struct nd_opt_hdr *nd_opt_hdr = (struct nd_opt_hdr *)p;
-               struct nd_opt_prefix_info *prf;
-               struct nd_opt_rdnss *rdnss;
-               struct nd_opt_dnssl *dnssl;
-               struct in6_addr *in6;
-               int i;
-               char *nssl;
-
-               len -= sizeof(struct nd_opt_hdr);
-               p += sizeof(struct nd_opt_hdr);
-
-               if (nd_opt_hdr->nd_opt_len * 8 - 2 > len) {
-                       log_warnx("invalid option len: %u > %ld",
-                           nd_opt_hdr->nd_opt_len, len);
-                       goto err;
-               }
-
-               switch (nd_opt_hdr->nd_opt_type) {
-               case ND_OPT_PREFIX_INFORMATION:
-                       if (nd_opt_hdr->nd_opt_len != 4) {
-                               log_warn("invalid ND_OPT_PREFIX_INFORMATION: "
-                                  "len != 4");
-                               goto err;
-                       }
-
-                       if ((prefix = calloc(1, sizeof(*prefix))) == NULL)
-                               fatal("calloc");
-
-                       prf = (struct nd_opt_prefix_info*) nd_opt_hdr;
-                       prefix->prefix = prf->nd_opt_pi_prefix;
-                       prefix->prefix_len = prf->nd_opt_pi_prefix_len;
-                       prefix->onlink = prf->nd_opt_pi_flags_reserved &
-                           ND_OPT_PI_FLAG_ONLINK;
-                       prefix->autonomous = prf->nd_opt_pi_flags_reserved &
-                           ND_OPT_PI_FLAG_AUTO;
-                       prefix->vltime = ntohl(prf->nd_opt_pi_valid_time);
-                       prefix->pltime = ntohl(prf->nd_opt_pi_preferred_time);
-                       if (radv->min_lifetime > prefix->pltime)
-                               radv->min_lifetime = prefix->pltime;
-
-                       LIST_INSERT_HEAD(&radv->prefixes, prefix, entries);
-
-                       break;
-
-               case ND_OPT_RDNSS:
-                       if (nd_opt_hdr->nd_opt_len  < 3) {
-                               log_warnx("invalid ND_OPT_RDNSS: len < 24");
-                               goto err;
-                       }
-
-                       if ((nd_opt_hdr->nd_opt_len - 1) % 2 != 0) {
-                               log_warnx("invalid ND_OPT_RDNSS: length with"
-                                   "out header is not multiply of 16: %d",
-                                   (nd_opt_hdr->nd_opt_len - 1) * 8);
-                               goto err;
-                       }
-
-                       rdnss = (struct nd_opt_rdnss*) nd_opt_hdr;
-
-                       radv->rdns_lifetime = ntohl(
-                           rdnss->nd_opt_rdnss_lifetime);
-                       if (radv->min_lifetime > radv->rdns_lifetime)
-                               radv->min_lifetime = radv->rdns_lifetime;
-
-                       in6 = (struct in6_addr*) (p + 6);
-                       for (i=0; i < (nd_opt_hdr->nd_opt_len - 1)/2; i++,
-                           in6++) {
-                               if((rdns = calloc(1, sizeof(*rdns))) == NULL)
-                                       fatal("calloc");
-                               memcpy(&rdns->rdns, in6, sizeof(rdns->rdns));
-                               LIST_INSERT_HEAD(&radv->rdns_servers, rdns,
-                                   entries);
-                       }
-                       break;
-               case ND_OPT_DNSSL:
-                       if (nd_opt_hdr->nd_opt_len  < 2) {
-                               log_warnx("invalid ND_OPT_DNSSL: len < 16");
-                               goto err;
-                       }
-
-                       dnssl = (struct nd_opt_dnssl*) nd_opt_hdr;
-
-                       if ((nssl = parse_dnssl(p + 6,
-                           (nd_opt_hdr->nd_opt_len - 1) * 8)) == NULL)
-                               goto err; /* error logging in parse_dnssl */
-
-                       if((ra_dnssl = calloc(1, sizeof(*ra_dnssl))) == NULL)
-                               fatal("calloc");
-
-                       radv->dnssl_lifetime = ntohl(
-                           dnssl->nd_opt_dnssl_lifetime);
-                       if (radv->min_lifetime > radv->dnssl_lifetime)
-                               radv->min_lifetime = radv->dnssl_lifetime;
-
-                       if (strlcpy(ra_dnssl->dnssl, nssl,
-                           sizeof(ra_dnssl->dnssl)) >=
-                           sizeof(ra_dnssl->dnssl)) {
-                               log_warn("dnssl too long");
-                               goto err;
-                       }
-                       free(nssl);
-
-                       LIST_INSERT_HEAD(&radv->dnssls, ra_dnssl, entries);
-
-                       break;
-               case ND_OPT_REDIRECTED_HEADER:
-               case ND_OPT_SOURCE_LINKADDR:
-               case ND_OPT_TARGET_LINKADDR:
-               case ND_OPT_MTU:
-               case ND_OPT_ROUTE_INFO:
-#if 0
-                       log_debug("\tOption: %u (len: %u) not implemented",
-                           nd_opt_hdr->nd_opt_type, nd_opt_hdr->nd_opt_len *
-                           8);
-#endif
-                       break;
-               default:
-                       log_debug("\t\tUNKNOWN: %d", nd_opt_hdr->nd_opt_type);
-                       break;
-
-               }
-               len -= nd_opt_hdr->nd_opt_len * 8 - 2;
-               p += nd_opt_hdr->nd_opt_len * 8 - 2;
-       }
-       update_iface_ra(iface, radv);
-       iface->state = IF_IDLE;
-       return;
-
-err:
-       free_ra(radv);
-}
-
-void
-gen_addr(struct slaacd_iface *iface, struct radv_prefix *prefix, struct
-    address_proposal *addr_proposal, int privacy)
-{
-       struct in6_addr priv_in6;
-
-       /* from in6_ifadd() in nd6_rtr.c */
-       /* XXX from in6.h, guarded by #ifdef _KERNEL   XXX nonstandard */
-#define s6_addr32 __u6_addr.__u6_addr32
-
-       /* XXX from in6_ifattach.c */
-#define EUI64_GBIT     0x01
-#define EUI64_UBIT     0x02
-
-       if (privacy) {
-               arc4random_buf(&priv_in6.s6_addr32[2], 8);
-               priv_in6.s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */
-               priv_in6.s6_addr[8] |= EUI64_UBIT;  /* u bit to "local" */
-               /* convert EUI64 into IPv6 interface identifier */
-               priv_in6.s6_addr[8] ^= EUI64_UBIT;
-       }
-
-       in6_prefixlen2mask(&addr_proposal->mask, addr_proposal->prefix_len);
-
-       memset(&addr_proposal->addr, 0, sizeof(addr_proposal->addr));
-
-       addr_proposal->addr.sin6_family = AF_INET6;
-       addr_proposal->addr.sin6_len = sizeof(addr_proposal->addr);
-
-       memcpy(&addr_proposal->addr.sin6_addr, &prefix->prefix,
-           sizeof(addr_proposal->addr.sin6_addr));
-
-       addr_proposal->addr.sin6_addr.s6_addr32[0] &=
-           addr_proposal->mask.s6_addr32[0];
-       addr_proposal->addr.sin6_addr.s6_addr32[1] &=
-           addr_proposal->mask.s6_addr32[1];
-       addr_proposal->addr.sin6_addr.s6_addr32[2] &=
-           addr_proposal->mask.s6_addr32[2];
-       addr_proposal->addr.sin6_addr.s6_addr32[3] &=
-           addr_proposal->mask.s6_addr32[3];
-
-       if (privacy) {
-               addr_proposal->addr.sin6_addr.s6_addr32[0] |=
-                   (priv_in6.s6_addr32[0] & ~addr_proposal->mask.s6_addr32[0]);
-               addr_proposal->addr.sin6_addr.s6_addr32[1] |=
-                   (priv_in6.s6_addr32[1] & ~addr_proposal->mask.s6_addr32[1]);
-               addr_proposal->addr.sin6_addr.s6_addr32[2] |=
-                   (priv_in6.s6_addr32[2] & ~addr_proposal->mask.s6_addr32[2]);
-               addr_proposal->addr.sin6_addr.s6_addr32[3] |=
-                   (priv_in6.s6_addr32[3] & ~addr_proposal->mask.s6_addr32[3]);
-       } else {
-               addr_proposal->addr.sin6_addr.s6_addr32[0] |=
-                   (iface->ll_address.sin6_addr.s6_addr32[0] &
-                   ~addr_proposal->mask.s6_addr32[0]);
-               addr_proposal->addr.sin6_addr.s6_addr32[1] |=
-                   (iface->ll_address.sin6_addr.s6_addr32[1] &
-                   ~addr_proposal->mask.s6_addr32[1]);
-               addr_proposal->addr.sin6_addr.s6_addr32[2] |=
-                   (iface->ll_address.sin6_addr.s6_addr32[2] &
-                   ~addr_proposal->mask.s6_addr32[2]);
-               addr_proposal->addr.sin6_addr.s6_addr32[3] |=
-                   (iface->ll_address.sin6_addr.s6_addr32[3] &
-                   ~addr_proposal->mask.s6_addr32[3]);
-       }
-
-#undef s6_addr32
-}
-
-/* from sys/netinet6/in6.c */
-void
-in6_prefixlen2mask(struct in6_addr *maskp, int len)
-{
-       u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff};
-       int bytelen, bitlen, i;
-
-       if (0 > len || len > 128)
-               fatal("%s: invalid prefix length(%d)\n", __func__, len);
-
-       bzero(maskp, sizeof(*maskp));
-       bytelen = len / 8;
-       bitlen = len % 8;
-       for (i = 0; i < bytelen; i++)
-               maskp->s6_addr[i] = 0xff;
-       /* len == 128 is ok because bitlen == 0 then */
-       if (bitlen)
-               maskp->s6_addr[bytelen] = maskarray[bitlen - 1];
-}
-
-void
-debug_log_ra(struct imsg_ra *ra)
-{
-       struct nd_router_advert *nd_ra;
-       ssize_t                  len = ra->len;
-       char                     hbuf[NI_MAXHOST], ntopbuf[INET6_ADDRSTRLEN];
-       uint8_t                 *p;
-
-       if (getnameinfo((struct sockaddr *)&ra->from, ra->from.sin6_len, hbuf,
-           sizeof(hbuf), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV)) {
-               log_warn("cannot get router IP");
-               strlcpy(hbuf, "unknown", sizeof(hbuf));
-       }
-
-       if (!IN6_IS_ADDR_LINKLOCAL(&ra->from.sin6_addr)) {
-               log_warn("RA from non link local address %s", hbuf);
-               return;
-       }
-
-       if ((size_t)len < sizeof(struct nd_router_advert)) {
-               log_warn("received too short message (%ld) from %s", len, hbuf);
-               return;
-       }
-
-       p = ra->packet;
-       nd_ra = (struct nd_router_advert *)p;
-       len -= sizeof(struct nd_router_advert);
-       p += sizeof(struct nd_router_advert);
-
-       log_debug("ICMPv6 type(%d), code(%d) from %s of length %ld",
-           nd_ra->nd_ra_type, nd_ra->nd_ra_code, hbuf, len);
-
-       if (nd_ra->nd_ra_type != ND_ROUTER_ADVERT) {
-               log_warnx("invalid ICMPv6 type (%d) from %s", nd_ra->nd_ra_type,
-                   hbuf);
-               return;
-       }
-
-       if (nd_ra->nd_ra_code != 0) {
-               log_warn("invalid ICMPv6 code (%d) from %s", nd_ra->nd_ra_code,
-                   hbuf);
-               return;
-       }
-
-       log_debug("---");
-       log_debug("RA from %s", hbuf);
-       log_debug("\tCur Hop Limit: %u", nd_ra->nd_ra_curhoplimit);
-       log_debug("\tManaged address configuration: %d",
-           (nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) ? 1 : 0);
-       log_debug("\tOther configuration: %d",
-           (nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) ? 1 : 0);
-       switch (nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_RTPREF_MASK) {
-       case ND_RA_FLAG_RTPREF_HIGH:
-               log_debug("\tRouter Preference: high");
-               break;
-       case ND_RA_FLAG_RTPREF_MEDIUM:
-               log_debug("\tRouter Preference: medium");
-               break;
-       case ND_RA_FLAG_RTPREF_LOW:
-               log_debug("\tRouter Preference: low");
-               break;
-       case ND_RA_FLAG_RTPREF_RSV:
-               log_debug("\tRouter Preference: reserved");
-               break;
-       }
-       log_debug("\tRouter Lifetime: %hds",
-           ntohs(nd_ra->nd_ra_router_lifetime));
-       log_debug("\tReachable Time: %ums", ntohl(nd_ra->nd_ra_reachable));
-       log_debug("\tRetrans Timer: %ums", ntohl(nd_ra->nd_ra_retransmit));
-
-       while ((size_t)len >= sizeof(struct nd_opt_hdr)) {
-               struct nd_opt_hdr *nd_opt_hdr = (struct nd_opt_hdr *)p;
-               struct nd_opt_mtu *mtu;
-               struct nd_opt_prefix_info *prf;
-               struct nd_opt_rdnss *rdnss;
-               struct nd_opt_dnssl *dnssl;
-               struct in6_addr *in6;
-               int i;
-               char *nssl;
-
-               len -= sizeof(struct nd_opt_hdr);
-               p += sizeof(struct nd_opt_hdr);
-               if (nd_opt_hdr->nd_opt_len * 8 - 2 > len) {
-                       log_warnx("invalid option len: %u > %ld",
-                           nd_opt_hdr->nd_opt_len, len);
-                       return;
-               }
-               log_debug("\tOption: %u (len: %u)", nd_opt_hdr->nd_opt_type,
-                   nd_opt_hdr->nd_opt_len * 8);
-               switch (nd_opt_hdr->nd_opt_type) {
-               case ND_OPT_SOURCE_LINKADDR:
-                       if (nd_opt_hdr->nd_opt_len == 1)
-                               log_debug("\t\tND_OPT_SOURCE_LINKADDR: "
-                                   "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
-                                   p[0], p[1], p[2], p[3], p[4], p[5], p[6],
-                                   p[7]);
-                       else
-                               log_debug("\t\tND_OPT_SOURCE_LINKADDR");
-                       break;
-               case ND_OPT_TARGET_LINKADDR:
-                       if (nd_opt_hdr->nd_opt_len == 1)
-                               log_debug("\t\tND_OPT_TARGET_LINKADDR: "
-                                   "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
-                                   p[0], p[1], p[2], p[3], p[4], p[5], p[6],
-                                   p[7]);
-                       else
-                               log_debug("\t\tND_OPT_TARGET_LINKADDR");
-                       break;
-               case ND_OPT_PREFIX_INFORMATION:
-                       if (nd_opt_hdr->nd_opt_len != 4) {
-                               log_warn("invalid ND_OPT_PREFIX_INFORMATION: "
-                                  "len != 4");
-                               return;
-                       }
-                       prf = (struct nd_opt_prefix_info*) nd_opt_hdr;
-
-                       log_debug("\t\tND_OPT_PREFIX_INFORMATION: %s/%u",
-                           inet_ntop(AF_INET6, &prf->nd_opt_pi_prefix,
-                           ntopbuf, INET6_ADDRSTRLEN),
-                           prf->nd_opt_pi_prefix_len);
-                       log_debug("\t\t\tOn-link: %d",
-                           prf->nd_opt_pi_flags_reserved &
-                           ND_OPT_PI_FLAG_ONLINK ? 1:0);
-                       log_debug("\t\t\tAutonomous address-configuration: %d",
-                           prf->nd_opt_pi_flags_reserved &
-                           ND_OPT_PI_FLAG_AUTO ? 1 : 0);
-                       log_debug("\t\t\tvltime: %u",
-                           ntohl(prf->nd_opt_pi_valid_time));
-                       log_debug("\t\t\tpltime: %u",
-                           ntohl(prf->nd_opt_pi_preferred_time));
-                       break;
-               case ND_OPT_REDIRECTED_HEADER:
-                       log_debug("\t\tND_OPT_REDIRECTED_HEADER");
-                       break;
-               case ND_OPT_MTU:
-                       if (nd_opt_hdr->nd_opt_len != 1) {
-                               log_warn("invalid ND_OPT_MTU: len != 1");
-                               return;
-                       }
-                       mtu = (struct nd_opt_mtu*) nd_opt_hdr;
-                       log_debug("\t\tND_OPT_MTU: %u",
-                           ntohl(mtu->nd_opt_mtu_mtu));
-                       break;
-               case ND_OPT_ROUTE_INFO:
-                       log_debug("\t\tND_OPT_ROUTE_INFO");
-                       break;
-               case ND_OPT_RDNSS:
-                       if (nd_opt_hdr->nd_opt_len  < 3) {
-                               log_warnx("invalid ND_OPT_RDNSS: len < 24");
-                               return;
-                       }
-                       if ((nd_opt_hdr->nd_opt_len - 1) % 2 != 0) {
-                               log_warnx("invalid ND_OPT_RDNSS: length with"
-                                   "out header is not multiply of 16: %d",
-                                   (nd_opt_hdr->nd_opt_len - 1) * 8);
-                               return;
-                       }
-                       rdnss = (struct nd_opt_rdnss*) nd_opt_hdr;
-                       log_debug("\t\tND_OPT_RDNSS: lifetime: %u", ntohl(
-                           rdnss->nd_opt_rdnss_lifetime));
-                       in6 = (struct in6_addr*) (p + 6);
-                       for (i=0; i < (nd_opt_hdr->nd_opt_len - 1)/2; i++,
-                           in6++) {
-                               log_debug("\t\t\t%s", inet_ntop(AF_INET6, in6,
-                                   ntopbuf, INET6_ADDRSTRLEN));
-                       }
-                       break;
-               case ND_OPT_DNSSL:
-                       if (nd_opt_hdr->nd_opt_len  < 2) {
-                               log_warnx("invalid ND_OPT_DNSSL: len < 16");
-                               return;
-                       }
-                       dnssl = (struct nd_opt_dnssl*) nd_opt_hdr;
-                       nssl = parse_dnssl(p + 6, (nd_opt_hdr->nd_opt_len - 1)
-                           * 8);
-
-                       if (nssl == NULL)
-                               return;
-
-                       log_debug("\t\tND_OPT_DNSSL: lifetime: %u", ntohl(
-                           dnssl->nd_opt_dnssl_lifetime));
-                       log_debug("\t\t\tsearch: %s", nssl);
-
-                       free(nssl);
-                       break;
-               default:
-                       log_debug("\t\tUNKNOWN: %d", nd_opt_hdr->nd_opt_type);
-                       break;
-
-               }
-               len -= nd_opt_hdr->nd_opt_len * 8 - 2;
-               p += nd_opt_hdr->nd_opt_len * 8 - 2;
-       }
-}
-
-char*
-parse_dnssl(char* data, int datalen)
-{
-       int len, pos;
-       char *nssl, *nsslp;
-
-       if((nssl = calloc(1, datalen + 1)) == NULL) {
-               log_warn("malloc");
-               return NULL;
-       }
-       nsslp = nssl;
-
-       pos = 0;
-
-       do {
-               len = data[pos];
-               if (len > 63 || len + pos + 1 > datalen) {
-                       free(nssl);
-                       log_warnx("invalid label in DNSSL");
-                       return NULL;
-               }
-               if (len == 0) {
-                       if (pos < datalen && data[pos + 1] != 0)
-                               *nsslp++ = ' '; /* seperator for next domain */
-                       else
-                               break;
-               } else {
-                       if (pos != 0 && data[pos - 1] != 0) /* no . at front */
-                               *nsslp++ = '.';
-                       memcpy(nsslp, data + pos + 1, len);
-                       nsslp += len;
-               }
-               pos += len + 1;
-       } while(pos < datalen);
-       if (len != 0) {
-               free(nssl);
-               log_warnx("invalid label in DNSSL");
-               return NULL;
-       }
-       return nssl;
-}
-
-void update_iface_ra(struct slaacd_iface *iface, struct radv *ra)
-{
-       struct radv             *old_ra;
-       struct radv_prefix      *prefix;
-       struct address_proposal *addr_proposal;
-       struct dfr_proposal     *dfr_proposal, *tmp;
-       int                      found, found_privacy;
-       char                     hbuf[NI_MAXHOST];
-
-       if ((old_ra = find_ra(iface, &ra->from)) == NULL)
-               LIST_INSERT_HEAD(&iface->radvs, ra, entries);
-       else {
-               LIST_REPLACE(old_ra, ra, entries);
-               free_ra(old_ra);
-       }
-       if (ra->router_lifetime == 0) {
-               LIST_FOREACH_SAFE(dfr_proposal, &iface->dfr_proposals, entries,
-                   tmp) {
-                       if (memcmp(&dfr_proposal->addr,
-                           &ra->from, sizeof(struct sockaddr_in6)) ==
-                           0) {
-                               free_dfr_proposal(dfr_proposal);
-                       }
-               }
-       } else {
-               found = 0;
-               LIST_FOREACH(dfr_proposal, &iface->dfr_proposals, entries) {
-                       if (memcmp(&dfr_proposal->addr,
-                           &ra->from, sizeof(struct sockaddr_in6)) ==
-                           0) {
-                               found = 1;
-                               if (real_lifetime(&dfr_proposal->uptime,
-                                   dfr_proposal->router_lifetime) >=
-                                   ra->router_lifetime)
-                                       log_warn("ignoring router advertisement"
-                                           " that lowers router lifetime");
-                               else {
-                                       dfr_proposal->when = ra->when;
-                                       dfr_proposal->uptime = ra->uptime;
-                                       dfr_proposal->router_lifetime =
-                                           ra->router_lifetime;
-
-                                       log_debug("%s, dfr state: %s, rl: %d",
-                                           __func__, proposal_state_name[
-                                           dfr_proposal->state],
-                                           real_lifetime(&dfr_proposal->uptime,
-                                           dfr_proposal->router_lifetime));
-
-                                       switch (dfr_proposal->state) {
-                                       case PROPOSAL_CONFIGURED:
-                                       case PROPOSAL_NEARLY_EXPIRED:
-                                               log_debug("updating dfr");
-                                               configure_dfr(dfr_proposal);
-                                               break;
-                                       default:
-                                               if (getnameinfo((struct
-                                                   sockaddr *)
-                                                   &dfr_proposal->addr,
-                                                   dfr_proposal->
-                                                   addr.sin6_len, hbuf,
-                                                   sizeof(hbuf), NULL, 0,
-                                                   NI_NUMERICHOST |
-                                                   NI_NUMERICSERV)) {
-                                                       log_warn("cannot get "
-                                                           "proposal IP");
-                                                       strlcpy(hbuf, "unknown",
-                                                           sizeof(hbuf));
-                                               }
-                                               log_debug("%s: iface %d: %s",
-                                                   __func__, iface->if_index,
-                                                   hbuf);
-                                               break;
-                                       }
-                               }
-
-                               break;
-                       }
-               }
-               if (!found)
-                       /* new proposal */
-                       gen_dfr_proposal(iface, ra);
-
-               LIST_FOREACH(prefix, &ra->prefixes, entries) {
-                       found = 0;
-                       found_privacy = 0;
-                       LIST_FOREACH(addr_proposal, &iface->addr_proposals,
-                           entries) {
-                               if (prefix->prefix_len ==
-                                   addr_proposal-> prefix_len &&
-                                   memcmp(&prefix->prefix,
-                                   &addr_proposal->prefix,
-                                   sizeof(struct in6_addr)) != 0)
-                                       continue;
-
-                               if (memcmp(&addr_proposal->hw_address,
-                                   &iface->hw_address,
-                                   sizeof(addr_proposal->hw_address)) != 0)
-                                       continue;
-
-                               if (addr_proposal->privacy) {
-                                       /*
-                                        * create new privacy address if old
-                                        * expires
-                                        */
-                                       if (addr_proposal->state !=
-                                           PROPOSAL_NEARLY_EXPIRED)
-                                               found_privacy = 1;
-
-                                       if (!iface->autoconfprivacy)
-                                               log_debug("%s XXX need to "
-                                                   "remove privacy address",
-                                                   __func__);
-
-                                       log_debug("%s, privacy addr state: %s",
-                                           __func__, proposal_state_name[
-                                           addr_proposal->state]);
-
-                                       /* privacy addresses just expire */
-                                       continue;
-                               }
-
-                               found = 1;
-
-                               if (real_lifetime(&addr_proposal->uptime,
-                                   addr_proposal->vltime) >= prefix->vltime) {
-                                       log_warn("ignoring router advertisement"
-                                           " that lowers vltime");
-                                       continue;
-                               }
-
-                               addr_proposal->when = ra->when;
-                               addr_proposal->uptime = ra->uptime;
-                               addr_proposal->vltime = prefix->vltime;
-                               addr_proposal->pltime = prefix->pltime;
-
-                               log_debug("%s, addr state: %s", __func__,
-                                   proposal_state_name[addr_proposal->state]);
-
-                               switch (addr_proposal->state) {
-                               case PROPOSAL_CONFIGURED:
-                               case PROPOSAL_NEARLY_EXPIRED:
-                                       log_debug("updating address");
-                                       configure_address(addr_proposal);
-                                       break;
-                               default:
-                                       if (getnameinfo((struct sockaddr *)
-                                           &addr_proposal->addr,
-                                           addr_proposal->addr.sin6_len, hbuf,
-                                           sizeof(hbuf), NULL, 0,
-                                           NI_NUMERICHOST | NI_NUMERICSERV)) {
-                                               log_warn("cannot get proposal "
-                                                   "IP");
-                                               strlcpy(hbuf, "unknown",
-                                                   sizeof(hbuf));
-                                       }
-                                       log_debug("%s: iface %d: %s", __func__,
-                                           iface->if_index, hbuf);
-                                       break;
-                               }
-                       }
-
-                       if (!found)
-                               /* new proposal */
-                               gen_address_proposal(iface, ra, prefix, 0);
-
-                       if (!found_privacy && iface->autoconfprivacy)
-                               /* new privacy proposal */
-                               gen_address_proposal(iface, ra, prefix, 1);
-               }
-       }
-}
-
-void
-configure_address(struct address_proposal *addr_proposal)
-{
-       struct imsg_configure_address    address;
-       struct timeval                   tv;
-
-       addr_proposal->next_timeout = addr_proposal->pltime -
-           MAX_RTR_SOLICITATIONS * (RTR_SOLICITATION_INTERVAL + 1);
-
-       tv.tv_sec = addr_proposal->next_timeout;
-       tv.tv_usec = arc4random_uniform(1000000);
-       evtimer_add(&addr_proposal->timer, &tv);
-
-       addr_proposal->state = PROPOSAL_CONFIGURED;
-
-       log_debug("%s: %d", __func__, addr_proposal->if_index);
-
-       address.if_index = addr_proposal->if_index;
-       memcpy(&address.addr, &addr_proposal->addr, sizeof(address.addr));
-       memcpy(&address.mask, &addr_proposal->mask, sizeof(address.mask));
-       address.vltime = addr_proposal->vltime;
-       address.pltime = addr_proposal->pltime;
-       address.privacy = addr_proposal->privacy;
-
-       engine_imsg_compose_main(IMSG_CONFIGURE_ADDRESS, 0, &address,
-           sizeof(address));
-}
-
-void
-gen_address_proposal(struct slaacd_iface *iface, struct radv *ra, struct
-    radv_prefix *prefix, int privacy)
-{
-       struct address_proposal *addr_proposal;
-       struct timeval           tv;
-       char                     hbuf[NI_MAXHOST];
-
-       if ((addr_proposal = calloc(1, sizeof(*addr_proposal))) == NULL)
-               fatal("calloc");
-       evtimer_set(&addr_proposal->timer, address_proposal_timeout,
-           addr_proposal);
-       addr_proposal->next_timeout = 1;
-       addr_proposal->timeout_count = 0;
-       addr_proposal->state = PROPOSAL_NOT_CONFIGURED;
-       addr_proposal->when = ra->when;
-       addr_proposal->uptime = ra->uptime;
-       addr_proposal->if_index = iface->if_index;
-       memcpy(&addr_proposal->hw_address, &iface->hw_address,
-           sizeof(addr_proposal->hw_address));
-       addr_proposal->privacy = privacy;
-       memcpy(&addr_proposal->prefix, &prefix->prefix,
-           sizeof(addr_proposal->prefix));
-       addr_proposal->prefix_len = prefix->prefix_len;
-
-       if (privacy) {
-               if (prefix->vltime > ND6_PRIV_VALID_LIFETIME)
-                       addr_proposal->vltime = ND6_PRIV_VALID_LIFETIME;
-               else
-                       addr_proposal->vltime = prefix->vltime;
-
-               if (prefix->pltime > ND6_PRIV_PREFERRED_LIFETIME)
-                       addr_proposal->pltime = ND6_PRIV_PREFERRED_LIFETIME;
-               else
-                       addr_proposal->pltime = prefix->pltime;
-       } else {
-               addr_proposal->vltime = prefix->vltime;
-               addr_proposal->pltime = prefix->pltime;
-       }
-
-       gen_addr(iface, prefix, addr_proposal, privacy);
-
-       tv.tv_sec = 0;
-       tv.tv_usec = 0;
-       evtimer_add(&addr_proposal->timer, &tv);
-
-       LIST_INSERT_HEAD(&iface->addr_proposals, addr_proposal, entries);
-
-       if (getnameinfo((struct sockaddr *)&addr_proposal->addr,
-           addr_proposal->addr.sin6_len, hbuf, sizeof(hbuf), NULL, 0,
-           NI_NUMERICHOST | NI_NUMERICSERV)) {
-               log_warn("cannot get router IP");
-               strlcpy(hbuf, "unknown", sizeof(hbuf));
-       }
-       log_debug("%s: iface %d: %s: %lld s", __func__,
-           iface->if_index, hbuf, tv.tv_sec);
-}
-
-void
-gen_dfr_proposal(struct slaacd_iface *iface, struct radv *ra)
-{
-       struct dfr_proposal     *dfr_proposal;
-       struct timeval           tv;
-       char                     hbuf[NI_MAXHOST];
-
-       if ((dfr_proposal = calloc(1, sizeof(*dfr_proposal))) == NULL)
-               fatal("calloc");
-       evtimer_set(&dfr_proposal->timer, dfr_proposal_timeout,
-           dfr_proposal);
-       dfr_proposal->next_timeout = 1;
-       dfr_proposal->timeout_count = 0;
-       dfr_proposal->state = PROPOSAL_NOT_CONFIGURED;
-       dfr_proposal->when = ra->when;
-       dfr_proposal->uptime = ra->uptime;
-       dfr_proposal->if_index = iface->if_index;
-       memcpy(&dfr_proposal->addr, &ra->from,
-           sizeof(dfr_proposal->addr));
-       dfr_proposal->router_lifetime = ra->router_lifetime;
-       dfr_proposal->rpref = ra->rpref;
-
-       tv.tv_sec = 0;
-       tv.tv_usec = 0;
-       evtimer_add(&dfr_proposal->timer, &tv);
-
-       LIST_INSERT_HEAD(&iface->dfr_proposals, dfr_proposal, entries);
-
-       if (getnameinfo((struct sockaddr *)&dfr_proposal->addr,
-           dfr_proposal->addr.sin6_len, hbuf, sizeof(hbuf), NULL, 0,
-           NI_NUMERICHOST | NI_NUMERICSERV)) {
-               log_warn("cannot get router IP");
-               strlcpy(hbuf, "unknown", sizeof(hbuf));
-       }
-       log_debug("%s: iface %d: %s: %lld s", __func__,
-           iface->if_index, hbuf, tv.tv_sec);
-}
-
-void
-configure_dfr(struct dfr_proposal *dfr_proposal)
-{
-       struct imsg_configure_dfr        dfr;
-       struct timeval                   tv;
-       enum proposal_state              prev_state;
-
-       dfr_proposal->next_timeout = dfr_proposal->router_lifetime -
-           MAX_RTR_SOLICITATIONS * (RTR_SOLICITATION_INTERVAL + 1);
-
-       tv.tv_sec = dfr_proposal->next_timeout;
-       tv.tv_usec = arc4random_uniform(1000000);
-       evtimer_add(&dfr_proposal->timer, &tv);
-
-       prev_state = dfr_proposal->state;
-
-       dfr_proposal->state = PROPOSAL_CONFIGURED;
-
-       log_debug("%s: %d", __func__, dfr_proposal->if_index);
-
-       if (prev_state == PROPOSAL_CONFIGURED || prev_state ==
-           PROPOSAL_NEARLY_EXPIRED) {
-               /*
-                * nothing to do here, routes do not expire in the kernel
-                * XXX check if the route got deleted and re-add it?
-                */
-               return;
-       }
-
-       dfr.if_index = dfr_proposal->if_index;
-       memcpy(&dfr.addr, &dfr_proposal->addr, sizeof(dfr.addr));
-       dfr.router_lifetime = dfr_proposal->router_lifetime;
-
-       engine_imsg_compose_main(IMSG_CONFIGURE_DFR, 0, &dfr, sizeof(dfr));
-}
-
-void
-withdraw_dfr(struct dfr_proposal *dfr_proposal)
-{
-       struct imsg_configure_dfr        dfr;
-
-       log_debug("%s: %d", __func__, dfr_proposal->if_index);
-
-       dfr.if_index = dfr_proposal->if_index;
-       memcpy(&dfr.addr, &dfr_proposal->addr, sizeof(dfr.addr));
-       dfr.router_lifetime = dfr_proposal->router_lifetime;
-
-       engine_imsg_compose_main(IMSG_WITHDRAW_DFR, 0, &dfr, sizeof(dfr));
-}
-
-void
-free_dfr_proposal(struct dfr_proposal *dfr_proposal)
-{
-
-       LIST_REMOVE(dfr_proposal, entries);
-       evtimer_del(&dfr_proposal->timer);
-       switch (dfr_proposal->state) {
-       case PROPOSAL_CONFIGURED:
-       case PROPOSAL_NEARLY_EXPIRED:
-               withdraw_dfr(dfr_proposal);
-               break;
-       default:
-               break;
-       }
-       free(dfr_proposal);
-}
-
-void
-send_proposal(struct imsg_proposal *proposal)
-{
-#ifndef SKIP_PROPOSAL
-       engine_imsg_compose_main(IMSG_PROPOSAL, 0, proposal, sizeof(*proposal));
-#else
-       struct imsg_proposal_ack        ack;
-       ack.id = proposal->id;
-       ack.pid = proposal->pid;
-       ack.if_index = proposal->if_index;
-       engine_imsg_compose_frontend(IMSG_FAKE_ACK, 0, &ack, sizeof(ack));
-#endif
-}
-
-void
-start_probe(struct slaacd_iface *iface)
-{
-       struct timeval  tv;
-
-       iface->state = IF_DELAY;
-       iface->probes = 0;
-
-       tv.tv_sec = 0;
-       tv.tv_usec = arc4random_uniform(MAX_RTR_SOLICITATION_DELAY_USEC);
-
-       log_debug("%s: iface %d: sleeping for %ldusec", __func__,
-           iface->if_index, tv.tv_usec);
-
-       evtimer_add(&iface->timer, &tv);
-}
-
-void
-address_proposal_timeout(int fd, short events, void *arg)
-{
-       struct address_proposal *addr_proposal;
-       struct imsg_proposal     proposal;
-       struct timeval           tv;
-       char                     hbuf[NI_MAXHOST];
-
-       addr_proposal = (struct address_proposal *)arg;
-
-       if (getnameinfo((struct sockaddr *)&addr_proposal->addr,
-           addr_proposal->addr.sin6_len, hbuf, sizeof(hbuf), NULL, 0,
-           NI_NUMERICHOST | NI_NUMERICSERV)) {
-               log_warn("cannot get router IP");
-               strlcpy(hbuf, "unknown", sizeof(hbuf));
-       }
-       log_debug("%s: iface %d: %s [%s], priv: %s", __func__,
-           addr_proposal->if_index, hbuf,
-           proposal_state_name[addr_proposal->state],
-           addr_proposal->privacy ? "y" : "n");
-
-       switch (addr_proposal->state) {
-       case PROPOSAL_NOT_CONFIGURED:
-       case PROPOSAL_SENT:
-               if (addr_proposal->timeout_count++ < 6) {
-                       addr_proposal->id = ++proposal_id;
-
-                       memset(&proposal, 0, sizeof(proposal));
-                       proposal.if_index = addr_proposal->if_index;
-                       proposal.pid = getpid();
-                       proposal.id = addr_proposal->id;
-                       memcpy(&proposal.addr, &addr_proposal->addr,
-                           sizeof(proposal.addr));
-                       memcpy(&proposal.mask, &addr_proposal->mask,
-                           sizeof(proposal.mask));
-
-                       proposal.rtm_addrs = RTA_NETMASK | RTA_IFA;
-
-                       addr_proposal->state = PROPOSAL_SENT;
-
-                       send_proposal(&proposal);
-
-                       tv.tv_sec = addr_proposal->next_timeout;
-                       tv.tv_usec = arc4random_uniform(1000000);
-                       addr_proposal->next_timeout *= 2;
-                       evtimer_add(&addr_proposal->timer, &tv);
-                       log_debug("%s: scheduling new timeout in %llds.%06ld",
-                           __func__, tv.tv_sec, tv.tv_usec);
-               } else {
-                       log_debug("%s: giving up, no response to proposal",
-                           __func__);
-                       LIST_REMOVE(addr_proposal, entries);
-                       evtimer_del(&addr_proposal->timer);
-                       free(addr_proposal);
-               }
-               break;
-       case PROPOSAL_CONFIGURED:
-               log_debug("PROPOSAL_CONFIGURED timeout: id: %lld, privacy: %s",
-                   addr_proposal->id, addr_proposal->privacy ? "y" : "n");
-
-               addr_proposal->next_timeout = 1;
-               addr_proposal->timeout_count = 0;
-               addr_proposal->state = PROPOSAL_NEARLY_EXPIRED;
-
-               tv.tv_sec = 0;
-               tv.tv_usec = 0;
-               evtimer_add(&addr_proposal->timer, &tv);
-
-               break;
-       case PROPOSAL_NEARLY_EXPIRED:
-               log_debug("%s: rl: %d", __func__,
-                   real_lifetime(&addr_proposal->uptime,
-                   addr_proposal->vltime));
-               /*
-                * we should have gotten a RTM_DELADDR from the kernel,
-                * in case we missed it, delete to not waste memory
-                */
-               if (real_lifetime(&addr_proposal->uptime,
-                   addr_proposal->vltime) == 0) {
-                       evtimer_del(&addr_proposal->timer);
-                       LIST_REMOVE(addr_proposal, entries);
-                       free(addr_proposal);
-                       log_debug("%s: removing address proposal", __func__);
-                       break;
-               }
-               if (addr_proposal->privacy)
-                       break; /* just let it expire */
-
-               engine_imsg_compose_frontend(IMSG_CTL_SEND_SOLICITATION,
-                   0, &addr_proposal->if_index,
-                   sizeof(addr_proposal->if_index));
-               tv.tv_sec = addr_proposal->next_timeout;
-               tv.tv_usec = arc4random_uniform(1000000);
-               addr_proposal->next_timeout *= 2;
-               evtimer_add(&addr_proposal->timer, &tv);
-               log_debug("%s: scheduling new timeout in %llds.%06ld",
-                   __func__, tv.tv_sec, tv.tv_usec);
-               break;
-       default:
-               log_debug("%s: unhandled state: %s", __func__,
-                   proposal_state_name[addr_proposal->state]);
-       }
-}
-
-void
-dfr_proposal_timeout(int fd, short events, void *arg)
-{
-       struct dfr_proposal     *dfr_proposal;
-       struct imsg_proposal     proposal;
-       struct timeval           tv;
-       char                     hbuf[NI_MAXHOST];
-
-       dfr_proposal = (struct dfr_proposal *)arg;
-
-       if (getnameinfo((struct sockaddr *)&dfr_proposal->addr,
-           dfr_proposal->addr.sin6_len, hbuf, sizeof(hbuf), NULL, 0,
-           NI_NUMERICHOST | NI_NUMERICSERV)) {
-               log_warn("cannot get router IP");
-               strlcpy(hbuf, "unknown", sizeof(hbuf));
-       }
-       log_debug("%s: iface %d: %s [%s]", __func__, dfr_proposal->if_index,
-           hbuf, proposal_state_name[dfr_proposal->state]);
-
-       switch (dfr_proposal->state) {
-       case PROPOSAL_NOT_CONFIGURED:
-       case PROPOSAL_SENT:
-               if (dfr_proposal->timeout_count++ < 6) {
-                       dfr_proposal->id = ++proposal_id;
-
-                       memset(&proposal, 0, sizeof(proposal));
-                       proposal.if_index = dfr_proposal->if_index;
-                       proposal.pid = getpid();
-                       proposal.id = dfr_proposal->id;
-                       memcpy(&proposal.addr, &dfr_proposal->addr,
-                           sizeof(proposal.addr));
-
-                       proposal.rtm_addrs = RTA_GATEWAY;
-
-                       dfr_proposal->state = PROPOSAL_SENT;
-
-                       send_proposal(&proposal);
-
-                       tv.tv_sec = dfr_proposal->next_timeout;
-                       tv.tv_usec = arc4random_uniform(1000000);
-                       dfr_proposal->next_timeout *= 2;
-                       evtimer_add(&dfr_proposal->timer, &tv);
-                       log_debug("%s: scheduling new timeout in %llds.%06ld",
-                           __func__, tv.tv_sec, tv.tv_usec);
-               } else {
-                       log_debug("%s: giving up, no response to proposal",
-                           __func__);
-                       free_dfr_proposal(dfr_proposal);
-               }
-               break;
-       case PROPOSAL_CONFIGURED:
-               log_debug("PROPOSAL_CONFIGURED timeout: id: %lld",
-                   dfr_proposal->id);
-
-               dfr_proposal->next_timeout = 1;
-               dfr_proposal->timeout_count = 0;
-               dfr_proposal->state = PROPOSAL_NEARLY_EXPIRED;
-
-               tv.tv_sec = 0;
-               tv.tv_usec = 0;
-               evtimer_add(&dfr_proposal->timer, &tv);
-
-               break;
-       case PROPOSAL_NEARLY_EXPIRED:
-               if (real_lifetime(&dfr_proposal->uptime,
-                   dfr_proposal->router_lifetime) == 0) {
-                       free_dfr_proposal(dfr_proposal);
-                       log_debug("%s: removing dfr proposal", __func__);
-                       break;
-               }
-               engine_imsg_compose_frontend(IMSG_CTL_SEND_SOLICITATION,
-                   0, &dfr_proposal->if_index,
-                   sizeof(dfr_proposal->if_index));
-               tv.tv_sec = dfr_proposal->next_timeout;
-               tv.tv_usec = arc4random_uniform(1000000);
-               dfr_proposal->next_timeout *= 2;
-               evtimer_add(&dfr_proposal->timer, &tv);
-               log_debug("%s: scheduling new timeout in %llds.%06ld",
-                   __func__, tv.tv_sec, tv.tv_usec);
-               break;
-       default:
-               log_debug("%s: unhandled state: %s", __func__,
-                   proposal_state_name[dfr_proposal->state]);
-       }
-}
-
-void
-iface_timeout(int fd, short events, void *arg)
-{
-       struct slaacd_iface     *iface = (struct slaacd_iface *)arg;
-       struct timeval           tv;
-
-       log_debug("%s[%d]: %s", __func__, iface->if_index,
-           if_state_name[iface->state]);
-
-       switch (iface->state) {
-               case IF_DELAY:
-               case IF_PROBE:
-                       iface->state = IF_PROBE;
-                       engine_imsg_compose_frontend(
-                           IMSG_CTL_SEND_SOLICITATION, 0, &iface->if_index,
-                           sizeof(iface->if_index));
-                       if (++iface->probes >= MAX_RTR_SOLICITATIONS)
-                               iface->state = IF_IDLE;
-                       else {
-                               tv.tv_sec = RTR_SOLICITATION_INTERVAL;
-                               tv.tv_usec = arc4random_uniform(1000000);
-                               evtimer_add(&iface->timer, &tv);
-                       }
-                       break;
-               case IF_DOWN:
-               case IF_IDLE:
-               default:
-                       break;
-       }
-}
-
-struct radv*
-find_ra(struct slaacd_iface *iface, struct sockaddr_in6 *from)
-{
-       struct radv     *ra;
-
-       LIST_FOREACH (ra, &iface->radvs, entries) {
-               if (memcmp(&ra->from.sin6_addr, &from->sin6_addr,
-                   sizeof(from->sin6_addr)) == 0)
-                       return (ra);
-       }
-
-       return (NULL);
-}
-
-struct address_proposal*
-find_address_proposal_by_id(struct slaacd_iface *iface, int64_t id)
-{
-       struct address_proposal *addr_proposal;
-
-       LIST_FOREACH (addr_proposal, &iface->addr_proposals, entries) {
-               if (addr_proposal->id == id)
-                       return (addr_proposal);
-       }
-
-       return (NULL);
-}
-
-struct address_proposal*
-find_address_proposal_by_addr(struct slaacd_iface *iface, struct sockaddr_in6
-    *addr)
-{
-       struct address_proposal *addr_proposal;
-
-       LIST_FOREACH (addr_proposal, &iface->addr_proposals, entries) {
-               if (memcmp(&addr_proposal->addr, addr, sizeof(*addr)) == 0)
-                       return (addr_proposal);
-       }
-
-       return (NULL);
-}
-
-struct dfr_proposal*
-find_dfr_proposal_by_id(struct slaacd_iface *iface, int64_t id)
-{
-       struct dfr_proposal     *dfr_proposal;
-
-       LIST_FOREACH (dfr_proposal, &iface->dfr_proposals, entries) {
-               if (dfr_proposal->id == id)
-                       return (dfr_proposal);
-       }
-
-       return (NULL);
-}
-
-
-/* XXX currently unused */
-void
-find_prefix(struct slaacd_iface *iface, struct address_proposal *addr_proposal,
-    struct radv **result_ra, struct radv_prefix **result_prefix)
-{
-       struct radv             *ra;
-       struct radv_prefix      *prefix;
-       uint32_t                 lifetime, max_lifetime = 0;
-
-       *result_ra = NULL;
-       *result_prefix = NULL;
-
-       LIST_FOREACH(ra, &iface->radvs, entries) {
-               LIST_FOREACH(prefix, &ra->prefixes, entries) {
-                       if (memcmp(&prefix->prefix, &addr_proposal->prefix,
-                           sizeof(addr_proposal->prefix)) != 0)
-                               continue;
-                       lifetime = real_lifetime(&ra->uptime,
-                           prefix->vltime);
-                       if (lifetime > max_lifetime) {
-                               max_lifetime = lifetime;
-                               *result_ra = ra;
-                               *result_prefix = prefix;
-                       }
-               }
-       }
-}
-
-uint32_t
-real_lifetime(struct timespec *received_uptime, uint32_t ltime)
-{
-       struct timespec  now, diff;
-       int64_t          remaining;
-
-       if (clock_gettime(CLOCK_MONOTONIC, &now))
-               fatal("clock_gettime");
-
-       timespecsub(&now, received_uptime, &diff);
-
-       remaining = ((int64_t)ltime) - diff.tv_sec;
-
-       if (remaining < 0)
-               remaining = 0;
-
-       return (remaining);
-}
diff --git a/usr.sbin/slaacd/engine.h b/usr.sbin/slaacd/engine.h
deleted file mode 100644 (file)
index 0956f7d..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*     $OpenBSD: engine.h,v 1.7 2017/05/28 19:57:38 florian Exp $      */
-
-/*
- * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-struct imsg_proposal {
-       uint32_t                        if_index;
-       pid_t                           pid;
-       int64_t                         id;
-       struct sockaddr_in6             addr;
-       struct in6_addr                 mask;
-       struct sockaddr_in6             gateway;
-       struct sockaddr_rtdns           rdns;
-       struct sockaddr_rtsearch        dnssl;
-       int                             rtm_addrs;
-};
-
-struct imsg_configure_address {
-       uint32_t                 if_index;
-       struct sockaddr_in6      addr;
-       struct in6_addr          mask;
-       uint32_t                 vltime;
-       uint32_t                 pltime;
-       int                      privacy;
-};
-
-struct imsg_configure_dfr {
-       uint32_t                 if_index;
-       struct sockaddr_in6      addr;
-       uint32_t                 router_lifetime;
-};
-
-void            engine(int, int);
-int             engine_imsg_compose_frontend(int, pid_t, void *, uint16_t);
diff --git a/usr.sbin/slaacd/frontend.c b/usr.sbin/slaacd/frontend.c
deleted file mode 100644 (file)
index 39198f1..0000000
+++ /dev/null
@@ -1,826 +0,0 @@
-/*     $OpenBSD: frontend.c,v 1.16 2017/05/31 07:14:58 florian Exp $   */
-
-/*
- * Copyright (c) 2017 Florian Obser <florian@openbsd.org>
- * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
- * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/queue.h>
-#include <sys/socket.h>
-#include <sys/syslog.h>
-#include <sys/uio.h>
-
-#include <net/if.h>
-#include <net/if_dl.h>
-#include <net/if_types.h>
-#include <net/route.h>
-
-#include <arpa/inet.h>
-
-#include <netinet/in.h>
-#include <netinet/if_ether.h>
-#include <netinet/ip6.h>
-#include <netinet6/ip6_var.h>
-#include <netinet/icmp6.h>
-
-#include <errno.h>
-#include <event.h>
-#include <ifaddrs.h>
-#include <imsg.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "log.h"
-#include "slaacd.h"
-#include "frontend.h"
-#include "control.h"
-
-#define        ROUTE_SOCKET_BUF_SIZE   16384
-#define        ALLROUTER               "ff02::2"
-
-__dead void     frontend_shutdown(void);
-void            frontend_sig_handler(int, short, void *);
-void            update_iface(uint32_t, char*);
-void            frontend_startup(void);
-void            route_receive(int, short, void *);
-void            handle_route_message(struct rt_msghdr *, struct sockaddr **);
-void            get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
-void            icmp6_receive(int, short, void *);
-int             get_flags(char *);
-int             get_xflags(char *);
-void            get_lladdr(char *, struct ether_addr *, struct sockaddr_in6 *);
-void            send_solicitation(uint32_t);
-
-struct imsgev                  *iev_main;
-struct imsgev                  *iev_engine;
-struct event                    ev_route;
-struct msghdr                   sndmhdr;
-struct iovec                    sndiov[4];
-struct nd_router_solicit        rs;
-struct nd_opt_hdr               nd_opt_hdr;
-struct ether_addr               nd_opt_source_link_addr;
-struct sockaddr_in6             dst;
-int             icmp6sock, routesock, xflagssock;
-
-struct icmp6_ev {
-       struct event             ev;
-       uint8_t                  answer[1500];
-       struct msghdr            rcvmhdr;
-       struct iovec             rcviov[1];
-       struct sockaddr_in6      from;
-} icmp6ev;
-
-void
-frontend_sig_handler(int sig, short event, void *bula)
-{
-       /*
-        * Normal signal handler rules don't apply because libevent
-        * decouples for us.
-        */
-
-       switch (sig) {
-       case SIGINT:
-       case SIGTERM:
-               frontend_shutdown();
-       default:
-               fatalx("unexpected signal");
-       }
-}
-
-void
-frontend(int debug, int verbose, char *sockname)
-{
-       struct event             ev_sigint, ev_sigterm;
-       struct passwd           *pw;
-       struct icmp6_filter      filt;
-       struct in6_pktinfo      *pi;
-       struct cmsghdr          *cm;
-       size_t                   rcvcmsglen, sndcmsglen;
-       int                      hoplimit = 255, on = 1, rtfilter;
-       uint8_t                 *rcvcmsgbuf, *sndcmsgbuf;
-
-       log_init(debug, LOG_DAEMON);
-       log_setverbose(verbose);
-
-       /* Create slaacd control socket outside chroot. */
-       if (control_init(sockname) == -1)
-               fatalx("control socket setup failed");
-
-       if ((pw = getpwnam(SLAACD_USER)) == NULL)
-               fatal("getpwnam");
-
-       if (chroot(pw->pw_dir) == -1)
-               fatal("chroot");
-       if (chdir("/") == -1)
-               fatal("chdir(\"/\")");
-
-       slaacd_process = PROC_FRONTEND;
-       setproctitle("%s", log_procnames[slaacd_process]);
-       log_procinit(log_procnames[slaacd_process]);
-
-       if ((icmp6sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0)
-               fatal("ICMPv6 socket");
-
-       if ((routesock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0)
-               fatal("route socket");
-
-       if ((xflagssock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
-               fatal("socket");
-
-       if (setgroups(1, &pw->pw_gid) ||
-           setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
-           setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
-               fatal("can't drop privileges");
-
-       if (setsockopt(icmp6sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
-           sizeof(on)) < 0)
-               fatal("IPV6_RECVPKTINFO");
-
-       if (setsockopt(icmp6sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
-           sizeof(on)) < 0)
-               fatal("IPV6_RECVHOPLIMIT");
-
-       /* only router advertisements */
-       ICMP6_FILTER_SETBLOCKALL(&filt);
-       ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
-       if (setsockopt(icmp6sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
-           sizeof(filt)) == -1)
-               fatal("ICMP6_FILTER");
-
-       rtfilter = ROUTE_FILTER(RTM_IFINFO) | ROUTE_FILTER(RTM_NEWADDR) |
-           ROUTE_FILTER(RTM_DELADDR) | ROUTE_FILTER(RTM_PROPOSAL);
-       if (setsockopt(routesock, PF_ROUTE, ROUTE_MSGFILTER,
-           &rtfilter, sizeof(rtfilter)) < 0)
-               fatal("setsockopt(ROUTE_MSGFILTER)");
-
-       if (pledge("stdio inet recvfd route", NULL) == -1)
-               fatal("pledge");
-
-       event_init();
-
-       /* Setup signal handler. */
-       signal_set(&ev_sigint, SIGINT, frontend_sig_handler, NULL);
-       signal_set(&ev_sigterm, SIGTERM, frontend_sig_handler, NULL);
-       signal_add(&ev_sigint, NULL);
-       signal_add(&ev_sigterm, NULL);
-       signal(SIGPIPE, SIG_IGN);
-       signal(SIGHUP, SIG_IGN);
-
-       /* Setup pipe and event handler to the parent process. */
-       if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
-               fatal(NULL);
-       imsg_init(&iev_main->ibuf, 3);
-       iev_main->handler = frontend_dispatch_main;
-       iev_main->events = EV_READ;
-       event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
-           iev_main->handler, iev_main);
-       event_add(&iev_main->ev, NULL);
-
-       /* Listen on control socket. */
-       TAILQ_INIT(&ctl_conns);
-       control_listen();
-
-       event_set(&ev_route, routesock, EV_READ | EV_PERSIST, route_receive,
-           NULL);
-
-       event_set(&icmp6ev.ev, icmp6sock, EV_READ | EV_PERSIST, icmp6_receive,
-           NULL);
-
-       rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
-           CMSG_SPACE(sizeof(int));
-       if((rcvcmsgbuf = malloc(rcvcmsglen)) == NULL)
-               fatal("malloc");
-
-       icmp6ev.rcviov[0].iov_base = (caddr_t)icmp6ev.answer;
-       icmp6ev.rcviov[0].iov_len = sizeof(icmp6ev.answer);
-       icmp6ev.rcvmhdr.msg_name = (caddr_t)&icmp6ev.from;
-       icmp6ev.rcvmhdr.msg_namelen = sizeof(icmp6ev.from);
-       icmp6ev.rcvmhdr.msg_iov = icmp6ev.rcviov;
-       icmp6ev.rcvmhdr.msg_iovlen = 1;
-       icmp6ev.rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf;
-       icmp6ev.rcvmhdr.msg_controllen = rcvcmsglen;
-
-       sndcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
-           CMSG_SPACE(sizeof(int));
-
-       if ((sndcmsgbuf = malloc(sndcmsglen)) == NULL)
-               fatal("malloc");
-
-       rs.nd_rs_type = ND_ROUTER_SOLICIT;
-       rs.nd_rs_code = 0;
-       rs.nd_rs_cksum = 0;
-       rs.nd_rs_reserved = 0;
-
-       nd_opt_hdr.nd_opt_type = ND_OPT_SOURCE_LINKADDR;
-       nd_opt_hdr.nd_opt_len = 1;
-
-       memset(&dst, 0, sizeof(dst));
-       dst.sin6_family = AF_INET6;
-       if (inet_pton(AF_INET6, ALLROUTER, &dst.sin6_addr.s6_addr) != 1)
-               fatal("inet_pton");
-
-       sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
-       sndmhdr.msg_iov = sndiov;
-       sndmhdr.msg_iovlen = 3;
-       sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
-       sndmhdr.msg_controllen = sndcmsglen;
-
-       sndmhdr.msg_name = (caddr_t)&dst;
-       sndmhdr.msg_iov[0].iov_base = (caddr_t)&rs;
-       sndmhdr.msg_iov[0].iov_len = sizeof(rs);
-       sndmhdr.msg_iov[1].iov_base = (caddr_t)&nd_opt_hdr;
-       sndmhdr.msg_iov[1].iov_len = sizeof(nd_opt_hdr);
-       sndmhdr.msg_iov[2].iov_base = (caddr_t)&nd_opt_source_link_addr;
-       sndmhdr.msg_iov[2].iov_len = sizeof(nd_opt_source_link_addr);
-
-       cm = CMSG_FIRSTHDR(&sndmhdr);
-
-       cm->cmsg_level = IPPROTO_IPV6;
-       cm->cmsg_type = IPV6_PKTINFO;
-       cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
-       pi = (struct in6_pktinfo *)CMSG_DATA(cm);
-       memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr));
-       pi->ipi6_ifindex = 0;
-
-       cm = CMSG_NXTHDR(&sndmhdr, cm);
-       cm->cmsg_level = IPPROTO_IPV6;
-       cm->cmsg_type = IPV6_HOPLIMIT;
-       cm->cmsg_len = CMSG_LEN(sizeof(int));
-       memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
-
-       event_dispatch();
-
-       frontend_shutdown();
-}
-
-__dead void
-frontend_shutdown(void)
-{
-       /* Close pipes. */
-       msgbuf_write(&iev_engine->ibuf.w);
-       msgbuf_clear(&iev_engine->ibuf.w);
-       close(iev_engine->ibuf.fd);
-       msgbuf_write(&iev_main->ibuf.w);
-       msgbuf_clear(&iev_main->ibuf.w);
-       close(iev_main->ibuf.fd);
-
-       free(iev_engine);
-       free(iev_main);
-
-       log_info("frontend exiting");
-       exit(0);
-}
-
-int
-frontend_imsg_compose_main(int type, pid_t pid, void *data,
-    uint16_t datalen)
-{
-       return (imsg_compose_event(iev_main, type, 0, pid, -1, data,
-           datalen));
-}
-
-int
-frontend_imsg_compose_engine(int type, uint32_t peerid, pid_t pid,
-    void *data, uint16_t datalen)
-{
-       return (imsg_compose_event(iev_engine, type, peerid, pid, -1,
-           data, datalen));
-}
-
-void
-frontend_dispatch_main(int fd, short event, void *bula)
-{
-       struct imsg              imsg;
-       struct imsgev           *iev = bula;
-       struct imsgbuf          *ibuf = &iev->ibuf;
-       ssize_t                  n;
-       int                      shut = 0;
-
-       if (event & EV_READ) {
-               if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
-                       fatal("imsg_read error");
-               if (n == 0)     /* Connection closed. */
-                       shut = 1;
-       }
-       if (event & EV_WRITE) {
-               if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
-                       fatal("msgbuf_write");
-               if (n == 0)     /* Connection closed. */
-                       shut = 1;
-       }
-
-       for (;;) {
-               if ((n = imsg_get(ibuf, &imsg)) == -1)
-                       fatal("%s: imsg_get error", __func__);
-               if (n == 0)     /* No more messages. */
-                       break;
-
-               switch (imsg.hdr.type) {
-               case IMSG_SOCKET_IPC:
-                       /*
-                        * Setup pipe and event handler to the engine
-                        * process.
-                        */
-                       if (iev_engine) {
-                               log_warnx("%s: received unexpected imsg fd "
-                                   "to frontend", __func__);
-                               break;
-                       }
-                       if ((fd = imsg.fd) == -1) {
-                               log_warnx("%s: expected to receive imsg fd to "
-                                  "frontend but didn't receive any",
-                                  __func__);
-                               break;
-                       }
-
-                       iev_engine = malloc(sizeof(struct imsgev));
-                       if (iev_engine == NULL)
-                               fatal(NULL);
-
-                       imsg_init(&iev_engine->ibuf, fd);
-                       iev_engine->handler = frontend_dispatch_engine;
-                       iev_engine->events = EV_READ;
-
-                       event_set(&iev_engine->ev, iev_engine->ibuf.fd,
-                       iev_engine->events, iev_engine->handler, iev_engine);
-                       event_add(&iev_engine->ev, NULL);
-
-                       if (pledge("stdio inet route", NULL) == -1)
-                               fatal("pledge");
-
-                       break;
-               case IMSG_STARTUP:
-                       frontend_startup();
-                       break;
-               case IMSG_CTL_END:
-                       control_imsg_relay(&imsg);
-                       break;
-               default:
-                       log_debug("%s: error handling imsg %d", __func__,
-                           imsg.hdr.type);
-                       break;
-               }
-               imsg_free(&imsg);
-       }
-       if (!shut)
-               imsg_event_add(iev);
-       else {
-               /* This pipe is dead. Remove its event handler. */
-               event_del(&iev->ev);
-               event_loopexit(NULL);
-       }
-}
-
-void
-frontend_dispatch_engine(int fd, short event, void *bula)
-{
-       struct imsgev           *iev = bula;
-       struct imsgbuf          *ibuf = &iev->ibuf;
-       struct imsg              imsg;
-       ssize_t                  n;
-       int                      shut = 0;
-       uint32_t                 if_index;
-
-       if (event & EV_READ) {
-               if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
-                       fatal("imsg_read error");
-               if (n == 0)     /* Connection closed. */
-                       shut = 1;
-       }
-       if (event & EV_WRITE) {
-               if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
-                       fatal("msgbuf_write");
-               if (n == 0)     /* Connection closed. */
-                       shut = 1;
-       }
-
-       for (;;) {
-               if ((n = imsg_get(ibuf, &imsg)) == -1)
-                       fatal("%s: imsg_get error", __func__);
-               if (n == 0)     /* No more messages. */
-                       break;
-
-               switch (imsg.hdr.type) {
-               case IMSG_CTL_END:
-               case IMSG_CTL_SHOW_INTERFACE_INFO:
-               case IMSG_CTL_SHOW_INTERFACE_INFO_RA:
-               case IMSG_CTL_SHOW_INTERFACE_INFO_RA_PREFIX:
-               case IMSG_CTL_SHOW_INTERFACE_INFO_RA_RDNS:
-               case IMSG_CTL_SHOW_INTERFACE_INFO_RA_DNSSL:
-               case IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSALS:
-               case IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSAL:
-               case IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSALS:
-               case IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSAL:
-                       control_imsg_relay(&imsg);
-                       break;
-               case IMSG_CTL_SEND_SOLICITATION:
-                       if_index = *((uint32_t *)imsg.data);
-                       send_solicitation(if_index);
-                       break;
-               case IMSG_FAKE_ACK:
-                       frontend_imsg_compose_engine(IMSG_PROPOSAL_ACK,
-                          0, 0, imsg.data, sizeof(struct imsg_proposal_ack));
-                       break;
-               default:
-                       log_debug("%s: error handling imsg %d", __func__,
-                           imsg.hdr.type);
-                       break;
-               }
-               imsg_free(&imsg);
-       }
-       if (!shut)
-               imsg_event_add(iev);
-       else {
-               /* This pipe is dead. Remove its event handler. */
-               event_del(&iev->ev);
-               event_loopexit(NULL);
-       }
-}
-
-int
-get_flags(char *if_name)
-{
-       struct ifreq             ifr;
-
-       (void) strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
-       if (ioctl(xflagssock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0)
-               fatal("SIOCGIFFLAGS");
-       return ifr.ifr_flags;
-}
-
-int
-get_xflags(char *if_name)
-{
-       struct ifreq             ifr;
-
-       (void) strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
-       if (ioctl(xflagssock, SIOCGIFXFLAGS, (caddr_t)&ifr) < 0)
-               fatal("SIOCGIFXFLAGS");
-       return ifr.ifr_flags;
-}
-
-void
-update_iface(uint32_t if_index, char* if_name)
-{
-       struct imsg_ifinfo       imsg_ifinfo;
-       int                      flags, xflags;
-
-       flags = get_flags(if_name);
-       xflags = get_xflags(if_name);
-
-       if (!(xflags & IFXF_AUTOCONF6))
-               return;
-
-       memset(&imsg_ifinfo, 0, sizeof(imsg_ifinfo));
-
-       imsg_ifinfo.if_index = if_index;
-       imsg_ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP |
-           IFF_RUNNING);
-       imsg_ifinfo.autoconfprivacy = !(xflags & IFXF_INET6_NOPRIVACY);
-       get_lladdr(if_name, &imsg_ifinfo.hw_address, &imsg_ifinfo.ll_address);
-
-       memcpy(&nd_opt_source_link_addr, &imsg_ifinfo.hw_address,
-           sizeof(nd_opt_source_link_addr));
-
-       frontend_imsg_compose_engine(IMSG_UPDATE_IF, 0, 0, &imsg_ifinfo,
-           sizeof(imsg_ifinfo));
-}
-
-void
-frontend_startup(void)
-{
-       struct if_nameindex     *ifnidxp, *ifnidx;
-
-       event_add(&ev_route, NULL);
-       event_add(&icmp6ev.ev, NULL);
-
-       if ((ifnidxp = if_nameindex()) == NULL)
-               fatalx("if_nameindex");
-
-       for(ifnidx = ifnidxp; ifnidx->if_index !=0 && ifnidx->if_name != NULL;
-           ifnidx++)
-               update_iface(ifnidx->if_index, ifnidx->if_name);
-
-       if_freenameindex(ifnidxp);
-}
-
-void
-route_receive(int fd, short events, void *arg)
-{
-       static uint8_t                   buf[ROUTE_SOCKET_BUF_SIZE];
-
-       struct rt_msghdr                *rtm;
-       struct sockaddr                 *sa, *rti_info[RTAX_MAX];
-       size_t                           len, offset;
-       ssize_t                          n;
-       char                            *next;
-
-       if ((n = read(fd, &buf, sizeof(buf))) == -1) {
-               if (errno == EAGAIN || errno == EINTR)
-                       return;
-               log_warn("dispatch_rtmsg: read error");
-               return;
-       }
-
-       if (n == 0) {
-               log_warnx("routing socket closed");
-               return;
-       }
-
-       len = n;
-       for (offset = 0; offset < len; offset += rtm->rtm_msglen) {
-               next = buf + offset;
-               rtm = (struct rt_msghdr *)next;
-               if (len < offset + sizeof(u_short) ||
-                   len < offset + rtm->rtm_msglen)
-                       fatalx("rtmsg_process: partial rtm in buffer");
-               if (rtm->rtm_version != RTM_VERSION)
-                       continue;
-
-               sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
-               get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
-
-               handle_route_message(rtm, rti_info);
-       }
-}
-
-void
-handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info)
-{
-       struct if_msghdr                *ifm;
-       struct imsg_proposal_ack         proposal_ack;
-       struct imsg_del_addr             del_addr;
-       struct sockaddr_rtlabel         *rl;
-       int64_t                          id, pid;
-       int                              flags, xflags, if_index;
-       char                             ifnamebuf[IFNAMSIZ];
-       char                            *if_name;
-       char                            **ap, *argv[4], *p;
-       const char                      *errstr;
-
-       switch (rtm->rtm_type) {
-       case RTM_IFINFO:
-               ifm = (struct if_msghdr *)rtm;
-               if_name = if_indextoname(ifm->ifm_index, ifnamebuf);
-               if (if_name == NULL) {
-                       log_debug("RTM_IFINFO: lost if %d", ifm->ifm_index);
-                       if_index = ifm->ifm_index;
-                       frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
-                           &if_index, sizeof(if_index));
-               } else {
-                       xflags = get_xflags(if_name);
-                       flags = get_flags(if_name);
-                       if (!(xflags & IFXF_AUTOCONF6)) {
-                               log_debug("RTM_IFINFO: %s(%d) no(longer) "
-                                  "autoconf6", if_name, ifm->ifm_index);
-                               if_index = ifm->ifm_index;
-                               frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0,
-                                   0, &if_index, sizeof(if_index));
-                       } else
-                               update_iface(ifm->ifm_index, if_name);
-               }
-               break;
-       case RTM_NEWADDR:
-               ifm = (struct if_msghdr *)rtm;
-               if_name = if_indextoname(ifm->ifm_index, ifnamebuf);
-               log_debug("RTM_NEWADDR: %s[%u]", if_name, ifm->ifm_index);
-               update_iface(ifm->ifm_index, if_name);
-               break;
-       case RTM_DELADDR:
-               ifm = (struct if_msghdr *)rtm;
-               if_name = if_indextoname(ifm->ifm_index, ifnamebuf);
-               if (rtm->rtm_addrs & RTA_IFA && rti_info[RTAX_IFA]->sa_family
-                   == AF_INET6) {
-                       del_addr.if_index = ifm->ifm_index;
-                       memcpy(&del_addr.addr, rti_info[RTAX_IFA], sizeof(
-                           del_addr.addr));
-                       frontend_imsg_compose_engine(IMSG_DEL_ADDRESS,
-                                   0, 0, &del_addr, sizeof(del_addr));
-                       log_debug("RTM_DELADDR: %s[%u]", if_name,
-                           ifm->ifm_index);
-               }
-               break;
-       case RTM_PROPOSAL:
-               ifm = (struct if_msghdr *)rtm;
-               if_name = if_indextoname(ifm->ifm_index, ifnamebuf);
-
-               if ((rtm->rtm_flags & (RTF_DONE | RTF_PROTO1)) ==
-                   (RTF_DONE | RTF_PROTO1) && rtm->rtm_addrs == RTA_LABEL) {
-                       rl = (struct sockaddr_rtlabel *)rti_info[RTAX_LABEL];
-                       /* XXX validate rl */
-
-                       p = rl->sr_label;
-
-                       for (ap = argv; ap < &argv[3] && (*ap =
-                           strsep(&p, " ")) != NULL;) {
-                               if (**ap != '\0')
-                                       ap++;
-                       }
-                       *ap = NULL;
-
-                       if (argv[0] != NULL && strncmp(argv[0], "slaacd:",
-                           strlen("slaacd:")) == 0 && argv[1] != NULL &&
-                           argv[2] != NULL && argv[3] == NULL) {
-                               id = strtonum(argv[1], 0, INT64_MAX, &errstr);
-                               if (errstr != NULL) {
-                                       log_warn("%s: proposal seq is %s: %s",
-                                           __func__, errstr, argv[1]);
-                                       break;
-                               }
-                               pid = strtonum(argv[2], 0, INT32_MAX, &errstr);
-                               if (errstr != NULL) {
-                                       log_warn("%s: pid is %s: %s",
-                                           __func__, errstr, argv[2]);
-                                       break;
-                               }
-                               proposal_ack.id = id;
-                               proposal_ack.pid = pid;
-                               proposal_ack.if_index = ifm->ifm_index;
-
-                               frontend_imsg_compose_engine(IMSG_PROPOSAL_ACK,
-                                   0, 0, &proposal_ack, sizeof(proposal_ack));
-                       } else {
-                               log_debug("cannot parse: %s", rl->sr_label);
-                       }
-               } else {
-#if 0
-                       log_debug("%s: got flags %x, expcted %x", __func__,
-                           rtm->rtm_flags, (RTF_DONE | RTF_PROTO1));
-#endif
-               }
-
-               break;
-       default:
-               log_debug("unexpected RTM: %d", rtm->rtm_type);
-               break;
-       }
-
-}
-
-#define        ROUNDUP(a)      \
-    (((a) & (sizeof(long) - 1)) ? (1 + ((a) | (sizeof(long) - 1))) : (a))
-
-void
-get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
-{
-       int     i;
-
-       for (i = 0; i < RTAX_MAX; i++) {
-               if (addrs & (1 << i)) {
-                       rti_info[i] = sa;
-                       sa = (struct sockaddr *)((char *)(sa) +
-                           ROUNDUP(sa->sa_len));
-               } else
-                       rti_info[i] = NULL;
-       }
-}
-
-void
-get_lladdr(char *if_name, struct ether_addr *mac, struct sockaddr_in6 *ll)
-{
-       struct ifaddrs          *ifap, *ifa;
-       struct sockaddr_dl      *sdl;
-       struct sockaddr_in6     *sin6;
-
-       if (getifaddrs(&ifap) != 0)
-               fatal("getifaddrs");
-
-       memset(mac, 0, sizeof(*mac));
-       memset(ll, 0, sizeof(*ll));
-
-       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
-               if (strcmp(if_name, ifa->ifa_name) != 0)
-                       continue;
-
-               switch(ifa->ifa_addr->sa_family) {
-               case AF_LINK:
-                       sdl = (struct sockaddr_dl *)ifa->ifa_addr;
-                       if (sdl->sdl_type != IFT_ETHER ||
-                           sdl->sdl_alen != ETHER_ADDR_LEN)
-                               continue;
-
-                       memcpy(mac->ether_addr_octet, LLADDR(sdl),
-                           ETHER_ADDR_LEN);
-                       break;
-               case AF_INET6:
-                       sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
-                       if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
-                               sin6->sin6_scope_id = ntohs(*(u_int16_t *)
-                                   &sin6->sin6_addr.s6_addr[2]);
-                               sin6->sin6_addr.s6_addr[2] =
-                                   sin6->sin6_addr.s6_addr[3] = 0;
-                               memcpy(ll, sin6, sizeof(*ll));
-                       }
-                       break;
-               default:
-                       break;
-               }
-       }
-       freeifaddrs(ifap);
-}
-
-void
-icmp6_receive(int fd, short events, void *arg)
-{
-       struct imsg_ra           ra;
-
-       struct in6_pktinfo      *pi = NULL;
-       struct cmsghdr          *cm;
-       ssize_t                  len;
-       int                      if_index = 0, *hlimp = NULL;
-       char                     ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
-       uint8_t                 *p;
-
-       p = icmp6ev.answer;
-
-       if ((len = recvmsg(fd, &icmp6ev.rcvmhdr, 0)) < 0) {
-               log_warn("recvmsg");
-               return;
-       }
-
-       /* extract optional information via Advanced API */
-       for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&icmp6ev.rcvmhdr); cm;
-           cm = (struct cmsghdr *)CMSG_NXTHDR(&icmp6ev.rcvmhdr, cm)) {
-               if (cm->cmsg_level == IPPROTO_IPV6 &&
-                   cm->cmsg_type == IPV6_PKTINFO &&
-                   cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
-                       pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
-                       if_index = pi->ipi6_ifindex;
-               }
-               if (cm->cmsg_level == IPPROTO_IPV6 &&
-                   cm->cmsg_type == IPV6_HOPLIMIT &&
-                   cm->cmsg_len == CMSG_LEN(sizeof(int)))
-                       hlimp = (int *)CMSG_DATA(cm);
-       }
-
-       if (if_index == 0) {
-               log_warnx("failed to get receiving interface");
-               return;
-       }
-
-       if (hlimp == NULL) {
-               log_warnx("failed to get receiving hop limit");
-               return;
-       }
-
-       if (*hlimp != 255) {
-               log_warn("invalid RA with hop limit of %d from %s on %s",
-                   *hlimp, inet_ntop(AF_INET6, &icmp6ev.from.sin6_addr,
-                   ntopbuf, INET6_ADDRSTRLEN), if_indextoname(if_index,
-                   ifnamebuf));
-               return;
-       }
-
-       if ((size_t)len > sizeof(ra.packet)) {
-               log_warn("invalid RA with size %ld from %s on %s",
-                   len, inet_ntop(AF_INET6, &icmp6ev.from.sin6_addr,
-                   ntopbuf, INET6_ADDRSTRLEN), if_indextoname(if_index,
-                   ifnamebuf));
-               return;
-       }
-
-       ra.if_index = if_index;
-       memcpy(&ra.from,  &icmp6ev.from, sizeof(ra.from));
-       ra.len = len;
-       memcpy(ra.packet, icmp6ev.answer, len);
-
-       frontend_imsg_compose_engine(IMSG_RA, 0, 0, &ra, sizeof(ra));
-}
-
-void
-send_solicitation(uint32_t if_index)
-{
-       struct in6_pktinfo              *pi;
-       struct cmsghdr                  *cm;
-
-       log_debug("%s(%u)", __func__, if_index);
-
-       dst.sin6_scope_id = if_index;
-
-       cm = CMSG_FIRSTHDR(&sndmhdr);
-       pi = (struct in6_pktinfo *)CMSG_DATA(cm);
-       pi->ipi6_ifindex = if_index;
-
-       if (sendmsg(icmp6sock, &sndmhdr, 0) != sizeof(rs) +
-           sizeof(nd_opt_hdr) + sizeof(nd_opt_source_link_addr))
-               log_warn("sendmsg");
-}
diff --git a/usr.sbin/slaacd/frontend.h b/usr.sbin/slaacd/frontend.h
deleted file mode 100644 (file)
index 45c743f..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*     $OpenBSD: frontend.h,v 1.1 2017/03/18 17:33:13 florian Exp $    */
-
-/*
- * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-TAILQ_HEAD(ctl_conns, ctl_conn)        ctl_conns;
-
-void            frontend(int, int, char *);
-void            frontend_dispatch_main(int, short, void *);
-void            frontend_dispatch_engine(int, short, void *);
-int             frontend_imsg_compose_main(int, pid_t, void *, uint16_t);
-int             frontend_imsg_compose_engine(int, uint32_t, pid_t, void *,
-                    uint16_t);
diff --git a/usr.sbin/slaacd/log.c b/usr.sbin/slaacd/log.c
deleted file mode 100644 (file)
index d261550..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-/*     $OpenBSD: log.c,v 1.2 2017/03/21 12:38:23 bluhm Exp $   */
-
-/*
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <syslog.h>
-#include <errno.h>
-#include <time.h>
-
-#include "log.h"
-
-static int              debug;
-static int              verbose;
-static const char      *log_procname;
-
-void
-log_init(int n_debug, int facility)
-{
-       extern char     *__progname;
-
-       debug = n_debug;
-       verbose = n_debug;
-       log_procinit(__progname);
-
-       if (!debug)
-               openlog(__progname, LOG_PID | LOG_NDELAY, facility);
-
-       tzset();
-}
-
-void
-log_procinit(const char *procname)
-{
-       if (procname != NULL)
-               log_procname = procname;
-}
-
-void
-log_setverbose(int v)
-{
-       verbose = v;
-}
-
-int
-log_getverbose(void)
-{
-       return (verbose);
-}
-
-void
-logit(int pri, const char *fmt, ...)
-{
-       va_list ap;
-
-       va_start(ap, fmt);
-       vlog(pri, fmt, ap);
-       va_end(ap);
-}
-
-void
-vlog(int pri, const char *fmt, va_list ap)
-{
-       char    *nfmt;
-       int      saved_errno = errno;
-
-       if (debug) {
-               /* best effort in out of mem situations */
-               if (asprintf(&nfmt, "%s\n", fmt) == -1) {
-                       vfprintf(stderr, fmt, ap);
-                       fprintf(stderr, "\n");
-               } else {
-                       vfprintf(stderr, nfmt, ap);
-                       free(nfmt);
-               }
-               fflush(stderr);
-       } else
-               vsyslog(pri, fmt, ap);
-
-       errno = saved_errno;
-}
-
-void
-log_warn(const char *emsg, ...)
-{
-       char            *nfmt;
-       va_list          ap;
-       int              saved_errno = errno;
-
-       /* best effort to even work in out of memory situations */
-       if (emsg == NULL)
-               logit(LOG_ERR, "%s", strerror(saved_errno));
-       else {
-               va_start(ap, emsg);
-
-               if (asprintf(&nfmt, "%s: %s", emsg,
-                   strerror(saved_errno)) == -1) {
-                       /* we tried it... */
-                       vlog(LOG_ERR, emsg, ap);
-                       logit(LOG_ERR, "%s", strerror(saved_errno));
-               } else {
-                       vlog(LOG_ERR, nfmt, ap);
-                       free(nfmt);
-               }
-               va_end(ap);
-       }
-
-       errno = saved_errno;
-}
-
-void
-log_warnx(const char *emsg, ...)
-{
-       va_list  ap;
-
-       va_start(ap, emsg);
-       vlog(LOG_ERR, emsg, ap);
-       va_end(ap);
-}
-
-void
-log_info(const char *emsg, ...)
-{
-       va_list  ap;
-
-       va_start(ap, emsg);
-       vlog(LOG_INFO, emsg, ap);
-       va_end(ap);
-}
-
-void
-log_debug(const char *emsg, ...)
-{
-       va_list  ap;
-
-       if (verbose) {
-               va_start(ap, emsg);
-               vlog(LOG_DEBUG, emsg, ap);
-               va_end(ap);
-       }
-}
-
-static void
-vfatalc(int code, const char *emsg, va_list ap)
-{
-       static char     s[BUFSIZ];
-       const char      *sep;
-
-       if (emsg != NULL) {
-               (void)vsnprintf(s, sizeof(s), emsg, ap);
-               sep = ": ";
-       } else {
-               s[0] = '\0';
-               sep = "";
-       }
-       if (code)
-               logit(LOG_CRIT, "fatal in %s: %s%s%s",
-                   log_procname, s, sep, strerror(code));
-       else
-               logit(LOG_CRIT, "fatal in %s%s%s", log_procname, sep, s);
-}
-
-void
-fatal(const char *emsg, ...)
-{
-       va_list ap;
-
-       va_start(ap, emsg);
-       vfatalc(errno, emsg, ap);
-       va_end(ap);
-       exit(1);
-}
-
-void
-fatalx(const char *emsg, ...)
-{
-       va_list ap;
-
-       va_start(ap, emsg);
-       vfatalc(0, emsg, ap);
-       va_end(ap);
-       exit(1);
-}
diff --git a/usr.sbin/slaacd/log.h b/usr.sbin/slaacd/log.h
deleted file mode 100644 (file)
index adb45af..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*     $OpenBSD: log.h,v 1.1 2017/03/18 17:33:13 florian Exp $ */
-
-/*
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef LOG_H
-#define LOG_H
-
-#include <stdarg.h>
-#include <sys/cdefs.h>
-
-void   log_init(int, int);
-void   log_procinit(const char *);
-void   log_setverbose(int);
-int    log_getverbose(void);
-void   log_warn(const char *, ...)
-           __attribute__((__format__ (printf, 1, 2)));
-void   log_warnx(const char *, ...)
-           __attribute__((__format__ (printf, 1, 2)));
-void   log_info(const char *, ...)
-           __attribute__((__format__ (printf, 1, 2)));
-void   log_debug(const char *, ...)
-           __attribute__((__format__ (printf, 1, 2)));
-void   logit(int, const char *, ...)
-           __attribute__((__format__ (printf, 2, 3)));
-void   vlog(int, const char *, va_list)
-           __attribute__((__format__ (printf, 2, 0)));
-__dead void fatal(const char *, ...)
-           __attribute__((__format__ (printf, 1, 2)));
-__dead void fatalx(const char *, ...)
-           __attribute__((__format__ (printf, 1, 2)));
-
-#endif /* LOG_H */
diff --git a/usr.sbin/slaacd/slaacd.8 b/usr.sbin/slaacd/slaacd.8
deleted file mode 100644 (file)
index 2d71b0a..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-.\"    $OpenBSD: slaacd.8,v 1.5 2017/05/30 12:39:33 jmc Exp $
-.\"
-.\" Copyright (c) 2017 Florian Obser <florian@openbsd.org>
-.\" Copyright (c) 2016 Kenneth R Westerback <kwesterback@gmail.com>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.Dd $Mdocdate: May 30 2017 $
-.Dt SLAACD 8
-.Os
-.Sh NAME
-.Nm slaacd
-.Nd a stateless address autoconfiguration daemon
-.Sh SYNOPSIS
-.Nm
-.Op Fl dv
-.Op Fl s Ar socket
-.Sh DESCRIPTION
-.Nm
-is a stateless address autoconfiguration (SLAAC) daemon.
-It listens for IPv6 router advertisement messages on interfaces with the
-.Sy AUTOCONF6
-flag set.
-.Nm
-derives IPv6 addresses and default routes from received router
-advertisements and installs them in the kernel.
-See
-.Xr hostname.if 5
-and
-.Xr ifconfig 8
-on how to enable auto configuration on an interface.
-.Pp
-.Nm
-monitors network interface states (interface going up or down,
-auto configuration enabled or disabled etc.) and sends router solicitations
-when necessary.
-.Pp
-A running
-.Nm
-can be controlled with the
-.Xr slaacctl 8
-utility.
-.Pp
-The options are as follows:
-.Bl -tag -width Ds
-.It Fl d
-Do not daemonize.
-If this option is specified,
-.Nm
-will run in the foreground and log to
-.Em stderr .
-.It Fl s Ar socket
-Use an alternate location for the default control socket.
-.It Fl v
-Produce more verbose output.
-.El
-.Sh FILES
-.Bl -tag -width "/var/run/slaacd.sockXX" -compact
-.It Pa /var/run/slaacd.sock
-.Ux Ns -domain
-socket used for communication with
-.Xr slaacctl 8 .
-.El
-.Sh SEE ALSO
-.Xr hostname.if 5 ,
-.Xr ifconfig 8 ,
-.Xr slaacctl 8
-.Sh STANDARDS
-.Rs
-.%A T. Narten
-.%A E. Nordmark
-.%A W. Simpson
-.%A H. Soliman
-.%D September 2007
-.%R RFC 4861
-.%T Neighbor Discovery for IP version 6 (IPv6)
-.Re
-.Pp
-.Rs
-.%A J. Jeong
-.%A S. Park
-.%A L. Beloeil
-.%A S. Madanapalli
-.%D November 2010
-.%R RFC 6106
-.%T IPv6 Router Advertisement Options for DNS Configuration
-.Re
-.Pp
-.Rs
-.%A R. Draves
-.%A D. Thaler
-.%D November 2005
-.%R RFC 4191
-.%T Default Router Preferences and More-Specific Routes
-.Re
-.Sh HISTORY
-The
-.Nm
-program first appeared in
-.Ox 6.2 .
-.Sh AUTHORS
-.An -nosplit
-The
-.Nm
-program was written by
-.An Florian Obser Aq Mt florian@openbsd.org .
diff --git a/usr.sbin/slaacd/slaacd.c b/usr.sbin/slaacd/slaacd.c
deleted file mode 100644 (file)
index 61df0d6..0000000
+++ /dev/null
@@ -1,768 +0,0 @@
-/*     $OpenBSD: slaacd.c,v 1.23 2017/05/30 22:04:46 florian Exp $     */
-
-/*
- * Copyright (c) 2017 Florian Obser <florian@openbsd.org>
- * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
- * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/queue.h>
-#include <sys/socket.h>
-#include <sys/syslog.h>
-#include <sys/uio.h>
-#include <sys/wait.h>
-
-#include <net/if.h>
-#include <net/route.h>
-#include <netinet/in.h>
-#include <netinet/if_ether.h>
-#include <netinet6/in6_var.h>
-
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <pwd.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <signal.h>
-#include <unistd.h>
-
-#include "log.h"
-#include "slaacd.h"
-#include "frontend.h"
-#include "engine.h"
-#include "control.h"
-
-const char* imsg_type_name[] = {
-       "IMSG_NONE",
-       "IMSG_CTL_LOG_VERBOSE",
-       "IMSG_CTL_SHOW_INTERFACE_INFO",
-       "IMSG_CTL_SHOW_INTERFACE_INFO_RA",
-       "IMSG_CTL_SHOW_INTERFACE_INFO_RA_PREFIX",
-       "IMSG_CTL_SHOW_INTERFACE_INFO_RA_RDNS",
-       "IMSG_CTL_SHOW_INTERFACE_INFO_RA_DNSSL",
-       "IMSG_CTL_SHOW_FRONTEND_INFO",
-       "IMSG_CTL_SHOW_MAIN_INFO",
-       "IMSG_CTL_END",
-       "IMSG_SOCKET_IPC",
-       "IMSG_STARTUP",
-       "IMSG_UPDATE_IF",
-       "IMSG_REMOVE_IF",
-       "IMSG_RA",
-       "IMSG_CTL_SEND_SOLICITATION",
-       "IMSG_PROPOSAL",
-       "IMSG_PROPOSAL_ACK",
-       "IMSG_CONFIGURE_ADDRESS",
-       "IMSG_DEL_ADDRESS",
-       "IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSAL",
-       "IMSG_FAKE_ACK",
-       "IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSALS",
-       "IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSAL",
-       "IMSG_CONFIGURE_DFR",
-       "IMSG_WITHDRAW_DFR",
-};
-
-__dead void    usage(void);
-__dead void    main_shutdown(void);
-
-void   main_sig_handler(int, short, void *);
-
-static pid_t   start_child(int, char *, int, int, int, char *);
-
-void   main_dispatch_frontend(int, short, void *);
-void   main_dispatch_engine(int, short, void *);
-void   handle_proposal(struct imsg_proposal *);
-void   configure_interface(struct imsg_configure_address *);
-void   configure_gateway(struct imsg_configure_dfr *, uint8_t);
-void   add_gateway(struct imsg_configure_dfr *);
-void   delete_gateway(struct imsg_configure_dfr *);
-
-static int     main_imsg_send_ipc_sockets(struct imsgbuf *, struct imsgbuf *);
-
-struct imsgev          *iev_frontend;
-struct imsgev          *iev_engine;
-
-pid_t   frontend_pid;
-pid_t   engine_pid;
-
-uint32_t cmd_opts;
-
-int     routesock, ioctl_sock;
-
-char   *csock;
-
-int     rtm_seq = 0;
-
-void
-main_sig_handler(int sig, short event, void *arg)
-{
-       /*
-        * Normal signal handler rules don't apply because libevent
-        * decouples for us.
-        */
-
-       switch (sig) {
-       case SIGTERM:
-       case SIGINT:
-               main_shutdown();
-       case SIGHUP:
-               log_debug("sighub received");
-               break;
-       default:
-               fatalx("unexpected signal");
-       }
-}
-
-__dead void
-usage(void)
-{
-       extern char *__progname;
-
-       fprintf(stderr, "usage: %s [-dv] [-s socket]\n",
-           __progname);
-       exit(1);
-}
-
-int
-main(int argc, char *argv[])
-{
-       struct event     ev_sigint, ev_sigterm, ev_sighup;
-       int              ch;
-       int              debug = 0, engine_flag = 0, frontend_flag = 0;
-       char            *saved_argv0;
-       int              pipe_main2frontend[2];
-       int              pipe_main2engine[2];
-
-       csock = SLAACD_SOCKET;
-
-       log_init(1, LOG_DAEMON);        /* Log to stderr until daemonized. */
-       log_setverbose(1);
-
-       saved_argv0 = argv[0];
-       if (saved_argv0 == NULL)
-               saved_argv0 = "slaacd";
-
-       while ((ch = getopt(argc, argv, "dEFs:v")) != -1) {
-               switch (ch) {
-               case 'd':
-                       debug = 1;
-                       break;
-               case 'E':
-                       engine_flag = 1;
-                       break;
-               case 'F':
-                       frontend_flag = 1;
-                       break;
-               case 's':
-                       csock = optarg;
-                       break;
-               case 'v':
-                       if (cmd_opts & OPT_VERBOSE)
-                               cmd_opts |= OPT_VERBOSE2;
-                       cmd_opts |= OPT_VERBOSE;
-                       break;
-               default:
-                       usage();
-               }
-       }
-
-       argc -= optind;
-       argv += optind;
-       if (argc > 0 || (engine_flag && frontend_flag))
-               usage();
-
-       if (engine_flag)
-               engine(debug, cmd_opts & OPT_VERBOSE);
-       else if (frontend_flag)
-               frontend(debug, cmd_opts & OPT_VERBOSE, csock);
-
-       /* Check for root privileges. */
-       if (geteuid())
-               errx(1, "need root privileges");
-
-       /* Check for assigned daemon user */
-       if (getpwnam(SLAACD_USER) == NULL)
-               errx(1, "unknown user %s", SLAACD_USER);
-
-       log_init(debug, LOG_DAEMON);
-       log_setverbose(cmd_opts & OPT_VERBOSE);
-
-       if (!debug)
-               daemon(1, 0);
-
-       log_info("startup");
-
-       if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
-           PF_UNSPEC, pipe_main2frontend) == -1)
-               fatal("main2frontend socketpair");
-       if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
-           PF_UNSPEC, pipe_main2engine) == -1)
-               fatal("main2engine socketpair");
-
-       /* Start children. */
-       engine_pid = start_child(PROC_ENGINE, saved_argv0, pipe_main2engine[1],
-           debug, cmd_opts & OPT_VERBOSE, NULL);
-       frontend_pid = start_child(PROC_FRONTEND, saved_argv0,
-           pipe_main2frontend[1], debug, cmd_opts & OPT_VERBOSE, csock);
-
-       slaacd_process = PROC_MAIN;
-
-       log_procinit(log_procnames[slaacd_process]);
-
-       if ((routesock = socket(PF_ROUTE, SOCK_RAW | SOCK_CLOEXEC |
-           SOCK_NONBLOCK, 0)) < 0)
-               fatal("route socket");
-
-       event_init();
-
-       /* Setup signal handler. */
-       signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL);
-       signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL);
-       signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL);
-       signal_add(&ev_sigint, NULL);
-       signal_add(&ev_sigterm, NULL);
-       signal_add(&ev_sighup, NULL);
-       signal(SIGPIPE, SIG_IGN);
-
-       /* Setup pipes to children. */
-
-       if ((iev_frontend = malloc(sizeof(struct imsgev))) == NULL ||
-           (iev_engine = malloc(sizeof(struct imsgev))) == NULL)
-               fatal(NULL);
-       imsg_init(&iev_frontend->ibuf, pipe_main2frontend[0]);
-       iev_frontend->handler = main_dispatch_frontend;
-       imsg_init(&iev_engine->ibuf, pipe_main2engine[0]);
-       iev_engine->handler = main_dispatch_engine;
-
-       /* Setup event handlers for pipes to engine & frontend. */
-       iev_frontend->events = EV_READ;
-       event_set(&iev_frontend->ev, iev_frontend->ibuf.fd,
-           iev_frontend->events, iev_frontend->handler, iev_frontend);
-       event_add(&iev_frontend->ev, NULL);
-
-       iev_engine->events = EV_READ;
-       event_set(&iev_engine->ev, iev_engine->ibuf.fd, iev_engine->events,
-           iev_engine->handler, iev_engine);
-       event_add(&iev_engine->ev, NULL);
-
-       if (main_imsg_send_ipc_sockets(&iev_frontend->ibuf, &iev_engine->ibuf))
-               fatal("could not establish imsg links");
-
-       if ((ioctl_sock = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0)
-               fatal("socket");
-
-#if 0
-       /* XXX ioctl SIOCAIFADDR_IN6 */
-       if (pledge("rpath stdio sendfd cpath", NULL) == -1)
-               fatal("pledge");
-#endif
-
-       main_imsg_compose_frontend(IMSG_STARTUP, 0, NULL, 0);
-
-       event_dispatch();
-
-       main_shutdown();
-       return (0);
-}
-
-__dead void
-main_shutdown(void)
-{
-       pid_t    pid;
-       int      status;
-
-       /* Close pipes. */
-       msgbuf_clear(&iev_frontend->ibuf.w);
-       close(iev_frontend->ibuf.fd);
-       msgbuf_clear(&iev_engine->ibuf.w);
-       close(iev_engine->ibuf.fd);
-
-       log_debug("waiting for children to terminate");
-       do {
-               pid = wait(&status);
-               if (pid == -1) {
-                       if (errno != EINTR && errno != ECHILD)
-                               fatal("wait");
-               } else if (WIFSIGNALED(status))
-                       log_warnx("%s terminated; signal %d",
-                           (pid == engine_pid) ? "engine" :
-                           "frontend", WTERMSIG(status));
-       } while (pid != -1 || (pid == -1 && errno == EINTR));
-
-       free(iev_frontend);
-       free(iev_engine);
-
-       control_cleanup(csock);
-
-       log_info("terminating");
-       exit(0);
-}
-
-static pid_t
-start_child(int p, char *argv0, int fd, int debug, int verbose, char *sockname)
-{
-       char    *argv[7];
-       int      argc = 0;
-       pid_t    pid;
-
-       switch (pid = fork()) {
-       case -1:
-               fatal("cannot fork");
-       case 0:
-               break;
-       default:
-               close(fd);
-               return (pid);
-       }
-
-       if (dup2(fd, 3) == -1)
-               fatal("cannot setup imsg fd");
-
-       argv[argc++] = argv0;
-       switch (p) {
-       case PROC_MAIN:
-               fatalx("Can not start main process");
-       case PROC_ENGINE:
-               argv[argc++] = "-E";
-               break;
-       case PROC_FRONTEND:
-               argv[argc++] = "-F";
-               break;
-       }
-       if (debug)
-               argv[argc++] = "-d";
-       if (verbose)
-               argv[argc++] = "-v";
-       if (sockname) {
-               argv[argc++] = "-s";
-               argv[argc++] = sockname;
-       }
-       argv[argc++] = NULL;
-
-       execvp(argv0, argv);
-       fatal("execvp");
-}
-
-void
-main_dispatch_frontend(int fd, short event, void *bula)
-{
-       struct imsgev           *iev = bula;
-       struct imsgbuf          *ibuf;
-       struct imsg              imsg;
-       ssize_t                  n;
-       int                      shut = 0, verbose;
-
-       ibuf = &iev->ibuf;
-
-       if (event & EV_READ) {
-               if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
-                       fatal("imsg_read error");
-               if (n == 0)     /* Connection closed. */
-                       shut = 1;
-       }
-       if (event & EV_WRITE) {
-               if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
-                       fatal("msgbuf_write");
-               if (n == 0)     /* Connection closed. */
-                       shut = 1;
-       }
-
-       for (;;) {
-               if ((n = imsg_get(ibuf, &imsg)) == -1)
-                       fatal("imsg_get");
-               if (n == 0)     /* No more messages. */
-                       break;
-
-               switch (imsg.hdr.type) {
-               case IMSG_CTL_LOG_VERBOSE:
-                       /* Already checked by frontend. */
-                       memcpy(&verbose, imsg.data, sizeof(verbose));
-                       log_setverbose(verbose);
-                       break;
-               default:
-                       log_debug("%s: error handling imsg %d", __func__,
-                           imsg.hdr.type);
-                       break;
-               }
-               imsg_free(&imsg);
-       }
-       if (!shut)
-               imsg_event_add(iev);
-       else {
-               /* This pipe is dead. Remove its event handler */
-               event_del(&iev->ev);
-               event_loopexit(NULL);
-       }
-}
-
-void
-main_dispatch_engine(int fd, short event, void *bula)
-{
-       struct imsgev                   *iev = bula;
-       struct imsgbuf                  *ibuf;
-       struct imsg                      imsg;
-       struct imsg_proposal             proposal;
-       struct imsg_configure_address    address;
-       struct imsg_configure_dfr        dfr;
-       ssize_t                          n;
-       int                              shut = 0;
-
-       ibuf = &iev->ibuf;
-
-       if (event & EV_READ) {
-               if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
-                       fatal("imsg_read error");
-               if (n == 0)     /* Connection closed. */
-                       shut = 1;
-       }
-       if (event & EV_WRITE) {
-               if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
-                       fatal("msgbuf_write");
-               if (n == 0)     /* Connection closed. */
-                       shut = 1;
-       }
-
-       for (;;) {
-               if ((n = imsg_get(ibuf, &imsg)) == -1)
-                       fatal("imsg_get");
-               if (n == 0)     /* No more messages. */
-                       break;
-
-               switch (imsg.hdr.type) {
-               case IMSG_PROPOSAL:
-                       if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(proposal))
-                               fatal("%s: IMSG_PROPOSAL wrong "
-                                   "length: %d", __func__, imsg.hdr.len);
-                       memcpy(&proposal, imsg.data, sizeof(proposal));
-                       handle_proposal(&proposal);
-                       break;
-               case IMSG_CONFIGURE_ADDRESS:
-                       if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(address))
-                               fatal("%s: IMSG_CONFIGURE_ADDRESS wrong "
-                                   "length: %d", __func__, imsg.hdr.len);
-                       memcpy(&address, imsg.data, sizeof(address));
-                       configure_interface(&address);
-                       break;
-               case IMSG_CONFIGURE_DFR:
-                       if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(dfr))
-                               fatal("%s: IMSG_CONFIGURE_DFR wrong "
-                                   "length: %d", __func__, imsg.hdr.len);
-                       memcpy(&dfr, imsg.data, sizeof(dfr));
-                       add_gateway(&dfr);
-                       break;
-               case IMSG_WITHDRAW_DFR:
-                       if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(dfr))
-                               fatal("%s: IMSG_CONFIGURE_DFR wrong "
-                                   "length: %d", __func__, imsg.hdr.len);
-                       memcpy(&dfr, imsg.data, sizeof(dfr));
-                       delete_gateway(&dfr);
-                       break;
-               default:
-                       log_debug("%s: error handling imsg %d", __func__,
-                           imsg.hdr.type);
-                       break;
-               }
-               imsg_free(&imsg);
-       }
-       if (!shut)
-               imsg_event_add(iev);
-       else {
-               /* This pipe is dead. Remove its event handler. */
-               event_del(&iev->ev);
-               event_loopexit(NULL);
-       }
-}
-
-int
-main_imsg_compose_frontend(int type, pid_t pid, void *data, uint16_t datalen)
-{
-       if (iev_frontend)
-               return (imsg_compose_event(iev_frontend, type, 0, pid, -1, data,
-                   datalen));
-       else
-               return (-1);
-}
-
-int
-main_imsg_compose_engine(int type, pid_t pid, void *data, uint16_t datalen)
-{
-       if (iev_engine)
-               return(imsg_compose_event(iev_engine, type, 0, pid, -1, data,
-                   datalen));
-       else
-               return (-1);
-}
-
-void
-imsg_event_add(struct imsgev *iev)
-{
-       iev->events = EV_READ;
-       if (iev->ibuf.w.queued)
-               iev->events |= EV_WRITE;
-
-       event_del(&iev->ev);
-       event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev);
-       event_add(&iev->ev, NULL);
-}
-
-int
-imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
-    pid_t pid, int fd, void *data, uint16_t datalen)
-{
-       int     ret;
-
-       if ((ret = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data,
-           datalen)) != -1)
-               imsg_event_add(iev);
-
-       return (ret);
-}
-
-static int
-main_imsg_send_ipc_sockets(struct imsgbuf *frontend_buf,
-    struct imsgbuf *engine_buf)
-{
-       int pipe_frontend2engine[2];
-
-       if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
-           PF_UNSPEC, pipe_frontend2engine) == -1)
-               return (-1);
-
-       if (imsg_compose(frontend_buf, IMSG_SOCKET_IPC, 0, 0,
-           pipe_frontend2engine[0], NULL, 0) == -1)
-               return (-1);
-       imsg_flush(frontend_buf);
-       if (imsg_compose(engine_buf, IMSG_SOCKET_IPC, 0, 0,
-           pipe_frontend2engine[1], NULL, 0) == -1)
-               return (-1);
-       imsg_flush(engine_buf);
-       return (0);
-}
-
-#define        ROUNDUP(a)      \
-    (((a) & (sizeof(long) - 1)) ? (1 + ((a) | (sizeof(long) - 1))) : (a))
-
-void
-handle_proposal(struct imsg_proposal *proposal)
-{
-       struct rt_msghdr                 rtm;
-       struct sockaddr_in6              ifa, mask;
-       struct sockaddr_rtlabel          rl;
-       struct iovec                     iov[13];
-       long                             pad = 0;
-       int                              iovcnt = 0, padlen;
-
-       memset(&rtm, 0, sizeof(rtm));
-
-       rtm.rtm_version = RTM_VERSION;
-       rtm.rtm_type = RTM_PROPOSAL;
-       rtm.rtm_msglen = sizeof(rtm);
-       rtm.rtm_tableid = 0; /* XXX imsg->rdomain; */
-       rtm.rtm_index = proposal->if_index;
-       rtm.rtm_seq = ++rtm_seq;
-       rtm.rtm_priority = RTP_PROPOSAL_SLAAC;
-       rtm.rtm_addrs = (proposal->rtm_addrs & (RTA_NETMASK | RTA_IFA)) |
-           RTA_LABEL;
-       rtm.rtm_flags = RTF_UP;
-
-       iov[iovcnt].iov_base = &rtm;
-       iov[iovcnt++].iov_len = sizeof(rtm);
-
-       if (rtm.rtm_addrs & RTA_NETMASK) {
-               memset(&mask, 0, sizeof(mask));
-               mask.sin6_family = AF_INET6;
-               mask.sin6_len = sizeof(struct sockaddr_in6);
-               mask.sin6_addr = proposal->mask;
-
-               iov[iovcnt].iov_base = &mask;
-               iov[iovcnt++].iov_len = sizeof(mask);
-               rtm.rtm_msglen += sizeof(mask);
-               padlen = ROUNDUP(sizeof(mask)) - sizeof(mask);
-               if (padlen > 0) {
-                       iov[iovcnt].iov_base = &pad;
-                       iov[iovcnt++].iov_len = padlen;
-                       rtm.rtm_msglen += padlen;
-               }
-       }
-
-       if (rtm.rtm_addrs & RTA_IFA) {
-               memcpy(&ifa, &proposal->addr, sizeof(ifa));
-
-               if (ifa.sin6_family != AF_INET6 || ifa.sin6_len !=
-                   sizeof(struct sockaddr_in6)) {
-                       log_warnx("%s: invalid address", __func__);
-                       return;
-               }
-
-               iov[iovcnt].iov_base = &ifa;
-               iov[iovcnt++].iov_len = sizeof(ifa);
-               rtm.rtm_msglen += sizeof(ifa);
-               padlen = ROUNDUP(sizeof(ifa)) - sizeof(ifa);
-               if (padlen > 0) {
-                       iov[iovcnt].iov_base = &pad;
-                       iov[iovcnt++].iov_len = padlen;
-                       rtm.rtm_msglen += padlen;
-               }
-       }
-
-       rl.sr_len = sizeof(rl);
-       rl.sr_family = AF_UNSPEC;
-       if (snprintf(rl.sr_label, sizeof(rl.sr_label), "%s: %lld %d", "slaacd",
-           proposal->id, (int32_t)proposal->pid) >=
-           (ssize_t)sizeof(rl.sr_label))
-               log_warnx("route label truncated");
-
-       iov[iovcnt].iov_base = &rl;
-       iov[iovcnt++].iov_len = sizeof(rl);
-       rtm.rtm_msglen += sizeof(rl);
-       padlen = ROUNDUP(sizeof(rl)) - sizeof(rl);
-       if (padlen > 0) {
-               iov[iovcnt].iov_base = &pad;
-               iov[iovcnt++].iov_len = padlen;
-               rtm.rtm_msglen += padlen;
-       }
-
-       if (writev(routesock, iov, iovcnt) == -1)
-               log_warn("failed to send proposal");
-}
-
-void
-configure_interface(struct imsg_configure_address *address)
-{
-
-       struct in6_aliasreq      in6_addreq;
-       time_t                   t;
-       char                    *if_name;
-
-       memset(&in6_addreq, 0, sizeof(in6_addreq));
-
-       if_name = if_indextoname(address->if_index, in6_addreq.ifra_name);
-       if (if_name == NULL) {
-               log_warn("%s: cannot find interface %d", __func__,
-                   address->if_index);
-               return;
-       }
-
-       memcpy(&in6_addreq.ifra_addr, &address->addr,
-           sizeof(in6_addreq.ifra_addr));
-       memcpy(&in6_addreq.ifra_prefixmask.sin6_addr, &address->mask,
-           sizeof(in6_addreq.ifra_prefixmask.sin6_addr));
-       in6_addreq.ifra_prefixmask.sin6_family = AF_INET6;
-       in6_addreq.ifra_prefixmask.sin6_len =
-           sizeof(in6_addreq.ifra_prefixmask);
-
-       t = time(NULL);
-
-       in6_addreq.ifra_lifetime.ia6t_expire = t + address->vltime;
-       in6_addreq.ifra_lifetime.ia6t_vltime = address->vltime;
-
-       in6_addreq.ifra_lifetime.ia6t_preferred = t + address->pltime;
-       in6_addreq.ifra_lifetime.ia6t_pltime = address->pltime;
-
-       in6_addreq.ifra_flags |= IN6_IFF_AUTOCONF;
-
-       if (address->privacy)
-               in6_addreq.ifra_flags |= IN6_IFF_PRIVACY;
-
-       log_debug("%s: %s", __func__, if_name);
-
-       if (ioctl(ioctl_sock, SIOCAIFADDR_IN6, &in6_addreq) < 0)
-               fatal("SIOCAIFADDR_IN6");
-}
-
-void
-configure_gateway(struct imsg_configure_dfr *dfr, uint8_t rtm_type)
-{
-       struct rt_msghdr                 rtm;
-       struct sockaddr_in6              dst, gw, mask;
-       struct iovec                     iov[8];
-       long                             pad = 0;
-       int                              iovcnt = 0, padlen;
-
-       memset(&rtm, 0, sizeof(rtm));
-
-       rtm.rtm_version = RTM_VERSION;
-       rtm.rtm_type = rtm_type;
-       rtm.rtm_msglen = sizeof(rtm);
-       rtm.rtm_tableid = 0; /* XXX imsg->rdomain; */
-       rtm.rtm_index = dfr->if_index;
-       rtm.rtm_seq = ++rtm_seq;
-       rtm.rtm_priority = RTP_DEFAULT;
-       rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
-       rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
-
-       iov[iovcnt].iov_base = &rtm;
-       iov[iovcnt++].iov_len = sizeof(rtm);
-
-       memset(&dst, 0, sizeof(mask));
-       dst.sin6_family = AF_INET6;
-       dst.sin6_len = sizeof(struct sockaddr_in6);
-
-       iov[iovcnt].iov_base = &dst;
-       iov[iovcnt++].iov_len = sizeof(dst);
-       rtm.rtm_msglen += sizeof(dst);
-       padlen = ROUNDUP(sizeof(dst)) - sizeof(dst);
-       if (padlen > 0) {
-               iov[iovcnt].iov_base = &pad;
-               iov[iovcnt++].iov_len = padlen;
-               rtm.rtm_msglen += padlen;
-       }
-
-       memcpy(&gw, &dfr->addr, sizeof(gw));
-       *(u_int16_t *)& gw.sin6_addr.s6_addr[2] = htons(gw.sin6_scope_id);
-       /* gw.sin6_scope_id = 0; XXX route(8) does this*/
-       iov[iovcnt].iov_base = &gw;
-       iov[iovcnt++].iov_len = sizeof(gw);
-       rtm.rtm_msglen += sizeof(gw);
-       padlen = ROUNDUP(sizeof(gw)) - sizeof(gw);
-       if (padlen > 0) {
-               iov[iovcnt].iov_base = &pad;
-               iov[iovcnt++].iov_len = padlen;
-               rtm.rtm_msglen += padlen;
-       }
-
-       memset(&mask, 0, sizeof(mask));
-       mask.sin6_family = AF_INET6;
-       mask.sin6_len = 0;//sizeof(struct sockaddr_in6);
-       iov[iovcnt].iov_base = &mask;
-       iov[iovcnt++].iov_len = sizeof(mask);
-       rtm.rtm_msglen += sizeof(mask);
-       padlen = ROUNDUP(sizeof(mask)) - sizeof(mask);
-       if (padlen > 0) {
-               iov[iovcnt].iov_base = &pad;
-               iov[iovcnt++].iov_len = padlen;
-               rtm.rtm_msglen += padlen;
-       }
-
-       if (writev(routesock, iov, iovcnt) == -1)
-               log_warn("failed to send route message");
-}
-
-void
-add_gateway(struct imsg_configure_dfr *dfr)
-{
-       configure_gateway(dfr, RTM_ADD);
-}
-
-void
-delete_gateway(struct imsg_configure_dfr *dfr)
-{
-       configure_gateway(dfr, RTM_DELETE);
-}
diff --git a/usr.sbin/slaacd/slaacd.h b/usr.sbin/slaacd/slaacd.h
deleted file mode 100644 (file)
index 3d1a59f..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-/*     $OpenBSD: slaacd.h,v 1.17 2017/05/31 07:14:58 florian Exp $     */
-
-/*
- * Copyright (c) 2017 Florian Obser <florian@openbsd.org>
- * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#define        SLAACD_SOCKET           "/var/run/slaacd.sock"
-#define SLAACD_USER            "_slaacd"
-
-#define OPT_VERBOSE    0x00000001
-#define OPT_VERBOSE2   0x00000002
-
-#define SLAACD_MAXTEXT         256
-#define SLAACD_MAXGROUPNAME    16
-
-/* MAXDNAME from arpa/namesr.h */
-#define SLAACD_MAX_DNSSL       1025
-
-static const char * const log_procnames[] = {
-       "main",
-       "frontend",
-       "engine"
-};
-
-struct imsgev {
-       struct imsgbuf   ibuf;
-       void            (*handler)(int, short, void *);
-       struct event     ev;
-       short            events;
-};
-
-enum imsg_type {
-       IMSG_NONE,
-       IMSG_CTL_LOG_VERBOSE,
-       IMSG_CTL_SHOW_INTERFACE_INFO,
-       IMSG_CTL_SHOW_INTERFACE_INFO_RA,
-       IMSG_CTL_SHOW_INTERFACE_INFO_RA_PREFIX,
-       IMSG_CTL_SHOW_INTERFACE_INFO_RA_RDNS,
-       IMSG_CTL_SHOW_INTERFACE_INFO_RA_DNSSL,
-       IMSG_CTL_END,
-       IMSG_SOCKET_IPC,
-       IMSG_STARTUP,
-       IMSG_UPDATE_IF,
-       IMSG_REMOVE_IF,
-       IMSG_RA,
-       IMSG_CTL_SEND_SOLICITATION,
-       IMSG_PROPOSAL,
-       IMSG_PROPOSAL_ACK,
-       IMSG_CONFIGURE_ADDRESS,
-       IMSG_DEL_ADDRESS,
-       IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSALS,
-       IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSAL,
-       IMSG_FAKE_ACK,
-       IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSALS,
-       IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSAL,
-       IMSG_CONFIGURE_DFR,
-       IMSG_WITHDRAW_DFR,
-};
-
-extern const char* imsg_type_name[];
-
-enum {
-       PROC_MAIN,
-       PROC_ENGINE,
-       PROC_FRONTEND
-} slaacd_process;
-
-struct ctl_engine_info {
-       uint32_t                if_index;
-       int                     running;
-       int                     autoconfprivacy;
-       struct ether_addr       hw_address;
-       struct sockaddr_in6     ll_address;
-};
-
-enum rpref {
-       LOW,
-       MEDIUM,
-       HIGH,
-};
-
-struct ctl_engine_info_ra {
-       struct sockaddr_in6      from;
-       struct timespec          when;
-       struct timespec          uptime;
-       uint8_t                  curhoplimit;
-       int                      managed;
-       int                      other;
-       char                     rpref[sizeof("MEDIUM")];
-       uint16_t                 router_lifetime;       /* in seconds */
-       uint32_t                 reachable_time;        /* in milliseconds */
-       uint32_t                 retrans_time;          /* in milliseconds */
-};
-
-struct ctl_engine_info_ra_prefix {
-       struct in6_addr         prefix;
-       uint8_t                 prefix_len;
-       int                     onlink;
-       int                     autonomous;
-       uint32_t                vltime;
-       uint32_t                pltime;
-};
-
-struct ctl_engine_info_ra_rdns {
-       uint32_t                lifetime;
-       struct in6_addr         rdns;
-};
-
-struct ctl_engine_info_ra_dnssl {
-       uint32_t                lifetime;
-       char                    dnssl[SLAACD_MAX_DNSSL];
-};
-
-struct ctl_engine_info_address_proposal {
-       int64_t                  id;
-       char                     state[sizeof("PROPOSAL_NEARLY_EXPIRED")];
-       int                      next_timeout;
-       int                      timeout_count;
-       struct timespec          when;
-       struct timespec          uptime;
-       struct sockaddr_in6      addr;
-       struct in6_addr          prefix;
-       int                      privacy;
-       uint8_t                  prefix_len;
-       uint32_t                 vltime;
-       uint32_t                 pltime;
-};
-
-struct ctl_engine_info_dfr_proposal {
-       int64_t                  id;
-       char                     state[sizeof("PROPOSAL_NEARLY_EXPIRED")];
-       int                      next_timeout;
-       int                      timeout_count;
-       struct timespec          when;
-       struct timespec          uptime;
-       struct sockaddr_in6      addr;
-       uint32_t                 router_lifetime;
-       char                     rpref[sizeof("MEDIUM")];
-};
-
-struct imsg_ifinfo {
-       uint32_t                if_index;
-       int                     running;
-       int                     autoconfprivacy;
-       struct ether_addr       hw_address;
-       struct sockaddr_in6     ll_address;
-};
-
-struct imsg_del_addr {
-       uint32_t                if_index;
-       struct sockaddr_in6     addr;
-};
-
-struct imsg_proposal_ack {
-       int64_t          id;
-       pid_t            pid;
-       uint32_t         if_index;
-};
-
-struct imsg_ra {
-       uint32_t                if_index;
-       struct sockaddr_in6     from;
-       ssize_t                 len;
-       uint8_t                 packet[1500];
-};
-
-extern uint32_t         cmd_opts;
-
-/* slaacd.c */
-int    main_imsg_compose_frontend(int, pid_t, void *, uint16_t);
-int    main_imsg_compose_engine(int, pid_t, void *, uint16_t);
-void   imsg_event_add(struct imsgev *);
-int    imsg_compose_event(struct imsgev *, uint16_t, uint32_t, pid_t,
-           int, void *, uint16_t);