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