xref: /openbsd-src/usr.sbin/rad/frontend.c (revision f1b790a5738b7375271fee81f99119b1f82f2cfd)
1*f1b790a5Sclaudio /*	$OpenBSD: frontend.c,v 1.55 2024/11/21 13:38:15 claudio Exp $	*/
253293e44Sflorian 
353293e44Sflorian /*
453293e44Sflorian  * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
553293e44Sflorian  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
653293e44Sflorian  * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
753293e44Sflorian  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
853293e44Sflorian  *
953293e44Sflorian  * Permission to use, copy, modify, and distribute this software for any
1053293e44Sflorian  * purpose with or without fee is hereby granted, provided that the above
1153293e44Sflorian  * copyright notice and this permission notice appear in all copies.
1253293e44Sflorian  *
1353293e44Sflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1453293e44Sflorian  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1553293e44Sflorian  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1653293e44Sflorian  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1753293e44Sflorian  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1853293e44Sflorian  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1953293e44Sflorian  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2053293e44Sflorian  */
2153293e44Sflorian 
2253293e44Sflorian /*
2353293e44Sflorian  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
2453293e44Sflorian  * All rights reserved.
2553293e44Sflorian  *
2653293e44Sflorian  * Redistribution and use in source and binary forms, with or without
2753293e44Sflorian  * modification, are permitted provided that the following conditions
2853293e44Sflorian  * are met:
2953293e44Sflorian  * 1. Redistributions of source code must retain the above copyright
3053293e44Sflorian  *    notice, this list of conditions and the following disclaimer.
3153293e44Sflorian  * 2. Redistributions in binary form must reproduce the above copyright
3253293e44Sflorian  *    notice, this list of conditions and the following disclaimer in the
3353293e44Sflorian  *    documentation and/or other materials provided with the distribution.
3453293e44Sflorian  * 3. Neither the name of the project nor the names of its contributors
3553293e44Sflorian  *    may be used to endorse or promote products derived from this software
3653293e44Sflorian  *    without specific prior written permission.
3753293e44Sflorian  *
3853293e44Sflorian  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
3953293e44Sflorian  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4053293e44Sflorian  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4153293e44Sflorian  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
4253293e44Sflorian  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
4353293e44Sflorian  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
4453293e44Sflorian  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4553293e44Sflorian  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
4653293e44Sflorian  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
4753293e44Sflorian  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
4853293e44Sflorian  * SUCH DAMAGE.
4953293e44Sflorian  */
5053293e44Sflorian 
5153293e44Sflorian #include <sys/types.h>
5253293e44Sflorian #include <sys/ioctl.h>
5353293e44Sflorian #include <sys/queue.h>
5453293e44Sflorian #include <sys/socket.h>
5553293e44Sflorian #include <sys/syslog.h>
5653293e44Sflorian #include <sys/uio.h>
5753293e44Sflorian 
5853293e44Sflorian #include <net/if.h>
5930ca3407Sflorian #include <net/if_dl.h>
6053293e44Sflorian #include <net/if_types.h>
6153293e44Sflorian #include <net/route.h>
6253293e44Sflorian 
6353293e44Sflorian #include <arpa/inet.h>
6453293e44Sflorian 
6553293e44Sflorian #include <netinet/in.h>
6653293e44Sflorian #include <netinet/if_ether.h>
6753293e44Sflorian #include <netinet6/nd6.h>
6853293e44Sflorian #include <netinet6/in6_var.h>
6953293e44Sflorian #include <netinet/ip6.h>
7053293e44Sflorian #include <netinet/icmp6.h>
7153293e44Sflorian 
7292f66a25Sreyk #include <ctype.h>
7353293e44Sflorian #include <errno.h>
7453293e44Sflorian #include <event.h>
7553293e44Sflorian #include <ifaddrs.h>
7653293e44Sflorian #include <imsg.h>
7753293e44Sflorian #include <pwd.h>
7853293e44Sflorian #include <signal.h>
7953293e44Sflorian #include <stdio.h>
8053293e44Sflorian #include <stdlib.h>
8153293e44Sflorian #include <string.h>
8253293e44Sflorian #include <unistd.h>
8353293e44Sflorian 
8453293e44Sflorian #include "log.h"
8553293e44Sflorian #include "rad.h"
8653293e44Sflorian #include "frontend.h"
8753293e44Sflorian #include "control.h"
8853293e44Sflorian 
890c40990eSflorian #define	RA_MAX_SIZE		1500
9048e174fdSflorian #define	ROUTE_SOCKET_BUF_SIZE	16384
910c40990eSflorian 
9253293e44Sflorian struct icmp6_ev {
9353293e44Sflorian 	struct event		 ev;
9453293e44Sflorian 	uint8_t			 answer[1500];
9553293e44Sflorian 	struct msghdr		 rcvmhdr;
9653293e44Sflorian 	struct iovec		 rcviov[1];
9753293e44Sflorian 	struct sockaddr_in6	 from;
98e88dba76Sflorian 	int			 refcnt;
99e88dba76Sflorian };
10053293e44Sflorian 
10153293e44Sflorian struct ra_iface {
10253293e44Sflorian 	TAILQ_ENTRY(ra_iface)		 entry;
103e88dba76Sflorian 	struct icmp6_ev			*icmp6ev;
10453293e44Sflorian 	struct ra_prefix_conf_head	 prefixes;
105c358c7e3Sflorian 	struct ether_addr		 hw_addr;
10653293e44Sflorian 	char				 name[IF_NAMESIZE];
10792f66a25Sreyk 	char				 conf_name[IF_NAMESIZE];
10853293e44Sflorian 	uint32_t			 if_index;
109e88dba76Sflorian 	int				 rdomain;
11053293e44Sflorian 	int				 removed;
111abcf5f52Sflorian 	int				 link_state;
11253293e44Sflorian 	int				 prefix_count;
11320fc6e8eSflorian 	int				 ltime_decaying;
11453293e44Sflorian 	size_t				 datalen;
1150c40990eSflorian 	uint8_t				 data[RA_MAX_SIZE];
11653293e44Sflorian };
11753293e44Sflorian 
1185207bb19Sflorian #define ND_OPT_PREF64	38
1195207bb19Sflorian struct nd_opt_pref64 {
1205207bb19Sflorian 	u_int8_t	nd_opt_pref64_type;
1215207bb19Sflorian 	u_int8_t	nd_opt_pref64_len;
1225207bb19Sflorian 	u_int16_t	nd_opt_pref64_sltime_plc;
1235207bb19Sflorian 	u_int8_t	nd_opt_pref64[12];
1245207bb19Sflorian };
1255207bb19Sflorian 
12630ca3407Sflorian struct nd_opt_source_link_addr {
12730ca3407Sflorian 	u_int8_t		nd_opt_source_link_addr_type;
12830ca3407Sflorian 	u_int8_t		nd_opt_source_link_addr_len;
12930ca3407Sflorian 	struct ether_addr	nd_opt_source_link_addr_hw_addr;
13030ca3407Sflorian };
13130ca3407Sflorian 
13253293e44Sflorian TAILQ_HEAD(, ra_iface)	ra_interfaces;
13353293e44Sflorian 
13453293e44Sflorian __dead void		 frontend_shutdown(void);
13553293e44Sflorian void			 frontend_sig_handler(int, short, void *);
13653293e44Sflorian void			 frontend_startup(void);
13753293e44Sflorian void			 icmp6_receive(int, short, void *);
13853293e44Sflorian void			 join_all_routers_mcast_group(struct ra_iface *);
13953293e44Sflorian void			 leave_all_routers_mcast_group(struct ra_iface *);
140e88dba76Sflorian int			 get_ifrdomain(char *);
141c358c7e3Sflorian void			 merge_ra_interface(char *, char *, struct ifaddrs *);
14253293e44Sflorian void			 merge_ra_interfaces(void);
14353293e44Sflorian struct ra_iface		*find_ra_iface_by_id(uint32_t);
14453293e44Sflorian struct ra_iface		*find_ra_iface_by_name(char *);
14553293e44Sflorian struct ra_iface_conf	*find_ra_iface_conf(struct ra_iface_conf_head *,
14653293e44Sflorian 			    char *);
14753293e44Sflorian struct ra_prefix_conf	*find_ra_prefix_conf(struct ra_prefix_conf_head*,
14853293e44Sflorian 			    struct in6_addr *, int);
149e88dba76Sflorian struct icmp6_ev		*get_icmp6ev_by_rdomain(int);
150e88dba76Sflorian void			 unref_icmp6ev(struct ra_iface *);
151e88dba76Sflorian void			 set_icmp6sock(int, int);
15253293e44Sflorian void			 add_new_prefix_to_ra_iface(struct ra_iface *r,
15320fc6e8eSflorian 			    struct in6_addr *, int, struct ra_prefix_conf *,
15420fc6e8eSflorian 			    uint32_t, uint32_t);
15553293e44Sflorian void			 free_ra_iface(struct ra_iface *);
15653293e44Sflorian int			 in6_mask2prefixlen(struct in6_addr *);
15753293e44Sflorian void			 get_interface_prefixes(struct ra_iface *,
158c358c7e3Sflorian 			     struct ra_prefix_conf *, struct ifaddrs *);
15920fc6e8eSflorian int			 build_packet(struct ra_iface *);
1608c9b05a6Sflorian void			 build_leaving_packet(struct ra_iface *);
16153293e44Sflorian void			 ra_output(struct ra_iface *, struct sockaddr_in6 *);
16248e174fdSflorian void			 get_rtaddrs(int, struct sockaddr *,
16348e174fdSflorian 			     struct sockaddr **);
16448e174fdSflorian void			 route_receive(int, short, void *);
16548e174fdSflorian void			 handle_route_message(struct rt_msghdr *,
16648e174fdSflorian 			     struct sockaddr **);
16753293e44Sflorian 
16853293e44Sflorian struct rad_conf	*frontend_conf;
169032fa683Sflorian static struct imsgev	*iev_main;
170032fa683Sflorian static struct imsgev	*iev_engine;
17148e174fdSflorian struct event		 ev_route;
172e88dba76Sflorian int			 ioctlsock = -1, routesock = -1;
17353293e44Sflorian struct ipv6_mreq	 all_routers;
17453293e44Sflorian struct msghdr		 sndmhdr;
17553293e44Sflorian struct iovec		 sndiov[2];
17653293e44Sflorian 
17753293e44Sflorian void
17853293e44Sflorian frontend_sig_handler(int sig, short event, void *bula)
17953293e44Sflorian {
18053293e44Sflorian 	/*
18153293e44Sflorian 	 * Normal signal handler rules don't apply because libevent
18253293e44Sflorian 	 * decouples for us.
18353293e44Sflorian 	 */
18453293e44Sflorian 
18553293e44Sflorian 	switch (sig) {
18653293e44Sflorian 	case SIGINT:
18753293e44Sflorian 	case SIGTERM:
18853293e44Sflorian 		frontend_shutdown();
18953293e44Sflorian 	default:
19053293e44Sflorian 		fatalx("unexpected signal");
19153293e44Sflorian 	}
19253293e44Sflorian }
19353293e44Sflorian 
19453293e44Sflorian void
195a778af8bSflorian frontend(int debug, int verbose)
19653293e44Sflorian {
19753293e44Sflorian 	struct event		 ev_sigint, ev_sigterm;
19853293e44Sflorian 	struct passwd		*pw;
199e88dba76Sflorian 	size_t			 sndcmsgbuflen;
20053293e44Sflorian 	uint8_t			*sndcmsgbuf = NULL;
20153293e44Sflorian 
20253293e44Sflorian 	frontend_conf = config_new_empty();
20353293e44Sflorian 
20453293e44Sflorian 	log_init(debug, LOG_DAEMON);
20553293e44Sflorian 	log_setverbose(verbose);
20653293e44Sflorian 
20753293e44Sflorian 	if ((pw = getpwnam(RAD_USER)) == NULL)
20853293e44Sflorian 		fatal("getpwnam");
20953293e44Sflorian 
21053293e44Sflorian 	if (chroot(pw->pw_dir) == -1)
21153293e44Sflorian 		fatal("chroot");
21253293e44Sflorian 	if (chdir("/") == -1)
21353293e44Sflorian 		fatal("chdir(\"/\")");
21453293e44Sflorian 
2153c6be478Sflorian 	setproctitle("%s", "frontend");
2163c6be478Sflorian 	log_procinit("frontend");
21753293e44Sflorian 
21853293e44Sflorian 	if (setgroups(1, &pw->pw_gid) ||
21953293e44Sflorian 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
22053293e44Sflorian 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
22153293e44Sflorian 		fatal("can't drop privileges");
22253293e44Sflorian 
22353293e44Sflorian 	/* XXX pass in from main */
224df69c215Sderaadt 	if ((ioctlsock = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1)
22553293e44Sflorian 		fatal("socket");
22653293e44Sflorian 
22753293e44Sflorian 	if (pledge("stdio inet unix recvfd route mcast", NULL) == -1)
22853293e44Sflorian 		fatal("pledge");
22953293e44Sflorian 
23053293e44Sflorian 	event_init();
23153293e44Sflorian 
23253293e44Sflorian 	/* Setup signal handler. */
23353293e44Sflorian 	signal_set(&ev_sigint, SIGINT, frontend_sig_handler, NULL);
23453293e44Sflorian 	signal_set(&ev_sigterm, SIGTERM, frontend_sig_handler, NULL);
23553293e44Sflorian 	signal_add(&ev_sigint, NULL);
23653293e44Sflorian 	signal_add(&ev_sigterm, NULL);
23753293e44Sflorian 	signal(SIGPIPE, SIG_IGN);
23853293e44Sflorian 	signal(SIGHUP, SIG_IGN);
23953293e44Sflorian 
24053293e44Sflorian 	/* Setup pipe and event handler to the parent process. */
24153293e44Sflorian 	if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
24253293e44Sflorian 		fatal(NULL);
243*f1b790a5Sclaudio 	if (imsgbuf_init(&iev_main->ibuf, 3) == -1)
244*f1b790a5Sclaudio 		fatal(NULL);
245*f1b790a5Sclaudio 	imsgbuf_allow_fdpass(&iev_main->ibuf);
24653293e44Sflorian 	iev_main->handler = frontend_dispatch_main;
24753293e44Sflorian 	iev_main->events = EV_READ;
24853293e44Sflorian 	event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
24953293e44Sflorian 	    iev_main->handler, iev_main);
25053293e44Sflorian 	event_add(&iev_main->ev, NULL);
25153293e44Sflorian 
25253293e44Sflorian 	if (inet_pton(AF_INET6, "ff02::2",
25353293e44Sflorian 	    &all_routers.ipv6mr_multiaddr.s6_addr) == -1)
25453293e44Sflorian 		fatal("inet_pton");
25553293e44Sflorian 
25653293e44Sflorian 	sndcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
25753293e44Sflorian 	    CMSG_SPACE(sizeof(int));
25853293e44Sflorian 	if ((sndcmsgbuf = malloc(sndcmsgbuflen)) == NULL)
25953293e44Sflorian 		fatal("%s", __func__);
26053293e44Sflorian 
26153293e44Sflorian 	sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
26253293e44Sflorian 	sndmhdr.msg_iov = sndiov;
26353293e44Sflorian 	sndmhdr.msg_iovlen = 1;
26453293e44Sflorian 	sndmhdr.msg_control = sndcmsgbuf;
26553293e44Sflorian 	sndmhdr.msg_controllen = sndcmsgbuflen;
26653293e44Sflorian 
26753293e44Sflorian 	TAILQ_INIT(&ra_interfaces);
26853293e44Sflorian 
26953293e44Sflorian 	event_dispatch();
27053293e44Sflorian 
27153293e44Sflorian 	frontend_shutdown();
27253293e44Sflorian }
27353293e44Sflorian 
27453293e44Sflorian __dead void
27553293e44Sflorian frontend_shutdown(void)
27653293e44Sflorian {
27753293e44Sflorian 	/* Close pipes. */
278dd7efffeSclaudio 	imsgbuf_write(&iev_engine->ibuf);
2799cbf9e90Sclaudio 	imsgbuf_clear(&iev_engine->ibuf);
28053293e44Sflorian 	close(iev_engine->ibuf.fd);
281dd7efffeSclaudio 	imsgbuf_write(&iev_main->ibuf);
2829cbf9e90Sclaudio 	imsgbuf_clear(&iev_main->ibuf);
28353293e44Sflorian 	close(iev_main->ibuf.fd);
28453293e44Sflorian 
28553293e44Sflorian 	config_clear(frontend_conf);
28653293e44Sflorian 
28753293e44Sflorian 	free(iev_engine);
28853293e44Sflorian 	free(iev_main);
28953293e44Sflorian 
29053293e44Sflorian 	log_info("frontend exiting");
29153293e44Sflorian 	exit(0);
29253293e44Sflorian }
29353293e44Sflorian 
29453293e44Sflorian int
29553293e44Sflorian frontend_imsg_compose_main(int type, pid_t pid, void *data, uint16_t datalen)
29653293e44Sflorian {
29753293e44Sflorian 	return (imsg_compose_event(iev_main, type, 0, pid, -1, data,
29853293e44Sflorian 	    datalen));
29953293e44Sflorian }
30053293e44Sflorian 
30153293e44Sflorian int
30253293e44Sflorian frontend_imsg_compose_engine(int type, pid_t pid, void *data, uint16_t datalen)
30353293e44Sflorian {
30453293e44Sflorian 	return (imsg_compose_event(iev_engine, type, 0, pid, -1, data,
30553293e44Sflorian 	    datalen));
30653293e44Sflorian }
30753293e44Sflorian 
30853293e44Sflorian void
30953293e44Sflorian frontend_dispatch_main(int fd, short event, void *bula)
31053293e44Sflorian {
31153293e44Sflorian 	static struct rad_conf		*nconf;
31253293e44Sflorian 	static struct ra_iface_conf	*ra_iface_conf;
3138815eebdSflorian 	static struct ra_options_conf	*ra_options;
31453293e44Sflorian 	struct imsg			 imsg;
31553293e44Sflorian 	struct imsgev			*iev = bula;
31653293e44Sflorian 	struct imsgbuf			*ibuf = &iev->ibuf;
31753293e44Sflorian 	struct ra_prefix_conf		*ra_prefix_conf;
3184c40b7e8Sflorian 	struct ra_rdnss_conf		*ra_rdnss_conf;
3194c40b7e8Sflorian 	struct ra_dnssl_conf		*ra_dnssl_conf;
3205207bb19Sflorian 	struct ra_pref64_conf		*pref64;
321e88dba76Sflorian 	int				 n, shut = 0, icmp6sock, rdomain;
32253293e44Sflorian 
32353293e44Sflorian 	if (event & EV_READ) {
324668e5ba9Sclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
325dd7efffeSclaudio 			fatal("imsgbuf_read error");
32653293e44Sflorian 		if (n == 0)	/* Connection closed. */
32753293e44Sflorian 			shut = 1;
32853293e44Sflorian 	}
32953293e44Sflorian 	if (event & EV_WRITE) {
330dd7efffeSclaudio 		if (imsgbuf_write(ibuf) == -1) {
331c1aa9554Sclaudio 			if (errno == EPIPE)	/* connection closed */
33253293e44Sflorian 				shut = 1;
333c1aa9554Sclaudio 			else
334dd7efffeSclaudio 				fatal("imsgbuf_write");
335c1aa9554Sclaudio 		}
33653293e44Sflorian 	}
33753293e44Sflorian 
33853293e44Sflorian 	for (;;) {
33953293e44Sflorian 		if ((n = imsg_get(ibuf, &imsg)) == -1)
34053293e44Sflorian 			fatal("%s: imsg_get error", __func__);
34153293e44Sflorian 		if (n == 0)	/* No more messages. */
34253293e44Sflorian 			break;
34353293e44Sflorian 
34453293e44Sflorian 		switch (imsg.hdr.type) {
34553293e44Sflorian 		case IMSG_SOCKET_IPC:
34653293e44Sflorian 			/*
34753293e44Sflorian 			 * Setup pipe and event handler to the engine
34853293e44Sflorian 			 * process.
34953293e44Sflorian 			 */
3500eb5c43bSpamela 			if (iev_engine)
3510eb5c43bSpamela 				fatalx("%s: received unexpected imsg fd to "
3520eb5c43bSpamela 				    "frontend", __func__);
353d9be77f0Sclaudio 			if ((fd = imsg_get_fd(&imsg)) == -1)
3540eb5c43bSpamela 				fatalx("%s: expected to receive imsg fd to "
35553293e44Sflorian 				   "frontend but didn't receive any",
35653293e44Sflorian 				   __func__);
35753293e44Sflorian 
35853293e44Sflorian 			iev_engine = malloc(sizeof(struct imsgev));
35953293e44Sflorian 			if (iev_engine == NULL)
36053293e44Sflorian 				fatal(NULL);
36153293e44Sflorian 
362*f1b790a5Sclaudio 			if (imsgbuf_init(&iev_engine->ibuf, fd) == -1)
363*f1b790a5Sclaudio 				fatal(NULL);
36453293e44Sflorian 			iev_engine->handler = frontend_dispatch_engine;
36553293e44Sflorian 			iev_engine->events = EV_READ;
36653293e44Sflorian 
36753293e44Sflorian 			event_set(&iev_engine->ev, iev_engine->ibuf.fd,
36853293e44Sflorian 			iev_engine->events, iev_engine->handler, iev_engine);
36953293e44Sflorian 			event_add(&iev_engine->ev, NULL);
37053293e44Sflorian 			break;
37153293e44Sflorian 		case IMSG_RECONF_CONF:
3722b2996d8Sflorian 			if (nconf != NULL)
3732b2996d8Sflorian 				fatalx("%s: IMSG_RECONF_CONF already in "
3742b2996d8Sflorian 				    "progress", __func__);
3750eb5c43bSpamela 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct rad_conf))
3760eb5c43bSpamela 				fatalx("%s: IMSG_RECONF_CONF wrong length: %lu",
3770eb5c43bSpamela 				    __func__, IMSG_DATA_SIZE(imsg));
37853293e44Sflorian 			if ((nconf = malloc(sizeof(struct rad_conf))) ==
37953293e44Sflorian 			    NULL)
38053293e44Sflorian 				fatal(NULL);
38153293e44Sflorian 			memcpy(nconf, imsg.data, sizeof(struct rad_conf));
38253293e44Sflorian 			SIMPLEQ_INIT(&nconf->ra_iface_list);
3838815eebdSflorian 			SIMPLEQ_INIT(&nconf->ra_options.ra_rdnss_list);
3848815eebdSflorian 			SIMPLEQ_INIT(&nconf->ra_options.ra_dnssl_list);
3855207bb19Sflorian 			SIMPLEQ_INIT(&nconf->ra_options.ra_pref64_list);
3868815eebdSflorian 			ra_options = &nconf->ra_options;
38753293e44Sflorian 			break;
38853293e44Sflorian 		case IMSG_RECONF_RA_IFACE:
3890eb5c43bSpamela 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
3900eb5c43bSpamela 			    ra_iface_conf))
3910eb5c43bSpamela 				fatalx("%s: IMSG_RECONF_RA_IFACE wrong length: "
3920eb5c43bSpamela 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
39353293e44Sflorian 			if ((ra_iface_conf = malloc(sizeof(struct
39453293e44Sflorian 			    ra_iface_conf))) == NULL)
39553293e44Sflorian 				fatal(NULL);
39653293e44Sflorian 			memcpy(ra_iface_conf, imsg.data, sizeof(struct
39753293e44Sflorian 			    ra_iface_conf));
39853293e44Sflorian 			ra_iface_conf->autoprefix = NULL;
39953293e44Sflorian 			SIMPLEQ_INIT(&ra_iface_conf->ra_prefix_list);
4008815eebdSflorian 			SIMPLEQ_INIT(&ra_iface_conf->ra_options.ra_rdnss_list);
4018815eebdSflorian 			SIMPLEQ_INIT(&ra_iface_conf->ra_options.ra_dnssl_list);
4025207bb19Sflorian 			SIMPLEQ_INIT(&ra_iface_conf->ra_options.ra_pref64_list);
40353293e44Sflorian 			SIMPLEQ_INSERT_TAIL(&nconf->ra_iface_list,
40453293e44Sflorian 			    ra_iface_conf, entry);
4058815eebdSflorian 			ra_options = &ra_iface_conf->ra_options;
40653293e44Sflorian 			break;
40753293e44Sflorian 		case IMSG_RECONF_RA_AUTOPREFIX:
4080eb5c43bSpamela 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
4090eb5c43bSpamela 			    ra_prefix_conf))
4100eb5c43bSpamela 				fatalx("%s: IMSG_RECONF_RA_AUTOPREFIX wrong "
4110eb5c43bSpamela 				    "length: %lu", __func__,
4120eb5c43bSpamela 				    IMSG_DATA_SIZE(imsg));
41353293e44Sflorian 			if ((ra_iface_conf->autoprefix = malloc(sizeof(struct
41453293e44Sflorian 			    ra_prefix_conf))) == NULL)
41553293e44Sflorian 				fatal(NULL);
41653293e44Sflorian 			memcpy(ra_iface_conf->autoprefix, imsg.data,
41753293e44Sflorian 			    sizeof(struct ra_prefix_conf));
41853293e44Sflorian 			break;
41953293e44Sflorian 		case IMSG_RECONF_RA_PREFIX:
4200eb5c43bSpamela 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
4210eb5c43bSpamela 			    ra_prefix_conf))
4220eb5c43bSpamela 				fatalx("%s: IMSG_RECONF_RA_PREFIX wrong "
4230eb5c43bSpamela 				    "length: %lu", __func__,
4240eb5c43bSpamela 				    IMSG_DATA_SIZE(imsg));
42553293e44Sflorian 			if ((ra_prefix_conf = malloc(sizeof(struct
42653293e44Sflorian 			    ra_prefix_conf))) == NULL)
42753293e44Sflorian 				fatal(NULL);
42853293e44Sflorian 			memcpy(ra_prefix_conf, imsg.data,
42953293e44Sflorian 			    sizeof(struct ra_prefix_conf));
43053293e44Sflorian 			SIMPLEQ_INSERT_TAIL(&ra_iface_conf->ra_prefix_list,
43153293e44Sflorian 			    ra_prefix_conf, entry);
43253293e44Sflorian 			break;
4334c40b7e8Sflorian 		case IMSG_RECONF_RA_RDNSS:
4340eb5c43bSpamela 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
4350eb5c43bSpamela 			    ra_rdnss_conf))
4360eb5c43bSpamela 				fatalx("%s: IMSG_RECONF_RA_RDNSS wrong length: "
4370eb5c43bSpamela 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
4384c40b7e8Sflorian 			if ((ra_rdnss_conf = malloc(sizeof(struct
4394c40b7e8Sflorian 			    ra_rdnss_conf))) == NULL)
4404c40b7e8Sflorian 				fatal(NULL);
4414c40b7e8Sflorian 			memcpy(ra_rdnss_conf, imsg.data, sizeof(struct
4424c40b7e8Sflorian 			    ra_rdnss_conf));
4438815eebdSflorian 			SIMPLEQ_INSERT_TAIL(&ra_options->ra_rdnss_list,
4444c40b7e8Sflorian 			    ra_rdnss_conf, entry);
4454c40b7e8Sflorian 			break;
4464c40b7e8Sflorian 		case IMSG_RECONF_RA_DNSSL:
4470eb5c43bSpamela 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
4480eb5c43bSpamela 			    ra_dnssl_conf))
4490eb5c43bSpamela 				fatalx("%s: IMSG_RECONF_RA_DNSSL wrong length: "
4500eb5c43bSpamela 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
4514c40b7e8Sflorian 			if ((ra_dnssl_conf = malloc(sizeof(struct
4524c40b7e8Sflorian 			    ra_dnssl_conf))) == NULL)
4534c40b7e8Sflorian 				fatal(NULL);
4544c40b7e8Sflorian 			memcpy(ra_dnssl_conf, imsg.data, sizeof(struct
4554c40b7e8Sflorian 			    ra_dnssl_conf));
4568815eebdSflorian 			SIMPLEQ_INSERT_TAIL(&ra_options->ra_dnssl_list,
4574c40b7e8Sflorian 			    ra_dnssl_conf, entry);
4584c40b7e8Sflorian 			break;
4595207bb19Sflorian 		case IMSG_RECONF_RA_PREF64:
4605207bb19Sflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
4615207bb19Sflorian 			    ra_pref64_conf))
4625207bb19Sflorian 				fatalx("%s: IMSG_RECONF_RA_PREF64 wrong length: "
4635207bb19Sflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
4645207bb19Sflorian 			if ((pref64 = malloc(sizeof(struct ra_pref64_conf))) ==
4655207bb19Sflorian 			    NULL)
4665207bb19Sflorian 				fatal(NULL);
4675207bb19Sflorian 			memcpy(pref64, imsg.data, sizeof(struct ra_pref64_conf));
4685207bb19Sflorian 			SIMPLEQ_INSERT_TAIL(&ra_options->ra_pref64_list, pref64,
4695207bb19Sflorian 			    entry);
4705207bb19Sflorian 			break;
47153293e44Sflorian 		case IMSG_RECONF_END:
4722b2996d8Sflorian 			if (nconf == NULL)
4732b2996d8Sflorian 				fatalx("%s: IMSG_RECONF_END without "
4742b2996d8Sflorian 				    "IMSG_RECONF_CONF", __func__);
47553293e44Sflorian 			merge_config(frontend_conf, nconf);
47653293e44Sflorian 			merge_ra_interfaces();
47753293e44Sflorian 			nconf = NULL;
47853293e44Sflorian 			break;
47953293e44Sflorian 		case IMSG_ICMP6SOCK:
480d9be77f0Sclaudio 			if ((icmp6sock = imsg_get_fd(&imsg)) == -1)
48153293e44Sflorian 				fatalx("%s: expected to receive imsg "
48253293e44Sflorian 				    "ICMPv6 fd but didn't receive any",
48353293e44Sflorian 				    __func__);
484e88dba76Sflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(rdomain))
485e88dba76Sflorian 				fatalx("%s: IMSG_ICMP6SOCK wrong length: "
486e88dba76Sflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
487e88dba76Sflorian 			memcpy(&rdomain, imsg.data, sizeof(rdomain));
488e88dba76Sflorian 			set_icmp6sock(icmp6sock, rdomain);
489eb9f9513Sflorian 			break;
49048e174fdSflorian 		case IMSG_ROUTESOCK:
491eb9f9513Sflorian 			if (routesock != -1)
492eb9f9513Sflorian 				fatalx("%s: received unexpected routesock fd",
493eb9f9513Sflorian 				    __func__);
494d9be77f0Sclaudio 			if ((routesock = imsg_get_fd(&imsg)) == -1)
49548e174fdSflorian 				fatalx("%s: expected to receive imsg "
49648e174fdSflorian 				    "routesocket fd but didn't receive any",
49748e174fdSflorian 				    __func__);
498b542a0d7Sflorian 			event_set(&ev_route, routesock, EV_READ | EV_PERSIST,
49948e174fdSflorian 			    route_receive, NULL);
50048e174fdSflorian 			break;
50153293e44Sflorian 		case IMSG_STARTUP:
50253293e44Sflorian 			frontend_startup();
50353293e44Sflorian 			break;
504a778af8bSflorian 		case IMSG_CONTROLFD:
505d9be77f0Sclaudio 			if ((fd = imsg_get_fd(&imsg)) == -1)
506a778af8bSflorian 				fatalx("%s: expected to receive imsg "
507a778af8bSflorian 				    "control fd but didn't receive any",
508a778af8bSflorian 				    __func__);
509a778af8bSflorian 			/* Listen on control socket. */
510d258785eSflorian 			control_listen(fd);
511a778af8bSflorian 			break;
51253293e44Sflorian 		default:
51353293e44Sflorian 			log_debug("%s: error handling imsg %d", __func__,
51453293e44Sflorian 			    imsg.hdr.type);
51553293e44Sflorian 			break;
51653293e44Sflorian 		}
51753293e44Sflorian 		imsg_free(&imsg);
51853293e44Sflorian 	}
51953293e44Sflorian 	if (!shut)
52053293e44Sflorian 		imsg_event_add(iev);
52153293e44Sflorian 	else {
52253293e44Sflorian 		/* This pipe is dead. Remove its event handler. */
52353293e44Sflorian 		event_del(&iev->ev);
52453293e44Sflorian 		event_loopexit(NULL);
52553293e44Sflorian 	}
52653293e44Sflorian }
52753293e44Sflorian 
52853293e44Sflorian void
52953293e44Sflorian frontend_dispatch_engine(int fd, short event, void *bula)
53053293e44Sflorian {
53153293e44Sflorian 	struct imsgev		*iev = bula;
53253293e44Sflorian 	struct imsgbuf		*ibuf = &iev->ibuf;
53353293e44Sflorian 	struct imsg		 imsg;
53453293e44Sflorian 	struct imsg_send_ra	 send_ra;
53553293e44Sflorian 	struct ra_iface		*ra_iface;
5364a78c7cfSflorian 	uint32_t		 if_index;
53753293e44Sflorian 	int			 n, shut = 0;
53853293e44Sflorian 
53953293e44Sflorian 	if (event & EV_READ) {
540668e5ba9Sclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
541dd7efffeSclaudio 			fatal("imsgbuf_read error");
54253293e44Sflorian 		if (n == 0)	/* Connection closed. */
54353293e44Sflorian 			shut = 1;
54453293e44Sflorian 	}
54553293e44Sflorian 	if (event & EV_WRITE) {
546dd7efffeSclaudio 		if (imsgbuf_write(ibuf) == -1) {
547c1aa9554Sclaudio 			if (errno == EPIPE)	/* connection closed */
54853293e44Sflorian 				shut = 1;
549c1aa9554Sclaudio 			else
550dd7efffeSclaudio 				fatal("imsgbuf_write");
551c1aa9554Sclaudio 		}
55253293e44Sflorian 	}
55353293e44Sflorian 
55453293e44Sflorian 	for (;;) {
55553293e44Sflorian 		if ((n = imsg_get(ibuf, &imsg)) == -1)
55653293e44Sflorian 			fatal("%s: imsg_get error", __func__);
55753293e44Sflorian 		if (n == 0)	/* No more messages. */
55853293e44Sflorian 			break;
55953293e44Sflorian 
56053293e44Sflorian 		switch (imsg.hdr.type) {
56153293e44Sflorian 		case IMSG_SEND_RA:
562b17c900dSpamela 			if (IMSG_DATA_SIZE(imsg) != sizeof(send_ra))
56351a61992Spamela 				fatalx("%s: IMSG_SEND_RA wrong length: %lu",
564b17c900dSpamela 				    __func__, IMSG_DATA_SIZE(imsg));
56553293e44Sflorian 			memcpy(&send_ra, imsg.data, sizeof(send_ra));
56653293e44Sflorian 			ra_iface = find_ra_iface_by_id(send_ra.if_index);
56753293e44Sflorian 			if (ra_iface)
56853293e44Sflorian 				ra_output(ra_iface, &send_ra.to);
56953293e44Sflorian 			break;
5704a78c7cfSflorian 		case IMSG_REMOVE_IF:
571b17c900dSpamela 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
57251a61992Spamela 				fatalx("%s: IMSG_REMOVE_IF wrong length: %lu",
573b17c900dSpamela 				    __func__, IMSG_DATA_SIZE(imsg));
5744a78c7cfSflorian 			memcpy(&if_index, imsg.data, sizeof(if_index));
5754a78c7cfSflorian 			ra_iface = find_ra_iface_by_id(if_index);
5764a78c7cfSflorian 			if (ra_iface) {
5774a78c7cfSflorian 				TAILQ_REMOVE(&ra_interfaces, ra_iface, entry);
5784a78c7cfSflorian 				free_ra_iface(ra_iface);
5794a78c7cfSflorian 			}
5804a78c7cfSflorian 			break;
58153293e44Sflorian 		default:
58253293e44Sflorian 			log_debug("%s: error handling imsg %d", __func__,
58353293e44Sflorian 			    imsg.hdr.type);
58453293e44Sflorian 			break;
58553293e44Sflorian 		}
58653293e44Sflorian 		imsg_free(&imsg);
58753293e44Sflorian 	}
58853293e44Sflorian 	if (!shut)
58953293e44Sflorian 		imsg_event_add(iev);
59053293e44Sflorian 	else {
59153293e44Sflorian 		/* This pipe is dead. Remove its event handler. */
59253293e44Sflorian 		event_del(&iev->ev);
59353293e44Sflorian 		event_loopexit(NULL);
59453293e44Sflorian 	}
59553293e44Sflorian }
59653293e44Sflorian 
59753293e44Sflorian void
59853293e44Sflorian frontend_startup(void)
59953293e44Sflorian {
60053293e44Sflorian 	if (!event_initialized(&ev_route))
60153293e44Sflorian 		fatalx("%s: did not receive a route socket from the main "
60253293e44Sflorian 		    "process", __func__);
60353293e44Sflorian 
60453293e44Sflorian 	event_add(&ev_route, NULL);
60553293e44Sflorian }
60653293e44Sflorian 
60753293e44Sflorian 
60853293e44Sflorian void
60953293e44Sflorian icmp6_receive(int fd, short events, void *arg)
61053293e44Sflorian {
611e88dba76Sflorian 	struct icmp6_ev		*icmp6ev;
612e88dba76Sflorian 	struct icmp6_hdr	*icmp6_hdr;
61353293e44Sflorian 	struct imsg_ra_rs	 ra_rs;
61453293e44Sflorian 	struct in6_pktinfo	*pi = NULL;
61553293e44Sflorian 	struct cmsghdr		*cm;
61653293e44Sflorian 	ssize_t			 len;
61753293e44Sflorian 	int			 if_index = 0, *hlimp = NULL;
61853293e44Sflorian 	char			 ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
61953293e44Sflorian 
620e88dba76Sflorian 	icmp6ev = arg;
621e88dba76Sflorian 	if ((len = recvmsg(fd, &icmp6ev->rcvmhdr, 0)) == -1) {
62253293e44Sflorian 		log_warn("recvmsg");
62353293e44Sflorian 		return;
62453293e44Sflorian 	}
62553293e44Sflorian 
626e88dba76Sflorian 	if ((size_t)len < sizeof(struct icmp6_hdr))
627e88dba76Sflorian 		return;
628e88dba76Sflorian 
629e88dba76Sflorian 	icmp6_hdr = (struct icmp6_hdr *)icmp6ev->answer;
630e88dba76Sflorian 	if (icmp6_hdr->icmp6_type != ND_ROUTER_ADVERT &&
631e88dba76Sflorian 	    icmp6_hdr->icmp6_type != ND_ROUTER_SOLICIT)
632e88dba76Sflorian 		return;
633e88dba76Sflorian 
63453293e44Sflorian 	/* extract optional information via Advanced API */
635e88dba76Sflorian 	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&icmp6ev->rcvmhdr); cm;
636e88dba76Sflorian 	    cm = (struct cmsghdr *)CMSG_NXTHDR(&icmp6ev->rcvmhdr, cm)) {
63753293e44Sflorian 		if (cm->cmsg_level == IPPROTO_IPV6 &&
63853293e44Sflorian 		    cm->cmsg_type == IPV6_PKTINFO &&
63953293e44Sflorian 		    cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
64053293e44Sflorian 			pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
64153293e44Sflorian 			if_index = pi->ipi6_ifindex;
64253293e44Sflorian 		}
64353293e44Sflorian 		if (cm->cmsg_level == IPPROTO_IPV6 &&
64453293e44Sflorian 		    cm->cmsg_type == IPV6_HOPLIMIT &&
64553293e44Sflorian 		    cm->cmsg_len == CMSG_LEN(sizeof(int)))
64653293e44Sflorian 			hlimp = (int *)CMSG_DATA(cm);
64753293e44Sflorian 	}
64853293e44Sflorian 
64953293e44Sflorian 	if (if_index == 0) {
65053293e44Sflorian 		log_warnx("failed to get receiving interface");
65153293e44Sflorian 		return;
65253293e44Sflorian 	}
65353293e44Sflorian 
65453293e44Sflorian 	if (hlimp == NULL) {
65553293e44Sflorian 		log_warnx("failed to get receiving hop limit");
65653293e44Sflorian 		return;
65753293e44Sflorian 	}
65853293e44Sflorian 
65953293e44Sflorian 	if (*hlimp != 255) {
66053293e44Sflorian 		log_warnx("invalid RA or RS with hop limit of %d from %s on %s",
661e88dba76Sflorian 		    *hlimp, inet_ntop(AF_INET6, &icmp6ev->from.sin6_addr,
66253293e44Sflorian 		    ntopbuf, INET6_ADDRSTRLEN), if_indextoname(if_index,
66353293e44Sflorian 		    ifnamebuf));
66453293e44Sflorian 		return;
66553293e44Sflorian 	}
66653293e44Sflorian 
66793fdf396Sflorian 	log_debug("RA or RS with hop limit of %d from %s on %s",
668e88dba76Sflorian 	    *hlimp, inet_ntop(AF_INET6, &icmp6ev->from.sin6_addr,
66953293e44Sflorian 	    ntopbuf, INET6_ADDRSTRLEN), if_indextoname(if_index,
67053293e44Sflorian 	    ifnamebuf));
67153293e44Sflorian 
67253293e44Sflorian 	if ((size_t)len > sizeof(ra_rs.packet)) {
67353293e44Sflorian 		log_warnx("invalid RA or RS with size %ld from %s on %s",
674e88dba76Sflorian 		    len, inet_ntop(AF_INET6, &icmp6ev->from.sin6_addr,
67553293e44Sflorian 		    ntopbuf, INET6_ADDRSTRLEN), if_indextoname(if_index,
67653293e44Sflorian 		    ifnamebuf));
67753293e44Sflorian 		return;
67853293e44Sflorian 	}
67953293e44Sflorian 
68053293e44Sflorian 	ra_rs.if_index = if_index;
681e88dba76Sflorian 	memcpy(&ra_rs.from,  &icmp6ev->from, sizeof(ra_rs.from));
68253293e44Sflorian 	ra_rs.len = len;
683e88dba76Sflorian 	memcpy(ra_rs.packet, icmp6ev->answer, len);
68453293e44Sflorian 
68553293e44Sflorian 	frontend_imsg_compose_engine(IMSG_RA_RS, 0, &ra_rs, sizeof(ra_rs));
68653293e44Sflorian }
68753293e44Sflorian 
68853293e44Sflorian void
68953293e44Sflorian join_all_routers_mcast_group(struct ra_iface *ra_iface)
69053293e44Sflorian {
691e88dba76Sflorian 	if (!event_initialized(&ra_iface->icmp6ev->ev))
692e88dba76Sflorian 		return;
69353293e44Sflorian 	log_debug("joining multicast group on %s", ra_iface->name);
69453293e44Sflorian 	all_routers.ipv6mr_interface = ra_iface->if_index;
695e88dba76Sflorian 	if (setsockopt(EVENT_FD(&ra_iface->icmp6ev->ev), IPPROTO_IPV6,
696e88dba76Sflorian 	    IPV6_JOIN_GROUP, &all_routers, sizeof(all_routers)) == -1)
69753293e44Sflorian 		fatal("IPV6_JOIN_GROUP(%s)", ra_iface->name);
69853293e44Sflorian }
69953293e44Sflorian 
70053293e44Sflorian void
70153293e44Sflorian leave_all_routers_mcast_group(struct ra_iface *ra_iface)
70253293e44Sflorian {
703e88dba76Sflorian 	if (!event_initialized(&ra_iface->icmp6ev->ev))
704e88dba76Sflorian 		return;
70553293e44Sflorian 	log_debug("leaving multicast group on %s", ra_iface->name);
70653293e44Sflorian 	all_routers.ipv6mr_interface = ra_iface->if_index;
707e88dba76Sflorian 	setsockopt(EVENT_FD(&ra_iface->icmp6ev->ev), IPPROTO_IPV6,
708e88dba76Sflorian 	    IPV6_LEAVE_GROUP, &all_routers, sizeof(all_routers));
70953293e44Sflorian }
71053293e44Sflorian 
71153293e44Sflorian struct ra_iface*
71292e6ccd1Sreyk find_ra_iface_by_id(uint32_t if_index)
71392e6ccd1Sreyk {
71453293e44Sflorian 	struct ra_iface	*ra_iface;
71553293e44Sflorian 
71653293e44Sflorian 	TAILQ_FOREACH(ra_iface, &ra_interfaces, entry) {
71753293e44Sflorian 		if (ra_iface->if_index == if_index)
71853293e44Sflorian 			return ra_iface;
71953293e44Sflorian 	}
72053293e44Sflorian 	return (NULL);
72153293e44Sflorian }
72253293e44Sflorian 
72353293e44Sflorian struct ra_iface*
72453293e44Sflorian find_ra_iface_by_name(char *if_name)
72553293e44Sflorian {
72653293e44Sflorian 	struct ra_iface	*ra_iface;
72753293e44Sflorian 
72853293e44Sflorian 	TAILQ_FOREACH(ra_iface, &ra_interfaces, entry) {
72953293e44Sflorian 		if (strcmp(ra_iface->name, if_name) == 0)
73053293e44Sflorian 			return ra_iface;
73153293e44Sflorian 	}
73253293e44Sflorian 	return (NULL);
73353293e44Sflorian }
73453293e44Sflorian 
73553293e44Sflorian struct ra_iface_conf*
73653293e44Sflorian find_ra_iface_conf(struct ra_iface_conf_head *head, char *if_name)
73753293e44Sflorian {
73853293e44Sflorian 	struct ra_iface_conf	*ra_iface_conf;
73953293e44Sflorian 
74053293e44Sflorian 	SIMPLEQ_FOREACH(ra_iface_conf, head, entry) {
74153293e44Sflorian 		if (strcmp(ra_iface_conf->name, if_name) == 0)
74253293e44Sflorian 			return ra_iface_conf;
74353293e44Sflorian 	}
74453293e44Sflorian 	return (NULL);
74553293e44Sflorian }
74653293e44Sflorian 
747abcf5f52Sflorian int
748e88dba76Sflorian get_ifrdomain(char *if_name)
749e88dba76Sflorian {
750e88dba76Sflorian 	struct ifreq		 ifr;
751e88dba76Sflorian 
752e88dba76Sflorian 	strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
753e88dba76Sflorian 	if (ioctl(ioctlsock, SIOCGIFRDOMAIN, (caddr_t)&ifr) == -1) {
754e88dba76Sflorian 		log_warn("SIOCGIFRDOMAIN");
755e88dba76Sflorian 		return -1;
756e88dba76Sflorian 	}
757e88dba76Sflorian 	return ifr.ifr_rdomainid;
758e88dba76Sflorian }
759e88dba76Sflorian 
76053293e44Sflorian void
761c358c7e3Sflorian merge_ra_interface(char *if_name, char *conf_name, struct ifaddrs *ifap)
76292f66a25Sreyk {
76392f66a25Sreyk 	struct ra_iface		*ra_iface;
764c358c7e3Sflorian 	struct ifaddrs		*ifa;
765c358c7e3Sflorian 	struct sockaddr_in6	*sin6;
766c358c7e3Sflorian 	struct in6_ifreq	 ifr6;
767c358c7e3Sflorian 	struct sockaddr_dl	*sdl;
768c358c7e3Sflorian 	struct ether_addr	 hw_addr;
76992f66a25Sreyk 	uint32_t		 if_index;
770c358c7e3Sflorian 	int			 link_state = LINK_STATE_UNKNOWN;
771c358c7e3Sflorian 	int			 has_linklocal = 0, ifrdomain;
772c358c7e3Sflorian 	int			 has_hw_addr = 0;
773abcf5f52Sflorian 
774c358c7e3Sflorian 	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
775c358c7e3Sflorian 		if (ifa->ifa_addr == NULL)
776c358c7e3Sflorian 			continue;
777c358c7e3Sflorian 		if (ifa->ifa_addr->sa_family != AF_LINK &&
778c358c7e3Sflorian 		    ifa->ifa_addr->sa_family != AF_INET6)
779c358c7e3Sflorian 			continue;
780c358c7e3Sflorian 		if (strcmp(if_name, ifa->ifa_name) != 0)
781c358c7e3Sflorian 			continue;
78292f66a25Sreyk 
783c358c7e3Sflorian 		if (ifa->ifa_addr->sa_family == AF_LINK) {
784c358c7e3Sflorian 			link_state =
785c358c7e3Sflorian 			    ((struct if_data*)ifa->ifa_data)->ifi_link_state;
786c358c7e3Sflorian 			sdl = (struct sockaddr_dl *)ifa->ifa_addr;
78711d38187Sflorian 			if (sdl != NULL && (sdl->sdl_type == IFT_ETHER ||
78811d38187Sflorian 			    sdl->sdl_type == IFT_CARP) && sdl->sdl_alen ==
78911d38187Sflorian 			    ETHER_ADDR_LEN) {
790c358c7e3Sflorian 				has_hw_addr = 1;
791c358c7e3Sflorian 				memcpy(&hw_addr, LLADDR(sdl), ETHER_ADDR_LEN);
792c358c7e3Sflorian 			}
793c358c7e3Sflorian 		} else if (ifa->ifa_addr->sa_family == AF_INET6) {
794c358c7e3Sflorian 			sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
795c358c7e3Sflorian 
796c358c7e3Sflorian 			if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
797c358c7e3Sflorian 				continue;
798c358c7e3Sflorian 
799c358c7e3Sflorian 			memset(&ifr6, 0, sizeof(ifr6));
800c358c7e3Sflorian 			strlcpy(ifr6.ifr_name, if_name, sizeof(ifr6.ifr_name));
801c358c7e3Sflorian 			memcpy(&ifr6.ifr_addr, sin6, sizeof(ifr6.ifr_addr));
802c358c7e3Sflorian 			if (ioctl(ioctlsock, SIOCGIFAFLAG_IN6,
803c358c7e3Sflorian 			    (caddr_t)&ifr6) == -1) {
804c358c7e3Sflorian 				log_warn("SIOCGIFAFLAG_IN6");
805c358c7e3Sflorian 				continue;
806c358c7e3Sflorian 			}
807c358c7e3Sflorian 
808c358c7e3Sflorian 			if (ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_TENTATIVE |
809c358c7e3Sflorian 			    IN6_IFF_DUPLICATED))
810c358c7e3Sflorian 				continue;
811c358c7e3Sflorian 			has_linklocal = 1;
812c358c7e3Sflorian 		}
813c358c7e3Sflorian 	}
814c358c7e3Sflorian 
815c358c7e3Sflorian 	ifrdomain = get_ifrdomain(if_name);
816c358c7e3Sflorian 
817c358c7e3Sflorian 	if ((ra_iface = find_ra_iface_by_name(if_name)) != NULL) {
818abcf5f52Sflorian 		ra_iface->link_state = link_state;
819abcf5f52Sflorian 		if (!LINK_STATE_IS_UP(link_state)) {
820c358c7e3Sflorian 			log_debug("%s down, removing", if_name);
821e71a3804Sflorian 			ra_iface->removed = 1;
822e71a3804Sflorian 		} else if (!has_linklocal) {
823e71a3804Sflorian 			log_debug("%s has no IPv6 link-local address, "
824c358c7e3Sflorian 			    "removing", if_name);
825abcf5f52Sflorian 			ra_iface->removed = 1;
826e88dba76Sflorian 		} else if (ifrdomain == -1) {
827c358c7e3Sflorian 			log_debug("can't get rdomain for %s, removing", if_name);
828c358c7e3Sflorian 			ra_iface->removed = 1;
829c358c7e3Sflorian 		} else if (!has_hw_addr) {
830c358c7e3Sflorian 			log_debug("%s has no mac address, removing", if_name);
831e88dba76Sflorian 			ra_iface->removed = 1;
832e88dba76Sflorian 		} else if (ra_iface->rdomain != ifrdomain) {
833e88dba76Sflorian 			leave_all_routers_mcast_group(ra_iface);
834e88dba76Sflorian 			unref_icmp6ev(ra_iface);
835e88dba76Sflorian 			ra_iface->rdomain = ifrdomain;
836e88dba76Sflorian 			ra_iface->icmp6ev = get_icmp6ev_by_rdomain(ifrdomain);
837e88dba76Sflorian 			join_all_routers_mcast_group(ra_iface);
838e88dba76Sflorian 			ra_iface->removed = 0;
839abcf5f52Sflorian 		} else {
840c358c7e3Sflorian 			log_debug("keeping interface %s", if_name);
84192f66a25Sreyk 			ra_iface->removed = 0;
842abcf5f52Sflorian 		}
843c358c7e3Sflorian 		memcpy(&ra_iface->hw_addr, &hw_addr, sizeof(hw_addr));
844abcf5f52Sflorian 		return;
845abcf5f52Sflorian 	}
846abcf5f52Sflorian 
847abcf5f52Sflorian 	if (!LINK_STATE_IS_UP(link_state)) {
848c358c7e3Sflorian 		log_debug("%s down, ignoring", if_name);
84992f66a25Sreyk 		return;
85092f66a25Sreyk 	}
85192f66a25Sreyk 
852e71a3804Sflorian 	if (!has_linklocal) {
853c358c7e3Sflorian 		log_debug("%s has no IPv6 link-local address, ignoring",
854c358c7e3Sflorian 		    if_name);
855e71a3804Sflorian 		return;
856e71a3804Sflorian 	}
857e71a3804Sflorian 
858c358c7e3Sflorian 	if (ifrdomain == -1) {
859c358c7e3Sflorian 		log_debug("can't get rdomain for %s, ignoring", if_name);
860c358c7e3Sflorian 		return;
861c358c7e3Sflorian 	}
862c358c7e3Sflorian 
863c358c7e3Sflorian 	if (!has_hw_addr) {
864c358c7e3Sflorian 		log_debug("%s has no mac address, ignoring", if_name);
865c358c7e3Sflorian 		return;
866c358c7e3Sflorian 	}
867c358c7e3Sflorian 
868c358c7e3Sflorian 	log_debug("new interface %s", if_name);
869c358c7e3Sflorian 	if ((if_index = if_nametoindex(if_name)) == 0)
87092f66a25Sreyk 		return;
871abcf5f52Sflorian 
872c358c7e3Sflorian 	log_debug("adding interface %s", if_name);
87392f66a25Sreyk 	if ((ra_iface = calloc(1, sizeof(*ra_iface))) == NULL)
87492f66a25Sreyk 		fatal("%s", __func__);
87592f66a25Sreyk 
876c358c7e3Sflorian 	strlcpy(ra_iface->name, if_name, sizeof(ra_iface->name));
87792f66a25Sreyk 	strlcpy(ra_iface->conf_name, conf_name,
87892f66a25Sreyk 	    sizeof(ra_iface->conf_name));
87992f66a25Sreyk 
88092f66a25Sreyk 	ra_iface->if_index = if_index;
881e88dba76Sflorian 	ra_iface->rdomain = ifrdomain;
882c358c7e3Sflorian 	memcpy(&ra_iface->hw_addr, &hw_addr, sizeof(hw_addr));
88392f66a25Sreyk 	SIMPLEQ_INIT(&ra_iface->prefixes);
884e88dba76Sflorian 
885e88dba76Sflorian 	ra_iface->icmp6ev = get_icmp6ev_by_rdomain(ifrdomain);
88692f66a25Sreyk 	join_all_routers_mcast_group(ra_iface);
887e88dba76Sflorian 	TAILQ_INSERT_TAIL(&ra_interfaces, ra_iface, entry);
88892f66a25Sreyk }
88992f66a25Sreyk 
89092f66a25Sreyk void
89153293e44Sflorian merge_ra_interfaces(void)
89253293e44Sflorian {
89353293e44Sflorian 	struct ra_iface_conf	*ra_iface_conf;
89453293e44Sflorian 	struct ra_prefix_conf	*ra_prefix_conf;
8954a78c7cfSflorian 	struct ra_iface		*ra_iface;
89692f66a25Sreyk 	struct ifgroupreq	 ifgr;
89792f66a25Sreyk 	struct ifg_req		*ifg;
898c358c7e3Sflorian 	struct ifaddrs		*ifap;
89992f66a25Sreyk 	char			*conf_name;
90092f66a25Sreyk 	unsigned int		 len;
90153293e44Sflorian 
902c358c7e3Sflorian 	if (getifaddrs(&ifap) != 0) {
903c358c7e3Sflorian 		log_warn("getifaddrs");
904c358c7e3Sflorian 		return;
905c358c7e3Sflorian 	}
906c358c7e3Sflorian 
90753293e44Sflorian 	TAILQ_FOREACH(ra_iface, &ra_interfaces, entry)
90853293e44Sflorian 		ra_iface->removed = 1;
90953293e44Sflorian 
91053293e44Sflorian 	SIMPLEQ_FOREACH(ra_iface_conf, &frontend_conf->ra_iface_list, entry) {
91192f66a25Sreyk 		conf_name = ra_iface_conf->name;
91253293e44Sflorian 
91392f66a25Sreyk 		/* check if network interface or group */
91492f66a25Sreyk 		if (isdigit((unsigned char)conf_name[strlen(conf_name) - 1])) {
915c358c7e3Sflorian 			merge_ra_interface(conf_name, conf_name, ifap);
91653293e44Sflorian 		} else {
91792f66a25Sreyk 			log_debug("interface group %s", conf_name);
91892f66a25Sreyk 
91992f66a25Sreyk 			memset(&ifgr, 0, sizeof(ifgr));
92092f66a25Sreyk 			strlcpy(ifgr.ifgr_name, conf_name,
92192f66a25Sreyk 			    sizeof(ifgr.ifgr_name));
92292f66a25Sreyk 			if (ioctl(ioctlsock, SIOCGIFGMEMB,
92392f66a25Sreyk 			    (caddr_t)&ifgr) == -1)
92492f66a25Sreyk 				continue;
92592f66a25Sreyk 
92692f66a25Sreyk 			len = ifgr.ifgr_len;
92792f66a25Sreyk 			if ((ifgr.ifgr_groups = calloc(1, len)) == NULL)
92892f66a25Sreyk 				fatal("%s: calloc", __func__);
92992f66a25Sreyk 			if (ioctl(ioctlsock, SIOCGIFGMEMB,
93092f66a25Sreyk 			    (caddr_t)&ifgr) == -1) {
93192f66a25Sreyk 				log_debug("group %s without members",
93292f66a25Sreyk 				    conf_name);
93392f66a25Sreyk 				free(ifgr.ifgr_groups);
93492f66a25Sreyk 				continue;
93592f66a25Sreyk 			}
93692f66a25Sreyk 
93792f66a25Sreyk 			for (ifg = ifgr.ifgr_groups;
93892f66a25Sreyk 			    (ifg != NULL) && (len >= sizeof(struct ifg_req));
93992f66a25Sreyk 			    ifg++) {
94092f66a25Sreyk 				len -= sizeof(struct ifg_req);
94192f66a25Sreyk 				merge_ra_interface(ifg->ifgrq_member,
942c358c7e3Sflorian 				    conf_name, ifap);
94392f66a25Sreyk 			}
94492f66a25Sreyk 			free(ifgr.ifgr_groups);
94553293e44Sflorian 		}
94653293e44Sflorian 	}
94753293e44Sflorian 
94853293e44Sflorian 	TAILQ_FOREACH(ra_iface, &ra_interfaces, entry) {
94953293e44Sflorian 		while ((ra_prefix_conf = SIMPLEQ_FIRST(&ra_iface->prefixes))
95053293e44Sflorian 		    != NULL) {
95153293e44Sflorian 			SIMPLEQ_REMOVE_HEAD(&ra_iface->prefixes,
95253293e44Sflorian 			    entry);
95353293e44Sflorian 			free(ra_prefix_conf);
95453293e44Sflorian 		}
95553293e44Sflorian 		ra_iface->prefix_count = 0;
95653293e44Sflorian 
9574a78c7cfSflorian 		if (ra_iface->removed) {
9584a78c7cfSflorian 			log_debug("iface removed: %s", ra_iface->name);
9598c9b05a6Sflorian 			build_leaving_packet(ra_iface);
9604a78c7cfSflorian 			frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0,
9614a78c7cfSflorian 			    &ra_iface->if_index, sizeof(ra_iface->if_index));
9624a78c7cfSflorian 			continue;
9634a78c7cfSflorian 		}
9644a78c7cfSflorian 
96553293e44Sflorian 		ra_iface_conf = find_ra_iface_conf(
96692f66a25Sreyk 		    &frontend_conf->ra_iface_list, ra_iface->conf_name);
96753293e44Sflorian 
96853293e44Sflorian 		log_debug("add static prefixes for %s", ra_iface->name);
96953293e44Sflorian 
97053293e44Sflorian 		SIMPLEQ_FOREACH(ra_prefix_conf, &ra_iface_conf->ra_prefix_list,
97153293e44Sflorian 		    entry) {
97253293e44Sflorian 			add_new_prefix_to_ra_iface(ra_iface,
97353293e44Sflorian 			    &ra_prefix_conf->prefix,
97420fc6e8eSflorian 			    ra_prefix_conf->prefixlen, ra_prefix_conf,
97520fc6e8eSflorian 			    ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME);
97653293e44Sflorian 		}
977e3774183Sflorian 
978e3774183Sflorian 		if (ra_iface_conf->autoprefix)
979e3774183Sflorian 			get_interface_prefixes(ra_iface,
980c358c7e3Sflorian 			    ra_iface_conf->autoprefix, ifap);
981e3774183Sflorian 
98220fc6e8eSflorian 		if (build_packet(ra_iface)) {
98320fc6e8eSflorian 			/* packet changed; send new advertisements */
98420fc6e8eSflorian 			if (event_initialized(&ra_iface->icmp6ev->ev))
98520fc6e8eSflorian 				frontend_imsg_compose_engine(IMSG_UPDATE_IF, 0,
98620fc6e8eSflorian 				    &ra_iface->if_index,
98720fc6e8eSflorian 				    sizeof(ra_iface->if_index));
98820fc6e8eSflorian 		}
98953293e44Sflorian 	}
990c358c7e3Sflorian 	freeifaddrs(ifap);
99153293e44Sflorian }
99253293e44Sflorian 
99353293e44Sflorian void
99453293e44Sflorian free_ra_iface(struct ra_iface *ra_iface)
99553293e44Sflorian {
99653293e44Sflorian 	struct ra_prefix_conf	*prefix;
99753293e44Sflorian 
99853293e44Sflorian 	leave_all_routers_mcast_group(ra_iface);
99953293e44Sflorian 
100053293e44Sflorian 	while ((prefix = SIMPLEQ_FIRST(&ra_iface->prefixes)) != NULL) {
100153293e44Sflorian 		SIMPLEQ_REMOVE_HEAD(&ra_iface->prefixes, entry);
100253293e44Sflorian 		free(prefix);
100353293e44Sflorian 	}
100453293e44Sflorian 
1005e88dba76Sflorian 	unref_icmp6ev(ra_iface);
100653293e44Sflorian 	free(ra_iface);
100753293e44Sflorian }
100853293e44Sflorian 
100953293e44Sflorian /* from kame via ifconfig, where it's called prefix() */
101053293e44Sflorian int
101153293e44Sflorian in6_mask2prefixlen(struct in6_addr *in6)
101253293e44Sflorian {
101353293e44Sflorian 	u_char *nam = (u_char *)in6;
101453293e44Sflorian 	int byte, bit, plen = 0, size = sizeof(struct in6_addr);
101553293e44Sflorian 
101653293e44Sflorian 	for (byte = 0; byte < size; byte++, plen += 8)
101753293e44Sflorian 		if (nam[byte] != 0xff)
101853293e44Sflorian 			break;
101953293e44Sflorian 	if (byte == size)
102053293e44Sflorian 		return (plen);
102153293e44Sflorian 	for (bit = 7; bit != 0; bit--, plen++)
102253293e44Sflorian 		if (!(nam[byte] & (1 << bit)))
102353293e44Sflorian 			break;
102453293e44Sflorian 	for (; bit != 0; bit--)
102553293e44Sflorian 		if (nam[byte] & (1 << bit))
102653293e44Sflorian 			return (0);
102753293e44Sflorian 	byte++;
102853293e44Sflorian 	for (; byte < size; byte++)
102953293e44Sflorian 		if (nam[byte])
103053293e44Sflorian 			return (0);
103153293e44Sflorian 	return (plen);
103253293e44Sflorian }
103353293e44Sflorian 
103453293e44Sflorian void
103553293e44Sflorian get_interface_prefixes(struct ra_iface *ra_iface, struct ra_prefix_conf
103620fc6e8eSflorian     *autoprefix_conf, struct ifaddrs *ifap)
103753293e44Sflorian {
103853293e44Sflorian 	struct in6_ifreq	 ifr6;
1039c358c7e3Sflorian 	struct ifaddrs		*ifa;
104053293e44Sflorian 	struct sockaddr_in6	*sin6;
104120fc6e8eSflorian 	uint32_t		 decaying_vltime, decaying_pltime;
104253293e44Sflorian 	int			 prefixlen;
104353293e44Sflorian 
104453293e44Sflorian 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
104540019487Sbenno 		if (ifa->ifa_addr == NULL ||
104640019487Sbenno 		    ifa->ifa_addr->sa_family != AF_INET6)
104753293e44Sflorian 			continue;
104853293e44Sflorian 
104920fc6e8eSflorian 		if (strcmp(ra_iface->name, ifa->ifa_name) != 0)
105020fc6e8eSflorian 			continue;
105120fc6e8eSflorian 
105253293e44Sflorian 		sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
105353293e44Sflorian 
105453293e44Sflorian 		if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
105553293e44Sflorian 			continue;
105653293e44Sflorian 
105753293e44Sflorian 		memset(&ifr6, 0, sizeof(ifr6));
1058d131c648Sflorian 		strlcpy(ifr6.ifr_name, ra_iface->name, sizeof(ifr6.ifr_name));
105953293e44Sflorian 		memcpy(&ifr6.ifr_addr, sin6, sizeof(ifr6.ifr_addr));
106053293e44Sflorian 
106120fc6e8eSflorian 		decaying_vltime = ND6_INFINITE_LIFETIME;
106220fc6e8eSflorian 		decaying_pltime = ND6_INFINITE_LIFETIME;
106320fc6e8eSflorian 
106420fc6e8eSflorian 		if (ioctl(ioctlsock, SIOCGIFALIFETIME_IN6,
106520fc6e8eSflorian 		    (caddr_t)&ifr6) != -1) {
106620fc6e8eSflorian 			struct in6_addrlifetime	*lifetime;
106720fc6e8eSflorian 
106820fc6e8eSflorian 			lifetime = &ifr6.ifr_ifru.ifru_lifetime;
106920fc6e8eSflorian 			if (lifetime->ia6t_preferred)
107020fc6e8eSflorian 				decaying_pltime = lifetime->ia6t_preferred;
107120fc6e8eSflorian 			if (lifetime->ia6t_expire)
107220fc6e8eSflorian 				decaying_vltime = lifetime->ia6t_expire;
107320fc6e8eSflorian 		}
107420fc6e8eSflorian 
107520fc6e8eSflorian 		memset(&ifr6, 0, sizeof(ifr6));
107620fc6e8eSflorian 		strlcpy(ifr6.ifr_name, ra_iface->name, sizeof(ifr6.ifr_name));
107720fc6e8eSflorian 		memcpy(&ifr6.ifr_addr, sin6, sizeof(ifr6.ifr_addr));
107820fc6e8eSflorian 
1079df69c215Sderaadt 		if (ioctl(ioctlsock, SIOCGIFNETMASK_IN6, (caddr_t)&ifr6) == -1)
10801347ce87Sflorian 			continue; /* addr got deleted while we were looking */
108153293e44Sflorian 
108253293e44Sflorian 		prefixlen = in6_mask2prefixlen(&((struct sockaddr_in6 *)
108353293e44Sflorian 		    &ifr6.ifr_addr)->sin6_addr);
108453293e44Sflorian 
108553293e44Sflorian 		if (prefixlen == 128)
108653293e44Sflorian 			continue;
108753293e44Sflorian 
108853293e44Sflorian 		mask_prefix(&sin6->sin6_addr, prefixlen);
108953293e44Sflorian 
109053293e44Sflorian 		add_new_prefix_to_ra_iface(ra_iface, &sin6->sin6_addr,
109120fc6e8eSflorian 		    prefixlen, autoprefix_conf, decaying_vltime,
109220fc6e8eSflorian 		    decaying_pltime);
109353293e44Sflorian 	}
109453293e44Sflorian }
109553293e44Sflorian 
109653293e44Sflorian struct ra_prefix_conf*
109753293e44Sflorian find_ra_prefix_conf(struct ra_prefix_conf_head* head, struct in6_addr *prefix,
109853293e44Sflorian     int prefixlen)
109953293e44Sflorian {
110053293e44Sflorian 	struct ra_prefix_conf	*ra_prefix_conf;
110153293e44Sflorian 
110253293e44Sflorian 	SIMPLEQ_FOREACH(ra_prefix_conf, head, entry) {
110353293e44Sflorian 		if (ra_prefix_conf->prefixlen == prefixlen &&
110453293e44Sflorian 		    memcmp(&ra_prefix_conf->prefix, prefix, sizeof(*prefix)) ==
110553293e44Sflorian 		    0)
110653293e44Sflorian 			return (ra_prefix_conf);
110753293e44Sflorian 	}
110853293e44Sflorian 	return (NULL);
110953293e44Sflorian }
111053293e44Sflorian 
111153293e44Sflorian void
111253293e44Sflorian add_new_prefix_to_ra_iface(struct ra_iface *ra_iface, struct in6_addr *addr,
111320fc6e8eSflorian     int prefixlen, struct ra_prefix_conf *ra_prefix_conf,
111420fc6e8eSflorian     uint32_t decaying_vltime, uint32_t decaying_pltime)
111553293e44Sflorian {
111653293e44Sflorian 	struct ra_prefix_conf	*new_ra_prefix_conf;
111753293e44Sflorian 
111820fc6e8eSflorian 	if ((new_ra_prefix_conf = find_ra_prefix_conf(&ra_iface->prefixes, addr,
111920fc6e8eSflorian 	    prefixlen)) != NULL) {
112020fc6e8eSflorian 		if (decaying_vltime != ND6_INFINITE_LIFETIME ||
112120fc6e8eSflorian 		    decaying_pltime != ND6_INFINITE_LIFETIME) {
112220fc6e8eSflorian 			ra_iface->ltime_decaying = 1;
112320fc6e8eSflorian 			new_ra_prefix_conf->ltime_decaying = 0;
112420fc6e8eSflorian 			if (decaying_vltime != ND6_INFINITE_LIFETIME) {
112520fc6e8eSflorian 				new_ra_prefix_conf->vltime = decaying_vltime;
112620fc6e8eSflorian 				new_ra_prefix_conf->ltime_decaying |=
112720fc6e8eSflorian 				    VLTIME_DECAYING;
112820fc6e8eSflorian 			}
112920fc6e8eSflorian 			if (decaying_pltime != ND6_INFINITE_LIFETIME) {
113020fc6e8eSflorian 				new_ra_prefix_conf->pltime = decaying_pltime;
113120fc6e8eSflorian 				new_ra_prefix_conf->ltime_decaying |=
113220fc6e8eSflorian 				    PLTIME_DECAYING;
113320fc6e8eSflorian 			}
113420fc6e8eSflorian 		} else if (new_ra_prefix_conf->ltime_decaying) {
113520fc6e8eSflorian 			struct ra_prefix_conf *pc;
113620fc6e8eSflorian 
113720fc6e8eSflorian 			new_ra_prefix_conf->ltime_decaying = 0;
113820fc6e8eSflorian 			ra_iface->ltime_decaying = 0;
113920fc6e8eSflorian 			SIMPLEQ_FOREACH(pc, &ra_iface->prefixes, entry) {
114020fc6e8eSflorian 				if (pc->ltime_decaying) {
114120fc6e8eSflorian 					ra_iface->ltime_decaying = 1;
114220fc6e8eSflorian 					break;
114320fc6e8eSflorian 				}
114420fc6e8eSflorian 			}
114520fc6e8eSflorian 		} else
114653293e44Sflorian 			log_debug("ignoring duplicate %s/%d prefix",
114753293e44Sflorian 			    in6_to_str(addr), prefixlen);
114853293e44Sflorian 		return;
114953293e44Sflorian 	}
115053293e44Sflorian 
115153293e44Sflorian 	log_debug("adding %s/%d prefix", in6_to_str(addr), prefixlen);
115253293e44Sflorian 
115353293e44Sflorian 	if ((new_ra_prefix_conf = calloc(1, sizeof(*ra_prefix_conf))) == NULL)
115453293e44Sflorian 		fatal("%s", __func__);
115553293e44Sflorian 	new_ra_prefix_conf->prefix = *addr;
115653293e44Sflorian 	new_ra_prefix_conf->prefixlen = prefixlen;
115753293e44Sflorian 	new_ra_prefix_conf->vltime = ra_prefix_conf->vltime;
115853293e44Sflorian 	new_ra_prefix_conf->pltime = ra_prefix_conf->pltime;
115920fc6e8eSflorian 	if (decaying_vltime != ND6_INFINITE_LIFETIME ||
116020fc6e8eSflorian 	    decaying_pltime != ND6_INFINITE_LIFETIME) {
116120fc6e8eSflorian 		ra_iface->ltime_decaying = 1;
116220fc6e8eSflorian 		if (decaying_vltime != ND6_INFINITE_LIFETIME) {
116320fc6e8eSflorian 			new_ra_prefix_conf->vltime = decaying_vltime;
116420fc6e8eSflorian 			new_ra_prefix_conf->ltime_decaying |= VLTIME_DECAYING;
116520fc6e8eSflorian 		}
116620fc6e8eSflorian 		if (decaying_pltime != ND6_INFINITE_LIFETIME) {
116720fc6e8eSflorian 			new_ra_prefix_conf->pltime = decaying_pltime;
116820fc6e8eSflorian 			new_ra_prefix_conf->ltime_decaying |= PLTIME_DECAYING;
116920fc6e8eSflorian 		}
117020fc6e8eSflorian 	}
117153293e44Sflorian 	new_ra_prefix_conf->aflag = ra_prefix_conf->aflag;
117253293e44Sflorian 	new_ra_prefix_conf->lflag = ra_prefix_conf->lflag;
117353293e44Sflorian 	SIMPLEQ_INSERT_TAIL(&ra_iface->prefixes, new_ra_prefix_conf, entry);
117453293e44Sflorian 	ra_iface->prefix_count++;
117553293e44Sflorian }
117653293e44Sflorian 
117720fc6e8eSflorian int
11788c9b05a6Sflorian build_packet(struct ra_iface *ra_iface)
117953293e44Sflorian {
118053293e44Sflorian 	struct nd_router_advert		*ra;
118130ca3407Sflorian 	struct nd_opt_source_link_addr	*ndopt_source_link_addr;
1182cea17583Sbket 	struct nd_opt_mtu		*ndopt_mtu;
118353293e44Sflorian 	struct nd_opt_prefix_info	*ndopt_pi;
118453293e44Sflorian 	struct ra_iface_conf		*ra_iface_conf;
118553293e44Sflorian 	struct ra_options_conf		*ra_options_conf;
118653293e44Sflorian 	struct ra_prefix_conf		*ra_prefix_conf;
11874c40b7e8Sflorian 	struct nd_opt_rdnss		*ndopt_rdnss;
11884c40b7e8Sflorian 	struct nd_opt_dnssl		*ndopt_dnssl;
11895207bb19Sflorian 	struct nd_opt_pref64		*ndopt_pref64;
11904c40b7e8Sflorian 	struct ra_rdnss_conf		*ra_rdnss;
11914c40b7e8Sflorian 	struct ra_dnssl_conf		*ra_dnssl;
11925207bb19Sflorian 	struct ra_pref64_conf		*pref64;
11934c40b7e8Sflorian 	size_t				 len, label_len;
119420fc6e8eSflorian 	time_t				 t;
119520fc6e8eSflorian 	uint32_t			 vltime, pltime;
11960c40990eSflorian 	uint8_t				*p, buf[RA_MAX_SIZE];
11974c40b7e8Sflorian 	char				*label_start, *label_end;
119853293e44Sflorian 
119953293e44Sflorian 	ra_iface_conf = find_ra_iface_conf(&frontend_conf->ra_iface_list,
120092f66a25Sreyk 	    ra_iface->conf_name);
120153293e44Sflorian 	ra_options_conf = &ra_iface_conf->ra_options;
120220fc6e8eSflorian 	t = time(NULL);
12030c40990eSflorian 	len = sizeof(*ra);
120430ca3407Sflorian 	if (ra_iface_conf->ra_options.source_link_addr)
120530ca3407Sflorian 		len += sizeof(*ndopt_source_link_addr);
1206cea17583Sbket 	if (ra_options_conf->mtu > 0)
1207cea17583Sbket 		len += sizeof(*ndopt_mtu);
12080c40990eSflorian 	len += sizeof(*ndopt_pi) * ra_iface->prefix_count;
12098815eebdSflorian 	if (ra_iface_conf->ra_options.rdnss_count > 0)
12108815eebdSflorian 		len += sizeof(*ndopt_rdnss) +
12118815eebdSflorian 		    ra_iface_conf->ra_options.rdnss_count *
12124c40b7e8Sflorian 		    sizeof(struct in6_addr);
12134c40b7e8Sflorian 
12148815eebdSflorian 	if (ra_iface_conf->ra_options.dnssl_len > 0)
12154c40b7e8Sflorian 		/* round up to 8 byte boundary */
12168815eebdSflorian 		len += sizeof(*ndopt_dnssl) +
12178815eebdSflorian 		    ((ra_iface_conf->ra_options.dnssl_len + 7) & ~7);
121853293e44Sflorian 
12195207bb19Sflorian 	SIMPLEQ_FOREACH(pref64, &ra_iface_conf->ra_options.ra_pref64_list,
12205207bb19Sflorian 	    entry)
12215207bb19Sflorian 		len += sizeof(struct nd_opt_pref64);
12225207bb19Sflorian 
12230c40990eSflorian 	if (len > sizeof(ra_iface->data))
122451a61992Spamela 		fatalx("%s: packet too big", __func__); /* XXX send multiple */
122553293e44Sflorian 
12260c40990eSflorian 	p = buf;
122753293e44Sflorian 
12280c40990eSflorian 	ra = (struct nd_router_advert *)p;
122953293e44Sflorian 
123053293e44Sflorian 	memset(ra, 0, sizeof(*ra));
123153293e44Sflorian 
123253293e44Sflorian 	ra->nd_ra_type = ND_ROUTER_ADVERT;
123353293e44Sflorian 	ra->nd_ra_curhoplimit = ra_options_conf->cur_hl;
123453293e44Sflorian 	if (ra_options_conf->m_flag)
123553293e44Sflorian 		ra->nd_ra_flags_reserved |= ND_RA_FLAG_MANAGED;
123653293e44Sflorian 	if (ra_options_conf->o_flag)
123753293e44Sflorian 		ra->nd_ra_flags_reserved |= ND_RA_FLAG_OTHER;
12380c40990eSflorian 	if (ra_iface->removed)
12390c40990eSflorian 		/* tell clients that we are no longer a default router */
12400c40990eSflorian 		ra->nd_ra_router_lifetime = 0;
12410c40990eSflorian 	else if (ra_options_conf->dfr) {
124253293e44Sflorian 		ra->nd_ra_router_lifetime =
124353293e44Sflorian 		    htons(ra_options_conf->router_lifetime);
1244639a58baSflorian 		/*
1245639a58baSflorian 		 * RFC 4191
1246639a58baSflorian 		 * If the Router Lifetime is zero, the preference value MUST be
1247639a58baSflorian 		 * set to (00) by the sender and MUST be ignored by the
1248639a58baSflorian 		 * receiver.
1249639a58baSflorian 		 */
1250639a58baSflorian 		if (ra_options_conf->router_lifetime > 0)
1251639a58baSflorian 			ra->nd_ra_flags_reserved |= ra_options_conf->rtpref;
12520c40990eSflorian 	}
125353293e44Sflorian 	ra->nd_ra_reachable = htonl(ra_options_conf->reachable_time);
125453293e44Sflorian 	ra->nd_ra_retransmit = htonl(ra_options_conf->retrans_timer);
12550c40990eSflorian 	p += sizeof(*ra);
125653293e44Sflorian 
125730ca3407Sflorian 	if (ra_iface_conf->ra_options.source_link_addr) {
125830ca3407Sflorian 		ndopt_source_link_addr = (struct nd_opt_source_link_addr *)p;
125930ca3407Sflorian 		ndopt_source_link_addr->nd_opt_source_link_addr_type =
126030ca3407Sflorian 		    ND_OPT_SOURCE_LINKADDR;
126130ca3407Sflorian 		ndopt_source_link_addr->nd_opt_source_link_addr_len = 1;
1262c358c7e3Sflorian 		memcpy(&ndopt_source_link_addr->nd_opt_source_link_addr_hw_addr,
1263c358c7e3Sflorian 		    &ra_iface->hw_addr, ETHER_ADDR_LEN);
126430ca3407Sflorian 		p += sizeof(*ndopt_source_link_addr);
126530ca3407Sflorian 	}
126630ca3407Sflorian 
1267cea17583Sbket 	if (ra_options_conf->mtu > 0) {
1268cea17583Sbket 		ndopt_mtu = (struct nd_opt_mtu *)p;
1269cea17583Sbket 		ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU;
1270cea17583Sbket 		ndopt_mtu->nd_opt_mtu_len = 1;
1271cea17583Sbket 		ndopt_mtu->nd_opt_mtu_reserved = 0;
1272cea17583Sbket 		ndopt_mtu->nd_opt_mtu_mtu = htonl(ra_options_conf->mtu);
1273cea17583Sbket 		p += sizeof(*ndopt_mtu);
1274cea17583Sbket 	}
1275cea17583Sbket 
127653293e44Sflorian 	SIMPLEQ_FOREACH(ra_prefix_conf, &ra_iface->prefixes, entry) {
12770c40990eSflorian 		ndopt_pi = (struct nd_opt_prefix_info *)p;
127853293e44Sflorian 		memset(ndopt_pi, 0, sizeof(*ndopt_pi));
127953293e44Sflorian 		ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
128053293e44Sflorian 		ndopt_pi->nd_opt_pi_len = 4;
128153293e44Sflorian 		ndopt_pi->nd_opt_pi_prefix_len = ra_prefix_conf->prefixlen;
128253293e44Sflorian 		if (ra_prefix_conf->lflag)
128353293e44Sflorian 			ndopt_pi->nd_opt_pi_flags_reserved |=
128453293e44Sflorian 			    ND_OPT_PI_FLAG_ONLINK;
128553293e44Sflorian 		if (ra_prefix_conf->aflag)
128653293e44Sflorian 			ndopt_pi->nd_opt_pi_flags_reserved |=
128753293e44Sflorian 			    ND_OPT_PI_FLAG_AUTO;
128820fc6e8eSflorian 
128920fc6e8eSflorian 		if (ra_prefix_conf->ltime_decaying & VLTIME_DECAYING)
129020fc6e8eSflorian 			vltime = ra_prefix_conf->vltime < t ? 0 :
129120fc6e8eSflorian 			    ra_prefix_conf->vltime - t;
129220fc6e8eSflorian 		else
129320fc6e8eSflorian 			vltime = ra_prefix_conf->vltime;
129420fc6e8eSflorian 		if (ra_prefix_conf->ltime_decaying & PLTIME_DECAYING)
129520fc6e8eSflorian 			pltime = ra_prefix_conf->pltime < t ? 0 :
129620fc6e8eSflorian 			    ra_prefix_conf->pltime - t;
129720fc6e8eSflorian 		else
129820fc6e8eSflorian 			pltime = ra_prefix_conf->pltime;
129920fc6e8eSflorian 
130020fc6e8eSflorian 		ndopt_pi->nd_opt_pi_valid_time = htonl(vltime);
130120fc6e8eSflorian 		ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime);
130253293e44Sflorian 		ndopt_pi->nd_opt_pi_prefix = ra_prefix_conf->prefix;
130353293e44Sflorian 
13040c40990eSflorian 		p += sizeof(*ndopt_pi);
13050c40990eSflorian 	}
13060c40990eSflorian 
13078815eebdSflorian 	if (ra_iface_conf->ra_options.rdnss_count > 0) {
13084c40b7e8Sflorian 		ndopt_rdnss = (struct nd_opt_rdnss *)p;
13094c40b7e8Sflorian 		ndopt_rdnss->nd_opt_rdnss_type = ND_OPT_RDNSS;
13104c40b7e8Sflorian 		ndopt_rdnss->nd_opt_rdnss_len = 1 +
13118815eebdSflorian 		    ra_iface_conf->ra_options.rdnss_count * 2;
13124c40b7e8Sflorian 		ndopt_rdnss->nd_opt_rdnss_reserved = 0;
13134c40b7e8Sflorian 		ndopt_rdnss->nd_opt_rdnss_lifetime =
13148815eebdSflorian 		    htonl(ra_iface_conf->ra_options.rdns_lifetime);
13154c40b7e8Sflorian 		p += sizeof(struct nd_opt_rdnss);
13168815eebdSflorian 		SIMPLEQ_FOREACH(ra_rdnss,
13178815eebdSflorian 		    &ra_iface_conf->ra_options.ra_rdnss_list, entry) {
13184c40b7e8Sflorian 			memcpy(p, &ra_rdnss->rdnss, sizeof(ra_rdnss->rdnss));
13194c40b7e8Sflorian 			p += sizeof(ra_rdnss->rdnss);
13204c40b7e8Sflorian 		}
13214c40b7e8Sflorian 	}
13224c40b7e8Sflorian 
13238815eebdSflorian 	if (ra_iface_conf->ra_options.dnssl_len > 0) {
13244c40b7e8Sflorian 		ndopt_dnssl = (struct nd_opt_dnssl *)p;
13254c40b7e8Sflorian 		ndopt_dnssl->nd_opt_dnssl_type = ND_OPT_DNSSL;
13264c40b7e8Sflorian 		/* round up to 8 byte boundary */
13274c40b7e8Sflorian 		ndopt_dnssl->nd_opt_dnssl_len = 1 +
13288815eebdSflorian 		    ((ra_iface_conf->ra_options.dnssl_len + 7) & ~7) / 8;
13294c40b7e8Sflorian 		ndopt_dnssl->nd_opt_dnssl_reserved = 0;
13304c40b7e8Sflorian 		ndopt_dnssl->nd_opt_dnssl_lifetime =
13318815eebdSflorian 		    htonl(ra_iface_conf->ra_options.rdns_lifetime);
13324c40b7e8Sflorian 		p += sizeof(struct nd_opt_dnssl);
13334c40b7e8Sflorian 
13348815eebdSflorian 		SIMPLEQ_FOREACH(ra_dnssl,
13358815eebdSflorian 		    &ra_iface_conf->ra_options.ra_dnssl_list, entry) {
13364c40b7e8Sflorian 			label_start = ra_dnssl->search;
13374c40b7e8Sflorian 			while ((label_end = strchr(label_start, '.')) != NULL) {
13384c40b7e8Sflorian 				label_len = label_end - label_start;
13394c40b7e8Sflorian 				*p++ = label_len;
13404c40b7e8Sflorian 				memcpy(p, label_start, label_len);
13414c40b7e8Sflorian 				p += label_len;
13424c40b7e8Sflorian 				label_start = label_end + 1;
13434c40b7e8Sflorian 			}
13444c40b7e8Sflorian 			*p++ = '\0'; /* last dot */
13454c40b7e8Sflorian 		}
13464c40b7e8Sflorian 		/* zero pad */
13474c40b7e8Sflorian 		while (((uintptr_t)p) % 8 != 0)
13484c40b7e8Sflorian 			*p++ = '\0';
13494c40b7e8Sflorian 	}
13504c40b7e8Sflorian 
13515207bb19Sflorian 	SIMPLEQ_FOREACH(pref64, &ra_iface_conf->ra_options.ra_pref64_list,
13525207bb19Sflorian 	    entry) {
13535207bb19Sflorian 		uint16_t	sltime_plc;
13545207bb19Sflorian 
13555207bb19Sflorian 		/* scaled lifetime in units of 8 seconds */
13565207bb19Sflorian 		sltime_plc = pref64->ltime / 8;
13575207bb19Sflorian 		sltime_plc = sltime_plc << 3;
13583a50f0a9Sjmc 		/* encode prefix length in lower 3 bits */
13595207bb19Sflorian 		switch (pref64->prefixlen) {
13605207bb19Sflorian 		case 96:
13615207bb19Sflorian 			sltime_plc |= 0;
13625207bb19Sflorian 			break;
13635207bb19Sflorian 		case 64:
13645207bb19Sflorian 			sltime_plc |= 1;
13655207bb19Sflorian 			break;
13665207bb19Sflorian 		case 56:
13675207bb19Sflorian 			sltime_plc |= 2;
13685207bb19Sflorian 			break;
13695207bb19Sflorian 		case 48:
13705207bb19Sflorian 			sltime_plc |= 3;
13715207bb19Sflorian 			break;
13725207bb19Sflorian 		case 40:
13735207bb19Sflorian 			sltime_plc |= 4;
13745207bb19Sflorian 			break;
13755207bb19Sflorian 		case 32:
13765207bb19Sflorian 			sltime_plc |= 5;
13775207bb19Sflorian 			break;
13785207bb19Sflorian 		default:
13795207bb19Sflorian 			fatalx("%s: invalid pref64 length: %d", __func__,
13805207bb19Sflorian 			    pref64->prefixlen);
13815207bb19Sflorian 		}
13825207bb19Sflorian 		ndopt_pref64 = (struct nd_opt_pref64 *)p;
13835207bb19Sflorian 		ndopt_pref64->nd_opt_pref64_type = ND_OPT_PREF64;
13845207bb19Sflorian 		ndopt_pref64->nd_opt_pref64_len = 2;
13855207bb19Sflorian 		ndopt_pref64->nd_opt_pref64_sltime_plc = htons(sltime_plc);
13865207bb19Sflorian 		memcpy(ndopt_pref64->nd_opt_pref64, &pref64->prefix,
13875207bb19Sflorian 		    sizeof(ndopt_pref64->nd_opt_pref64));
13885207bb19Sflorian 		p += sizeof(struct nd_opt_pref64);
13895207bb19Sflorian 	}
13905207bb19Sflorian 
13910c40990eSflorian 	if (len != ra_iface->datalen || memcmp(buf, ra_iface->data, len)
13920c40990eSflorian 	    != 0) {
13930c40990eSflorian 		memcpy(ra_iface->data, buf, len);
13940c40990eSflorian 		ra_iface->datalen = len;
139520fc6e8eSflorian 		return 1;
139653293e44Sflorian 	}
139720fc6e8eSflorian 	return 0;
139853293e44Sflorian }
139953293e44Sflorian 
140053293e44Sflorian void
14018c9b05a6Sflorian build_leaving_packet(struct ra_iface *ra_iface)
14024a78c7cfSflorian {
14034a78c7cfSflorian 	struct nd_router_advert		 ra;
14044a78c7cfSflorian 
14054a78c7cfSflorian 	memset(&ra, 0, sizeof(ra));
14064a78c7cfSflorian 
14074a78c7cfSflorian 	ra.nd_ra_type = ND_ROUTER_ADVERT;
14084a78c7cfSflorian 
14094a78c7cfSflorian 	memcpy(ra_iface->data, &ra, sizeof(ra));
14104a78c7cfSflorian 	ra_iface->datalen = sizeof(ra);
14114a78c7cfSflorian }
14124a78c7cfSflorian 
14134a78c7cfSflorian void
141453293e44Sflorian ra_output(struct ra_iface *ra_iface, struct sockaddr_in6 *to)
141553293e44Sflorian {
141653293e44Sflorian 
141753293e44Sflorian 	struct cmsghdr		*cm;
141853293e44Sflorian 	struct in6_pktinfo	*pi;
141953293e44Sflorian 	ssize_t			 len;
142053293e44Sflorian 	int			 hoplimit = 255;
142153293e44Sflorian 
1422abcf5f52Sflorian 	if (!LINK_STATE_IS_UP(ra_iface->link_state))
1423abcf5f52Sflorian 		return;
1424abcf5f52Sflorian 
142520fc6e8eSflorian 	if (ra_iface->ltime_decaying)
142620fc6e8eSflorian 		/* update vltime & pltime */
142720fc6e8eSflorian 		build_packet(ra_iface);
142820fc6e8eSflorian 
142953293e44Sflorian 	sndmhdr.msg_name = to;
143053293e44Sflorian 	sndmhdr.msg_iov[0].iov_base = ra_iface->data;
143153293e44Sflorian 	sndmhdr.msg_iov[0].iov_len = ra_iface->datalen;
143253293e44Sflorian 
143353293e44Sflorian 	cm = CMSG_FIRSTHDR(&sndmhdr);
143453293e44Sflorian 	/* specify the outgoing interface */
143553293e44Sflorian 	cm->cmsg_level = IPPROTO_IPV6;
143653293e44Sflorian 	cm->cmsg_type = IPV6_PKTINFO;
143753293e44Sflorian 	cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
143853293e44Sflorian 	pi = (struct in6_pktinfo *)CMSG_DATA(cm);
143953293e44Sflorian 	memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr));
144053293e44Sflorian 	pi->ipi6_ifindex = ra_iface->if_index;
144153293e44Sflorian 
144253293e44Sflorian 	/* specify the hop limit of the packet */
144353293e44Sflorian 	cm = CMSG_NXTHDR(&sndmhdr, cm);
144453293e44Sflorian 	cm->cmsg_level = IPPROTO_IPV6;
144553293e44Sflorian 	cm->cmsg_type = IPV6_HOPLIMIT;
144653293e44Sflorian 	cm->cmsg_len = CMSG_LEN(sizeof(int));
144753293e44Sflorian 	memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
144853293e44Sflorian 
144953293e44Sflorian 	log_debug("send RA on %s", ra_iface->name);
145053293e44Sflorian 
1451e88dba76Sflorian 	len = sendmsg(EVENT_FD(&ra_iface->icmp6ev->ev), &sndmhdr, 0);
1452df69c215Sderaadt 	if (len == -1)
145353293e44Sflorian 		log_warn("sendmsg on %s", ra_iface->name);
145453293e44Sflorian 
145553293e44Sflorian }
145648e174fdSflorian 
145748e174fdSflorian #define ROUNDUP(a) \
145848e174fdSflorian 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
145948e174fdSflorian 
146048e174fdSflorian void
146148e174fdSflorian get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
146248e174fdSflorian {
146348e174fdSflorian 	int	i;
146448e174fdSflorian 
146548e174fdSflorian 	for (i = 0; i < RTAX_MAX; i++) {
146648e174fdSflorian 		if (addrs & (1 << i)) {
146748e174fdSflorian 			rti_info[i] = sa;
146848e174fdSflorian 			sa = (struct sockaddr *)((char *)(sa) +
146948e174fdSflorian 			    ROUNDUP(sa->sa_len));
147048e174fdSflorian 		} else
147148e174fdSflorian 			rti_info[i] = NULL;
147248e174fdSflorian 	}
147348e174fdSflorian }
147448e174fdSflorian 
147548e174fdSflorian void
147648e174fdSflorian route_receive(int fd, short events, void *arg)
147748e174fdSflorian {
147848e174fdSflorian 	static uint8_t			 *buf;
147948e174fdSflorian 
148048e174fdSflorian 	struct rt_msghdr		*rtm;
148148e174fdSflorian 	struct sockaddr			*sa, *rti_info[RTAX_MAX];
148248e174fdSflorian 	ssize_t				 n;
148348e174fdSflorian 
148448e174fdSflorian 	if (buf == NULL) {
148548e174fdSflorian 		buf = malloc(ROUTE_SOCKET_BUF_SIZE);
148648e174fdSflorian 		if (buf == NULL)
148748e174fdSflorian 			fatal("malloc");
148848e174fdSflorian 	}
148948e174fdSflorian 	rtm = (struct rt_msghdr *)buf;
149048e174fdSflorian 	if ((n = read(fd, buf, ROUTE_SOCKET_BUF_SIZE)) == -1) {
149148e174fdSflorian 		if (errno == EAGAIN || errno == EINTR)
149248e174fdSflorian 			return;
149348e174fdSflorian 		log_warn("dispatch_rtmsg: read error");
149448e174fdSflorian 		return;
149548e174fdSflorian 	}
149648e174fdSflorian 
149748e174fdSflorian 	if (n == 0)
149848e174fdSflorian 		fatal("routing socket closed");
149948e174fdSflorian 
150048e174fdSflorian 	if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) {
150148e174fdSflorian 		log_warnx("partial rtm of %zd in buffer", n);
150248e174fdSflorian 		return;
150348e174fdSflorian 	}
150448e174fdSflorian 
150548e174fdSflorian 	if (rtm->rtm_version != RTM_VERSION)
150648e174fdSflorian 		return;
150748e174fdSflorian 
150848e174fdSflorian 	sa = (struct sockaddr *)(buf + rtm->rtm_hdrlen);
150948e174fdSflorian 	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
151048e174fdSflorian 
151148e174fdSflorian 	handle_route_message(rtm, rti_info);
151248e174fdSflorian }
151348e174fdSflorian 
151448e174fdSflorian void
151548e174fdSflorian handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info)
151648e174fdSflorian {
15172ccdd6f1Sflorian 	switch (rtm->rtm_type) {
15182ccdd6f1Sflorian 	case RTM_IFINFO:
15192ccdd6f1Sflorian 	case RTM_NEWADDR:
15202ccdd6f1Sflorian 	case RTM_DELADDR:
1521e88dba76Sflorian 	case RTM_CHGADDRATTR:
15222ccdd6f1Sflorian 		/*
15232ccdd6f1Sflorian 		 * do the same thing as after a config reload when interfaces
15242ccdd6f1Sflorian 		 * change or IPv6 addresses show up / disappear
15252ccdd6f1Sflorian 		 */
15262ccdd6f1Sflorian 		merge_ra_interfaces();
15272ccdd6f1Sflorian 		break;
15282ccdd6f1Sflorian 	default:
15292ccdd6f1Sflorian 		log_debug("unexpected RTM: %d", rtm->rtm_type);
15302ccdd6f1Sflorian 		break;
15312ccdd6f1Sflorian 	}
153248e174fdSflorian }
1533e88dba76Sflorian 
1534e88dba76Sflorian struct icmp6_ev*
1535e88dba76Sflorian get_icmp6ev_by_rdomain(int rdomain)
1536e88dba76Sflorian {
1537e88dba76Sflorian 	struct ra_iface	*ra_iface;
1538e88dba76Sflorian 	struct icmp6_ev	*icmp6ev = NULL;
1539e88dba76Sflorian 
1540e88dba76Sflorian 	TAILQ_FOREACH (ra_iface, &ra_interfaces, entry) {
1541e88dba76Sflorian 		if (ra_iface->rdomain == rdomain) {
1542e88dba76Sflorian 			icmp6ev = ra_iface->icmp6ev;
1543e88dba76Sflorian 			break;
1544e88dba76Sflorian 		}
1545e88dba76Sflorian 	}
1546e88dba76Sflorian 
1547e88dba76Sflorian 	if (icmp6ev == NULL) {
1548e88dba76Sflorian 		if ((icmp6ev = calloc(1, sizeof(*icmp6ev))) == NULL)
1549e88dba76Sflorian 			fatal("calloc");
1550e88dba76Sflorian 
1551e88dba76Sflorian 		icmp6ev->rcviov[0].iov_base = (caddr_t)icmp6ev->answer;
1552e88dba76Sflorian 		icmp6ev->rcviov[0].iov_len = sizeof(icmp6ev->answer);
1553e88dba76Sflorian 		icmp6ev->rcvmhdr.msg_name = (caddr_t)&icmp6ev->from;
1554e88dba76Sflorian 		icmp6ev->rcvmhdr.msg_namelen = sizeof(icmp6ev->from);
1555e88dba76Sflorian 		icmp6ev->rcvmhdr.msg_iov = icmp6ev->rcviov;
1556e88dba76Sflorian 		icmp6ev->rcvmhdr.msg_iovlen = 1;
1557e88dba76Sflorian 		icmp6ev->rcvmhdr.msg_controllen =
1558e88dba76Sflorian 		    CMSG_SPACE(sizeof(struct in6_pktinfo)) +
1559e88dba76Sflorian 		    CMSG_SPACE(sizeof(int));
1560e88dba76Sflorian 		if ((icmp6ev->rcvmhdr.msg_control = malloc(icmp6ev->
1561e88dba76Sflorian 		    rcvmhdr.msg_controllen)) == NULL)
1562e88dba76Sflorian 			fatal("malloc");
1563e88dba76Sflorian 		frontend_imsg_compose_main(IMSG_OPEN_ICMP6SOCK, 0,
1564e88dba76Sflorian 		    &rdomain, sizeof(rdomain));
1565e88dba76Sflorian 	}
1566e88dba76Sflorian 
1567e88dba76Sflorian 	icmp6ev->refcnt++;
1568e88dba76Sflorian 	return (icmp6ev);
1569e88dba76Sflorian }
1570e88dba76Sflorian 
1571e88dba76Sflorian void
1572e88dba76Sflorian unref_icmp6ev(struct ra_iface *ra_iface)
1573e88dba76Sflorian {
1574e88dba76Sflorian 	struct icmp6_ev *icmp6ev = ra_iface->icmp6ev;
1575e88dba76Sflorian 
1576e88dba76Sflorian 	ra_iface->icmp6ev = NULL;
1577e88dba76Sflorian 
1578e88dba76Sflorian 	if (icmp6ev != NULL) {
1579e88dba76Sflorian 		icmp6ev->refcnt--;
1580e88dba76Sflorian 		if (icmp6ev->refcnt == 0) {
1581e88dba76Sflorian 			event_del(&icmp6ev->ev);
1582e88dba76Sflorian 			close(EVENT_FD(&icmp6ev->ev));
1583e88dba76Sflorian 			free(icmp6ev);
1584e88dba76Sflorian 		}
1585e88dba76Sflorian 	}
1586e88dba76Sflorian }
1587e88dba76Sflorian 
1588e88dba76Sflorian void
1589e88dba76Sflorian set_icmp6sock(int icmp6sock, int rdomain)
1590e88dba76Sflorian {
1591e88dba76Sflorian 	struct ra_iface	*ra_iface;
1592e88dba76Sflorian 
1593e88dba76Sflorian 	TAILQ_FOREACH (ra_iface, &ra_interfaces, entry) {
1594e88dba76Sflorian 		if (!event_initialized(&ra_iface->icmp6ev->ev) &&
1595e88dba76Sflorian 		    ra_iface->rdomain == rdomain) {
1596e88dba76Sflorian 			event_set(&ra_iface->icmp6ev->ev, icmp6sock, EV_READ |
1597e88dba76Sflorian 			    EV_PERSIST, icmp6_receive, ra_iface->icmp6ev);
1598e88dba76Sflorian 			event_add(&ra_iface->icmp6ev->ev, NULL);
1599e88dba76Sflorian 			icmp6sock = -1;
1600e88dba76Sflorian 			break;
1601e88dba76Sflorian 		}
1602e88dba76Sflorian 	}
1603e88dba76Sflorian 
1604e88dba76Sflorian 	if (icmp6sock != -1) {
1605e88dba76Sflorian 		/*
1606e88dba76Sflorian 		 * The interface disappeared or changed rdomain while we were
1607e88dba76Sflorian 		 * waiting for the parent process to open the raw socket.
1608e88dba76Sflorian 		 */
1609e88dba76Sflorian 		close(icmp6sock);
1610e88dba76Sflorian 		return;
1611e88dba76Sflorian 	}
1612e88dba76Sflorian 
1613e88dba76Sflorian 	TAILQ_FOREACH (ra_iface, &ra_interfaces, entry) {
1614e88dba76Sflorian 		if (ra_iface->rdomain == rdomain) {
1615e88dba76Sflorian 			join_all_routers_mcast_group(ra_iface);
1616e88dba76Sflorian 			frontend_imsg_compose_engine(IMSG_UPDATE_IF, 0,
1617e88dba76Sflorian 			    &ra_iface->if_index, sizeof(ra_iface->if_index));
1618e88dba76Sflorian 		}
1619e88dba76Sflorian 	}
1620e88dba76Sflorian }
1621