Import rad(8).
authorflorian <florian@openbsd.org>
Tue, 10 Jul 2018 16:39:54 +0000 (16:39 +0000)
committerflorian <florian@openbsd.org>
Tue, 10 Jul 2018 16:39:54 +0000 (16:39 +0000)
It's a Router Advertisement Daemon written using the standard 3
process privsep pattern and a parse.y based config file.
Commit early to continue work in tree.
OK jca
"it's totally rad" phessler@
"usr.sbin never runs out of space" deraadt@

15 files changed:
usr.sbin/rad/Makefile [new file with mode: 0644]
usr.sbin/rad/control.c [new file with mode: 0644]
usr.sbin/rad/control.h [new file with mode: 0644]
usr.sbin/rad/engine.c [new file with mode: 0644]
usr.sbin/rad/engine.h [new file with mode: 0644]
usr.sbin/rad/frontend.c [new file with mode: 0644]
usr.sbin/rad/frontend.h [new file with mode: 0644]
usr.sbin/rad/log.c [new file with mode: 0644]
usr.sbin/rad/log.h [new file with mode: 0644]
usr.sbin/rad/parse.y [new file with mode: 0644]
usr.sbin/rad/printconf.c [new file with mode: 0644]
usr.sbin/rad/rad.8 [new file with mode: 0644]
usr.sbin/rad/rad.c [new file with mode: 0644]
usr.sbin/rad/rad.conf.5 [new file with mode: 0644]
usr.sbin/rad/rad.h [new file with mode: 0644]

