#!/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
-# $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 \
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>
--- /dev/null
+# $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>
--- /dev/null
+/* $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));
+}
--- /dev/null
+/* $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 *);
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
--- /dev/null
+/* $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");
+}
--- /dev/null
+/* $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);
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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 */
--- /dev/null
+.\" $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 .
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
-# $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>
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 \
-# $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
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}
+++ /dev/null
-# $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>
+++ /dev/null
-/* $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));
-}
+++ /dev/null
-/* $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 *);
+++ /dev/null
-/* $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);
-}
+++ /dev/null
-/* $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);
+++ /dev/null
-/* $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");
-}
+++ /dev/null
-/* $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);
+++ /dev/null
-/* $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);
-}
+++ /dev/null
-/* $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 */
+++ /dev/null
-.\" $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 .
+++ /dev/null
-/* $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);
-}
+++ /dev/null
-/* $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);