xref: /openbsd-src/sbin/dhcp6leased/frontend.c (revision 0e59d0d19ca6a10a17663d531bcea1b99c1bfe09)
1*0e59d0d1Sclaudio /*	$OpenBSD: frontend.c,v 1.20 2024/11/21 13:35:20 claudio Exp $	*/
2ad7c548dSflorian 
3ad7c548dSflorian /*
4ad7c548dSflorian  * Copyright (c) 2017, 2021, 2024 Florian Obser <florian@openbsd.org>
5ad7c548dSflorian  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
6ad7c548dSflorian  * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
7ad7c548dSflorian  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
8ad7c548dSflorian  *
9ad7c548dSflorian  * Permission to use, copy, modify, and distribute this software for any
10ad7c548dSflorian  * purpose with or without fee is hereby granted, provided that the above
11ad7c548dSflorian  * copyright notice and this permission notice appear in all copies.
12ad7c548dSflorian  *
13ad7c548dSflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14ad7c548dSflorian  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15ad7c548dSflorian  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16ad7c548dSflorian  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17ad7c548dSflorian  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18ad7c548dSflorian  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19ad7c548dSflorian  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20ad7c548dSflorian  */
21ad7c548dSflorian #include <sys/types.h>
22ad7c548dSflorian #include <sys/ioctl.h>
23ad7c548dSflorian #include <sys/queue.h>
24ad7c548dSflorian #include <sys/socket.h>
25ad7c548dSflorian #include <sys/syslog.h>
26ad7c548dSflorian #include <sys/uio.h>
27ad7c548dSflorian #include <sys/utsname.h>
28ad7c548dSflorian 
29ad7c548dSflorian #include <net/if.h>
30ad7c548dSflorian #include <net/if_dl.h>
31ad7c548dSflorian #include <net/if_types.h>
32ad7c548dSflorian #include <net/route.h>
33ad7c548dSflorian 
34ad7c548dSflorian #include <netinet/in.h>
35ad7c548dSflorian #include <netinet/ip.h>
36ad7c548dSflorian 
37ad7c548dSflorian #include <arpa/inet.h>
38ad7c548dSflorian 
39ad7c548dSflorian #include <errno.h>
40ad7c548dSflorian #include <event.h>
41ad7c548dSflorian #include <ifaddrs.h>
42ad7c548dSflorian #include <imsg.h>
43ad7c548dSflorian #include <pwd.h>
44ad7c548dSflorian #include <signal.h>
45ad7c548dSflorian #include <stdio.h>
46ad7c548dSflorian #include <stdlib.h>
47ad7c548dSflorian #include <string.h>
48ad7c548dSflorian #include <unistd.h>
49ad7c548dSflorian 
50ad7c548dSflorian #include "log.h"
51ad7c548dSflorian #include "dhcp6leased.h"
52ad7c548dSflorian #include "frontend.h"
53ad7c548dSflorian #include "control.h"
54ad7c548dSflorian 
55ad7c548dSflorian #define	ALL_DHCP_RELAY_AGENTS_AND_SERVERS	"ff02::1:2"
56ad7c548dSflorian #define	ROUTE_SOCKET_BUF_SIZE			16384
57ad7c548dSflorian 
58ad7c548dSflorian struct iface {
59ad7c548dSflorian 	LIST_ENTRY(iface)	 entries;
60ad7c548dSflorian 	struct event		 udpev;
61ad7c548dSflorian 	struct imsg_ifinfo	 ifinfo;
62ad7c548dSflorian 	int			 send_solicit;
63ad7c548dSflorian 	int			 elapsed_time;
64ad7c548dSflorian 	uint8_t			 xid[XID_SIZE];
65ad7c548dSflorian 	int			 serverid_len;
66ad7c548dSflorian 	uint8_t			 serverid[SERVERID_SIZE];
67ad7c548dSflorian 	struct prefix		 pds[MAX_IA];
68ad7c548dSflorian };
69ad7c548dSflorian 
70ad7c548dSflorian __dead void	 frontend_shutdown(void);
71ad7c548dSflorian void		 frontend_sig_handler(int, short, void *);
72ad7c548dSflorian void		 frontend_startup(void);
73ad7c548dSflorian void		 update_iface(uint32_t);
74ad7c548dSflorian void		 route_receive(int, short, void *);
75ad7c548dSflorian void		 handle_route_message(struct rt_msghdr *, struct sockaddr **);
76ad7c548dSflorian void		 get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
77ad7c548dSflorian void		 udp_receive(int, short, void *);
78ad7c548dSflorian int		 get_flags(char *);
79ad7c548dSflorian struct iface	*get_iface_by_id(uint32_t);
80ad7c548dSflorian struct iface	*get_iface_by_name(const char *);
81ad7c548dSflorian void		 remove_iface(uint32_t);
82ad7c548dSflorian void		 set_udpsock(int, uint32_t);
83ad7c548dSflorian void		 iface_data_from_imsg(struct iface*, struct imsg_req_dhcp *);
84ad7c548dSflorian ssize_t		 build_packet(uint8_t, struct iface *, char *);
85ad7c548dSflorian void		 send_packet(uint8_t, struct iface *);
86ad7c548dSflorian int		 iface_conf_cmp(struct iface_conf *, struct iface_conf *);
87ad7c548dSflorian 
88ad7c548dSflorian LIST_HEAD(, iface)		 interfaces;
89ad7c548dSflorian struct dhcp6leased_conf		*frontend_conf;
90ad7c548dSflorian static struct imsgev		*iev_main;
91ad7c548dSflorian static struct imsgev		*iev_engine;
92ad7c548dSflorian struct event			 ev_route;
93f6870f0dSflorian struct sockaddr_in6		 dst;
94ad7c548dSflorian int				 ioctlsock;
95ad7c548dSflorian 
96ad7c548dSflorian uint8_t				 dhcp_packet[1500];
97ad7c548dSflorian static struct dhcp_duid		 duid;
98ad7c548dSflorian char				*vendor_class_data;
99ad7c548dSflorian int				 vendor_class_len;
100ad7c548dSflorian 
101ad7c548dSflorian void
102ad7c548dSflorian frontend_sig_handler(int sig, short event, void *bula)
103ad7c548dSflorian {
104ad7c548dSflorian 	/*
105ad7c548dSflorian 	 * Normal signal handler rules don't apply because libevent
106ad7c548dSflorian 	 * decouples for us.
107ad7c548dSflorian 	 */
108ad7c548dSflorian 
109ad7c548dSflorian 	switch (sig) {
110ad7c548dSflorian 	case SIGINT:
111ad7c548dSflorian 	case SIGTERM:
112ad7c548dSflorian 		frontend_shutdown();
113ad7c548dSflorian 	default:
114ad7c548dSflorian 		fatalx("unexpected signal");
115ad7c548dSflorian 	}
116ad7c548dSflorian }
117ad7c548dSflorian 
118ad7c548dSflorian void
119ad7c548dSflorian frontend(int debug, int verbose)
120ad7c548dSflorian {
121ad7c548dSflorian 	struct event		 ev_sigint, ev_sigterm;
122ad7c548dSflorian 	struct passwd		*pw;
123ad7c548dSflorian 	struct utsname		 utsname;
124ad7c548dSflorian 
125ad7c548dSflorian 	frontend_conf = config_new_empty();
126ad7c548dSflorian 
127ad7c548dSflorian 	log_init(debug, LOG_DAEMON);
128ad7c548dSflorian 	log_setverbose(verbose);
129ad7c548dSflorian 
130ad7c548dSflorian 	if ((pw = getpwnam(DHCP6LEASED_USER)) == NULL)
131ad7c548dSflorian 		fatal("getpwnam");
132ad7c548dSflorian 
133ad7c548dSflorian 	if (chdir("/") == -1)
134ad7c548dSflorian 		fatal("chdir(\"/\")");
135ad7c548dSflorian 
136ad7c548dSflorian 	if (unveil("/", "") == -1)
137ad7c548dSflorian 		fatal("unveil /");
138ad7c548dSflorian 	if (unveil(NULL, NULL) == -1)
139ad7c548dSflorian 		fatal("unveil");
140ad7c548dSflorian 
141ad7c548dSflorian 	setproctitle("%s", "frontend");
142ad7c548dSflorian 	log_procinit("frontend");
143ad7c548dSflorian 
144ad7c548dSflorian 	if ((ioctlsock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1)
145ad7c548dSflorian 		fatal("socket");
146ad7c548dSflorian 
147ad7c548dSflorian 	if (setgroups(1, &pw->pw_gid) ||
148ad7c548dSflorian 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
149ad7c548dSflorian 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
150ad7c548dSflorian 		fatal("can't drop privileges");
151ad7c548dSflorian 
152ad7c548dSflorian 	if (pledge("stdio unix recvfd route", NULL) == -1)
153ad7c548dSflorian 		fatal("pledge");
154ad7c548dSflorian 
155ad7c548dSflorian 	if (uname(&utsname) == -1)
156ad7c548dSflorian 		fatal("uname");
157ad7c548dSflorian 	vendor_class_len = asprintf(&vendor_class_data, "%s %s %s",
158ad7c548dSflorian 	    utsname.sysname, utsname.release, utsname.machine);
159ad7c548dSflorian 	if (vendor_class_len == -1)
160ad7c548dSflorian 		fatal("Cannot generate vendor-class-data");
161ad7c548dSflorian 
162f6870f0dSflorian 	memset(&dst, 0, sizeof(dst));
163f6870f0dSflorian 	dst.sin6_family = AF_INET6;
164f6870f0dSflorian 	if (inet_pton(AF_INET6, ALL_DHCP_RELAY_AGENTS_AND_SERVERS,
165f6870f0dSflorian 	    &dst.sin6_addr.s6_addr) != 1)
166f6870f0dSflorian 		fatal("inet_pton");
167f6870f0dSflorian 
168f6870f0dSflorian 	dst.sin6_port = ntohs(SERVER_PORT);
169f6870f0dSflorian 
170ad7c548dSflorian 	event_init();
171ad7c548dSflorian 
172ad7c548dSflorian 	/* Setup signal handler. */
173ad7c548dSflorian 	signal_set(&ev_sigint, SIGINT, frontend_sig_handler, NULL);
174ad7c548dSflorian 	signal_set(&ev_sigterm, SIGTERM, frontend_sig_handler, NULL);
175ad7c548dSflorian 	signal_add(&ev_sigint, NULL);
176ad7c548dSflorian 	signal_add(&ev_sigterm, NULL);
177ad7c548dSflorian 	signal(SIGPIPE, SIG_IGN);
178ad7c548dSflorian 	signal(SIGHUP, SIG_IGN);
179ad7c548dSflorian 
180ad7c548dSflorian 	/* Setup pipe and event handler to the parent process. */
181ad7c548dSflorian 	if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
182ad7c548dSflorian 		fatal(NULL);
183*0e59d0d1Sclaudio 	if (imsgbuf_init(&iev_main->ibuf, 3) == -1)
184*0e59d0d1Sclaudio 		fatal(NULL);
185*0e59d0d1Sclaudio 	imsgbuf_allow_fdpass(&iev_main->ibuf);
186ad7c548dSflorian 	iev_main->handler = frontend_dispatch_main;
187ad7c548dSflorian 	iev_main->events = EV_READ;
188ad7c548dSflorian 	event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
189ad7c548dSflorian 	    iev_main->handler, iev_main);
190ad7c548dSflorian 	event_add(&iev_main->ev, NULL);
191ad7c548dSflorian 
192ad7c548dSflorian 	LIST_INIT(&interfaces);
193ad7c548dSflorian 	event_dispatch();
194ad7c548dSflorian 
195ad7c548dSflorian 	frontend_shutdown();
196ad7c548dSflorian }
197ad7c548dSflorian 
198ad7c548dSflorian __dead void
199ad7c548dSflorian frontend_shutdown(void)
200ad7c548dSflorian {
201ad7c548dSflorian 	/* Close pipes. */
202dd7efffeSclaudio 	imsgbuf_write(&iev_engine->ibuf);
2039cbf9e90Sclaudio 	imsgbuf_clear(&iev_engine->ibuf);
204ad7c548dSflorian 	close(iev_engine->ibuf.fd);
205dd7efffeSclaudio 	imsgbuf_write(&iev_main->ibuf);
2069cbf9e90Sclaudio 	imsgbuf_clear(&iev_main->ibuf);
207ad7c548dSflorian 	close(iev_main->ibuf.fd);
208ad7c548dSflorian 
209ad7c548dSflorian 	config_clear(frontend_conf);
210ad7c548dSflorian 
211ad7c548dSflorian 	free(iev_engine);
212ad7c548dSflorian 	free(iev_main);
213ad7c548dSflorian 
214ad7c548dSflorian 	log_info("frontend exiting");
215ad7c548dSflorian 	exit(0);
216ad7c548dSflorian }
217ad7c548dSflorian 
218ad7c548dSflorian int
219ad7c548dSflorian frontend_imsg_compose_main(int type, pid_t pid, void *data,
220ad7c548dSflorian     uint16_t datalen)
221ad7c548dSflorian {
222ad7c548dSflorian 	return (imsg_compose_event(iev_main, type, 0, pid, -1, data,
223ad7c548dSflorian 	    datalen));
224ad7c548dSflorian }
225ad7c548dSflorian 
226ad7c548dSflorian int
227ad7c548dSflorian frontend_imsg_compose_engine(int type, uint32_t peerid, pid_t pid,
228ad7c548dSflorian     void *data, uint16_t datalen)
229ad7c548dSflorian {
230ad7c548dSflorian 	return (imsg_compose_event(iev_engine, type, peerid, pid, -1,
231ad7c548dSflorian 	    data, datalen));
232ad7c548dSflorian }
233ad7c548dSflorian 
234ad7c548dSflorian void
235ad7c548dSflorian frontend_dispatch_main(int fd, short event, void *bula)
236ad7c548dSflorian {
237ad7c548dSflorian 	static struct dhcp6leased_conf	*nconf;
238ad7c548dSflorian 	static struct iface_conf	*iface_conf;
239ad7c548dSflorian 	static struct iface_ia_conf	*iface_ia_conf;
240ad7c548dSflorian 	struct iface_pd_conf		*iface_pd_conf;
241ad7c548dSflorian 	struct imsg			 imsg;
242ad7c548dSflorian 	struct imsgev			*iev = bula;
243ad7c548dSflorian 	struct imsgbuf			*ibuf = &iev->ibuf;
244ad7c548dSflorian 	ssize_t				 n;
245ad7c548dSflorian 	int				 shut = 0, udpsock, if_index;
246ad7c548dSflorian 
247ad7c548dSflorian 	if (event & EV_READ) {
248668e5ba9Sclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
249dd7efffeSclaudio 			fatal("imsgbuf_read error");
250ad7c548dSflorian 		if (n == 0)	/* Connection closed. */
251ad7c548dSflorian 			shut = 1;
252ad7c548dSflorian 	}
253ad7c548dSflorian 	if (event & EV_WRITE) {
254dd7efffeSclaudio 		if (imsgbuf_write(ibuf) == -1) {
255e3b6409cSclaudio 			if (errno == EPIPE)	/* Connection closed. */
256ad7c548dSflorian 				shut = 1;
257e3b6409cSclaudio 			else
258dd7efffeSclaudio 				fatal("imsgbuf_write");
259e3b6409cSclaudio 		}
260ad7c548dSflorian 	}
261ad7c548dSflorian 
262ad7c548dSflorian 	for (;;) {
263ad7c548dSflorian 		if ((n = imsg_get(ibuf, &imsg)) == -1)
264ad7c548dSflorian 			fatal("%s: imsg_get error", __func__);
265ad7c548dSflorian 		if (n == 0)	/* No more messages. */
266ad7c548dSflorian 			break;
267ad7c548dSflorian 
268ad7c548dSflorian 		switch (imsg.hdr.type) {
269ad7c548dSflorian 		case IMSG_SOCKET_IPC:
270ad7c548dSflorian 			/*
271ad7c548dSflorian 			 * Setup pipe and event handler to the engine
272ad7c548dSflorian 			 * process.
273ad7c548dSflorian 			 */
274ad7c548dSflorian 			if (iev_engine)
275ad7c548dSflorian 				fatalx("%s: received unexpected imsg fd "
276ad7c548dSflorian 				    "to frontend", __func__);
277ad7c548dSflorian 
278ad7c548dSflorian 			if ((fd = imsg_get_fd(&imsg)) == -1)
279ad7c548dSflorian 				fatalx("%s: expected to receive imsg fd to "
280ad7c548dSflorian 				   "frontend but didn't receive any",
281ad7c548dSflorian 				   __func__);
282ad7c548dSflorian 
283ad7c548dSflorian 			iev_engine = malloc(sizeof(struct imsgev));
284ad7c548dSflorian 			if (iev_engine == NULL)
285ad7c548dSflorian 				fatal(NULL);
286ad7c548dSflorian 
287*0e59d0d1Sclaudio 			if (imsgbuf_init(&iev_engine->ibuf, fd) == -1)
288*0e59d0d1Sclaudio 				fatal(NULL);
289ad7c548dSflorian 			iev_engine->handler = frontend_dispatch_engine;
290ad7c548dSflorian 			iev_engine->events = EV_READ;
291ad7c548dSflorian 
292ad7c548dSflorian 			event_set(&iev_engine->ev, iev_engine->ibuf.fd,
293ad7c548dSflorian 			iev_engine->events, iev_engine->handler, iev_engine);
294ad7c548dSflorian 			event_add(&iev_engine->ev, NULL);
295ad7c548dSflorian 			break;
296ad7c548dSflorian 		case IMSG_UDPSOCK:
297ad7c548dSflorian 			if ((udpsock = imsg_get_fd(&imsg)) == -1)
298ad7c548dSflorian 				fatalx("%s: expected to receive imsg "
299ad7c548dSflorian 				    "udp fd but didn't receive any",
300ad7c548dSflorian 				    __func__);
301ad7c548dSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
302ad7c548dSflorian 				fatalx("%s: IMSG_UDPSOCK wrong length: "
303ad7c548dSflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
304ad7c548dSflorian 			memcpy(&if_index, imsg.data, sizeof(if_index));
305ad7c548dSflorian 			set_udpsock(udpsock, if_index);
306ad7c548dSflorian 			break;
307ad7c548dSflorian 		case IMSG_ROUTESOCK:
308ad7c548dSflorian 			if ((fd = imsg_get_fd(&imsg)) == -1)
309ad7c548dSflorian 				fatalx("%s: expected to receive imsg "
310ad7c548dSflorian 				    "routesocket fd but didn't receive any",
311ad7c548dSflorian 				    __func__);
312ad7c548dSflorian 			event_set(&ev_route, fd, EV_READ | EV_PERSIST,
313ad7c548dSflorian 			    route_receive, NULL);
314ad7c548dSflorian 			break;
315ad7c548dSflorian 		case IMSG_UUID:
316ad7c548dSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(duid.uuid))
317ad7c548dSflorian 				fatalx("%s: IMSG_UUID wrong length: "
318ad7c548dSflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
319ad7c548dSflorian 			duid.type = htons(DUID_UUID_TYPE);
320ad7c548dSflorian 			memcpy(duid.uuid, imsg.data, sizeof(duid.uuid));
321ad7c548dSflorian 			break;
322ad7c548dSflorian 		case IMSG_STARTUP:
323ad7c548dSflorian 			frontend_startup();
324ad7c548dSflorian 			break;
325ad7c548dSflorian 		case IMSG_RECONF_CONF:
326ad7c548dSflorian 			if (nconf != NULL)
327ad7c548dSflorian 				fatalx("%s: IMSG_RECONF_CONF already in "
328ad7c548dSflorian 				    "progress", __func__);
3293ae4b9dfSflorian 			if (IMSG_DATA_SIZE(imsg) !=
3303ae4b9dfSflorian 			    sizeof(struct dhcp6leased_conf))
3313ae4b9dfSflorian 				fatalx("%s: IMSG_RECONF_CONF wrong length: %lu",
3323ae4b9dfSflorian 				    __func__, IMSG_DATA_SIZE(imsg));
333ad7c548dSflorian 			if ((nconf = malloc(sizeof(struct dhcp6leased_conf))) ==
334ad7c548dSflorian 			    NULL)
335ad7c548dSflorian 				fatal(NULL);
3363ae4b9dfSflorian 			memcpy(nconf, imsg.data,
3373ae4b9dfSflorian 			    sizeof(struct dhcp6leased_conf));
338ad7c548dSflorian 			SIMPLEQ_INIT(&nconf->iface_list);
339ad7c548dSflorian 			break;
340ad7c548dSflorian 		case IMSG_RECONF_IFACE:
341ad7c548dSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
342ad7c548dSflorian 			    iface_conf))
343ad7c548dSflorian 				fatalx("%s: IMSG_RECONF_IFACE wrong length: "
344ad7c548dSflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
345ad7c548dSflorian 			if ((iface_conf = malloc(sizeof(struct iface_conf)))
346ad7c548dSflorian 			    == NULL)
347ad7c548dSflorian 				fatal(NULL);
348ad7c548dSflorian 			memcpy(iface_conf, imsg.data, sizeof(struct
349ad7c548dSflorian 			    iface_conf));
350ad7c548dSflorian 			SIMPLEQ_INIT(&iface_conf->iface_ia_list);
351ad7c548dSflorian 			SIMPLEQ_INSERT_TAIL(&nconf->iface_list,
352ad7c548dSflorian 			    iface_conf, entry);
353ad7c548dSflorian 			iface_conf->ia_count = 0;
354ad7c548dSflorian 			break;
355ad7c548dSflorian 		case IMSG_RECONF_IFACE_IA:
356ad7c548dSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
357ad7c548dSflorian 			    iface_ia_conf))
358ad7c548dSflorian 				fatalx("%s: IMSG_RECONF_IFACE_IA wrong "
359ad7c548dSflorian 				    "length: %lu", __func__,
360ad7c548dSflorian 				    IMSG_DATA_SIZE(imsg));
361ad7c548dSflorian 			if ((iface_ia_conf =
362ad7c548dSflorian 			    malloc(sizeof(struct iface_ia_conf))) == NULL)
363ad7c548dSflorian 				fatal(NULL);
364ad7c548dSflorian 			memcpy(iface_ia_conf, imsg.data, sizeof(struct
365ad7c548dSflorian 			    iface_ia_conf));
366ad7c548dSflorian 			SIMPLEQ_INIT(&iface_ia_conf->iface_pd_list);
367ad7c548dSflorian 			SIMPLEQ_INSERT_TAIL(&iface_conf->iface_ia_list,
368ad7c548dSflorian 			    iface_ia_conf, entry);
36992119d76Sflorian 			iface_ia_conf->id = iface_conf->ia_count++;
370ad7c548dSflorian 			if (iface_conf->ia_count > MAX_IA)
371ad7c548dSflorian 				fatalx("Too many prefix delegation requests.");
372ad7c548dSflorian 			break;
373ad7c548dSflorian 		case IMSG_RECONF_IFACE_PD:
374ad7c548dSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
375ad7c548dSflorian 			    iface_pd_conf))
376ad7c548dSflorian 				fatalx("%s: IMSG_RECONF_IFACE_PD wrong length: "
377ad7c548dSflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
378ad7c548dSflorian 			if ((iface_pd_conf =
379ad7c548dSflorian 			    malloc(sizeof(struct iface_pd_conf))) == NULL)
380ad7c548dSflorian 				fatal(NULL);
381ad7c548dSflorian 			memcpy(iface_pd_conf, imsg.data, sizeof(struct
382ad7c548dSflorian 			    iface_pd_conf));
383ad7c548dSflorian 			SIMPLEQ_INSERT_TAIL(&iface_ia_conf->iface_pd_list,
384ad7c548dSflorian 			    iface_pd_conf, entry);
385ad7c548dSflorian 			break;
386ad7c548dSflorian 		case IMSG_RECONF_IFACE_IA_END:
387ad7c548dSflorian 			iface_ia_conf = NULL;
388ad7c548dSflorian 			break;
389ad7c548dSflorian 		case IMSG_RECONF_IFACE_END:
390ad7c548dSflorian 			iface_conf = NULL;
391ad7c548dSflorian 			break;
392ad7c548dSflorian 		case IMSG_RECONF_END: {
393ad7c548dSflorian 			int	 i;
394ad7c548dSflorian 			int	*ifaces;
395ad7c548dSflorian 			char	 ifnamebuf[IF_NAMESIZE], *if_name;
396ad7c548dSflorian 
397ad7c548dSflorian 			if (nconf == NULL)
398ad7c548dSflorian 				fatalx("%s: IMSG_RECONF_END without "
399ad7c548dSflorian 				    "IMSG_RECONF_CONF", __func__);
400ad7c548dSflorian 
401ad7c548dSflorian 			ifaces = changed_ifaces(frontend_conf, nconf);
402ad7c548dSflorian 			merge_config(frontend_conf, nconf);
403ad7c548dSflorian 			nconf = NULL;
404ad7c548dSflorian 			for (i = 0; ifaces[i] != 0; i++) {
405ad7c548dSflorian 				if_index = ifaces[i];
406ad7c548dSflorian 				if_name = if_indextoname(if_index, ifnamebuf);
407ad7c548dSflorian 				log_debug("changed iface: %s[%d]", if_name !=
408ad7c548dSflorian 				    NULL ? if_name : "<unknown>", if_index);
409ad7c548dSflorian 				update_iface(if_index);
410ad7c548dSflorian 				frontend_imsg_compose_engine(
411ad7c548dSflorian 				    IMSG_REQUEST_REBOOT, 0, 0, &if_index,
412ad7c548dSflorian 				    sizeof(if_index));
413ad7c548dSflorian 			}
414ad7c548dSflorian 			free(ifaces);
415ad7c548dSflorian 			break;
416ad7c548dSflorian 		}
417ad7c548dSflorian 		case IMSG_CONTROLFD:
418ad7c548dSflorian 			if ((fd = imsg_get_fd(&imsg)) == -1)
419ad7c548dSflorian 				fatalx("%s: expected to receive imsg "
420ad7c548dSflorian 				    "control fd but didn't receive any",
421ad7c548dSflorian 				    __func__);
422ad7c548dSflorian 			/* Listen on control socket. */
423ad7c548dSflorian 			control_listen(fd);
424ad7c548dSflorian 			break;
425ad7c548dSflorian 		case IMSG_CTL_END:
426ad7c548dSflorian 			control_imsg_relay(&imsg);
427ad7c548dSflorian 			break;
428ad7c548dSflorian 		default:
429ad7c548dSflorian 			log_debug("%s: error handling imsg %d", __func__,
430ad7c548dSflorian 			    imsg.hdr.type);
431ad7c548dSflorian 			break;
432ad7c548dSflorian 		}
433ad7c548dSflorian 		imsg_free(&imsg);
434ad7c548dSflorian 	}
435ad7c548dSflorian 	if (!shut)
436ad7c548dSflorian 		imsg_event_add(iev);
437ad7c548dSflorian 	else {
438ad7c548dSflorian 		/* This pipe is dead. Remove its event handler. */
439ad7c548dSflorian 		event_del(&iev->ev);
440ad7c548dSflorian 		event_loopexit(NULL);
441ad7c548dSflorian 	}
442ad7c548dSflorian }
443ad7c548dSflorian 
444ad7c548dSflorian void
445ad7c548dSflorian frontend_dispatch_engine(int fd, short event, void *bula)
446ad7c548dSflorian {
447ad7c548dSflorian 	struct imsgev		*iev = bula;
448ad7c548dSflorian 	struct imsgbuf		*ibuf = &iev->ibuf;
449ad7c548dSflorian 	struct imsg		 imsg;
450ad7c548dSflorian 	struct iface		*iface;
451ad7c548dSflorian 	ssize_t			 n;
452ad7c548dSflorian 	int			 shut = 0;
453ad7c548dSflorian 
454ad7c548dSflorian 	if (event & EV_READ) {
455668e5ba9Sclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
456dd7efffeSclaudio 			fatal("imsgbuf_read error");
457ad7c548dSflorian 		if (n == 0)	/* Connection closed. */
458ad7c548dSflorian 			shut = 1;
459ad7c548dSflorian 	}
460ad7c548dSflorian 	if (event & EV_WRITE) {
461dd7efffeSclaudio 		if (imsgbuf_write(ibuf) == -1) {
462e3b6409cSclaudio 			if (errno == EPIPE)	/* Connection closed. */
463ad7c548dSflorian 				shut = 1;
464e3b6409cSclaudio 			else
465dd7efffeSclaudio 				fatal("imsgbuf_write");
466e3b6409cSclaudio 		}
467ad7c548dSflorian 	}
468ad7c548dSflorian 
469ad7c548dSflorian 	for (;;) {
470ad7c548dSflorian 		if ((n = imsg_get(ibuf, &imsg)) == -1)
471ad7c548dSflorian 			fatal("%s: imsg_get error", __func__);
472ad7c548dSflorian 		if (n == 0)	/* No more messages. */
473ad7c548dSflorian 			break;
474ad7c548dSflorian 
475ad7c548dSflorian 		switch (imsg.hdr.type) {
476ad7c548dSflorian 		case IMSG_CTL_END:
477ad7c548dSflorian 		case IMSG_CTL_SHOW_INTERFACE_INFO:
478ad7c548dSflorian 			control_imsg_relay(&imsg);
479ad7c548dSflorian 			break;
4801c8c2e64Sflorian 		case IMSG_SEND_SOLICIT:
4811c8c2e64Sflorian 		case IMSG_SEND_REQUEST:
4821c8c2e64Sflorian 		case IMSG_SEND_RENEW:
4831c8c2e64Sflorian 		case IMSG_SEND_REBIND: {
484ad7c548dSflorian 			struct imsg_req_dhcp	 imsg_req_dhcp;
485ad7c548dSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_dhcp))
486ad7c548dSflorian 				fatalx("%s: IMSG_SEND_DISCOVER wrong "
487ad7c548dSflorian 				    "length: %lu", __func__,
488ad7c548dSflorian 				    IMSG_DATA_SIZE(imsg));
489ad7c548dSflorian 			memcpy(&imsg_req_dhcp, imsg.data,
490ad7c548dSflorian 			    sizeof(imsg_req_dhcp));
491ad7c548dSflorian 
492ad7c548dSflorian 			iface = get_iface_by_id(imsg_req_dhcp.if_index);
493ad7c548dSflorian 
494ad7c548dSflorian 			if (iface == NULL)
495ad7c548dSflorian 				break;
496ad7c548dSflorian 
497ad7c548dSflorian 			iface_data_from_imsg(iface, &imsg_req_dhcp);
4981c8c2e64Sflorian 			switch (imsg.hdr.type) {
4991c8c2e64Sflorian 			case IMSG_SEND_SOLICIT:
500ad7c548dSflorian 				send_packet(DHCPSOLICIT, iface);
501ad7c548dSflorian 				break;
5021c8c2e64Sflorian 			case IMSG_SEND_REQUEST:
503ad7c548dSflorian 				send_packet(DHCPREQUEST, iface);
504ad7c548dSflorian 				break;
5051c8c2e64Sflorian 			case IMSG_SEND_RENEW:
5061c8c2e64Sflorian 				send_packet(DHCPRENEW, iface);
5071c8c2e64Sflorian 				break;
5081c8c2e64Sflorian 			case IMSG_SEND_REBIND:
5091c8c2e64Sflorian 				send_packet(DHCPREBIND, iface);
5101c8c2e64Sflorian 				break;
5111c8c2e64Sflorian 			}
5121c8c2e64Sflorian 			break;
513ad7c548dSflorian 		}
514ad7c548dSflorian 		default:
515ad7c548dSflorian 			log_debug("%s: error handling imsg %d", __func__,
516ad7c548dSflorian 			    imsg.hdr.type);
517ad7c548dSflorian 			break;
518ad7c548dSflorian 		}
519ad7c548dSflorian 		imsg_free(&imsg);
520ad7c548dSflorian 	}
521ad7c548dSflorian 	if (!shut)
522ad7c548dSflorian 		imsg_event_add(iev);
523ad7c548dSflorian 	else {
524ad7c548dSflorian 		/* This pipe is dead. Remove its event handler. */
525ad7c548dSflorian 		event_del(&iev->ev);
526ad7c548dSflorian 		event_loopexit(NULL);
527ad7c548dSflorian 	}
528ad7c548dSflorian }
529ad7c548dSflorian 
530ad7c548dSflorian int
531ad7c548dSflorian get_flags(char *if_name)
532ad7c548dSflorian {
533ad7c548dSflorian 	struct ifreq		 ifr;
534ad7c548dSflorian 
535ad7c548dSflorian 	strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
536ad7c548dSflorian 	if (ioctl(ioctlsock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) {
537ad7c548dSflorian 		log_warn("SIOCGIFFLAGS");
538ad7c548dSflorian 		return -1;
539ad7c548dSflorian 	}
540ad7c548dSflorian 	return ifr.ifr_flags;
541ad7c548dSflorian }
542ad7c548dSflorian 
543ad7c548dSflorian void
544ad7c548dSflorian update_iface(uint32_t if_index)
545ad7c548dSflorian {
546ad7c548dSflorian 	struct ifaddrs		*ifap, *ifa;
547ad7c548dSflorian 	struct iface		*iface;
548ad7c548dSflorian 	struct imsg_ifinfo	 ifinfo;
549ad7c548dSflorian 	int			 flags;
550ad7c548dSflorian 	char			 ifnamebuf[IF_NAMESIZE], *if_name;
551ad7c548dSflorian 
552ad7c548dSflorian 	if (getifaddrs(&ifap) != 0)
553ad7c548dSflorian 		fatal("getifaddrs");
554ad7c548dSflorian 
555ad7c548dSflorian 	if ((if_name = if_indextoname(if_index, ifnamebuf)) == NULL)
556ad7c548dSflorian 		return;
557ad7c548dSflorian 
558ad7c548dSflorian 	if ((flags = get_flags(if_name)) == -1)
559ad7c548dSflorian 		return;
560ad7c548dSflorian 
5617520e8faSflorian 	if (find_iface_conf(&frontend_conf->iface_list, if_name) == NULL)
5627520e8faSflorian 		return;
5637520e8faSflorian 
564ad7c548dSflorian 	memset(&ifinfo, 0, sizeof(ifinfo));
565ad7c548dSflorian 	ifinfo.if_index = if_index;
566ad7c548dSflorian 	ifinfo.link_state = -1;
567ad7c548dSflorian 	ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) ==
568ad7c548dSflorian 	    (IFF_UP | IFF_RUNNING);
569ad7c548dSflorian 
570ad7c548dSflorian 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
571ad7c548dSflorian 		if (strcmp(if_name, ifa->ifa_name) != 0)
572ad7c548dSflorian 			continue;
573ad7c548dSflorian 		if (ifa->ifa_addr == NULL)
574ad7c548dSflorian 			continue;
575ad7c548dSflorian 
576ad7c548dSflorian 		switch (ifa->ifa_addr->sa_family) {
577ad7c548dSflorian 		case AF_LINK: {
578ad7c548dSflorian 			struct if_data		*if_data;
579ad7c548dSflorian 
580ad7c548dSflorian 			if_data = (struct if_data *)ifa->ifa_data;
581ad7c548dSflorian 			ifinfo.link_state = if_data->ifi_link_state;
582ad7c548dSflorian 			ifinfo.rdomain = if_data->ifi_rdomain;
583ad7c548dSflorian 			goto out;
584ad7c548dSflorian 		}
585ad7c548dSflorian 		default:
586ad7c548dSflorian 			break;
587ad7c548dSflorian 		}
588ad7c548dSflorian 	}
589ad7c548dSflorian  out:
590c266488bSflorian 	freeifaddrs(ifap);
591ad7c548dSflorian 	iface = get_iface_by_id(if_index);
592ad7c548dSflorian 	if (iface == NULL) {
593ad7c548dSflorian 		if ((iface = calloc(1, sizeof(*iface))) == NULL)
594ad7c548dSflorian 			fatal("calloc");
595ad7c548dSflorian 		memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo));
596ad7c548dSflorian 		LIST_INSERT_HEAD(&interfaces, iface, entries);
597ad7c548dSflorian 		frontend_imsg_compose_main(IMSG_OPEN_UDPSOCK, 0,
598ad7c548dSflorian 		    &if_index, sizeof(if_index));
599ad7c548dSflorian 	} else
600ad7c548dSflorian 		/* XXX check rdomain changed ?*/
601ad7c548dSflorian 		memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo));
602ad7c548dSflorian 
603ad7c548dSflorian 	frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &iface->ifinfo,
604ad7c548dSflorian 	    sizeof(iface->ifinfo));
605ad7c548dSflorian }
606ad7c548dSflorian 
607ad7c548dSflorian void
608ad7c548dSflorian frontend_startup(void)
609ad7c548dSflorian {
610ad7c548dSflorian 	if (!event_initialized(&ev_route))
611ad7c548dSflorian 		fatalx("%s: did not receive a route socket from the main "
612ad7c548dSflorian 		    "process", __func__);
613ad7c548dSflorian 
614ad7c548dSflorian 	event_add(&ev_route, NULL);
615ad7c548dSflorian }
616ad7c548dSflorian 
617ad7c548dSflorian void
618ad7c548dSflorian route_receive(int fd, short events, void *arg)
619ad7c548dSflorian {
620ad7c548dSflorian 	static uint8_t			 *buf;
621ad7c548dSflorian 
622ad7c548dSflorian 	struct rt_msghdr		*rtm;
623ad7c548dSflorian 	struct sockaddr			*sa, *rti_info[RTAX_MAX];
624ad7c548dSflorian 	ssize_t				 n;
625ad7c548dSflorian 
626ad7c548dSflorian 	if (buf == NULL) {
627ad7c548dSflorian 		buf = malloc(ROUTE_SOCKET_BUF_SIZE);
628ad7c548dSflorian 		if (buf == NULL)
629ad7c548dSflorian 			fatal("malloc");
630ad7c548dSflorian 	}
631ad7c548dSflorian 	rtm = (struct rt_msghdr *)buf;
632ad7c548dSflorian 	if ((n = read(fd, buf, ROUTE_SOCKET_BUF_SIZE)) == -1) {
633ad7c548dSflorian 		if (errno == EAGAIN || errno == EINTR)
634ad7c548dSflorian 			return;
635ad7c548dSflorian 		log_warn("dispatch_rtmsg: read error");
636ad7c548dSflorian 		return;
637ad7c548dSflorian 	}
638ad7c548dSflorian 
639ad7c548dSflorian 	if (n == 0)
640ad7c548dSflorian 		fatal("routing socket closed");
641ad7c548dSflorian 
642ad7c548dSflorian 	if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) {
643ad7c548dSflorian 		log_warnx("partial rtm of %zd in buffer", n);
644ad7c548dSflorian 		return;
645ad7c548dSflorian 	}
646ad7c548dSflorian 
647ad7c548dSflorian 	if (rtm->rtm_version != RTM_VERSION)
648ad7c548dSflorian 		return;
649ad7c548dSflorian 
650ad7c548dSflorian 	sa = (struct sockaddr *)(buf + rtm->rtm_hdrlen);
651ad7c548dSflorian 	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
652ad7c548dSflorian 
653ad7c548dSflorian 	handle_route_message(rtm, rti_info);
654ad7c548dSflorian }
655ad7c548dSflorian 
656ad7c548dSflorian void
657ad7c548dSflorian handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info)
658ad7c548dSflorian {
659ad7c548dSflorian 	struct if_announcemsghdr	*ifan;
660ad7c548dSflorian 	uint32_t			 if_index;
661ad7c548dSflorian 
662ad7c548dSflorian 	switch (rtm->rtm_type) {
663ad7c548dSflorian 	case RTM_IFINFO:
6646912c931Sflorian 		if_index = ((struct if_msghdr *)rtm)->ifm_index;
6656912c931Sflorian 		update_iface(if_index);
666ad7c548dSflorian 		break;
667ad7c548dSflorian 	case RTM_IFANNOUNCE:
668ad7c548dSflorian 		ifan = (struct if_announcemsghdr *)rtm;
669ad7c548dSflorian 		if_index = ifan->ifan_index;
670ad7c548dSflorian 		if (ifan->ifan_what == IFAN_DEPARTURE) {
671ad7c548dSflorian 			frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
672ad7c548dSflorian 			    &if_index, sizeof(if_index));
673ad7c548dSflorian 			remove_iface(if_index);
674ad7c548dSflorian 		}
675ad7c548dSflorian 		break;
676ad7c548dSflorian 	default:
677ad7c548dSflorian 		log_debug("unexpected RTM: %d", rtm->rtm_type);
678ad7c548dSflorian 		break;
679ad7c548dSflorian 	}
680ad7c548dSflorian }
681ad7c548dSflorian 
682ad7c548dSflorian #define ROUNDUP(a) \
683ad7c548dSflorian 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
684ad7c548dSflorian 
685ad7c548dSflorian void
686ad7c548dSflorian get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
687ad7c548dSflorian {
688ad7c548dSflorian 	int	i;
689ad7c548dSflorian 
690ad7c548dSflorian 	for (i = 0; i < RTAX_MAX; i++) {
691ad7c548dSflorian 		if (addrs & (1 << i)) {
692ad7c548dSflorian 			rti_info[i] = sa;
693ad7c548dSflorian 			sa = (struct sockaddr *)((char *)(sa) +
694ad7c548dSflorian 			    ROUNDUP(sa->sa_len));
695ad7c548dSflorian 		} else
696ad7c548dSflorian 			rti_info[i] = NULL;
697ad7c548dSflorian 	}
698ad7c548dSflorian }
699ad7c548dSflorian 
700ad7c548dSflorian void
701ad7c548dSflorian udp_receive(int fd, short events, void *arg)
702ad7c548dSflorian {
703ad7c548dSflorian 	struct imsg_dhcp	 imsg_dhcp;
704ad7c548dSflorian 	struct iface		*iface;
705ad7c548dSflorian 	ssize_t			 len;
706ad7c548dSflorian 
707ad7c548dSflorian 	iface = (struct iface *)arg;
708ad7c548dSflorian 	memset(&imsg_dhcp, 0, sizeof(imsg_dhcp));
709ad7c548dSflorian 
710ad7c548dSflorian 	if ((len = read(fd, imsg_dhcp.packet, 1500)) == -1) {
711ad7c548dSflorian 		log_warn("%s: read", __func__);
712ad7c548dSflorian 		return;
713ad7c548dSflorian 	}
714ad7c548dSflorian 
715ad7c548dSflorian 	if (len == 0)
716ad7c548dSflorian 		fatal("%s len == 0", __func__);
717ad7c548dSflorian 
718ad7c548dSflorian 	imsg_dhcp.if_index = iface->ifinfo.if_index;
719ad7c548dSflorian 	imsg_dhcp.len = len;
720ad7c548dSflorian 	frontend_imsg_compose_engine(IMSG_DHCP, 0, 0, &imsg_dhcp,
721ad7c548dSflorian 	    sizeof(imsg_dhcp));
722ad7c548dSflorian }
723ad7c548dSflorian 
724ad7c548dSflorian void
725ad7c548dSflorian iface_data_from_imsg(struct iface* iface, struct imsg_req_dhcp *imsg)
726ad7c548dSflorian {
727ad7c548dSflorian 	memcpy(iface->xid, imsg->xid, sizeof(iface->xid));
728ad7c548dSflorian 	iface->elapsed_time = imsg->elapsed_time;
729ad7c548dSflorian 	iface->serverid_len = imsg->serverid_len;
730ad7c548dSflorian 	memcpy(iface->serverid, imsg->serverid, SERVERID_SIZE);
731ad7c548dSflorian 	memcpy(iface->pds, imsg->pds, sizeof(iface->pds));
732ad7c548dSflorian }
733ad7c548dSflorian 
734ad7c548dSflorian ssize_t
735ad7c548dSflorian build_packet(uint8_t message_type, struct iface *iface, char *if_name)
736ad7c548dSflorian {
737ad7c548dSflorian 	struct iface_conf		*iface_conf;
738ad7c548dSflorian 	struct iface_ia_conf		*ia_conf;
739ad7c548dSflorian 	struct dhcp_hdr			 hdr;
740ad7c548dSflorian 	struct dhcp_option_hdr		 opt_hdr;
741ad7c548dSflorian 	struct dhcp_iapd		 iapd;
742ad7c548dSflorian 	struct dhcp_iaprefix		 iaprefix;
743ad7c548dSflorian 	struct dhcp_vendor_class	 vendor_class;
7441788a962Sflorian 	size_t				 i;
745ad7c548dSflorian 	ssize_t				 len;
746ad7c548dSflorian 	uint16_t			 request_option_code, elapsed_time;
7471788a962Sflorian 	const uint16_t			 options[] = {DHO_SOL_MAX_RT,
7481788a962Sflorian 					     DHO_INF_MAX_RT};
749ad7c548dSflorian 	uint8_t				*p;
750ad7c548dSflorian 
751ad7c548dSflorian 	switch (message_type) {
752ad7c548dSflorian 	case DHCPSOLICIT:
753ad7c548dSflorian 	case DHCPREQUEST:
7541c8c2e64Sflorian 	case DHCPRENEW:
7551c8c2e64Sflorian 	case DHCPREBIND:
756ad7c548dSflorian 		break;
757ad7c548dSflorian 	default:
758ad7c548dSflorian 		fatalx("%s: %s not implemented", __func__,
759ad7c548dSflorian 		    dhcp_message_type2str(message_type));
760ad7c548dSflorian 	}
761ad7c548dSflorian 
762ad7c548dSflorian 	iface_conf = find_iface_conf(&frontend_conf->iface_list, if_name);
763ad7c548dSflorian 
764ad7c548dSflorian 	memset(dhcp_packet, 0, sizeof(dhcp_packet));
765ad7c548dSflorian 
766ad7c548dSflorian 	p = dhcp_packet;
767ad7c548dSflorian 	hdr.msg_type = message_type;
768ad7c548dSflorian 	memcpy(hdr.xid, iface->xid, sizeof(hdr.xid));
769ad7c548dSflorian 	memcpy(p, &hdr, sizeof(struct dhcp_hdr));
770ad7c548dSflorian 	p += sizeof(struct dhcp_hdr);
771ad7c548dSflorian 
772ad7c548dSflorian 	opt_hdr.code = htons(DHO_CLIENTID);
773ad7c548dSflorian 	opt_hdr.len = htons(sizeof(struct dhcp_duid));
774ad7c548dSflorian 	memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr));
775ad7c548dSflorian 	p += sizeof(struct dhcp_option_hdr);
776ad7c548dSflorian 	memcpy(p, &duid, sizeof(struct dhcp_duid));
777ad7c548dSflorian 	p += sizeof(struct dhcp_duid);
778ad7c548dSflorian 
7791c8c2e64Sflorian 	switch (message_type) {
7801c8c2e64Sflorian 	case DHCPSOLICIT:
7811c8c2e64Sflorian 	case DHCPREBIND:
7821c8c2e64Sflorian 		break;
7831c8c2e64Sflorian 	case DHCPREQUEST:
7841c8c2e64Sflorian 	case DHCPRENEW:
785ad7c548dSflorian 		opt_hdr.code = htons(DHO_SERVERID);
786ad7c548dSflorian 		opt_hdr.len = htons(iface->serverid_len);
787ad7c548dSflorian 		memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr));
788ad7c548dSflorian 		p += sizeof(struct dhcp_option_hdr);
789ad7c548dSflorian 		memcpy(p, iface->serverid, iface->serverid_len);
790ad7c548dSflorian 		p += iface->serverid_len;
7911c8c2e64Sflorian 		break;
7921c8c2e64Sflorian 	default:
7931c8c2e64Sflorian 		fatalx("%s: %s not implemented", __func__,
7941c8c2e64Sflorian 		    dhcp_message_type2str(message_type));
795ad7c548dSflorian 	}
796ad7c548dSflorian 	SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) {
797ad7c548dSflorian 		struct prefix *pd;
798ad7c548dSflorian 
799ad7c548dSflorian 		opt_hdr.code = htons(DHO_IA_PD);
800ad7c548dSflorian 		opt_hdr.len = htons(sizeof(struct dhcp_iapd) +
801ad7c548dSflorian 		    sizeof(struct dhcp_option_hdr) +
802ad7c548dSflorian 		    sizeof(struct dhcp_iaprefix));
803ad7c548dSflorian 		memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr));
804ad7c548dSflorian 		p += sizeof(struct dhcp_option_hdr);
805ad7c548dSflorian 		iapd.iaid = htonl(ia_conf->id);
806ad7c548dSflorian 		iapd.t1 = 0;
807ad7c548dSflorian 		iapd.t2 = 0;
808ad7c548dSflorian 		memcpy(p, &iapd, sizeof(struct dhcp_iapd));
809ad7c548dSflorian 		p += sizeof(struct dhcp_iapd);
810ad7c548dSflorian 
811ad7c548dSflorian 		opt_hdr.code = htons(DHO_IA_PREFIX);
812ad7c548dSflorian 		opt_hdr.len = htons(sizeof(struct dhcp_iaprefix));
813ad7c548dSflorian 		memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr));
814ad7c548dSflorian 		p += sizeof(struct dhcp_option_hdr);
815ad7c548dSflorian 
816ad7c548dSflorian 		memset(&iaprefix, 0, sizeof(struct dhcp_iaprefix));
817ad7c548dSflorian 
818ad7c548dSflorian 		switch (message_type) {
819ad7c548dSflorian 		case DHCPSOLICIT:
820ad7c548dSflorian 			iaprefix.prefix_len = ia_conf->prefix_len;
821ad7c548dSflorian 			break;
822ad7c548dSflorian 		case DHCPREQUEST:
8231c8c2e64Sflorian 		case DHCPRENEW:
8241c8c2e64Sflorian 		case DHCPREBIND:
82592119d76Sflorian 			pd = &iface->pds[ia_conf->id];
8266bb68921Sflorian 			if (pd->prefix_len > 0) {
827ad7c548dSflorian 				iaprefix.prefix_len = pd->prefix_len;
828ad7c548dSflorian 				memcpy(&iaprefix.prefix, &pd->prefix,
829ad7c548dSflorian 				    sizeof(struct in6_addr));
8306bb68921Sflorian 			} else
8316bb68921Sflorian 				iaprefix.prefix_len = ia_conf->prefix_len;
832ad7c548dSflorian 			break;
833ad7c548dSflorian 		default:
834ad7c548dSflorian 			fatalx("%s: %s not implemented", __func__,
835ad7c548dSflorian 			    dhcp_message_type2str(message_type));
836ad7c548dSflorian 		}
837ad7c548dSflorian 		memcpy(p, &iaprefix, sizeof(struct dhcp_iaprefix));
838ad7c548dSflorian 		p += sizeof(struct dhcp_iaprefix);
839ad7c548dSflorian 	}
840ad7c548dSflorian 
841ad7c548dSflorian 	opt_hdr.code = htons(DHO_ORO);
8421788a962Sflorian 	opt_hdr.len = htons(sizeof(request_option_code) * nitems(options));
843ad7c548dSflorian 	memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr));
844ad7c548dSflorian 	p += sizeof(struct dhcp_option_hdr);
8451788a962Sflorian 	for (i = 0; i < nitems(options); i++) {
8461788a962Sflorian 		request_option_code = htons(options[i]);
847ad7c548dSflorian 		memcpy(p, &request_option_code, sizeof(uint16_t));
848ad7c548dSflorian 		p += sizeof(uint16_t);
8491788a962Sflorian 	}
850ad7c548dSflorian 
851ad7c548dSflorian 	opt_hdr.code = htons(DHO_ELAPSED_TIME);
852ad7c548dSflorian 	opt_hdr.len = htons(2);
853ad7c548dSflorian 	memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr));
854ad7c548dSflorian 	p += sizeof(struct dhcp_option_hdr);
855ad7c548dSflorian 	elapsed_time = htons(iface->elapsed_time);
856ad7c548dSflorian 	memcpy(p, &elapsed_time, sizeof(uint16_t));
857ad7c548dSflorian 	p += sizeof(uint16_t);
858ad7c548dSflorian 
8593ae4b9dfSflorian 	if (message_type == DHCPSOLICIT && frontend_conf->rapid_commit) {
860ad7c548dSflorian 		opt_hdr.code = htons(DHO_RAPID_COMMIT);
861ad7c548dSflorian 		opt_hdr.len = htons(0);
862ad7c548dSflorian 		memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr));
863ad7c548dSflorian 		p += sizeof(struct dhcp_option_hdr);
8643ae4b9dfSflorian 	}
865ad7c548dSflorian 
866ad7c548dSflorian 	opt_hdr.code = htons(DHO_VENDOR_CLASS);
867ad7c548dSflorian 	opt_hdr.len = htons(sizeof(struct dhcp_vendor_class) +
868ad7c548dSflorian 	    vendor_class_len);
869ad7c548dSflorian 	memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr));
870ad7c548dSflorian 	p += sizeof(struct dhcp_option_hdr);
871ad7c548dSflorian 	vendor_class.enterprise_number = htonl(OPENBSD_ENTERPRISENO);
872ad7c548dSflorian 	vendor_class.vendor_class_len = htons(vendor_class_len);
873ad7c548dSflorian 	memcpy(p, &vendor_class, sizeof(struct dhcp_vendor_class));
874ad7c548dSflorian 	p += sizeof(struct dhcp_vendor_class);
875ad7c548dSflorian 	/* Not a C-string, leave out \0 */
876ad7c548dSflorian 	memcpy(p, vendor_class_data, vendor_class_len);
877ad7c548dSflorian 	p += vendor_class_len;
878ad7c548dSflorian 
879ad7c548dSflorian 	len = p - dhcp_packet;
880ad7c548dSflorian 	return (len);
881ad7c548dSflorian }
882ad7c548dSflorian 
883ad7c548dSflorian void
884ad7c548dSflorian send_packet(uint8_t message_type, struct iface *iface)
885ad7c548dSflorian {
886ad7c548dSflorian 	ssize_t	 pkt_len;
8870498f896Sflorian 	char	 ifnamebuf[IF_NAMESIZE], *if_name, *message_name;
888ad7c548dSflorian 
889ad7c548dSflorian 	if (!event_initialized(&iface->udpev)) {
890ad7c548dSflorian 		iface->send_solicit = 1;
891ad7c548dSflorian 		return;
892ad7c548dSflorian 	}
893ad7c548dSflorian 
894ad7c548dSflorian 	iface->send_solicit = 0;
895ad7c548dSflorian 
896ad7c548dSflorian 	if ((if_name = if_indextoname(iface->ifinfo.if_index, ifnamebuf))
897ad7c548dSflorian 	    == NULL)
898ad7c548dSflorian 		return; /* iface went away, nothing to do */
899ad7c548dSflorian 
9000498f896Sflorian 	switch (message_type) {
9010498f896Sflorian 	case DHCPSOLICIT:
9020498f896Sflorian 		message_name = "Soliciting";
9030498f896Sflorian 		break;
9040498f896Sflorian 	case DHCPREQUEST:
9050498f896Sflorian 		message_name = "Requesting";
9060498f896Sflorian 		break;
9070498f896Sflorian 	case DHCPRENEW:
9080498f896Sflorian 		message_name = "Renewing";
9090498f896Sflorian 		break;
9100498f896Sflorian 	case DHCPREBIND:
9110498f896Sflorian 		message_name = "Rebinding";
9120498f896Sflorian 		break;
9130498f896Sflorian 	default:
9140498f896Sflorian 		message_name = NULL;
9150498f896Sflorian 		break;
9160498f896Sflorian 	}
9170498f896Sflorian 
9180498f896Sflorian 	if (message_name)
9190498f896Sflorian 		log_info("%s lease on %s", message_name, if_name);
920ad7c548dSflorian 
921ad7c548dSflorian 	pkt_len = build_packet(message_type, iface, if_name);
922ad7c548dSflorian 
923f6870f0dSflorian 	dst.sin6_scope_id = iface->ifinfo.if_index;
924ad7c548dSflorian 
925f6870f0dSflorian 	if (sendto(EVENT_FD(&iface->udpev), dhcp_packet, pkt_len, 0,
926f6870f0dSflorian 	    (struct sockaddr *)&dst, sizeof(dst)) == -1)
927ad7c548dSflorian 		log_warn("sendto");
928ad7c548dSflorian }
929ad7c548dSflorian 
930ad7c548dSflorian struct iface*
931ad7c548dSflorian get_iface_by_id(uint32_t if_index)
932ad7c548dSflorian {
933ad7c548dSflorian 	struct iface	*iface;
934ad7c548dSflorian 
935ad7c548dSflorian 	LIST_FOREACH (iface, &interfaces, entries) {
936ad7c548dSflorian 		if (iface->ifinfo.if_index == if_index)
937ad7c548dSflorian 			return (iface);
938ad7c548dSflorian 	}
939ad7c548dSflorian 
940ad7c548dSflorian 	return (NULL);
941ad7c548dSflorian }
942ad7c548dSflorian 
943ad7c548dSflorian struct iface*
944ad7c548dSflorian get_iface_by_name(const char *if_name)
945ad7c548dSflorian {
946ad7c548dSflorian 	uint32_t ifidx = if_nametoindex(if_name);
947ad7c548dSflorian 
948ad7c548dSflorian 	if (ifidx == 0)
949ad7c548dSflorian 		return (NULL);
950ad7c548dSflorian 	return get_iface_by_id(ifidx);
951ad7c548dSflorian }
952ad7c548dSflorian 
953ad7c548dSflorian void
954ad7c548dSflorian remove_iface(uint32_t if_index)
955ad7c548dSflorian {
956ad7c548dSflorian 	struct iface	*iface;
957ad7c548dSflorian 
958ad7c548dSflorian 	iface = get_iface_by_id(if_index);
959ad7c548dSflorian 
960ad7c548dSflorian 	if (iface == NULL)
961ad7c548dSflorian 		return;
962ad7c548dSflorian 
963ad7c548dSflorian 	LIST_REMOVE(iface, entries);
964ad7c548dSflorian 	if (event_initialized(&iface->udpev)) {
965ad7c548dSflorian 		event_del(&iface->udpev);
966ad7c548dSflorian 		close(EVENT_FD(&iface->udpev));
967ad7c548dSflorian 	}
968ad7c548dSflorian 	free(iface);
969ad7c548dSflorian }
970ad7c548dSflorian 
971ad7c548dSflorian void
972ad7c548dSflorian set_udpsock(int udpsock, uint32_t if_index)
973ad7c548dSflorian {
974ad7c548dSflorian 	struct iface	*iface;
975ad7c548dSflorian 
976ad7c548dSflorian 	iface = get_iface_by_id(if_index);
977ad7c548dSflorian 
978ad7c548dSflorian 	if (iface == NULL) {
979ad7c548dSflorian 		/*
980ad7c548dSflorian 		 * The interface disappeared while we were waiting for the
981ad7c548dSflorian 		 * parent process to open the udp socket.
982ad7c548dSflorian 		 */
983ad7c548dSflorian 		close(udpsock);
984ad7c548dSflorian 	} else if (event_initialized(&iface->udpev)) {
985ad7c548dSflorian 		/*
986ad7c548dSflorian 		 * XXX
987ad7c548dSflorian 		 * The autoconf flag is flapping and we have multiple udp
988ad7c548dSflorian 		 * sockets in flight. We don't need this one because we already
989ad7c548dSflorian 		 * got one.
990ad7c548dSflorian 		 */
991ad7c548dSflorian 		close(udpsock);
992ad7c548dSflorian 	} else {
993ad7c548dSflorian 		event_set(&iface->udpev, udpsock, EV_READ |
994ad7c548dSflorian 		    EV_PERSIST, udp_receive, iface);
995ad7c548dSflorian 		event_add(&iface->udpev, NULL);
996ad7c548dSflorian 		if (iface->send_solicit)
997ad7c548dSflorian 			send_packet(DHCPSOLICIT, iface);
998ad7c548dSflorian 	}
999ad7c548dSflorian }
1000ad7c548dSflorian 
1001ad7c548dSflorian struct iface_conf*
1002ad7c548dSflorian find_iface_conf(struct iface_conf_head *head, char *if_name)
1003ad7c548dSflorian {
1004ad7c548dSflorian 	struct iface_conf	*iface_conf;
1005ad7c548dSflorian 
1006ad7c548dSflorian 	if (if_name == NULL)
1007ad7c548dSflorian 		return (NULL);
1008ad7c548dSflorian 
1009ad7c548dSflorian 	SIMPLEQ_FOREACH(iface_conf, head, entry) {
1010ad7c548dSflorian 		if (strcmp(iface_conf->name, if_name) == 0)
1011ad7c548dSflorian 			return iface_conf;
1012ad7c548dSflorian 	}
1013ad7c548dSflorian 	return (NULL);
1014ad7c548dSflorian }
1015ad7c548dSflorian 
1016ad7c548dSflorian int*
1017ad7c548dSflorian changed_ifaces(struct dhcp6leased_conf *oconf, struct dhcp6leased_conf *nconf)
1018ad7c548dSflorian {
1019ad7c548dSflorian 	struct iface_conf	*iface_conf, *oiface_conf;
1020ad7c548dSflorian 	int			*ret, if_index, count = 0, i = 0;
1021ad7c548dSflorian 
1022ad7c548dSflorian 	/*
1023ad7c548dSflorian 	 * Worst case: All old interfaces replaced with new interfaces.
1024ad7c548dSflorian 	 * This should still be a small number
1025ad7c548dSflorian 	 */
1026ad7c548dSflorian 	SIMPLEQ_FOREACH(iface_conf, &oconf->iface_list, entry)
1027ad7c548dSflorian 	    count++;
1028ad7c548dSflorian 	SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry)
1029ad7c548dSflorian 	    count++;
1030ad7c548dSflorian 
1031ad7c548dSflorian 	ret = calloc(count + 1, sizeof(int));
1032ad7c548dSflorian 
1033ad7c548dSflorian 	SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry) {
1034ad7c548dSflorian 		if ((if_index = if_nametoindex(iface_conf->name)) == 0)
1035ad7c548dSflorian 			continue;
1036ad7c548dSflorian 		oiface_conf = find_iface_conf(&oconf->iface_list,
1037ad7c548dSflorian 		    iface_conf->name);
1038ad7c548dSflorian 		if (oiface_conf == NULL) {
1039ad7c548dSflorian 			/* new interface added to config */
1040ad7c548dSflorian 			ret[i++] = if_index;
1041ad7c548dSflorian 		} else if (iface_conf_cmp(iface_conf, oiface_conf) != 0) {
1042ad7c548dSflorian 			/* interface conf changed */
1043ad7c548dSflorian 			ret[i++] = if_index;
1044ad7c548dSflorian 		}
1045ad7c548dSflorian 	}
1046ad7c548dSflorian 	SIMPLEQ_FOREACH(oiface_conf, &oconf->iface_list, entry) {
1047ad7c548dSflorian 		if ((if_index = if_nametoindex(oiface_conf->name)) == 0)
1048ad7c548dSflorian 			continue;
1049ad7c548dSflorian 		if (find_iface_conf(&nconf->iface_list, oiface_conf->name) ==
1050ad7c548dSflorian 		    NULL) {
1051ad7c548dSflorian 			/* interface removed from config */
1052ad7c548dSflorian 			ret[i++] = if_index;
1053ad7c548dSflorian 		}
1054ad7c548dSflorian 	}
1055ad7c548dSflorian 	return ret;
1056ad7c548dSflorian }
1057ad7c548dSflorian 
1058ad7c548dSflorian int
1059ad7c548dSflorian iface_conf_cmp(struct iface_conf *a, struct iface_conf *b)
1060ad7c548dSflorian {
1061ad7c548dSflorian 	return 0;
1062ad7c548dSflorian }
1063