diff --git a/usr.sbin/rad/Makefile b/usr.sbin/rad/Makefile
new file mode 100644 (file)
index 0000000..bb1d1e0
--- /dev/null
@@ -0,0 +1,18 @@
+#      $OpenBSD: Makefile,v 1.1 2018/07/10 16:39:54 florian Exp $
+
+PROG=  rad
+SRCS=  control.c engine.c frontend.c log.c rad.c parse.y printconf.c
+
+MAN=   rad.8 rad.conf.5
+
+#DEBUG=        -g -DDEBUG=3 -O0
+CFLAGS+= -Wall -I${.CURDIR}
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+CFLAGS+= -Wsign-compare
+YFLAGS=
+LDADD+=        -levent -lutil
+DPADD+= ${LIBEVENT} ${LIBUTIL}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rad/control.c b/usr.sbin/rad/control.c
new file mode 100644 (file)
index 0000000..1771ad3
--- /dev/null
@@ -0,0 +1,307 @@
+/*     $OpenBSD: control.c,v 1.1 2018/07/10 16:39:54 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 <netinet/in.h>
+#include <net/if.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 "rad.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_RELOAD:
+                       frontend_imsg_compose_main(imsg.hdr.type, 0, NULL, 0);
+                       break;
+               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,
+                           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_MAIN_INFO:
+                       c->iev.ibuf.pid = imsg.hdr.pid;
+                       frontend_imsg_compose_main(imsg.hdr.type, imsg.hdr.pid,
+                           imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
+                       break;
+               case IMSG_CTL_SHOW_FRONTEND_INFO:
+                       frontend_showinfo_ctl(c);
+                       imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1,
+                           NULL, 0);
+                       break;
+               case IMSG_CTL_SHOW_ENGINE_INFO:
+                       c->iev.ibuf.pid = imsg.hdr.pid;
+                       frontend_imsg_compose_engine(imsg.hdr.type,
+                           imsg.hdr.pid,
+                           imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
+                       break;
+               default:
+                       log_debug("%s: error handling imsg %d", __func__,
+                           imsg.hdr.type);
+                       break;
+               }
+               imsg_free(&imsg);
+       }
+
+       imsg_event_add(&c->iev);
+}
+
+int
+control_imsg_relay(struct imsg *imsg)
+{
+       struct ctl_conn *c;
+
+       if ((c = control_connbypid(imsg->hdr.pid)) == NULL)
+               return (0);
+
+       return (imsg_compose_event(&c->iev, imsg->hdr.type, 0, imsg->hdr.pid,
+           -1, imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE));
+}
diff --git a/usr.sbin/rad/control.h b/usr.sbin/rad/control.h
new file mode 100644 (file)
index 0000000..7ac6952
--- /dev/null
@@ -0,0 +1,35 @@
+/*     $OpenBSD: control.h,v 1.1 2018/07/10 16:39:54 florian Exp $     */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+struct {
+       struct event    ev;
+       struct event    evt;
+       int             fd;
+} control_state;
+
+struct ctl_conn {
+       TAILQ_ENTRY(ctl_conn)   entry;
+       struct imsgev           iev;
+};
+
+int    control_init(char *);
+int    control_listen(void);
+void   control_accept(int, short, void *);
+void   control_dispatch_imsg(int, short, void *);
+int    control_imsg_relay(struct imsg *);
+void   control_cleanup(char *);
diff --git a/usr.sbin/rad/engine.c b/usr.sbin/rad/engine.c
new file mode 100644 (file)
index 0000000..3fd06d2
--- /dev/null
@@ -0,0 +1,478 @@
+/*     $OpenBSD: engine.c,v 1.1 2018/07/10 16:39:54 florian Exp $      */
+
+/*
+ * Copyright (c) 2018 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.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <netinet/icmp6.h>
+
+#include <errno.h>
+#include <event.h>
+#include <imsg.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "rad.h"
+#include "engine.h"
+
+__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            engine_showinfo_ctl(struct imsg *);
+void            parse_ra_rs(struct imsg_ra_rs *);
+void            parse_ra(struct imsg_ra_rs *);
+void            parse_rs(struct imsg_ra_rs *);
+
+struct rad_conf        *engine_conf;
+struct imsgev          *iev_frontend;
+struct imsgev          *iev_main;
+struct sockaddr_in6     all_nodes;
+
+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;
+
+       engine_conf = config_new_empty();
+
+       log_init(debug, LOG_DAEMON);
+       log_setverbose(verbose);
+
+       if ((pw = getpwnam(RAD_USER)) == NULL)
+               fatal("getpwnam");
+
+       if (chroot(pw->pw_dir) == -1)
+               fatal("chroot");
+       if (chdir("/") == -1)
+               fatal("chdir(\"/\")");
+
+       rad_process = PROC_ENGINE;
+       setproctitle("%s", log_procnames[rad_process]);
+       log_procinit(log_procnames[rad_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);
+
+       all_nodes.sin6_len = sizeof(all_nodes);
+       all_nodes.sin6_family = AF_INET6;
+       if (inet_pton(AF_INET6, "ff02::1", &all_nodes.sin6_addr) != 1)
+               fatal("inet_pton");
+
+       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);
+
+       config_clear(engine_conf);
+
+       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));
+}
+
+void
+engine_dispatch_frontend(int fd, short event, void *bula)
+{
+       struct imsgev           *iev = bula;
+       struct imsgbuf          *ibuf;
+       struct imsg              imsg;
+       struct imsg_ra_rs        ra_rs;
+       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("%s: imsg_get error", __func__);
+               if (n == 0)     /* No more messages. */
+                       break;
+
+               switch (imsg.hdr.type) {
+               case IMSG_RA_RS:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(ra_rs))
+                               fatal("%s: IMSG_RA_RS wrong length: %d",
+                                   __func__, imsg.hdr.len);
+                       memcpy(&ra_rs, imsg.data, sizeof(ra_rs));
+                       parse_ra_rs(&ra_rs);
+                       break;
+               case IMSG_CTL_LOG_VERBOSE:
+                       /* Already checked by frontend. */
+                       memcpy(&verbose, imsg.data, sizeof(verbose));
+                       log_setverbose(verbose);
+                       break;
+               case IMSG_CTL_SHOW_ENGINE_INFO:
+                       engine_showinfo_ctl(&imsg);
+                       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)
+{
+       static struct rad_conf          *nconf;
+       static struct ra_iface_conf     *ra_iface_conf;
+       struct imsg                      imsg;
+       struct imsgev                   *iev = bula;
+       struct imsgbuf                  *ibuf;
+       struct ra_prefix_conf           *ra_prefix_conf;
+       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("%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);
+                       break;
+               case IMSG_RECONF_CONF:
+                       if ((nconf = malloc(sizeof(struct rad_conf))) == NULL)
+                               fatal(NULL);
+                       memcpy(nconf, imsg.data, sizeof(struct rad_conf));
+                       SIMPLEQ_INIT(&nconf->ra_iface_list);
+                       break;
+               case IMSG_RECONF_RA_IFACE:
+                       if ((ra_iface_conf = malloc(sizeof(struct
+                           ra_iface_conf))) == NULL)
+                               fatal(NULL);
+                       memcpy(ra_iface_conf, imsg.data,
+                           sizeof(struct ra_iface_conf));
+                       ra_iface_conf->autoprefix = NULL;
+                       SIMPLEQ_INIT(&ra_iface_conf->ra_prefix_list);
+                       SIMPLEQ_INSERT_TAIL(&nconf->ra_iface_list,
+                           ra_iface_conf, entry);
+                       break;
+               case IMSG_RECONF_RA_AUTOPREFIX:
+                       if ((ra_iface_conf->autoprefix = malloc(sizeof(struct
+                           ra_prefix_conf))) == NULL)
+                               fatal(NULL);
+                       memcpy(ra_iface_conf->autoprefix, imsg.data,
+                           sizeof(struct ra_prefix_conf));
+                       break;
+               case IMSG_RECONF_RA_PREFIX:
+                       if ((ra_prefix_conf = malloc(sizeof(struct
+                           ra_prefix_conf))) == NULL)
+                               fatal(NULL);
+                       memcpy(ra_prefix_conf, imsg.data, sizeof(struct
+                           ra_prefix_conf));
+                       SIMPLEQ_INSERT_TAIL(&ra_iface_conf->ra_prefix_list,
+                           ra_prefix_conf, entry);
+                       break;
+               case IMSG_RECONF_END:
+                       merge_config(engine_conf, nconf);
+                       nconf = NULL;
+                       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_showinfo_ctl(struct imsg *imsg)
+{
+       char                     filter[IF_NAMESIZE];
+       struct ctl_engine_info   cei;
+       struct ra_iface_conf    *ra_iface_conf;
+
+       switch (imsg->hdr.type) {
+       case IMSG_CTL_SHOW_ENGINE_INFO:
+               memcpy(filter, imsg->data, sizeof(filter));
+               SIMPLEQ_FOREACH(ra_iface_conf, &engine_conf->ra_iface_list,
+                   entry) {
+                       if (filter[0] == '\0' || memcmp(filter,
+                           ra_iface_conf->name, sizeof(filter)) == 0) {
+                               memcpy(cei.name, ra_iface_conf->name,
+                                   sizeof(cei.name));
+
+                               engine_imsg_compose_frontend(
+                                   IMSG_CTL_SHOW_ENGINE_INFO, imsg->hdr.pid,
+                                   &cei, sizeof(cei));
+                       }
+               }
+               engine_imsg_compose_frontend(IMSG_CTL_END, imsg->hdr.pid, NULL,
+                   0);
+               break;
+       default:
+               log_debug("%s: error handling imsg", __func__);
+               break;
+       }
+}
+
+void
+parse_ra_rs(struct imsg_ra_rs *ra_rs)
+{
+       char                     ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+       struct icmp6_hdr        *hdr;
+
+       hdr = (struct icmp6_hdr *) ra_rs->packet;
+
+       switch (hdr->icmp6_type) {
+       case ND_ROUTER_ADVERT:
+               parse_ra(ra_rs);
+               break;
+       case ND_ROUTER_SOLICIT:
+               parse_rs(ra_rs);
+               break;
+       default:
+               log_warnx("unexpected icmp6_type: %d from %s on %s",
+                   hdr->icmp6_type, inet_ntop(AF_INET6, &ra_rs->from.sin6_addr,
+                   ntopbuf, INET6_ADDRSTRLEN), if_indextoname(ra_rs->if_index,
+                   ifnamebuf));
+               break;
+       }
+}
+
+void
+parse_ra(struct imsg_ra_rs *ra)
+{
+       char                     ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+       log_debug("got RA from %s on %s",
+           inet_ntop(AF_INET6, &ra->from.sin6_addr, ntopbuf,
+           INET6_ADDRSTRLEN), if_indextoname(ra->if_index,
+           ifnamebuf));
+       /* XXX not yet */
+}
+
+void
+parse_rs(struct imsg_ra_rs *rs)
+{
+       struct nd_router_solicit        *nd_rs;
+       struct imsg_send_ra              send_ra;
+       ssize_t                          len;
+       int                              source_linkaddr = 0;
+       const char                      *hbuf;
+       char                             ifnamebuf[IFNAMSIZ];
+       uint8_t                         *p;
+
+       hbuf = sin6_to_str(&rs->from);
+
+       send_ra.if_index = rs->if_index;
+       memcpy(&send_ra.to, &all_nodes, sizeof(send_ra.to));
+
+       log_debug("got RS from %s on %s", hbuf, if_indextoname(rs->if_index,
+           ifnamebuf));
+
+       len = rs->len;
+
+       if (!IN6_IS_ADDR_LINKLOCAL(&rs->from.sin6_addr)) {
+               log_warnx("RA from non link local address %s", hbuf);
+               return;
+       }
+
+       if ((size_t)len < sizeof(struct nd_router_solicit)) {
+               log_warnx("received too short message (%ld) from %s", len,
+                   hbuf);
+               return;
+       }
+
+       p = rs->packet;
+       nd_rs = (struct nd_router_solicit *)p;
+       len -= sizeof(struct nd_router_solicit);
+       p += sizeof(struct nd_router_solicit);
+
+       if (nd_rs->nd_rs_code != 0) {
+               log_warnx("invalid ICMPv6 code (%d) from %s", nd_rs->nd_rs_code,
+                   hbuf);
+               return;
+       }
+       while ((size_t)len >= sizeof(struct nd_opt_hdr)) {
+               struct nd_opt_hdr *nd_opt_hdr = (struct nd_opt_hdr *)p;
+
+               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;
+               }
+               switch (nd_opt_hdr->nd_opt_type) {
+               case ND_OPT_SOURCE_LINKADDR:
+                       log_debug("got RS with source linkaddr option");
+                       memcpy(&send_ra.to, &rs->from, sizeof(send_ra.to));
+                       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;
+       }
+       engine_imsg_compose_frontend(IMSG_SEND_RA, 0, &send_ra,
+           sizeof(send_ra));
+}
diff --git a/usr.sbin/rad/engine.h b/usr.sbin/rad/engine.h
new file mode 100644 (file)
index 0000000..68cb2b9
--- /dev/null
@@ -0,0 +1,21 @@
+/*     $OpenBSD: engine.h,v 1.1 2018/07/10 16:39:54 florian Exp $      */
+
+/*
+ * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
+ * 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.
+ */
+
+void            engine(int, int);
+int             engine_imsg_compose_frontend(int, pid_t, void *, uint16_t);
diff --git a/usr.sbin/rad/frontend.c b/usr.sbin/rad/frontend.c
new file mode 100644 (file)
index 0000000..5a33378
--- /dev/null
@@ -0,0 +1,936 @@
+/*     $OpenBSD: frontend.c,v 1.1 2018/07/10 16:39:54 florian Exp $    */
+
+/*
+ * Copyright (c) 2018 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.
+ */
+
+/*
+ * 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/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 <netinet6/nd6.h>
+#include <netinet6/in6_var.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 "rad.h"
+#include "frontend.h"
+#include "control.h"
+
+struct icmp6_ev {
+       struct event             ev;
+       uint8_t                  answer[1500];
+       struct msghdr            rcvmhdr;
+       struct iovec             rcviov[1];
+       struct sockaddr_in6      from;
+} icmp6ev;
+
+struct ra_iface {
+       TAILQ_ENTRY(ra_iface)           entry;
+       struct ra_prefix_conf_head      prefixes;
+       char                            name[IF_NAMESIZE];
+       uint32_t                        if_index;
+       int                             removed;
+       int                             prefix_count;
+       size_t                          datalen;
+       uint8_t                         data[1500];
+};
+
+TAILQ_HEAD(, ra_iface) ra_interfaces;
+
+__dead void             frontend_shutdown(void);
+void                    frontend_sig_handler(int, short, void *);
+void                    frontend_startup(void);
+void                    icmp6_receive(int, short, void *);
+void                    join_all_routers_mcast_group(struct ra_iface *);
+void                    leave_all_routers_mcast_group(struct ra_iface *);
+void                    merge_ra_interfaces(void);
+struct ra_iface                *find_ra_iface_by_id(uint32_t);
+struct ra_iface                *find_ra_iface_by_name(char *);
+struct ra_iface_conf   *find_ra_iface_conf(struct ra_iface_conf_head *,
+                           char *);
+struct ra_prefix_conf  *find_ra_prefix_conf(struct ra_prefix_conf_head*,
+                           struct in6_addr *, int);
+void                    add_new_prefix_to_ra_iface(struct ra_iface *r,
+                           struct in6_addr *, int, struct ra_prefix_conf *);
+void                    free_ra_iface(struct ra_iface *);
+int                     in6_mask2prefixlen(struct in6_addr *);
+void                    get_interface_prefixes(struct ra_iface *,
+                            struct ra_prefix_conf *);
+void                    build_package(struct ra_iface *);
+void                    ra_output(struct ra_iface *, struct sockaddr_in6 *);
+
+struct rad_conf        *frontend_conf;
+struct imsgev          *iev_main;
+struct imsgev          *iev_engine;
+int                     icmp6sock = -1, ioctlsock = -1;
+struct ipv6_mreq        all_routers;
+struct sockaddr_in6     all_nodes;
+struct msghdr           sndmhdr;
+struct iovec            sndiov[2];
+
+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;
+       size_t                   rcvcmsglen, sndcmsgbuflen;
+       uint8_t                 *rcvcmsgbuf;
+       uint8_t                 *sndcmsgbuf = NULL;
+
+       frontend_conf = config_new_empty();
+
+       log_init(debug, LOG_DAEMON);
+       log_setverbose(verbose);
+
+       /* XXX pass in from main */
+       /* Create rad control socket outside chroot. */
+       if (control_init(sockname) == -1)
+               fatalx("control socket setup failed");
+
+       if ((pw = getpwnam(RAD_USER)) == NULL)
+               fatal("getpwnam");
+
+       if (chroot(pw->pw_dir) == -1)
+               fatal("chroot");
+       if (chdir("/") == -1)
+               fatal("chdir(\"/\")");
+
+       rad_process = PROC_FRONTEND;
+       setproctitle("%s", log_procnames[rad_process]);
+       log_procinit(log_procnames[rad_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");
+
+       /* XXX pass in from main */
+       if ((ioctlsock = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0)
+               fatal("socket");
+
+       if (pledge("stdio inet unix recvfd route mcast", 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);
+
+       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;
+
+       if (inet_pton(AF_INET6, "ff02::2",
+           &all_routers.ipv6mr_multiaddr.s6_addr) == -1)
+               fatal("inet_pton");
+
+       all_nodes.sin6_len = sizeof(all_nodes);
+       all_nodes.sin6_family = AF_INET6;
+       if (inet_pton(AF_INET6, "ff02::1", &all_nodes.sin6_addr) != 1)
+               fatal("inet_pton");
+
+       sndcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+           CMSG_SPACE(sizeof(int));
+       if ((sndcmsgbuf = malloc(sndcmsgbuflen)) == NULL)
+               fatal("%s", __func__);
+
+       sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
+       sndmhdr.msg_iov = sndiov;
+       sndmhdr.msg_iovlen = 1;
+       sndmhdr.msg_control = sndcmsgbuf;
+       sndmhdr.msg_controllen = sndcmsgbuflen;
+
+       TAILQ_INIT(&ra_interfaces);
+
+       /* Listen on control socket. */
+       TAILQ_INIT(&ctl_conns);
+       control_listen();
+
+       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);
+
+       config_clear(frontend_conf);
+
+       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, pid_t pid, void *data, uint16_t datalen)
+{
+       return (imsg_compose_event(iev_engine, type, 0, pid, -1, data,
+           datalen));
+}
+
+void
+frontend_dispatch_main(int fd, short event, void *bula)
+{
+       static struct rad_conf          *nconf;
+       static struct ra_iface_conf     *ra_iface_conf;
+       struct imsg                      imsg;
+       struct imsgev                   *iev = bula;
+       struct imsgbuf                  *ibuf = &iev->ibuf;
+       struct ra_prefix_conf           *ra_prefix_conf;
+       int                              n, 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);
+                       break;
+               case IMSG_RECONF_CONF:
+                       if ((nconf = malloc(sizeof(struct rad_conf))) ==
+                           NULL)
+                               fatal(NULL);
+                       memcpy(nconf, imsg.data, sizeof(struct rad_conf));
+                       SIMPLEQ_INIT(&nconf->ra_iface_list);
+                       break;
+               case IMSG_RECONF_RA_IFACE:
+                       if ((ra_iface_conf = malloc(sizeof(struct
+                           ra_iface_conf))) == NULL)
+                               fatal(NULL);
+                       memcpy(ra_iface_conf, imsg.data, sizeof(struct
+                           ra_iface_conf));
+                       ra_iface_conf->autoprefix = NULL;
+                       SIMPLEQ_INIT(&ra_iface_conf->ra_prefix_list);
+                       SIMPLEQ_INSERT_TAIL(&nconf->ra_iface_list,
+                           ra_iface_conf, entry);
+                       break;
+               case IMSG_RECONF_RA_AUTOPREFIX:
+                       if ((ra_iface_conf->autoprefix = malloc(sizeof(struct
+                           ra_prefix_conf))) == NULL)
+                               fatal(NULL);
+                       memcpy(ra_iface_conf->autoprefix, imsg.data,
+                           sizeof(struct ra_prefix_conf));
+                       break;
+               case IMSG_RECONF_RA_PREFIX:
+                       if ((ra_prefix_conf = malloc(sizeof(struct
+                           ra_prefix_conf))) == NULL)
+                               fatal(NULL);
+                       memcpy(ra_prefix_conf, imsg.data,
+                           sizeof(struct ra_prefix_conf));
+                       SIMPLEQ_INSERT_TAIL(&ra_iface_conf->ra_prefix_list,
+                           ra_prefix_conf, entry);
+                       break;
+               case IMSG_RECONF_END:
+                       merge_config(frontend_conf, nconf);
+                       merge_ra_interfaces();
+                       nconf = NULL;
+                       break;
+               case IMSG_ICMP6SOCK:
+                       if ((icmp6sock = imsg.fd) == -1)
+                               fatalx("%s: expected to receive imsg "
+                                   "ICMPv6 fd but didn't receive any",
+                                   __func__);
+                       event_set(&icmp6ev.ev, icmp6sock, EV_READ | EV_PERSIST,
+                           icmp6_receive, NULL);
+               case IMSG_STARTUP:
+                       if (pledge("stdio inet unix route mcast", NULL) == -1)
+                               fatal("pledge");
+                       frontend_startup();
+                       break;
+               case IMSG_CTL_END:
+               case IMSG_CTL_SHOW_MAIN_INFO:
+                       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;
+       struct imsg_send_ra      send_ra;
+       struct ra_iface         *ra_iface;
+       int                      n, 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_CTL_END:
+               case IMSG_CTL_SHOW_ENGINE_INFO:
+                       control_imsg_relay(&imsg);
+                       break;
+               case IMSG_SEND_RA:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(send_ra))
+                               fatal("%s: IMSG_SEND_RA wrong length: %d",
+                                   __func__, imsg.hdr.len);
+                       memcpy(&send_ra, imsg.data, sizeof(send_ra));
+                       ra_iface = find_ra_iface_by_id(send_ra.if_index);
+                       if (ra_iface)
+                               ra_output(ra_iface, &send_ra.to);
+                       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_showinfo_ctl(struct ctl_conn *c)
+{
+       static struct ctl_frontend_info cfi;
+
+       imsg_compose_event(&c->iev, IMSG_CTL_SHOW_FRONTEND_INFO, 0, 0, -1,
+           &cfi, sizeof(struct ctl_frontend_info));
+}
+
+void
+frontend_startup(void)
+{
+#if 0
+       if (!event_initialized(&ev_route))
+               fatalx("%s: did not receive a route socket from the main "
+                   "process", __func__);
+
+       event_add(&ev_route, NULL);
+#endif
+
+       if (!event_initialized(&icmp6ev.ev))
+               fatalx("%s: did not receive a icmp6 socket fd from the main "
+                   "process", __func__);
+
+       event_add(&icmp6ev.ev, NULL);
+
+       frontend_imsg_compose_main(IMSG_STARTUP_DONE, 0, NULL, 0);
+}
+
+
+void
+icmp6_receive(int fd, short events, void *arg)
+{
+       struct imsg_ra_rs        ra_rs;
+       struct in6_pktinfo      *pi = NULL;
+       struct cmsghdr          *cm;
+       ssize_t                  len;
+       int                      if_index = 0, *hlimp = NULL;
+       char                     ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+
+       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_warnx("invalid RA or RS 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;
+       }
+
+       log_warnx("RA or RS 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));
+
+       if ((size_t)len > sizeof(ra_rs.packet)) {
+               log_warnx("invalid RA or RS 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_rs.if_index = if_index;
+       memcpy(&ra_rs.from,  &icmp6ev.from, sizeof(ra_rs.from));
+       ra_rs.len = len;
+       memcpy(ra_rs.packet, icmp6ev.answer, len);
+
+       frontend_imsg_compose_engine(IMSG_RA_RS, 0, &ra_rs, sizeof(ra_rs));
+}
+
+void
+join_all_routers_mcast_group(struct ra_iface *ra_iface)
+{
+       log_debug("joining multicast group on %s", ra_iface->name);
+       all_routers.ipv6mr_interface = ra_iface->if_index;
+       if (setsockopt(icmp6sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+           &all_routers, sizeof(all_routers)) == -1)
+               fatal("IPV6_JOIN_GROUP(%s)", ra_iface->name);
+}
+
+void
+leave_all_routers_mcast_group(struct ra_iface *ra_iface)
+{
+       log_debug("leaving multicast group on %s", ra_iface->name);
+       all_routers.ipv6mr_interface = ra_iface->if_index;
+       if (setsockopt(icmp6sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
+           &all_routers, sizeof(all_routers)) == -1)
+               fatal("IPV6_LEAVE_GROUP(%s)", ra_iface->name);
+}
+
+struct ra_iface*
+find_ra_iface_by_id(uint32_t if_index) {
+       struct ra_iface *ra_iface;
+
+       TAILQ_FOREACH(ra_iface, &ra_interfaces, entry) {
+               if (ra_iface->if_index == if_index)
+                       return ra_iface;
+       }
+       return (NULL);
+}
+
+struct ra_iface*
+find_ra_iface_by_name(char *if_name)
+{
+       struct ra_iface *ra_iface;
+
+       TAILQ_FOREACH(ra_iface, &ra_interfaces, entry) {
+               if (strcmp(ra_iface->name, if_name) == 0)
+                       return ra_iface;
+       }
+       return (NULL);
+}
+
+struct ra_iface_conf*
+find_ra_iface_conf(struct ra_iface_conf_head *head, char *if_name)
+{
+       struct ra_iface_conf    *ra_iface_conf;
+
+       SIMPLEQ_FOREACH(ra_iface_conf, head, entry) {
+               if (strcmp(ra_iface_conf->name, if_name) == 0)
+                       return ra_iface_conf;
+       }
+       return (NULL);
+}
+
+void
+merge_ra_interfaces(void)
+{
+       struct ra_iface_conf    *ra_iface_conf;
+       struct ra_prefix_conf   *ra_prefix_conf;
+       struct ra_iface         *ra_iface, *tmp;
+       uint32_t                 if_index;
+
+       TAILQ_FOREACH(ra_iface, &ra_interfaces, entry)
+               ra_iface->removed = 1;
+
+       SIMPLEQ_FOREACH(ra_iface_conf, &frontend_conf->ra_iface_list, entry) {
+               ra_iface = find_ra_iface_by_name(ra_iface_conf->name);
+               if (ra_iface == NULL) {
+                       log_debug("new interface %s", ra_iface_conf->name);
+                       if ((if_index = if_nametoindex(ra_iface_conf->name))
+                           == 0)
+                               continue;
+                       log_debug("adding interface %s", ra_iface_conf->name);
+                       if ((ra_iface = calloc(1, sizeof(*ra_iface))) == NULL)
+                               fatal("%s", __func__);
+
+                       (void) strlcpy(ra_iface->name, ra_iface_conf->name,
+                           sizeof(ra_iface->name));
+                       ra_iface->if_index = if_index;
+                       SIMPLEQ_INIT(&ra_iface->prefixes);
+                       TAILQ_INSERT_TAIL(&ra_interfaces, ra_iface, entry);
+                       join_all_routers_mcast_group(ra_iface);
+               } else {
+                       log_debug("keeping interface %s", ra_iface_conf->name);
+                       ra_iface->removed = 0;
+               }
+       }
+
+       TAILQ_FOREACH_SAFE(ra_iface, &ra_interfaces, entry, tmp) {
+               if (ra_iface->removed) {
+                       TAILQ_REMOVE(&ra_interfaces, ra_iface, entry);
+                       free_ra_iface(ra_iface);
+               }
+       }
+
+       TAILQ_FOREACH(ra_iface, &ra_interfaces, entry) {
+               while ((ra_prefix_conf = SIMPLEQ_FIRST(&ra_iface->prefixes))
+                   != NULL) {
+                       SIMPLEQ_REMOVE_HEAD(&ra_iface->prefixes,
+                           entry);
+                       free(ra_prefix_conf);
+               }
+               ra_iface->prefix_count = 0;
+
+               ra_iface_conf = find_ra_iface_conf(
+                   &frontend_conf->ra_iface_list, ra_iface->name);
+
+               if (ra_iface_conf->autoprefix)
+                       get_interface_prefixes(ra_iface,
+                           ra_iface_conf->autoprefix);
+
+               log_debug("add static prefixes for %s", ra_iface->name);
+
+               SIMPLEQ_FOREACH(ra_prefix_conf, &ra_iface_conf->ra_prefix_list,
+                   entry) {
+                       add_new_prefix_to_ra_iface(ra_iface,
+                           &ra_prefix_conf->prefix,
+                           ra_prefix_conf->prefixlen, ra_prefix_conf);
+               }
+               build_package(ra_iface);
+       }
+}
+
+void
+free_ra_iface(struct ra_iface *ra_iface)
+{
+       struct ra_prefix_conf   *prefix;
+
+       leave_all_routers_mcast_group(ra_iface);
+
+       while ((prefix = SIMPLEQ_FIRST(&ra_iface->prefixes)) != NULL) {
+               SIMPLEQ_REMOVE_HEAD(&ra_iface->prefixes, entry);
+               free(prefix);
+       }
+
+       free(ra_iface);
+}
+
+/* from kame via ifconfig, where it's called prefix() */
+int
+in6_mask2prefixlen(struct in6_addr *in6)
+{
+       u_char *nam = (u_char *)in6;
+       int byte, bit, plen = 0, size = sizeof(struct in6_addr);
+
+       for (byte = 0; byte < size; byte++, plen += 8)
+               if (nam[byte] != 0xff)
+                       break;
+       if (byte == size)
+               return (plen);
+       for (bit = 7; bit != 0; bit--, plen++)
+               if (!(nam[byte] & (1 << bit)))
+                       break;
+       for (; bit != 0; bit--)
+               if (nam[byte] & (1 << bit))
+                       return (0);
+       byte++;
+       for (; byte < size; byte++)
+               if (nam[byte])
+                       return (0);
+       return (plen);
+}
+
+void
+get_interface_prefixes(struct ra_iface *ra_iface, struct ra_prefix_conf
+    *autoprefix)
+{
+       struct in6_ifreq         ifr6;
+       struct ifaddrs          *ifap, *ifa;
+       struct sockaddr_in6     *sin6;
+       int                      prefixlen;
+
+       log_debug("%s: %s", __func__, ra_iface->name);
+
+       if (getifaddrs(&ifap) != 0)
+               fatal("getifaddrs");
+
+       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+               if (strcmp(ra_iface->name, ifa->ifa_name) != 0)
+                       continue;
+               if (ifa->ifa_addr->sa_family != AF_INET6)
+                       continue;
+
+               sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+
+               if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+                       continue;
+
+               memset(&ifr6, 0, sizeof(ifr6));
+               (void) strlcpy(ifr6.ifr_name, ra_iface->name,
+                   sizeof(ifr6.ifr_name));
+               memcpy(&ifr6.ifr_addr, sin6, sizeof(ifr6.ifr_addr));
+               
+               if (ioctl(ioctlsock, SIOCGIFNETMASK_IN6, (caddr_t)&ifr6) < 0)
+                       fatal("SIOCGIFNETMASK_IN6");
+
+               prefixlen = in6_mask2prefixlen(&((struct sockaddr_in6 *)
+                   &ifr6.ifr_addr)->sin6_addr);
+
+               if (prefixlen == 128)
+                       continue;
+
+               mask_prefix(&sin6->sin6_addr, prefixlen);
+
+               add_new_prefix_to_ra_iface(ra_iface, &sin6->sin6_addr,
+                   prefixlen, autoprefix);
+
+       }
+}
+
+struct ra_prefix_conf*
+find_ra_prefix_conf(struct ra_prefix_conf_head* head, struct in6_addr *prefix,
+    int prefixlen)
+{
+       struct ra_prefix_conf   *ra_prefix_conf;
+
+       SIMPLEQ_FOREACH(ra_prefix_conf, head, entry) {
+               if (ra_prefix_conf->prefixlen == prefixlen &&
+                   memcmp(&ra_prefix_conf->prefix, prefix, sizeof(*prefix)) ==
+                   0)
+                       return (ra_prefix_conf);
+       }
+       return (NULL);
+}
+
+void
+add_new_prefix_to_ra_iface(struct ra_iface *ra_iface, struct in6_addr *addr,
+    int prefixlen, struct ra_prefix_conf *ra_prefix_conf)
+{
+       struct ra_prefix_conf   *new_ra_prefix_conf;
+
+       if (find_ra_prefix_conf(&ra_iface->prefixes, addr, prefixlen)) {
+               log_debug("ignoring duplicate %s/%d prefix",
+                   in6_to_str(addr), prefixlen);
+               return;
+       }
+
+       log_debug("adding %s/%d prefix", in6_to_str(addr), prefixlen);
+
+       if ((new_ra_prefix_conf = calloc(1, sizeof(*ra_prefix_conf))) == NULL)
+               fatal("%s", __func__);
+       new_ra_prefix_conf->prefix = *addr;
+       new_ra_prefix_conf->prefixlen = prefixlen;
+       new_ra_prefix_conf->vltime = ra_prefix_conf->vltime;
+       new_ra_prefix_conf->pltime = ra_prefix_conf->pltime;
+       new_ra_prefix_conf->aflag = ra_prefix_conf->aflag;
+       new_ra_prefix_conf->lflag = ra_prefix_conf->lflag;
+       SIMPLEQ_INSERT_TAIL(&ra_iface->prefixes, new_ra_prefix_conf, entry);
+       ra_iface->prefix_count++;
+}
+
+void
+build_package(struct ra_iface *ra_iface)
+{
+       struct nd_router_advert         *ra;
+       struct nd_opt_prefix_info       *ndopt_pi;
+       struct ra_iface_conf            *ra_iface_conf;
+       struct ra_options_conf          *ra_options_conf;
+       struct ra_prefix_conf           *ra_prefix_conf;
+       uint8_t                         *buf;
+
+       ra_iface_conf = find_ra_iface_conf(&frontend_conf->ra_iface_list,
+           ra_iface->name);
+       ra_options_conf = &ra_iface_conf->ra_options;
+
+       ra_iface->datalen = sizeof(*ra);
+       ra_iface->datalen += sizeof(*ndopt_pi) * ra_iface->prefix_count;
+
+       if (ra_iface->datalen > sizeof(ra_iface->data))
+               fatal("%s: packet too big", __func__); /* XXX send multiple */
+
+       buf = ra_iface->data;
+
+       ra = (struct nd_router_advert *)buf;
+
+       memset(ra, 0, sizeof(*ra));
+
+       ra->nd_ra_type = ND_ROUTER_ADVERT;
+       ra->nd_ra_curhoplimit = ra_options_conf->cur_hl;
+       if (ra_options_conf->m_flag)
+               ra->nd_ra_flags_reserved |= ND_RA_FLAG_MANAGED;
+       if (ra_options_conf->o_flag)
+               ra->nd_ra_flags_reserved |= ND_RA_FLAG_OTHER;
+       if (ra_options_conf->dfr)
+               ra->nd_ra_router_lifetime =
+                   htons(ra_options_conf->router_lifetime);
+       ra->nd_ra_reachable = htonl(ra_options_conf->reachable_time);
+       ra->nd_ra_retransmit = htonl(ra_options_conf->retrans_timer);
+       buf += sizeof(*ra);
+
+       SIMPLEQ_FOREACH(ra_prefix_conf, &ra_iface->prefixes, entry) {
+               ndopt_pi = (struct nd_opt_prefix_info *)buf;
+               memset(ndopt_pi, 0, sizeof(*ndopt_pi));
+               ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
+               ndopt_pi->nd_opt_pi_len = 4;
+               ndopt_pi->nd_opt_pi_prefix_len = ra_prefix_conf->prefixlen;
+               if (ra_prefix_conf->lflag)
+                       ndopt_pi->nd_opt_pi_flags_reserved |=
+                           ND_OPT_PI_FLAG_ONLINK;
+               if (ra_prefix_conf->aflag)
+                       ndopt_pi->nd_opt_pi_flags_reserved |=
+                           ND_OPT_PI_FLAG_AUTO;
+               ndopt_pi->nd_opt_pi_valid_time = htonl(ra_prefix_conf->vltime);
+               ndopt_pi->nd_opt_pi_preferred_time =
+                   htonl(ra_prefix_conf->pltime);
+               ndopt_pi->nd_opt_pi_prefix = ra_prefix_conf->prefix;
+
+               buf += sizeof(*ndopt_pi);
+       }
+}
+
+void
+ra_output(struct ra_iface *ra_iface, struct sockaddr_in6 *to)
+{
+
+       struct cmsghdr          *cm;
+       struct in6_pktinfo      *pi;
+       ssize_t                  len;
+       int                      hoplimit = 255;
+
+       sndmhdr.msg_name = to;
+       sndmhdr.msg_iov[0].iov_base = ra_iface->data;
+       sndmhdr.msg_iov[0].iov_len = ra_iface->datalen;
+
+       cm = CMSG_FIRSTHDR(&sndmhdr);
+       /* specify the outgoing interface */
+       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 = ra_iface->if_index;
+
+       /* specify the hop limit of the packet */
+       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));
+
+       log_debug("send RA on %s", ra_iface->name);
+
+       len = sendmsg(icmp6sock, &sndmhdr, 0);
+       if (len < 0)
+               log_warn("sendmsg on %s", ra_iface->name);
+
+}
diff --git a/usr.sbin/rad/frontend.h b/usr.sbin/rad/frontend.h
new file mode 100644 (file)
index 0000000..ac368cb
--- /dev/null
@@ -0,0 +1,27 @@
+/*     $OpenBSD: frontend.h,v 1.1 2018/07/10 16:39:54 florian Exp $    */
+
+/*
+ * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
+ * 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, pid_t, void *, uint16_t);
+void            frontend_showinfo_ctl(struct ctl_conn *);
diff --git a/usr.sbin/rad/log.c b/usr.sbin/rad/log.c
new file mode 100644 (file)
index 0000000..50b0a0e
--- /dev/null
@@ -0,0 +1,199 @@
+/*     $OpenBSD: log.c,v 1.1 2018/07/10 16:39:54 florian Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <syslog.h>
+#include <errno.h>
+#include <time.h>
+
+#include "log.h"
+
+static int              debug;
+static int              verbose;
+static const char      *log_procname;
+
+void
+log_init(int n_debug, int facility)
+{
+       extern char     *__progname;
+
+       debug = n_debug;
+       verbose = n_debug;
+       log_procinit(__progname);
+
+       if (!debug)
+               openlog(__progname, LOG_PID | LOG_NDELAY, facility);
+
+       tzset();
+}
+
+void
+log_procinit(const char *procname)
+{
+       if (procname != NULL)
+               log_procname = procname;
+}
+
+void
+log_setverbose(int v)
+{
+       verbose = v;
+}
+
+int
+log_getverbose(void)
+{
+       return (verbose);
+}
+
+void
+logit(int pri, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       vlog(pri, fmt, ap);
+       va_end(ap);
+}
+
+void
+vlog(int pri, const char *fmt, va_list ap)
+{
+       char    *nfmt;
+       int      saved_errno = errno;
+
+       if (debug) {
+               /* best effort in out of mem situations */
+               if (asprintf(&nfmt, "%s\n", fmt) == -1) {
+                       vfprintf(stderr, fmt, ap);
+                       fprintf(stderr, "\n");
+               } else {
+                       vfprintf(stderr, nfmt, ap);
+                       free(nfmt);
+               }
+               fflush(stderr);
+       } else
+               vsyslog(pri, fmt, ap);
+
+       errno = saved_errno;
+}
+
+void
+log_warn(const char *emsg, ...)
+{
+       char            *nfmt;
+       va_list          ap;
+       int              saved_errno = errno;
+
+       /* best effort to even work in out of memory situations */
+       if (emsg == NULL)
+               logit(LOG_ERR, "%s", strerror(saved_errno));
+       else {
+               va_start(ap, emsg);
+
+               if (asprintf(&nfmt, "%s: %s", emsg,
+                   strerror(saved_errno)) == -1) {
+                       /* we tried it... */
+                       vlog(LOG_ERR, emsg, ap);
+                       logit(LOG_ERR, "%s", strerror(saved_errno));
+               } else {
+                       vlog(LOG_ERR, nfmt, ap);
+                       free(nfmt);
+               }
+               va_end(ap);
+       }
+
+       errno = saved_errno;
+}
+
+void
+log_warnx(const char *emsg, ...)
+{
+       va_list  ap;
+
+       va_start(ap, emsg);
+       vlog(LOG_ERR, emsg, ap);
+       va_end(ap);
+}
+
+void
+log_info(const char *emsg, ...)
+{
+       va_list  ap;
+
+       va_start(ap, emsg);
+       vlog(LOG_INFO, emsg, ap);
+       va_end(ap);
+}
+
+void
+log_debug(const char *emsg, ...)
+{
+       va_list  ap;
+
+       if (verbose) {
+               va_start(ap, emsg);
+               vlog(LOG_DEBUG, emsg, ap);
+               va_end(ap);
+       }
+}
+
+static void
+vfatalc(int code, const char *emsg, va_list ap)
+{
+       static char     s[BUFSIZ];
+       const char      *sep;
+
+       if (emsg != NULL) {
+               (void)vsnprintf(s, sizeof(s), emsg, ap);
+               sep = ": ";
+       } else {
+               s[0] = '\0';
+               sep = "";
+       }
+       if (code)
+               logit(LOG_CRIT, "fatal in %s: %s%s%s",
+                   log_procname, s, sep, strerror(code));
+       else
+               logit(LOG_CRIT, "fatal in %s%s%s", log_procname, sep, s);
+}
+
+void
+fatal(const char *emsg, ...)
+{
+       va_list ap;
+
+       va_start(ap, emsg);
+       vfatalc(errno, emsg, ap);
+       va_end(ap);
+       exit(1);
+}
+
+void
+fatalx(const char *emsg, ...)
+{
+       va_list ap;
+
+       va_start(ap, emsg);
+       vfatalc(0, emsg, ap);
+       va_end(ap);
+       exit(1);
+}
diff --git a/usr.sbin/rad/log.h b/usr.sbin/rad/log.h
new file mode 100644 (file)
index 0000000..317b331
--- /dev/null
@@ -0,0 +1,46 @@
+/*     $OpenBSD: log.h,v 1.1 2018/07/10 16:39:54 florian Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef LOG_H
+#define LOG_H
+
+#include <stdarg.h>
+#include <sys/cdefs.h>
+
+void   log_init(int, int);
+void   log_procinit(const char *);
+void   log_setverbose(int);
+int    log_getverbose(void);
+void   log_warn(const char *, ...)
+           __attribute__((__format__ (printf, 1, 2)));
+void   log_warnx(const char *, ...)
+           __attribute__((__format__ (printf, 1, 2)));
+void   log_info(const char *, ...)
+           __attribute__((__format__ (printf, 1, 2)));
+void   log_debug(const char *, ...)
+           __attribute__((__format__ (printf, 1, 2)));
+void   logit(int, const char *, ...)
+           __attribute__((__format__ (printf, 2, 3)));
+void   vlog(int, const char *, va_list)
+           __attribute__((__format__ (printf, 2, 0)));
+__dead void fatal(const char *, ...)
+           __attribute__((__format__ (printf, 1, 2)));
+__dead void fatalx(const char *, ...)
+           __attribute__((__format__ (printf, 1, 2)));
+
+#endif /* LOG_H */
diff --git a/usr.sbin/rad/parse.y b/usr.sbin/rad/parse.y
new file mode 100644 (file)
index 0000000..ab533c8
--- /dev/null
@@ -0,0 +1,891 @@
+/*     $OpenBSD: parse.y,v 1.1 2018/07/10 16:39:54 florian Exp $       */
+
+/*
+ * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
+ * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2001 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
+ *
+ * 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/socket.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <ifaddrs.h>
+#include <imsg.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "rad.h"
+#include "frontend.h"
+
+TAILQ_HEAD(files, file)                 files = TAILQ_HEAD_INITIALIZER(files);
+static struct file {
+       TAILQ_ENTRY(file)        entry;
+       FILE                    *stream;
+       char                    *name;
+       size_t                   ungetpos;
+       size_t                   ungetsize;
+       u_char                  *ungetbuf;
+       int                      eof_reached;
+       int                      lineno;
+       int                      errors;
+} *file, *topfile;
+struct file    *pushfile(const char *, int);
+int             popfile(void);
+int             check_file_secrecy(int, const char *);
+int             yyparse(void);
+int             yylex(void);
+int             yyerror(const char *, ...)
+    __attribute__((__format__ (printf, 1, 2)))
+    __attribute__((__nonnull__ (1)));
+int             kw_cmp(const void *, const void *);
+int             lookup(char *);
+int             igetc(void);
+int             lgetc(int);
+void            lungetc(int);
+int             findeol(void);
+
+TAILQ_HEAD(symhead, sym)        symhead = TAILQ_HEAD_INITIALIZER(symhead);
+struct sym {
+       TAILQ_ENTRY(sym)         entry;
+       int                      used;
+       int                      persist;
+       char                    *nam;
+       char                    *val;
+};
+
+int     symset(const char *, const char *, int);
+char   *symget(const char *);
+
+void    clear_config(struct rad_conf *xconf);
+
+static struct rad_conf         *conf;
+static struct ra_options_conf  *ra_options;
+static int                      errors;
+
+static struct ra_iface_conf    *ra_iface_conf;
+static struct ra_prefix_conf   *ra_prefix_conf;
+
+struct ra_prefix_conf  *conf_get_ra_prefix(struct in6_addr*, int);
+struct ra_iface_conf   *conf_get_ra_iface(char *);
+
+typedef struct {
+       union {
+               int64_t          number;
+               char            *string;
+       } v;
+       int lineno;
+} YYSTYPE;
+
+%}
+
+%token RA_IFACE YES NO INCLUDE ERROR
+%token DEFAULT ROUTER HOP LIMIT MANAGED ADDRESS
+%token CONFIGURATION OTHER LIFETIME REACHABLE TIME RETRANS TIMER
+%token AUTO PREFIX VALID PREFERRED LIFETIME ONLINK AUTONOMOUS
+%token ADDRESS_CONFIGURATION
+
+%token <v.string>      STRING
+%token <v.number>      NUMBER
+%type  <v.number>      yesno
+%type  <v.string>      string
+
+%%
+
+grammar                : /* empty */
+               | grammar include '\n'
+               | grammar '\n'
+               | grammar { ra_options = &conf->ra_options; } conf_main '\n'
+               | grammar varset '\n'
+               | grammar ra_iface '\n'
+               | grammar error '\n'            { file->errors++; }
+               ;
+
+include                : INCLUDE STRING                {
+                       struct file     *nfile;
+
+                       if ((nfile = pushfile($2, 1)) == NULL) {
+                               yyerror("failed to include file %s", $2);
+                               free($2);
+                               YYERROR;
+                       }
+                       free($2);
+
+                       file = nfile;
+                       lungetc('\n');
+               }
+               ;
+
+string         : string STRING {
+                       if (asprintf(&$$, "%s %s", $1, $2) == -1) {
+                               free($1);
+                               free($2);
+                               yyerror("string: asprintf");
+                               YYERROR;
+                       }
+                       free($1);
+                       free($2);
+               }
+               | STRING
+               ;
+
+yesno          : YES   { $$ = 1; }
+               | NO    { $$ = 0; }
+               ;
+
+varset         : STRING '=' string             {
+                       char *s = $1;
+                       if (cmd_opts & OPT_VERBOSE)
+                               printf("%s = \"%s\"\n", $1, $3);
+                       while (*s++) {
+                               if (isspace((unsigned char)*s)) {
+                                       yyerror("macro name cannot contain "
+                                           "whitespace");
+                                       YYERROR;
+                               }
+                       }
+                       if (symset($1, $3, 0) == -1)
+                               fatal("cannot store variable");
+                       free($1);
+                       free($3);
+               }
+               ;
+
+conf_main      : ra_opt_block {
+                       ra_options = &conf->ra_options;
+               } 
+               ;
+
+ra_opt_block   : DEFAULT ROUTER yesno {
+                       ra_options->dfr = $3;
+               }
+               | HOP LIMIT NUMBER {
+                       ra_options->cur_hl = $3;
+               }
+               | MANAGED ADDRESS CONFIGURATION yesno {
+                       ra_options->m_flag = $4;
+               }
+               | OTHER CONFIGURATION yesno {
+                       ra_options->o_flag = $3;
+               }
+               | ROUTER LIFETIME NUMBER {
+                       ra_options->router_lifetime = $3;
+               }
+               | REACHABLE TIME NUMBER {
+                       ra_options->reachable_time = $3;
+               }
+               | RETRANS TIMER NUMBER {
+                       ra_options->retrans_timer = $3;
+               }
+               ;
+
+optnl          : '\n' optnl            /* zero or more newlines */
+               | /*empty*/
+               ;
+
+nl             : '\n' optnl            /* one or more newlines */
+               ;
+
+ra_iface       : RA_IFACE STRING {
+                       ra_iface_conf = conf_get_ra_iface($2);
+                       /* set auto prefix defaults */
+                       ra_iface_conf->autoprefix = conf_get_ra_prefix(NULL, 0);
+                       ra_options = &ra_iface_conf->ra_options;
+               } ra_iface_block {
+                       ra_iface_conf = NULL;
+               }
+               ;
+
+ra_iface_block : '{' optnl ra_ifaceopts_l '}'
+               | '{' optnl '}'
+               | /* empty */
+               ;
+
+ra_ifaceopts_l : ra_ifaceopts_l ra_ifaceoptsl nl
+               | ra_ifaceoptsl optnl
+               ;
+
+ra_ifaceoptsl  : NO AUTO PREFIX {
+                       free(ra_iface_conf->autoprefix);
+                       ra_iface_conf->autoprefix = NULL;
+               }
+               | AUTO PREFIX {
+                       if (ra_iface_conf->autoprefix == NULL)
+                               ra_iface_conf->autoprefix = 
+                       ra_prefix_conf = conf_get_ra_prefix(NULL, 0);
+               } ra_prefix_block {
+                       ra_prefix_conf = NULL;
+               }
+               | PREFIX STRING {
+                       struct in6_addr  addr;
+                       int              prefixlen;
+
+                       memset(&addr, 0, sizeof(addr));
+                       prefixlen = inet_net_pton(AF_INET6, $2, &addr,
+                           sizeof(addr));
+                       if (prefixlen == -1) {
+                               yyerror("error parsing prefix");
+                               free($2);
+                               YYERROR;
+                       }
+                       mask_prefix(&addr, prefixlen);
+                       ra_prefix_conf = conf_get_ra_prefix(&addr, prefixlen);
+               } ra_prefix_block {
+                       ra_prefix_conf = NULL;
+               }
+               | ra_opt_block
+               ;
+
+ra_prefix_block        : '{' optnl ra_prefixopts_l '}'
+               | '{' optnl '}'
+               | /* empty */
+               ;
+
+ra_prefixopts_l        : ra_prefixopts_l ra_prefixoptsl nl
+               | ra_prefixoptsl optnl
+               ;
+
+ra_prefixoptsl : VALID LIFETIME NUMBER {
+                       ra_prefix_conf->vltime = $3;
+               }
+               | PREFERRED LIFETIME NUMBER {
+                       ra_prefix_conf->pltime = $3;
+               }
+               | ONLINK yesno {
+                       ra_prefix_conf->lflag = $2;
+               }
+               | AUTONOMOUS ADDRESS_CONFIGURATION yesno {
+                       ra_prefix_conf->aflag = $3;
+               }
+               ;
+
+%%
+
+struct keywords {
+       const char      *k_name;
+       int              k_val;
+};
+
+int
+yyerror(const char *fmt, ...)
+{
+       va_list          ap;
+       char            *msg;
+
+       file->errors++;
+       va_start(ap, fmt);
+       if (vasprintf(&msg, fmt, ap) == -1)
+               fatalx("yyerror vasprintf");
+       va_end(ap);
+       logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
+       free(msg);
+       return (0);
+}
+
+int
+kw_cmp(const void *k, const void *e)
+{
+       return (strcmp(k, ((const struct keywords *)e)->k_name));
+}
+
+int
+lookup(char *s)
+{
+       /* This has to be sorted always. */
+       static const struct keywords keywords[] = {
+               {"address",             ADDRESS},
+               {"address-configuration",       ADDRESS_CONFIGURATION},
+               {"auto",                AUTO},
+               {"autonomous",          AUTONOMOUS},
+               {"configuration",       CONFIGURATION},
+               {"default",             DEFAULT},
+               {"hop",                 HOP},
+               {"include",             INCLUDE},
+               {"interface",           RA_IFACE},
+               {"lifetime",            LIFETIME},
+               {"limit",               LIMIT},
+               {"managed",             MANAGED},
+               {"no",                  NO},
+               {"on-link",             ONLINK},
+               {"other",               OTHER},
+               {"preferred",           PREFERRED},
+               {"prefix",              PREFIX},
+               {"reachable",           REACHABLE},
+               {"retrans",             RETRANS},
+               {"router",              ROUTER},
+               {"time",                TIME},
+               {"timer",               TIMER},
+               {"valid",               VALID},
+               {"yes",                 YES},
+       };
+       const struct keywords   *p;
+
+       p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+           sizeof(keywords[0]), kw_cmp);
+
+       if (p)
+               return (p->k_val);
+       else
+               return (STRING);
+}
+
+#define START_EXPAND   1
+#define DONE_EXPAND    2
+
+static int     expanding;
+
+int
+igetc(void)
+{
+       int     c;
+
+       while (1) {
+               if (file->ungetpos > 0)
+                       c = file->ungetbuf[--file->ungetpos];
+               else
+                       c = getc(file->stream);
+
+               if (c == START_EXPAND)
+                       expanding = 1;
+               else if (c == DONE_EXPAND)
+                       expanding = 0;
+               else
+                       break;
+       }
+       return (c);
+}
+
+int
+lgetc(int quotec)
+{
+       int             c, next;
+
+       if (quotec) {
+               if ((c = igetc()) == EOF) {
+                       yyerror("reached end of file while parsing "
+                           "quoted string");
+                       if (file == topfile || popfile() == EOF)
+                               return (EOF);
+                       return (quotec);
+               }
+               return (c);
+       }
+
+       while ((c = igetc()) == '\\') {
+               next = igetc();
+               if (next != '\n') {
+                       c = next;
+                       break;
+               }
+               yylval.lineno = file->lineno;
+               file->lineno++;
+       }
+
+       if (c == EOF) {
+               /*
+                * Fake EOL when hit EOF for the first time. This gets line
+                * count right if last line in included file is syntactically
+                * invalid and has no newline.
+                */
+               if (file->eof_reached == 0) {
+                       file->eof_reached = 1;
+                       return ('\n');
+               }
+               while (c == EOF) {
+                       if (file == topfile || popfile() == EOF)
+                               return (EOF);
+                       c = igetc();
+               }
+       }
+       return (c);
+}
+
+void
+lungetc(int c)
+{
+       if (c == EOF)
+               return;
+
+       if (file->ungetpos >= file->ungetsize) {
+               void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
+               if (p == NULL)
+                       err(1, "lungetc");
+               file->ungetbuf = p;
+               file->ungetsize *= 2;
+       }
+       file->ungetbuf[file->ungetpos++] = c;
+}
+
+int
+findeol(void)
+{
+       int     c;
+
+       /* Skip to either EOF or the first real EOL. */
+       while (1) {
+               c = lgetc(0);
+               if (c == '\n') {
+                       file->lineno++;
+                       break;
+               }
+               if (c == EOF)
+                       break;
+       }
+       return (ERROR);
+}
+
+int
+yylex(void)
+{
+       unsigned char    buf[8096];
+       unsigned char   *p, *val;
+       int              quotec, next, c;
+       int              token;
+
+top:
+       p = buf;
+       while ((c = lgetc(0)) == ' ' || c == '\t')
+               ; /* nothing */
+
+       yylval.lineno = file->lineno;
+       if (c == '#')
+               while ((c = lgetc(0)) != '\n' && c != EOF)
+                       ; /* nothing */
+       if (c == '$' && !expanding) {
+               while (1) {
+                       if ((c = lgetc(0)) == EOF)
+                               return (0);
+
+                       if (p + 1 >= buf + sizeof(buf) - 1) {
+                               yyerror("string too long");
+                               return (findeol());
+                       }
+                       if (isalnum(c) || c == '_') {
+                               *p++ = c;
+                               continue;
+                       }
+                       *p = '\0';
+                       lungetc(c);
+                       break;
+               }
+               val = symget(buf);
+               if (val == NULL) {
+                       yyerror("macro '%s' not defined", buf);
+                       return (findeol());
+               }
+               p = val + strlen(val) - 1;
+               lungetc(DONE_EXPAND);
+               while (p >= val) {
+                       lungetc(*p);
+                       p--;
+               }
+               lungetc(START_EXPAND);
+               goto top;
+       }
+
+       switch (c) {
+       case '\'':
+       case '"':
+               quotec = c;
+               while (1) {
+                       if ((c = lgetc(quotec)) == EOF)
+                               return (0);
+                       if (c == '\n') {
+                               file->lineno++;
+                               continue;
+                       } else if (c == '\\') {
+                               if ((next = lgetc(quotec)) == EOF)
+                                       return (0);
+                               if (next == quotec || c == ' ' || c == '\t')
+                                       c = next;
+                               else if (next == '\n') {
+                                       file->lineno++;
+                                       continue;
+                               } else
+                                       lungetc(next);
+                       } else if (c == quotec) {
+                               *p = '\0';
+                               break;
+                       } else if (c == '\0') {
+                               yyerror("syntax error");
+                               return (findeol());
+                       }
+                       if (p + 1 >= buf + sizeof(buf) - 1) {
+                               yyerror("string too long");
+                               return (findeol());
+                       }
+                       *p++ = c;
+               }
+               yylval.v.string = strdup(buf);
+               if (yylval.v.string == NULL)
+                       err(1, "yylex: strdup");
+               return (STRING);
+       }
+
+#define allowed_to_end_number(x) \
+       (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
+
+       if (c == '-' || isdigit(c)) {
+               do {
+                       *p++ = c;
+                       if ((unsigned)(p-buf) >= sizeof(buf)) {
+                               yyerror("string too long");
+                               return (findeol());
+                       }
+               } while ((c = lgetc(0)) != EOF && isdigit(c));
+               lungetc(c);
+               if (p == buf + 1 && buf[0] == '-')
+                       goto nodigits;
+               if (c == EOF || allowed_to_end_number(c)) {
+                       const char *errstr = NULL;
+
+                       *p = '\0';
+                       yylval.v.number = strtonum(buf, LLONG_MIN,
+                           LLONG_MAX, &errstr);
+                       if (errstr) {
+                               yyerror("\"%s\" invalid number: %s",
+                                   buf, errstr);
+                               return (findeol());
+                       }
+                       return (NUMBER);
+               } else {
+nodigits:
+                       while (p > buf + 1)
+                               lungetc(*--p);
+                       c = *--p;
+                       if (c == '-')
+                               return (c);
+               }
+       }
+
+#define allowed_in_string(x) \
+       (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+       x != '{' && x != '}' && \
+       x != '!' && x != '=' && x != '#' && \
+       x != ','))
+
+       if (isalnum(c) || c == ':' || c == '_') {
+               do {
+                       *p++ = c;
+                       if ((unsigned)(p-buf) >= sizeof(buf)) {
+                               yyerror("string too long");
+                               return (findeol());
+                       }
+               } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
+               lungetc(c);
+               *p = '\0';
+               if ((token = lookup(buf)) == STRING)
+                       if ((yylval.v.string = strdup(buf)) == NULL)
+                               err(1, "yylex: strdup");
+               return (token);
+       }
+       if (c == '\n') {
+               yylval.lineno = file->lineno;
+               file->lineno++;
+       }
+       if (c == EOF)
+               return (0);
+       return (c);
+}
+
+int
+check_file_secrecy(int fd, const char *fname)
+{
+       struct stat     st;
+
+       if (fstat(fd, &st)) {
+               log_warn("cannot stat %s", fname);
+               return (-1);
+       }
+       if (st.st_uid != 0 && st.st_uid != getuid()) {
+               log_warnx("%s: owner not root or current user", fname);
+               return (-1);
+       }
+       if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
+               log_warnx("%s: group writable or world read/writable", fname);
+               return (-1);
+       }
+       return (0);
+}
+
+struct file *
+pushfile(const char *name, int secret)
+{
+       struct file     *nfile;
+
+       if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
+               log_warn("calloc");
+               return (NULL);
+       }
+       if ((nfile->name = strdup(name)) == NULL) {
+               log_warn("strdup");
+               free(nfile);
+               return (NULL);
+       }
+       if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
+               log_warn("%s", nfile->name);
+               free(nfile->name);
+               free(nfile);
+               return (NULL);
+       } else if (secret &&
+           check_file_secrecy(fileno(nfile->stream), nfile->name)) {
+               fclose(nfile->stream);
+               free(nfile->name);
+               free(nfile);
+               return (NULL);
+       }
+       nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
+       nfile->ungetsize = 16;
+       nfile->ungetbuf = malloc(nfile->ungetsize);
+       if (nfile->ungetbuf == NULL) {
+               log_warn("malloc");
+               fclose(nfile->stream);
+               free(nfile->name);
+               free(nfile);
+               return (NULL);
+       }
+       TAILQ_INSERT_TAIL(&files, nfile, entry);
+       return (nfile);
+}
+
+int
+popfile(void)
+{
+       struct file     *prev;
+
+       if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
+               prev->errors += file->errors;
+
+       TAILQ_REMOVE(&files, file, entry);
+       fclose(file->stream);
+       free(file->name);
+       free(file->ungetbuf);
+       free(file);
+       file = prev;
+       return (file ? 0 : EOF);
+}
+
+struct rad_conf *
+parse_config(char *filename)
+{
+       struct sym      *sym, *next;
+
+       conf = config_new_empty();
+       ra_options = NULL;
+
+       file = pushfile(filename, !(cmd_opts & OPT_NOACTION));
+       if (file == NULL) {
+               free(conf);
+               return (NULL);
+       }
+       topfile = file;
+
+       yyparse();
+       errors = file->errors;
+       popfile();
+
+       /* Free macros and check which have not been used. */
+       TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
+               if ((cmd_opts & OPT_VERBOSE2) && !sym->used)
+                       fprintf(stderr, "warning: macro '%s' not used\n",
+                           sym->nam);
+               if (!sym->persist) {
+                       free(sym->nam);
+                       free(sym->val);
+                       TAILQ_REMOVE(&symhead, sym, entry);
+                       free(sym);
+               }
+       }
+
+       if (errors) {
+               clear_config(conf);
+               return (NULL);
+       }
+
+       return (conf);
+}
+
+int
+symset(const char *nam, const char *val, int persist)
+{
+       struct sym      *sym;
+
+       TAILQ_FOREACH(sym, &symhead, entry) {
+               if (strcmp(nam, sym->nam) == 0)
+                       break;
+       }
+
+       if (sym != NULL) {
+               if (sym->persist == 1)
+                       return (0);
+               else {
+                       free(sym->nam);
+                       free(sym->val);
+                       TAILQ_REMOVE(&symhead, sym, entry);
+                       free(sym);
+               }
+       }
+       if ((sym = calloc(1, sizeof(*sym))) == NULL)
+               return (-1);
+
+       sym->nam = strdup(nam);
+       if (sym->nam == NULL) {
+               free(sym);
+               return (-1);
+       }
+       sym->val = strdup(val);
+       if (sym->val == NULL) {
+               free(sym->nam);
+               free(sym);
+               return (-1);
+       }
+       sym->used = 0;
+       sym->persist = persist;
+       TAILQ_INSERT_TAIL(&symhead, sym, entry);
+       return (0);
+}
+
+int
+cmdline_symset(char *s)
+{
+       char    *sym, *val;
+       int     ret;
+       size_t  len;
+
+       if ((val = strrchr(s, '=')) == NULL)
+               return (-1);
+
+       len = strlen(s) - strlen(val) + 1;
+       if ((sym = malloc(len)) == NULL)
+               errx(1, "cmdline_symset: malloc");
+
+       strlcpy(sym, s, len);
+
+       ret = symset(sym, val + 1, 1);
+       free(sym);
+
+       return (ret);
+}
+
+char *
+symget(const char *nam)
+{
+       struct sym      *sym;
+
+       TAILQ_FOREACH(sym, &symhead, entry) {
+               if (strcmp(nam, sym->nam) == 0) {
+                       sym->used = 1;
+                       return (sym->val);
+               }
+       }
+       return (NULL);
+}
+
+struct ra_prefix_conf *
+conf_get_ra_prefix(struct in6_addr *addr, int prefixlen)
+{
+       struct ra_prefix_conf   *prefix;
+
+       if (addr == NULL) {
+               if (ra_iface_conf->autoprefix != NULL)
+                       return (ra_iface_conf->autoprefix);
+       } else {
+               SIMPLEQ_FOREACH(prefix, &ra_iface_conf->ra_prefix_list, entry) {
+                       if (prefix->prefixlen == prefixlen && memcmp(addr,
+                           &prefix->prefix, sizeof(*addr)) == 0)
+                               return (prefix);
+               }
+       }
+
+       prefix = calloc(1, sizeof(*prefix));
+       if (prefix == NULL)
+               errx(1, "%s: calloc", __func__);
+       prefix->prefixlen = prefixlen;
+       prefix->vltime = 2592000;       /* 30 days */
+       prefix->pltime = 604800;        /* 7 days */
+       prefix->lflag = 1;
+       prefix->aflag = 1;
+
+       if (addr == NULL)
+               ra_iface_conf->autoprefix = prefix;
+       else {
+               prefix->prefix = *addr;
+               SIMPLEQ_INSERT_TAIL(&ra_iface_conf->ra_prefix_list, prefix,
+                   entry);
+       }
+
+       return (prefix);
+}
+
+struct ra_iface_conf *
+conf_get_ra_iface(char *name)
+{
+       struct ra_iface_conf    *iface;
+       size_t                   n;
+
+       SIMPLEQ_FOREACH(iface, &conf->ra_iface_list, entry) {
+               if (strcmp(name, iface->name) == 0)
+                       return (iface);
+       }
+
+       iface = calloc(1, sizeof(*iface));
+       if (iface == NULL)
+               errx(1, "%s: calloc", __func__);
+       n = strlcpy(iface->name, name, sizeof(iface->name));
+       if (n >= sizeof(iface->name))
+               errx(1, "%s: name too long", __func__);
+
+       /* Inherit attributes set in global section. */
+       iface->ra_options = conf->ra_options;
+
+       SIMPLEQ_INIT(&iface->ra_prefix_list);
+
+       SIMPLEQ_INSERT_TAIL(&conf->ra_iface_list, iface, entry);
+
+       return (iface);
+}
+
+void
+clear_config(struct rad_conf *xconf)
+{
+       struct ra_iface_conf    *iface;
+
+       while((iface = SIMPLEQ_FIRST(&xconf->ra_iface_list)) != NULL) {
+               SIMPLEQ_REMOVE_HEAD(&xconf->ra_iface_list, entry);
+               free(iface);
+       }
+
+       free(xconf);
+}
diff --git a/usr.sbin/rad/printconf.c b/usr.sbin/rad/printconf.c
new file mode 100644 (file)
index 0000000..57ccb2c
--- /dev/null
@@ -0,0 +1,102 @@
+/*     $OpenBSD: printconf.c,v 1.1 2018/07/10 16:39:54 florian Exp $   */
+
+/*
+ * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
+ * 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.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <arpa/inet.h>
+
+#include <event.h>
+#include <imsg.h>
+#include <stdio.h>
+
+#include "rad.h"
+
+const char*    yesno(int);
+void           print_ra_options(const char*, const struct ra_options_conf*);
+void           print_prefix_options(const char*, const struct ra_prefix_conf*);
+
+const char*
+yesno(int flag)
+{
+       return flag ? "yes" : "no";
+}
+
+void
+print_ra_options(const char *indent, const struct ra_options_conf *ra_options)
+{
+       printf("%sdefault router %s\n", indent, yesno(ra_options->dfr));
+       printf("%shop limit %d\n", indent, ra_options->cur_hl);
+       printf("%smanaged address configuration %s\n", indent,
+           yesno(ra_options->m_flag));
+       printf("%sother configuration %s\n", indent, yesno(ra_options->o_flag));
+       printf("%srouter lifetime %d\n", indent, ra_options->router_lifetime);
+       printf("%sreachable time %u\n", indent, ra_options->reachable_time);
+       printf("%sretrans timer %u\n", indent, ra_options->retrans_timer);
+}
+
+void
+print_prefix_options(const char *indent, const struct ra_prefix_conf
+    *ra_prefix_conf)
+{
+       printf("%svalid lifetime %u\n", indent, ra_prefix_conf->vltime);
+       printf("%spreferred lifetime %u\n", indent, ra_prefix_conf->pltime);
+       printf("%son-link %s\n", indent, yesno(ra_prefix_conf->lflag));
+       printf("%sautonomous address-configuration %s\n", indent,
+           yesno(ra_prefix_conf->aflag));
+}
+
+void
+print_config(struct rad_conf *conf)
+{
+       struct ra_iface_conf    *iface;
+       struct ra_prefix_conf   *prefix;
+       char                     buf[INET6_ADDRSTRLEN], *bufp;
+
+       print_ra_options("", &conf->ra_options);
+       printf("\n");
+
+       SIMPLEQ_FOREACH(iface, &conf->ra_iface_list, entry) {
+               printf("interface %s {\n", iface->name);
+
+               print_ra_options("\t", &iface->ra_options);
+
+               if (iface->autoprefix) {
+                       printf("\tauto prefix {\n");
+                       print_prefix_options("\t\t", iface->autoprefix);
+                       printf("\t}\n");
+               } else
+                       printf("\tno auto prefix\n");
+
+               SIMPLEQ_FOREACH(prefix, &iface->ra_prefix_list, entry) {
+                       bufp = inet_net_ntop(AF_INET6, &prefix->prefix,
+                           prefix->prefixlen, buf, sizeof(buf));
+                       printf("\tprefix %s {\n", bufp);
+                       print_prefix_options("\t\t", prefix);
+                       printf("\t}\n");
+               }
+
+               printf("}\n");
+       }
+}
diff --git a/usr.sbin/rad/rad.8 b/usr.sbin/rad/rad.8
new file mode 100644 (file)
index 0000000..c1017ae
--- /dev/null
@@ -0,0 +1,147 @@
+.\"    $OpenBSD: rad.8,v 1.1 2018/07/10 16:39:54 florian Exp $
+.\"
+.\" Copyright (c) 2018 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: July 10 2018 $
+.Dt RAD 8
+.Os
+.Sh NAME
+.Nm rad
+.Nd router advertisement daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl dnv
+.Op Fl f Ar file
+.Op Fl s Ar socket
+.Sh DESCRIPTION
+.Nm
+is an IPv6 router advertisement daemon.
+It periodically sends IPv6 router advertisement messages with prefix
+and default router informations.
+Clients like
+.Xr slaacd 8
+use these to configure IPv6 addresses on network interfaces and set default
+routes.
+Additionally it listens for IPv6 router solicitation messages and responds
+with router advertisements.
+.Pp
+.Nm
+is usually started at boot time, and can be enabled by
+setting the following in
+.Pa /etc/rc.conf.local :
+.Pp
+.Dl rad_flags=\&"\&"
+.Pp
+See
+.Xr rc 8
+and
+.Xr rc.conf 8
+for more information on the boot process
+and enabling daemons.
+.Pp
+A running
+.Nm
+can be controlled with the
+.Xr ractl 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 f Ar file
+Specify an alternative configuration file.
+.It Fl n
+Configtest mode.
+Only check the configuration file for validity.
+.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/rad.sockXX" -compact
+.It Pa /etc/rad.conf
+Default
+.Nm
+configuration file.
+.It Pa /var/run/rad.sock
+.Ux Ns -domain
+socket used for communication with
+.Xr ractl 8 .
+.El
+.Sh SEE ALSO
+.Xr rad.conf 5 ,
+.Xr ractl 8
+.Sh STANDARDS
+.Rs
+.%A R. Draves
+.%A D. Thaler
+.%D November 2005
+.%R RFC 4191
+.%T Default Router Preferences and More-Specific Routes
+.Re
+.Pp
+.Rs
+.%A R. Hinden
+.%A S. Deering
+.%D February 2006
+.%R RFC 4291
+.%T IP Version 6 Addressing Architecture
+.Re
+.Pp
+.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 A. Yourtchenko
+.%A L. Colitti
+.%D February 2016
+.%R RFC 7772
+.%T Reducing Energy Consumption of Router Advertisements
+.Re
+.Pp
+.Rs
+.%A J. Jeong
+.%A S. Park
+.%A L. Beloeil
+.%A S. Madanapalli
+.%D March 2017
+.%R RFC 8106
+.%T IPv6 Router Advertisement Options for DNS Configuration
+.Re
+.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Ox 6.4 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+program was written by
+.An Florian Obser Aq Mt florian@openbsd.org .
diff --git a/usr.sbin/rad/rad.c b/usr.sbin/rad/rad.c
new file mode 100644 (file)
index 0000000..ae3a423
--- /dev/null
@@ -0,0 +1,783 @@
+/*     $OpenBSD: rad.c,v 1.1 2018/07/10 16:39:54 florian Exp $ */
+
+/*
+ * Copyright (c) 2018 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/queue.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet6/in6_var.h>
+#include <netinet/icmp6.h>
+
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <imsg.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "rad.h"
+#include "frontend.h"
+#include "engine.h"
+#include "control.h"
+
+const char* imsg_type_name[] = {
+       "IMSG_NONE",
+       "IMSG_CTL_LOG_VERBOSE",
+       "IMSG_CTL_RELOAD",
+       "IMSG_CTL_SHOW_ENGINE_INFO",
+       "IMSG_CTL_SHOW_FRONTEND_INFO",
+       "IMSG_CTL_SHOW_MAIN_INFO",
+       "IMSG_CTL_END",
+       "IMSG_RECONF_CONF",
+       "IMSG_RECONF_RA_IFACE",
+       "IMSG_RECONF_RA_AUTOPREFIX",
+       "IMSG_RECONF_RA_PREFIX",
+       "IMSG_RECONF_END",
+       "IMSG_ICMP6SOCK",
+       "IMSG_STARTUP",
+       "IMSG_STARTUP_DONE",
+       "IMSG_SOCKET_IPC",
+};
+
+__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 *);
+
+static int     main_imsg_send_ipc_sockets(struct imsgbuf *, struct imsgbuf *);
+static int     main_imsg_send_config(struct rad_conf *);
+
+int    main_reload(void);
+int    main_sendboth(enum imsg_type, void *, uint16_t);
+void   main_showinfo_ctl(struct imsg *);
+
+void   free_ra_iface_conf(struct ra_iface_conf *);
+void   in6_prefixlen2mask(struct in6_addr *, int len);
+
+struct rad_conf        *main_conf;
+struct imsgev          *iev_frontend;
+struct imsgev          *iev_engine;
+char                   *conffile;
+char                   *csock;
+
+pid_t   frontend_pid;
+pid_t   engine_pid;
+
+uint32_t cmd_opts;
+
+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:
+               if (main_reload() == -1)
+                       log_warnx("configuration reload failed");
+               else
+                       log_debug("configuration reloaded");
+               break;
+       default:
+               fatalx("unexpected signal");
+       }
+}
+
+__dead void
+usage(void)
+{
+       extern char *__progname;
+
+       fprintf(stderr, "usage: %s [-dnv] [-f file] [-s socket]\n",
+           __progname);
+       exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+       struct event             ev_sigint, ev_sigterm, ev_sighup;
+       struct icmp6_filter      filt;
+       int                      ch;
+       int                      debug = 0, engine_flag = 0, frontend_flag = 0;
+       char                    *saved_argv0;
+       int                      pipe_main2frontend[2];
+       int                      pipe_main2engine[2];
+       int                      icmp6sock, on = 1;
+
+       conffile = CONF_FILE;
+       csock = RAD_SOCKET;
+
+       log_init(1, LOG_DAEMON);        /* Log to stderr until daemonized. */
+       log_setverbose(1);
+
+       saved_argv0 = argv[0];
+       if (saved_argv0 == NULL)
+               saved_argv0 = "rad";
+
+       while ((ch = getopt(argc, argv, "dEFf:ns:v")) != -1) {
+               switch (ch) {
+               case 'd':
+                       debug = 1;
+                       break;
+               case 'E':
+                       engine_flag = 1;
+                       break;
+               case 'F':
+                       frontend_flag = 1;
+                       break;
+               case 'f':
+                       conffile = optarg;
+                       break;
+               case 'n':
+                       cmd_opts |= OPT_NOACTION;
+                       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);
+
+       /* parse config file */
+       if ((main_conf = parse_config(conffile)) == NULL) {
+               exit(1);
+       }
+
+       if (cmd_opts & OPT_NOACTION) {
+               if (cmd_opts & OPT_VERBOSE)
+                       print_config(main_conf);
+               else
+                       fprintf(stderr, "configuration OK\n");
+               exit(0);
+       }
+
+       /* Check for root privileges. */
+       if (geteuid())
+               errx(1, "need root privileges");
+
+       /* Check for assigned daemon user */
+       if (getpwnam(RAD_USER) == NULL)
+               errx(1, "unknown user %s", RAD_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);
+
+       rad_process = PROC_MAIN;
+       log_procinit(log_procnames[rad_process]);
+
+       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 ((icmp6sock = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC,
+           IPPROTO_ICMPV6)) < 0)
+               fatal("ICMPv6 socket");
+
+       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 and solicitations */
+       ICMP6_FILTER_SETBLOCKALL(&filt);
+       ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
+       ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filt);
+       if (setsockopt(icmp6sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+           sizeof(filt)) == -1)
+               fatal("ICMP6_FILTER");
+
+       main_imsg_compose_frontend_fd(IMSG_ICMP6SOCK, 0, icmp6sock);
+
+       main_imsg_send_config(main_conf);
+
+       if (pledge("stdio rpath cpath sendfd", NULL) == -1)
+               fatal("pledge");
+
+       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);
+
+       config_clear(main_conf);
+
+       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_STARTUP_DONE:
+                       if (pledge("stdio rpath cpath", NULL) == -1)
+                               fatal("pledge");
+                       break;
+               case IMSG_CTL_RELOAD:
+                       if (main_reload() == -1)
+                               log_warnx("configuration reload failed");
+                       else
+                               log_warnx("configuration reloaded");
+                       break;
+               case IMSG_CTL_LOG_VERBOSE:
+                       /* Already checked by frontend. */
+                       memcpy(&verbose, imsg.data, sizeof(verbose));
+                       log_setverbose(verbose);
+                       break;
+               case IMSG_CTL_SHOW_MAIN_INFO:
+                       main_showinfo_ctl(&imsg);
+                       break;
+               default:
+                       log_debug("%s: error handling imsg %s", __func__,
+                           imsg_type_name[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;
+       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) {
+               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_imsg_compose_frontend(int type, pid_t pid, void *data, uint16_t datalen)
+{
+       if (iev_frontend)
+               imsg_compose_event(iev_frontend, type, 0, pid, -1, data,
+                   datalen);
+}
+
+void
+main_imsg_compose_frontend_fd(int type, pid_t pid, int fd)
+{
+       if (iev_frontend)
+               imsg_compose_event(iev_frontend, type, 0, pid, fd, NULL, 0);
+}
+
+
+void
+main_imsg_compose_engine(int type, pid_t pid, void *data, uint16_t datalen)
+{
+       if (iev_engine)
+               imsg_compose_event(iev_engine, type, 0, pid, -1, data,
+                   datalen);
+}
+
+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);
+       if (imsg_compose(engine_buf, IMSG_SOCKET_IPC, 0, 0,
+           pipe_frontend2engine[1], NULL, 0) == -1)
+               return (-1);
+
+       return (0);
+}
+
+int
+main_reload(void)
+{
+       struct rad_conf *xconf;
+
+       if ((xconf = parse_config(conffile)) == NULL)
+               return (-1);
+
+       if (main_imsg_send_config(xconf) == -1)
+               return (-1);
+
+       merge_config(main_conf, xconf);
+
+       return (0);
+}
+
+int
+main_imsg_send_config(struct rad_conf *xconf)
+{
+       struct ra_iface_conf    *ra_iface_conf;
+       struct ra_prefix_conf   *ra_prefix_conf;
+
+       /* Send fixed part of config to children. */
+       if (main_sendboth(IMSG_RECONF_CONF, xconf, sizeof(*xconf)) == -1)
+               return (-1);
+
+       /* Send the interface list to children. */
+       SIMPLEQ_FOREACH(ra_iface_conf, &xconf->ra_iface_list, entry) {
+               if (main_sendboth(IMSG_RECONF_RA_IFACE, ra_iface_conf,
+                   sizeof(*ra_iface_conf)) == -1)
+                       return (-1);
+               if (ra_iface_conf->autoprefix) {
+                       if (main_sendboth(IMSG_RECONF_RA_AUTOPREFIX,
+                           ra_iface_conf->autoprefix,
+                           sizeof(*ra_iface_conf->autoprefix)) == -1)
+                               return (-1);
+               }
+               SIMPLEQ_FOREACH(ra_prefix_conf, &ra_iface_conf->ra_prefix_list,
+                   entry) {
+                       if (main_sendboth(IMSG_RECONF_RA_PREFIX,
+                           ra_prefix_conf, sizeof(*ra_prefix_conf)) == -1)
+                               return (-1);
+               }
+       }
+
+       /* Tell children the revised config is now complete. */
+       if (main_sendboth(IMSG_RECONF_END, NULL, 0) == -1)
+               return (-1);
+
+       return (0);
+}
+
+int
+main_sendboth(enum imsg_type type, void *buf, uint16_t len)
+{
+       if (imsg_compose_event(iev_frontend, type, 0, 0, -1, buf, len) == -1)
+               return (-1);
+       if (imsg_compose_event(iev_engine, type, 0, 0, -1, buf, len) == -1)
+               return (-1);
+       return (0);
+}
+
+void
+main_showinfo_ctl(struct imsg *imsg)
+{
+       struct ctl_main_info cmi;
+       size_t n;
+
+       switch (imsg->hdr.type) {
+       case IMSG_CTL_SHOW_MAIN_INFO:
+               memset(cmi.text, 0, sizeof(cmi.text));
+               n = strlcpy(cmi.text, "I'm a little teapot.",
+                   sizeof(cmi.text));
+               if (n >= sizeof(cmi.text))
+                       log_debug("%s: I was cut off!", __func__);
+               main_imsg_compose_frontend(IMSG_CTL_SHOW_MAIN_INFO,
+                   imsg->hdr.pid, &cmi, sizeof(cmi));
+               memset(cmi.text, 0, sizeof(cmi.text));
+               n = strlcpy(cmi.text, "Full of sencha.",
+                   sizeof(cmi.text));
+               if (n >= sizeof(cmi.text))
+                       log_debug("%s: I was cut off!", __func__);
+               main_imsg_compose_frontend(IMSG_CTL_SHOW_MAIN_INFO,
+                   imsg->hdr.pid, &cmi, sizeof(cmi));
+               main_imsg_compose_frontend(IMSG_CTL_END, imsg->hdr.pid, NULL,
+                   0);
+               break;
+       default:
+               log_debug("%s: error handling imsg", __func__);
+               break;
+       }
+}
+
+void
+free_ra_iface_conf(struct ra_iface_conf *ra_iface_conf)
+{
+       struct ra_prefix_conf   *prefix;
+
+       if (!ra_iface_conf)
+               return;
+
+       free(ra_iface_conf->autoprefix);
+
+       while ((prefix = SIMPLEQ_FIRST(&ra_iface_conf->ra_prefix_list))
+           != NULL) {
+               SIMPLEQ_REMOVE_HEAD(&ra_iface_conf->ra_prefix_list, entry);
+               free(prefix);
+       }
+
+       free(ra_iface_conf);
+}
+
+void
+merge_config(struct rad_conf *conf, struct rad_conf *xconf)
+{
+       struct ra_iface_conf    *ra_iface_conf;
+
+       conf->ra_options = xconf->ra_options;
+
+       /* Remove & discard existing interfaces. */
+       while ((ra_iface_conf = SIMPLEQ_FIRST(&conf->ra_iface_list)) != NULL) {
+               SIMPLEQ_REMOVE_HEAD(&conf->ra_iface_list, entry);
+               free_ra_iface_conf(ra_iface_conf);
+       }
+
+       /* Add new interfaces. */
+       while ((ra_iface_conf = SIMPLEQ_FIRST(&xconf->ra_iface_list)) != NULL) {
+               SIMPLEQ_REMOVE_HEAD(&xconf->ra_iface_list, entry);
+               SIMPLEQ_INSERT_TAIL(&conf->ra_iface_list, ra_iface_conf, entry);
+       }
+
+       free(xconf);
+}
+
+struct rad_conf *
+config_new_empty(void)
+{
+       struct rad_conf *xconf;
+
+       xconf = calloc(1, sizeof(*xconf));
+       if (xconf == NULL)
+               fatal(NULL);
+
+       SIMPLEQ_INIT(&xconf->ra_iface_list);
+
+       xconf->ra_options.dfr = 1;
+       xconf->ra_options.cur_hl = 0;
+       xconf->ra_options.m_flag = 0;
+       xconf->ra_options.o_flag = 0;
+       xconf->ra_options.router_lifetime = 1800;
+       xconf->ra_options.reachable_time = 0;
+       xconf->ra_options.retrans_timer = 0;
+
+       return (xconf);
+}
+
+void
+config_clear(struct rad_conf *conf)
+{
+       struct rad_conf *xconf;
+
+       /* Merge current config with an empty config. */
+       xconf = config_new_empty();
+       merge_config(conf, xconf);
+
+       free(conf);
+}
+
+void mask_prefix(struct in6_addr* in6, int len)
+{
+       uint8_t bitmask[8] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe};
+       int     i, skip;
+
+       if (len < 0 || len > 128)
+               fatalx("invalid prefix length: %d", len);
+
+       skip = len / 8;
+
+       in6->s6_addr[skip] &= bitmask[len % 8];
+
+       for (i = skip + 1; i < 16; i++)
+               in6->s6_addr[i] = 0;
+}
+
+const char*
+sin6_to_str(struct sockaddr_in6 *sin6)
+{
+       static char hbuf[NI_MAXHOST];
+       int error;
+
+       error = getnameinfo((struct sockaddr *)sin6, sin6->sin6_len, hbuf,
+           sizeof(hbuf), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV);
+       if (error) {
+               log_warnx("%s", gai_strerror(error));
+               strlcpy(hbuf, "unknown", sizeof(hbuf));
+       }
+       return hbuf;
+}
+
+const char*
+in6_to_str(struct in6_addr *in6)
+{
+
+       struct sockaddr_in6     sin6;
+
+       memset(&sin6, 0, sizeof(sin6));
+       sin6.sin6_len = sizeof(sin6);
+       sin6.sin6_family = AF_INET6;
+       sin6.sin6_addr = *in6;
+
+       return (sin6_to_str(&sin6));
+}
diff --git a/usr.sbin/rad/rad.conf.5 b/usr.sbin/rad/rad.conf.5
new file mode 100644 (file)
index 0000000..4cd9083
--- /dev/null
@@ -0,0 +1,148 @@
+.\"    $OpenBSD: rad.conf.5,v 1.1 2018/07/10 16:39:54 florian Exp $
+.\"
+.\" Copyright (c) 2018 Florian Obser <florian@openbsd.org>
+.\" Copyright (c) 2005 Esben Norby <norby@openbsd.org>
+.\" Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
+.\" Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+.\" Copyright (c) 2002 Daniel Hartmeier <dhartmei@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.
+.\"
+.Dd $Mdocdate: July 10 2018 $
+.Dt RAD.CONF 5
+.Os
+.Sh NAME
+.Nm rad.conf
+.Nd router advertisement daemon configuration file
+.Sh DESCRIPTION
+The
+.Xr rad 8
+daemon is an IPv6 router advertisement daemon.
+.Pp
+The
+.Nm
+config file is divided into the following main sections:
+.Bl -tag -width xxxx
+.It Sy Macros
+User-defined variables may be defined and used later, simplifying the
+configuration file.
+.It Sy Global Configuration
+Global settings for
+.Xr rad 8 .
+These are used as default values for
+.Ic interface
+definitions and can be overwritten in an
+.Ic interface
+block.
+.It Sy Interfaces
+.Xr rad 8
+sends IPv6 router advertisement messages.
+This section defines on which interfaces to advertise prefix information
+and their associated parameters.
+.El
+.Pp
+Additional configuration files can be included with the
+.Ic include
+keyword.
+.Sh MACROS
+Macros can be defined that will later be expanded in context.
+Macro names must start with a letter, digit, or underscore,
+and may contain any of those characters.
+Macro names may not be reserved words (for example,
+.Ic interface )
+Macros are not expanded inside quotes.
+.Sh GLOBAL CONFIGURATION
+The global configuration section sets defaults for router advertisement
+messages.
+These can be overwritten in interface blocks.
+.Pp
+.Bl -tag -width Ds -compact
+.It Ic default router Pq Ic yes Ns | Ns Ic no
+act as a default router or not.
+Default is yes.
+.It Ic hop limit Ar hops
+specifies the diameter of the internet.
+Default is 0 meaning unspecified by this router.
+.It managed address configuration Pq Ic yes Ns | Ns Ic no
+if set to yes indicates that stateless address configuration prefixes are
+not available and hosts should consult DHCPv6.
+Default is no.
+.It other configuration Pq Ic yes Ns | Ns Ic no
+if set to yes hosts should consult DHCPv6 for additional configuration
+like NTP servers or DNS resolvers.
+.It Ic router lifetime Ar seconds
+number of seconds this router is a valid default router after receiving
+a router advertisement message.
+Default 1800 seconds.
+.\" .It Ic reachable time Ar number
+.\" XXX
+.\" .It Ic retrans timer Ar number
+.\" XXX
+.El
+.Sh INTERFACES
+A list of interfaces to send advertisments on.
+.Bl -tag -width interface
+.It Ic interface Ar name Op { prefix list }
+.El
+.Pp
+Options set in the global section can be overwritten inside an interface
+block.
+In addition an interface block can contain a list of prefixes:
+.Bl -tag -width prefix
+.It Oo Ic no Oc Ic auto prefix Op { prefix options }
+.It Ic prefix Ar prefix Op { prefix options }
+.El
+.Pp
+The default is to discover prefixes to announce by inspecting the IPv6
+addresses configured on an interface.
+This can be disabled with
+.Ic no auto prefix .
+.Pp
+.Ic prefix
+options are as follows:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ic autonomous address-configuration Pq Ic yes Ns | Ns Ic no
+this prefix can be used to generate IPv6 addresses.
+The default is yes.
+.It Ic on-link Pq Ic yes Ns | Ns Ic no
+this prefix shall be considered on-link.
+The default is yes.
+.It Ic preferred lifetime Ar seconds
+preferred lifetime (pltime) in seconds for addresses generated from this
+prefix.
+The default is 604800.
+.It Ic valid lifetime Ar seconds
+valid lifetime (vltime) in seconds for addresses generated from this
+prefix.
+The default is 2592000.
+.El
+.Sh FILES
+.Bl -tag -width "/etc/rad.conf" -compact
+.It Pa /etc/rad.conf
+.Xr rad 8
+configuration file
+.El
+.Sh EXAMPLES
+.Bd -literal -offset indent
+interface ix1
+.Ed
+.Sh SEE ALSO
+.Xr ractl 8 ,
+.Xr rad 8 ,
+.Xr rc.conf.local 8
+.Sh HISTORY
+The
+.Nm
+file format first appeared in
+.Ox 6.4 .
diff --git a/usr.sbin/rad/rad.h b/usr.sbin/rad/rad.h
new file mode 100644 (file)
index 0000000..d670d1d
--- /dev/null
@@ -0,0 +1,161 @@
+/*     $OpenBSD: rad.h,v 1.1 2018/07/10 16:39:54 florian Exp $ */
+
+/*
+ * Copyright (c) 2018 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 CONF_FILE              "/etc/rad.conf"
+#define        RAD_SOCKET              "/var/run/rad.sock"
+#define RAD_USER               "_rtadvd"       /* XXX */
+
+#define OPT_VERBOSE    0x00000001
+#define OPT_VERBOSE2   0x00000002
+#define OPT_NOACTION   0x00000004
+
+#define RAD_MAXTEXT            256
+
+enum {
+       PROC_MAIN,
+       PROC_ENGINE,
+       PROC_FRONTEND
+} rad_process;
+
+static const char * const log_procnames[] = {
+       "main",
+       "engine",
+       "frontend",
+};
+
+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_RELOAD,
+       IMSG_CTL_SHOW_ENGINE_INFO,
+       IMSG_CTL_SHOW_FRONTEND_INFO,
+       IMSG_CTL_SHOW_MAIN_INFO,
+       IMSG_CTL_END,
+       IMSG_RECONF_CONF,
+       IMSG_RECONF_RA_IFACE,
+       IMSG_RECONF_RA_AUTOPREFIX,
+       IMSG_RECONF_RA_PREFIX,
+       IMSG_RECONF_END,
+       IMSG_ICMP6SOCK,
+       IMSG_STARTUP,
+       IMSG_STARTUP_DONE,
+       IMSG_RA_RS,
+       IMSG_SEND_RA,
+       IMSG_SOCKET_IPC
+};
+
+extern const char* imsg_type_name[];
+
+/* RFC 4861 Section 4.2 */
+struct ra_options_conf {
+       int             dfr;                    /* is default router? */
+       int             cur_hl;                 /* current hop limit */
+       int             m_flag;                 /* managed address conf flag */
+       int             o_flag;                 /* other conf flag */
+       int             router_lifetime;        /* default router lifetime */
+       uint32_t        reachable_time;
+       uint32_t        retrans_timer;
+};
+
+/* RFC 4861 Section 4.6.2 */
+struct ra_prefix_conf {
+       SIMPLEQ_ENTRY(ra_prefix_conf)    entry;
+       struct in6_addr                  prefix;        /* prefix */
+       int                              prefixlen;     /* prefix length */
+       uint32_t                         vltime;        /* valid lifetime */
+       uint32_t                         pltime;        /* prefered lifetime */
+       int                              lflag;         /* on-link flag*/
+       int                              aflag;         /* autonom. addr flag */
+};
+
+struct ra_iface_conf {
+       SIMPLEQ_ENTRY(ra_iface_conf)     entry;
+       struct ra_options_conf           ra_options;
+       struct ra_prefix_conf           *autoprefix;
+       SIMPLEQ_HEAD(ra_prefix_conf_head,
+           ra_prefix_conf)              ra_prefix_list;
+       char                             name[IF_NAMESIZE];
+};
+
+struct rad_conf {
+       struct ra_options_conf                           ra_options;
+       SIMPLEQ_HEAD(ra_iface_conf_head, ra_iface_conf)  ra_iface_list;
+};
+
+struct ctl_frontend_info {
+       int             yesno;
+       int             integer;
+       char            global_text[RAD_MAXTEXT];
+};
+
+struct ctl_engine_info {
+       char            name[IF_NAMESIZE];
+       int             yesno;
+       int             integer;
+};
+
+struct ctl_main_info {
+       char            text[RAD_MAXTEXT];
+};
+
+struct imsg_ra_rs {
+       uint32_t                if_index;
+       struct sockaddr_in6     from;
+       ssize_t                 len;
+       uint8_t                 packet[1500];
+};
+
+struct imsg_send_ra {
+       uint32_t                if_index;
+       struct sockaddr_in6     to;
+};
+
+extern uint32_t         cmd_opts;
+
+/* rad.c */
+void   main_imsg_compose_frontend(int, pid_t, void *, uint16_t);
+void   main_imsg_compose_frontend_fd(int, pid_t, int);
+
+void   main_imsg_compose_engine(int, pid_t, void *, uint16_t);
+void   merge_config(struct rad_conf *, struct rad_conf *);
+void   imsg_event_add(struct imsgev *);
+int    imsg_compose_event(struct imsgev *, uint16_t, uint32_t, pid_t,
+           int, void *, uint16_t);
+
+struct rad_conf        *config_new_empty(void);
+void            config_clear(struct rad_conf *);
+
+void   mask_prefix(struct in6_addr*, int len);
+const char     *sin6_to_str(struct sockaddr_in6 *);
+const char     *in6_to_str(struct in6_addr *);
+
+/* printconf.c */
+void   print_config(struct rad_conf *);
+
+/* parse.y */
+struct rad_conf        *parse_config(char *);
+int                     cmdline_symset(char *);