xref: /openbsd-src/sbin/dhcp6leased/engine.c (revision efb7adaa3d67e82ad4c56d3a17e32178919224b1)
1 /*	$OpenBSD: engine.c,v 1.31 2024/12/24 17:40:06 florian Exp $	*/
2 
3 /*
4  * Copyright (c) 2017, 2021, 2024 Florian Obser <florian@openbsd.org>
5  * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org>
6  * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
7  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 
22 #include <sys/types.h>
23 #include <sys/queue.h>
24 #include <sys/socket.h>
25 #include <sys/syslog.h>
26 #include <sys/uio.h>
27 #include <sys/mbuf.h>
28 
29 #include <net/if.h>
30 #include <net/route.h>
31 #include <arpa/inet.h>
32 #include <netinet/in.h>
33 #include <netinet/ip.h>
34 
35 #include <errno.h>
36 #include <event.h>
37 #include <imsg.h>
38 #include <pwd.h>
39 #include <signal.h>
40 #include <stddef.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <time.h>
45 #include <unistd.h>
46 #include <vis.h>
47 
48 #include "log.h"
49 #include "dhcp6leased.h"
50 #include "engine.h"
51 
52 /*
53  * RFC 2131 4.1 p23 has "SHOULD be 4 seconds", we are a bit more aggressive,
54  * networks are faster these days.
55  */
56 #define	START_EXP_BACKOFF	 1
57 #define	MAX_EXP_BACKOFF_SLOW	64 /* RFC 2131 4.1 p23 */
58 #define	MAX_EXP_BACKOFF_FAST	 2
59 #define	MINIMUM(a, b)		(((a) < (b)) ? (a) : (b))
60 
61 enum if_state {
62 	IF_DOWN,
63 	IF_INIT,
64 	/* IF_SELECTING, */
65 	IF_REQUESTING,
66 	IF_BOUND,
67 	IF_RENEWING,
68 	IF_REBINDING,
69 	/* IF_INIT_REBOOT, */
70 	IF_REBOOTING,
71 };
72 
73 const char* if_state_name[] = {
74 	"Down",
75 	"Init",
76 	/* "Selecting", */
77 	"Requesting",
78 	"Bound",
79 	"Renewing",
80 	"Rebinding",
81 	/* "Init-Reboot", */
82 	"Rebooting",
83 	"IPv6 only",
84 };
85 
86 enum reconfigure_action {
87 	CONFIGURE,
88 	DECONFIGURE,
89 };
90 
91 const char* reconfigure_action_name[] = {
92 	"configure",
93 	"deconfigure",
94 };
95 
96 struct dhcp6leased_iface {
97 	LIST_ENTRY(dhcp6leased_iface)	 entries;
98 	enum if_state			 state;
99 	struct event			 timer;
100 	struct timeval			 timo;
101 	uint32_t			 if_index;
102 	int				 rdomain;
103 	int				 running;
104 	int				 link_state;
105 	uint8_t				 xid[XID_SIZE];
106 	int				 serverid_len;
107 	uint8_t				 serverid[SERVERID_SIZE];
108 	struct prefix			 pds[MAX_IA];
109 	struct prefix			 new_pds[MAX_IA];
110 	struct timespec			 request_time;
111 	struct timespec			 elapsed_time_start;
112 	uint32_t			 lease_time;
113 	uint32_t			 t1;
114 	uint32_t			 t2;
115 };
116 
117 LIST_HEAD(, dhcp6leased_iface) dhcp6leased_interfaces;
118 
119 __dead void		 engine_shutdown(void);
120 void			 engine_sig_handler(int sig, short, void *);
121 void			 engine_dispatch_frontend(int, short, void *);
122 void			 engine_dispatch_main(int, short, void *);
123 void			 send_interface_info(struct dhcp6leased_iface *, pid_t);
124 void			 engine_showinfo_ctl(struct imsg *, uint32_t);
125 void			 engine_update_iface(struct imsg_ifinfo *);
126 struct dhcp6leased_iface	*get_dhcp6leased_iface_by_id(uint32_t);
127 void			 remove_dhcp6leased_iface(uint32_t);
128 void			 parse_dhcp(struct dhcp6leased_iface *,
129 			     struct imsg_dhcp *);
130 int			 parse_ia_pd_options(uint8_t *, size_t, struct prefix *);
131 void			 state_transition(struct dhcp6leased_iface *, enum
132 			     if_state);
133 void			 iface_timeout(int, short, void *);
134 void			 request_dhcp_discover(struct dhcp6leased_iface *);
135 void			 request_dhcp_request(struct dhcp6leased_iface *);
136 void			 configure_interfaces(struct dhcp6leased_iface *);
137 void			 deconfigure_interfaces(struct dhcp6leased_iface *);
138 void			 deprecate_interfaces(struct dhcp6leased_iface *);
139 int			 prefixcmp(struct prefix *, struct prefix *, int);
140 void			 send_reconfigure_interface(struct iface_pd_conf *,
141 			     struct prefix *, enum reconfigure_action);
142 int			 engine_imsg_compose_main(int, pid_t, void *, uint16_t);
143 const char		*dhcp_option_type2str(int);
144 const char		*dhcp_duid2str(int, uint8_t *);
145 const char		*dhcp_status2str(int);
146 void			 in6_prefixlen2mask(struct in6_addr *, int len);
147 
148 struct dhcp6leased_conf	*engine_conf;
149 
150 static struct imsgev	*iev_frontend;
151 static struct imsgev	*iev_main;
152 int64_t			 proposal_id;
153 static struct dhcp_duid	 duid;
154 
155 void
156 engine_sig_handler(int sig, short event, void *arg)
157 {
158 	/*
159 	 * Normal signal handler rules don't apply because libevent
160 	 * decouples for us.
161 	 */
162 
163 	switch (sig) {
164 	case SIGINT:
165 	case SIGTERM:
166 		engine_shutdown();
167 	default:
168 		fatalx("unexpected signal");
169 	}
170 }
171 
172 void
173 engine(int debug, int verbose)
174 {
175 	struct event		 ev_sigint, ev_sigterm;
176 	struct passwd		*pw;
177 
178 	engine_conf = config_new_empty();
179 
180 	log_init(debug, LOG_DAEMON);
181 	log_setverbose(verbose);
182 
183 	if ((pw = getpwnam(DHCP6LEASED_USER)) == NULL)
184 		fatal("getpwnam");
185 
186 	if (chdir("/") == -1)
187 		fatal("chdir(\"/\")");
188 
189 	if (unveil("/", "") == -1)
190 		fatal("unveil /");
191 	if (unveil(NULL, NULL) == -1)
192 		fatal("unveil");
193 
194 	setproctitle("%s", "engine");
195 	log_procinit("engine");
196 
197 	if (setgroups(1, &pw->pw_gid) ||
198 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
199 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
200 		fatal("can't drop privileges");
201 
202 	if (pledge("stdio recvfd", NULL) == -1)
203 		fatal("pledge");
204 
205 	event_init();
206 
207 	/* Setup signal handler(s). */
208 	signal_set(&ev_sigint, SIGINT, engine_sig_handler, NULL);
209 	signal_set(&ev_sigterm, SIGTERM, engine_sig_handler, NULL);
210 	signal_add(&ev_sigint, NULL);
211 	signal_add(&ev_sigterm, NULL);
212 	signal(SIGPIPE, SIG_IGN);
213 	signal(SIGHUP, SIG_IGN);
214 
215 	/* Setup pipe and event handler to the main process. */
216 	if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
217 		fatal(NULL);
218 
219 	if (imsgbuf_init(&iev_main->ibuf, 3) == -1)
220 		fatal(NULL);
221 	imsgbuf_allow_fdpass(&iev_main->ibuf);
222 	iev_main->handler = engine_dispatch_main;
223 
224 	/* Setup event handlers. */
225 	iev_main->events = EV_READ;
226 	event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
227 	    iev_main->handler, iev_main);
228 	event_add(&iev_main->ev, NULL);
229 
230 	LIST_INIT(&dhcp6leased_interfaces);
231 
232 	event_dispatch();
233 
234 	engine_shutdown();
235 }
236 
237 __dead void
238 engine_shutdown(void)
239 {
240 	/* Close pipes. */
241 	imsgbuf_clear(&iev_frontend->ibuf);
242 	close(iev_frontend->ibuf.fd);
243 	imsgbuf_clear(&iev_main->ibuf);
244 	close(iev_main->ibuf.fd);
245 
246 	free(iev_frontend);
247 	free(iev_main);
248 
249 	log_info("engine exiting");
250 	exit(0);
251 }
252 
253 int
254 engine_imsg_compose_frontend(int type, pid_t pid, void *data,
255     uint16_t datalen)
256 {
257 	return (imsg_compose_event(iev_frontend, type, 0, pid, -1,
258 	    data, datalen));
259 }
260 
261 int
262 engine_imsg_compose_main(int type, pid_t pid, void *data,
263     uint16_t datalen)
264 {
265 	return (imsg_compose_event(iev_main, type, 0, pid, -1,
266 	    data, datalen));
267 }
268 
269 void
270 engine_dispatch_frontend(int fd, short event, void *bula)
271 {
272 	struct imsgev			*iev = bula;
273 	struct imsgbuf			*ibuf = &iev->ibuf;
274 	struct imsg			 imsg;
275 	struct dhcp6leased_iface		*iface;
276 	ssize_t				 n;
277 	int				 shut = 0;
278 	int				 verbose;
279 	uint32_t			 if_index;
280 
281 	if (event & EV_READ) {
282 		if ((n = imsgbuf_read(ibuf)) == -1)
283 			fatal("imsgbuf_read error");
284 		if (n == 0)	/* Connection closed. */
285 			shut = 1;
286 	}
287 	if (event & EV_WRITE) {
288 		if (imsgbuf_write(ibuf) == -1) {
289 			if (errno == EPIPE)	/* Connection closed. */
290 				shut = 1;
291 			else
292 				fatal("imsgbuf_write");
293 		}
294 	}
295 
296 	for (;;) {
297 		if ((n = imsg_get(ibuf, &imsg)) == -1)
298 			fatal("%s: imsg_get error", __func__);
299 		if (n == 0)	/* No more messages. */
300 			break;
301 
302 		switch (imsg.hdr.type) {
303 		case IMSG_CTL_LOG_VERBOSE:
304 			if (IMSG_DATA_SIZE(imsg) != sizeof(verbose))
305 				fatalx("%s: IMSG_CTL_LOG_VERBOSE wrong length: "
306 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
307 			memcpy(&verbose, imsg.data, sizeof(verbose));
308 			log_setverbose(verbose);
309 			break;
310 		case IMSG_CTL_SHOW_INTERFACE_INFO:
311 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
312 				fatalx("%s: IMSG_CTL_SHOW_INTERFACE_INFO wrong "
313 				    "length: %lu", __func__,
314 				    IMSG_DATA_SIZE(imsg));
315 			memcpy(&if_index, imsg.data, sizeof(if_index));
316 			engine_showinfo_ctl(&imsg, if_index);
317 			break;
318 		case IMSG_REQUEST_REBOOT:
319 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
320 				fatalx("%s: IMSG_CTL_SEND_DISCOVER wrong "
321 				    "length: %lu", __func__,
322 				    IMSG_DATA_SIZE(imsg));
323 			memcpy(&if_index, imsg.data, sizeof(if_index));
324 			iface = get_dhcp6leased_iface_by_id(if_index);
325 			if (iface != NULL) {
326 				switch (iface->state) {
327 				case IF_DOWN:
328 					break;
329 				case IF_INIT:
330 				case IF_REQUESTING:
331 					state_transition(iface, iface->state);
332 					break;
333 				case IF_RENEWING:
334 				case IF_REBINDING:
335 				case IF_REBOOTING:
336 				case IF_BOUND:
337 					state_transition(iface, IF_REBOOTING);
338 					break;
339 				}
340 			}
341 			break;
342 		case IMSG_REMOVE_IF:
343 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
344 				fatalx("%s: IMSG_REMOVE_IF wrong length: %lu",
345 				    __func__, IMSG_DATA_SIZE(imsg));
346 			memcpy(&if_index, imsg.data, sizeof(if_index));
347 			remove_dhcp6leased_iface(if_index);
348 			break;
349 		case IMSG_DHCP: {
350 			struct imsg_dhcp	imsg_dhcp;
351 			if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_dhcp))
352 				fatalx("%s: IMSG_DHCP wrong length: %lu",
353 				    __func__, IMSG_DATA_SIZE(imsg));
354 			memcpy(&imsg_dhcp, imsg.data, sizeof(imsg_dhcp));
355 			iface = get_dhcp6leased_iface_by_id(imsg_dhcp.if_index);
356 			if (iface != NULL)
357 				parse_dhcp(iface, &imsg_dhcp);
358 			break;
359 		}
360 		default:
361 			log_debug("%s: unexpected imsg %d", __func__,
362 			    imsg.hdr.type);
363 			break;
364 		}
365 		imsg_free(&imsg);
366 	}
367 	if (!shut)
368 		imsg_event_add(iev);
369 	else {
370 		/* This pipe is dead. Remove its event handler. */
371 		event_del(&iev->ev);
372 		event_loopexit(NULL);
373 	}
374 }
375 
376 void
377 engine_dispatch_main(int fd, short event, void *bula)
378 {
379 	static struct dhcp6leased_conf	*nconf;
380 	static struct iface_conf	*iface_conf;
381 	static struct iface_ia_conf	*iface_ia_conf;
382 	struct iface_pd_conf		*iface_pd_conf;
383 	struct imsg			 imsg;
384 	struct imsgev			*iev = bula;
385 	struct imsgbuf			*ibuf = &iev->ibuf;
386 	struct imsg_ifinfo		 imsg_ifinfo;
387 	ssize_t				 n;
388 	int				 shut = 0;
389 
390 	if (event & EV_READ) {
391 		if ((n = imsgbuf_read(ibuf)) == -1)
392 			fatal("imsgbuf_read error");
393 		if (n == 0)	/* Connection closed. */
394 			shut = 1;
395 	}
396 	if (event & EV_WRITE) {
397 		if (imsgbuf_write(ibuf) == -1) {
398 			if (errno == EPIPE)	/* Connection closed. */
399 				shut = 1;
400 			else
401 				fatal("imsgbuf_write");
402 		}
403 	}
404 
405 	for (;;) {
406 		if ((n = imsg_get(ibuf, &imsg)) == -1)
407 			fatal("%s: imsg_get error", __func__);
408 		if (n == 0)	/* No more messages. */
409 			break;
410 
411 		switch (imsg.hdr.type) {
412 		case IMSG_SOCKET_IPC:
413 			/*
414 			 * Setup pipe and event handler to the frontend
415 			 * process.
416 			 */
417 			if (iev_frontend)
418 				fatalx("%s: received unexpected imsg fd "
419 				    "to engine", __func__);
420 
421 			if ((fd = imsg_get_fd(&imsg)) == -1)
422 				fatalx("%s: expected to receive imsg fd to "
423 				   "engine but didn't receive any", __func__);
424 
425 			iev_frontend = malloc(sizeof(struct imsgev));
426 			if (iev_frontend == NULL)
427 				fatal(NULL);
428 
429 			if (imsgbuf_init(&iev_frontend->ibuf, fd) == -1)
430 				fatal(NULL);
431 			iev_frontend->handler = engine_dispatch_frontend;
432 			iev_frontend->events = EV_READ;
433 
434 			event_set(&iev_frontend->ev, iev_frontend->ibuf.fd,
435 			iev_frontend->events, iev_frontend->handler,
436 			    iev_frontend);
437 			event_add(&iev_frontend->ev, NULL);
438 
439 			if (pledge("stdio", NULL) == -1)
440 				fatal("pledge");
441 
442 			break;
443 		case IMSG_UUID:
444 			if (IMSG_DATA_SIZE(imsg) != sizeof(duid.uuid))
445 				fatalx("%s: IMSG_UUID wrong length: "
446 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
447 			duid.type = htons(DUID_UUID_TYPE);
448 			memcpy(duid.uuid, imsg.data, sizeof(duid.uuid));
449 			break;
450 		case IMSG_UPDATE_IF:
451 			if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_ifinfo))
452 				fatalx("%s: IMSG_UPDATE_IF wrong length: %lu",
453 				    __func__, IMSG_DATA_SIZE(imsg));
454 			memcpy(&imsg_ifinfo, imsg.data, sizeof(imsg_ifinfo));
455 			engine_update_iface(&imsg_ifinfo);
456 			break;
457 		case IMSG_RECONF_CONF:
458 			if (nconf != NULL)
459 				fatalx("%s: IMSG_RECONF_CONF already in "
460 				    "progress", __func__);
461 			if (IMSG_DATA_SIZE(imsg) !=
462 			    sizeof(struct dhcp6leased_conf))
463 				fatalx("%s: IMSG_RECONF_CONF wrong length: %lu",
464 				    __func__, IMSG_DATA_SIZE(imsg));
465 			if ((nconf = malloc(sizeof(struct dhcp6leased_conf))) ==
466 			    NULL)
467 				fatal(NULL);
468 			memcpy(nconf, imsg.data,
469 			    sizeof(struct dhcp6leased_conf));
470 			SIMPLEQ_INIT(&nconf->iface_list);
471 			break;
472 		case IMSG_RECONF_IFACE:
473 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
474 			    iface_conf))
475 				fatalx("%s: IMSG_RECONF_IFACE wrong length: "
476 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
477 			if ((iface_conf = malloc(sizeof(struct iface_conf)))
478 			    == NULL)
479 				fatal(NULL);
480 			memcpy(iface_conf, imsg.data, sizeof(struct
481 			    iface_conf));
482 			SIMPLEQ_INIT(&iface_conf->iface_ia_list);
483 			SIMPLEQ_INSERT_TAIL(&nconf->iface_list,
484 			    iface_conf, entry);
485 			iface_conf->ia_count = 0;
486 			break;
487 		case IMSG_RECONF_IFACE_IA:
488 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
489 			    iface_ia_conf))
490 				fatalx("%s: IMSG_RECONF_IFACE_IA wrong "
491 				    "length: %lu", __func__,
492 				    IMSG_DATA_SIZE(imsg));
493 			if ((iface_ia_conf =
494 			    malloc(sizeof(struct iface_ia_conf))) == NULL)
495 				fatal(NULL);
496 			memcpy(iface_ia_conf, imsg.data, sizeof(struct
497 			    iface_ia_conf));
498 			SIMPLEQ_INIT(&iface_ia_conf->iface_pd_list);
499 			SIMPLEQ_INSERT_TAIL(&iface_conf->iface_ia_list,
500 			    iface_ia_conf, entry);
501 			iface_ia_conf->id = iface_conf->ia_count++;
502 			if (iface_conf->ia_count > MAX_IA)
503 				fatalx("Too many prefix delegation requests.");
504 			break;
505 		case IMSG_RECONF_IFACE_PD:
506 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
507 			    iface_pd_conf))
508 				fatalx("%s: IMSG_RECONF_IFACE_PD wrong length: "
509 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
510 			if ((iface_pd_conf =
511 			    malloc(sizeof(struct iface_pd_conf))) == NULL)
512 				fatal(NULL);
513 			memcpy(iface_pd_conf, imsg.data, sizeof(struct
514 			    iface_pd_conf));
515 			SIMPLEQ_INSERT_TAIL(&iface_ia_conf->iface_pd_list,
516 			    iface_pd_conf, entry);
517 			break;
518 		case IMSG_RECONF_IFACE_IA_END:
519 			iface_ia_conf = NULL;
520 			break;
521 		case IMSG_RECONF_IFACE_END:
522 			iface_conf = NULL;
523 			break;
524 		case IMSG_RECONF_END: {
525 			struct dhcp6leased_iface	*iface;
526 			int			*ifaces;
527 			int			 i, if_index;
528 			char			*if_name;
529 			char			 ifnamebuf[IF_NAMESIZE];
530 
531 			if (nconf == NULL)
532 				fatalx("%s: IMSG_RECONF_END without "
533 				    "IMSG_RECONF_CONF", __func__);
534 			ifaces = changed_ifaces(engine_conf, nconf);
535 			merge_config(engine_conf, nconf);
536 			nconf = NULL;
537 			for (i = 0; ifaces[i] != 0; i++) {
538 				if_index = ifaces[i];
539 				if_name = if_indextoname(if_index, ifnamebuf);
540 				iface = get_dhcp6leased_iface_by_id(if_index);
541 				if (if_name == NULL || iface == NULL)
542 					continue;
543 				iface_conf = find_iface_conf(
544 				    &engine_conf->iface_list, if_name);
545 				if (iface_conf == NULL)
546 					continue;
547 			}
548 			free(ifaces);
549 			break;
550 		}
551 		default:
552 			log_debug("%s: unexpected imsg %d", __func__,
553 			    imsg.hdr.type);
554 			break;
555 		}
556 		imsg_free(&imsg);
557 	}
558 	if (!shut)
559 		imsg_event_add(iev);
560 	else {
561 		/* This pipe is dead. Remove its event handler. */
562 		event_del(&iev->ev);
563 		event_loopexit(NULL);
564 	}
565 }
566 
567 void
568 send_interface_info(struct dhcp6leased_iface *iface, pid_t pid)
569 {
570 	struct ctl_engine_info	 cei;
571 
572 	memset(&cei, 0, sizeof(cei));
573 	cei.if_index = iface->if_index;
574 	cei.running = iface->running;
575 	cei.link_state = iface->link_state;
576 	strlcpy(cei.state, if_state_name[iface->state], sizeof(cei.state));
577 	memcpy(&cei.request_time, &iface->request_time,
578 	    sizeof(cei.request_time));
579 	cei.lease_time = iface->lease_time;
580 	cei.t1 = iface->t1;
581 	cei.t2 = iface->t2;
582 	memcpy(&cei.pds, &iface->pds, sizeof(cei.pds));
583 	engine_imsg_compose_frontend(IMSG_CTL_SHOW_INTERFACE_INFO, pid, &cei,
584 	    sizeof(cei));
585 }
586 
587 void
588 engine_showinfo_ctl(struct imsg *imsg, uint32_t if_index)
589 {
590 	struct dhcp6leased_iface			*iface;
591 
592 	switch (imsg->hdr.type) {
593 	case IMSG_CTL_SHOW_INTERFACE_INFO:
594 		if ((iface = get_dhcp6leased_iface_by_id(if_index)) != NULL)
595 			send_interface_info(iface, imsg->hdr.pid);
596 		else
597 			engine_imsg_compose_frontend(IMSG_CTL_END,
598 			    imsg->hdr.pid, NULL, 0);
599 		break;
600 	default:
601 		log_debug("%s: error handling imsg", __func__);
602 		break;
603 	}
604 }
605 
606 void
607 engine_update_iface(struct imsg_ifinfo *imsg_ifinfo)
608 {
609 	struct dhcp6leased_iface	*iface;
610 	struct iface_conf		*iface_conf;
611 	int				 need_refresh = 0;
612 	char				 ifnamebuf[IF_NAMESIZE], *if_name;
613 
614 	iface = get_dhcp6leased_iface_by_id(imsg_ifinfo->if_index);
615 
616 	if (iface == NULL) {
617 		if ((iface = calloc(1, sizeof(*iface))) == NULL)
618 			fatal("calloc");
619 		iface->state = IF_DOWN;
620 		arc4random_buf(iface->xid, sizeof(iface->xid));
621 		iface->timo.tv_usec = arc4random_uniform(1000000);
622 		evtimer_set(&iface->timer, iface_timeout, iface);
623 		iface->if_index = imsg_ifinfo->if_index;
624 		iface->rdomain = imsg_ifinfo->rdomain;
625 		iface->running = imsg_ifinfo->running;
626 		iface->link_state = imsg_ifinfo->link_state;
627 		LIST_INSERT_HEAD(&dhcp6leased_interfaces, iface, entries);
628 		need_refresh = 1;
629 	} else {
630 		if (imsg_ifinfo->rdomain != iface->rdomain) {
631 			iface->rdomain = imsg_ifinfo->rdomain;
632 			need_refresh = 1;
633 		}
634 		if (imsg_ifinfo->running != iface->running) {
635 			iface->running = imsg_ifinfo->running;
636 			need_refresh = 1;
637 		}
638 
639 		if (imsg_ifinfo->link_state != iface->link_state) {
640 			iface->link_state = imsg_ifinfo->link_state;
641 			need_refresh = 1;
642 		}
643 	}
644 
645 	if (!need_refresh)
646 		return;
647 
648 	if ((if_name = if_indextoname(iface->if_index, ifnamebuf)) == NULL) {
649 		log_debug("%s: unknown interface %d", __func__,
650 		    iface->if_index);
651 		return;
652 	}
653 
654 	if ((iface_conf = find_iface_conf(&engine_conf->iface_list, if_name))
655 	    == NULL) {
656 		log_debug("%s: no interface configuration for %d", __func__,
657 		    iface->if_index);
658 		return;
659 	}
660 
661 	if (iface->running && LINK_STATE_IS_UP(iface->link_state)) {
662 		uint32_t	 i;
663 		int		 got_lease;
664 
665 		if (iface->pds[0].prefix_len == 0)
666 			memcpy(iface->pds, imsg_ifinfo->pds,
667 			    sizeof(iface->pds));
668 
669 		got_lease = 0;
670 		for (i = 0; i < iface_conf->ia_count; i++) {
671 			if (iface->pds[i].prefix_len > 0) {
672 				got_lease = 1;
673 				break;
674 			}
675 		}
676 		if (got_lease)
677 			state_transition(iface, IF_REBOOTING);
678 		else
679 			state_transition(iface, IF_INIT);
680 	} else
681 		state_transition(iface, IF_DOWN);
682 }
683 struct dhcp6leased_iface*
684 get_dhcp6leased_iface_by_id(uint32_t if_index)
685 {
686 	struct dhcp6leased_iface	*iface;
687 	LIST_FOREACH (iface, &dhcp6leased_interfaces, entries) {
688 		if (iface->if_index == if_index)
689 			return (iface);
690 	}
691 
692 	return (NULL);
693 }
694 
695 void
696 remove_dhcp6leased_iface(uint32_t if_index)
697 {
698 	struct dhcp6leased_iface	*iface;
699 
700 	iface = get_dhcp6leased_iface_by_id(if_index);
701 
702 	if (iface == NULL)
703 		return;
704 
705 	deconfigure_interfaces(iface);
706 	LIST_REMOVE(iface, entries);
707 	evtimer_del(&iface->timer);
708 	free(iface);
709 }
710 
711 void
712 parse_dhcp(struct dhcp6leased_iface *iface, struct imsg_dhcp *dhcp)
713 {
714 	struct iface_conf	*iface_conf;
715 	struct iface_ia_conf	*ia_conf;
716 	struct dhcp_hdr		 hdr;
717 	struct dhcp_option_hdr	 opt_hdr;
718 	struct dhcp_iapd	 iapd;
719 	size_t			 rem;
720 	uint32_t		 t1, t2, lease_time;
721 	int			 serverid_len, rapid_commit = 0;
722 	uint8_t			 serverid[SERVERID_SIZE];
723 	uint8_t			*p;
724 	char			 ifnamebuf[IF_NAMESIZE], *if_name;
725 	char			 ntopbuf[INET6_ADDRSTRLEN];
726 
727 	if ((if_name = if_indextoname(iface->if_index, ifnamebuf)) == NULL) {
728 		log_debug("%s: unknown interface %d", __func__,
729 		    iface->if_index);
730 		goto out;
731 	}
732 	if ((iface_conf = find_iface_conf(&engine_conf->iface_list, if_name))
733 	    == NULL) {
734 		log_debug("%s: no interface configuration for %d", __func__,
735 		    iface->if_index);
736 		goto out;
737 	}
738 
739 	log_debug("%s: %s ia_count: %d", __func__, if_name,
740 	    iface_conf->ia_count);
741 
742 	serverid_len = t1 = t2 = lease_time = 0;
743 	memset(iface->new_pds, 0, sizeof(iface->new_pds));
744 
745 	p = dhcp->packet;
746 	rem = dhcp->len;
747 
748 	if (rem < sizeof(struct dhcp_hdr)) {
749 		log_warnx("%s: message too short", __func__);
750 		goto out;
751 	}
752 	memcpy(&hdr, p, sizeof(struct dhcp_hdr));
753 	p += sizeof(struct dhcp_hdr);
754 	rem -= sizeof(struct dhcp_hdr);
755 
756 	if (log_getverbose() > 1)
757 		log_debug("%s: %s, xid: 0x%02x%02x%02x", __func__,
758 		    dhcp_message_type2str(hdr.msg_type), hdr.xid[0], hdr.xid[1],
759 		    hdr.xid[2]);
760 
761 	while (rem >= sizeof(struct dhcp_option_hdr)) {
762 		memcpy(&opt_hdr, p, sizeof(struct dhcp_option_hdr));
763 		opt_hdr.code = ntohs(opt_hdr.code);
764 		opt_hdr.len = ntohs(opt_hdr.len);
765 		p += sizeof(struct dhcp_option_hdr);
766 		rem -= sizeof(struct dhcp_option_hdr);
767 		if (log_getverbose() > 1)
768 			log_debug("%s: %s, len: %u", __func__,
769 			    dhcp_option_type2str(opt_hdr.code), opt_hdr.len);
770 
771 		if (rem < opt_hdr.len) {
772 			log_warnx("%s: malformed packet, ignoring", __func__);
773 			goto out;
774 		}
775 
776 		switch (opt_hdr.code) {
777 		case DHO_CLIENTID:
778 			if (opt_hdr.len != sizeof(struct dhcp_duid) ||
779 			    memcmp(&duid, p, sizeof(struct dhcp_duid)) != 0) {
780 				log_debug("%s: message not for us", __func__);
781 				goto out;
782 			}
783 			break;
784 		case DHO_SERVERID:
785 			/*
786 			 * RFC 8415, 11.1:
787 			 * The length of the DUID (not including the type code)
788 			 * is at least 1 octet and at most 128 octets.
789 			 */
790 			if (opt_hdr.len < 2 + 1) {
791 				log_warnx("%s: SERVERID too short", __func__);
792 				goto out;
793 			}
794 			if (opt_hdr.len > SERVERID_SIZE) {
795 				log_warnx("%s: SERVERID too long", __func__);
796 				goto out;
797 			}
798 			log_debug("%s: SERVERID: %s", __func__,
799 			    dhcp_duid2str(opt_hdr.len, p));
800 			if (serverid_len != 0) {
801 				log_warnx("%s: duplicate SERVERID option",
802 				    __func__);
803 				goto out;
804 			}
805 			serverid_len = opt_hdr.len;
806 			memcpy(serverid, p, serverid_len);
807 			break;
808 		case DHO_IA_PD:
809 			if (opt_hdr.len < sizeof(struct dhcp_iapd)) {
810 				log_warnx("%s: IA_PD too short", __func__);
811 				goto out;
812 			}
813 			memcpy(&iapd, p, sizeof(struct dhcp_iapd));
814 
815 			if (t1 == 0 || t1 > ntohl(iapd.t1))
816 				t1 = ntohl(iapd.t1);
817 			if (t2 == 0 || t2 > ntohl(iapd.t2))
818 				t2 = ntohl(iapd.t2);
819 
820 			log_debug("%s: IA_PD, IAID: %08x, T1: %u, T2: %u",
821 			    __func__, ntohl(iapd.iaid), ntohl(iapd.t1),
822 			    ntohl(iapd.t2));
823 			if (ntohl(iapd.iaid) < iface_conf->ia_count) {
824 				int status_code;
825 				status_code = parse_ia_pd_options(p +
826 				    sizeof(struct dhcp_iapd), opt_hdr.len -
827 				    sizeof(struct dhcp_iapd),
828 				    &iface->new_pds[ntohl(iapd.iaid)]);
829 
830 				if (status_code != DHCP_STATUS_SUCCESS &&
831 				    iface->state == IF_RENEWING) {
832 					state_transition(iface, IF_REBINDING);
833 					goto out;
834 				}
835 			}
836 			break;
837 		case DHO_RAPID_COMMIT:
838 			if (opt_hdr.len != 0) {
839 				log_warnx("%s: invalid rapid commit option",
840 				    __func__);
841 				goto out;
842 			}
843 			rapid_commit = 1;
844 			break;
845 		default:
846 			log_debug("unhandled option: %u", opt_hdr.code);
847 			break;
848 		}
849 
850 		p += opt_hdr.len;
851 		rem -= opt_hdr.len;
852 	}
853 
854 	/* check that we got all the information we need */
855 	if (serverid_len == 0) {
856 		log_warnx("%s: Did not receive server identifier", __func__);
857 		goto out;
858 	}
859 
860 
861 	SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) {
862 		struct prefix	*pd = &iface->new_pds[ia_conf->id];
863 
864 		if (pd->prefix_len == 0) {
865 			log_warnx("%s: no IA for IAID %d found", __func__,
866 			    ia_conf->id);
867 			goto out;
868 		}
869 		if (pd->prefix_len > ia_conf->prefix_len) {
870 			log_warnx("%s: prefix for IAID %d too small: %d > %d",
871 			    __func__, ia_conf->id, pd->prefix_len,
872 			    ia_conf->prefix_len);
873 			goto out;
874 		}
875 
876 		if (lease_time < pd->vltime)
877 			lease_time = pd->vltime;
878 
879 		log_debug("%s: pltime: %u, vltime: %u, prefix: %s/%u",
880 		    __func__, pd->pltime, pd->vltime, inet_ntop(AF_INET6,
881 		    &pd->prefix, ntopbuf, INET6_ADDRSTRLEN), pd->prefix_len);
882 	}
883 
884 	switch (hdr.msg_type) {
885 	case DHCPSOLICIT:
886 	case DHCPREQUEST:
887 	case DHCPCONFIRM:
888 	case DHCPRENEW:
889 	case DHCPREBIND:
890 	case DHCPRELEASE:
891 	case DHCPDECLINE:
892 	case DHCPINFORMATIONREQUEST:
893 		log_warnx("%s: Ignoring client-only message (%s) from server",
894 		    __func__, dhcp_message_type2str(hdr.msg_type));
895 		goto out;
896 	case DHCPRELAYFORW:
897 	case DHCPRELAYREPL:
898 		log_warnx("%s: Ignoring relay-agent-only message (%s) from "
899 		    "server", __func__, dhcp_message_type2str(hdr.msg_type));
900 		goto out;
901 	case DHCPADVERTISE:
902 		if (iface->state != IF_INIT) {
903 			log_debug("%s: ignoring unexpected %s", __func__,
904 			    dhcp_message_type2str(hdr.msg_type));
905 			goto out;
906 		}
907 		iface->serverid_len = serverid_len;
908 		memcpy(iface->serverid, serverid, SERVERID_SIZE);
909 		memcpy(iface->pds, iface->new_pds, sizeof(iface->pds));
910 		state_transition(iface, IF_REQUESTING);
911 		break;
912 	case DHCPREPLY:
913 		switch (iface->state) {
914 		case IF_REQUESTING:
915 		case IF_RENEWING:
916 		case IF_REBINDING:
917 		case IF_REBOOTING:
918 			break;
919 		case IF_INIT:
920 			if (rapid_commit && engine_conf->rapid_commit)
921 				break;
922 			/* fall through */
923 		default:
924 			log_debug("%s: ignoring unexpected %s", __func__,
925 			    dhcp_message_type2str(hdr.msg_type));
926 			goto out;
927 		}
928 		iface->serverid_len = serverid_len;
929 		memcpy(iface->serverid, serverid, SERVERID_SIZE);
930 
931 		/* XXX handle t1 = 0 or t2 = 0 */
932 		iface->t1 = t1;
933 		iface->t2 = t2;
934 		iface->lease_time = lease_time;
935 		clock_gettime(CLOCK_MONOTONIC, &iface->request_time);
936 		state_transition(iface, IF_BOUND);
937 		break;
938 	case DHCPRECONFIGURE:
939 		log_warnx("%s: Ignoring %s from server",
940 		    __func__, dhcp_message_type2str(hdr.msg_type));
941 		goto out;
942 	default:
943 		fatalx("%s: %s unhandled",
944 		    __func__, dhcp_message_type2str(hdr.msg_type));
945 		break;
946 	}
947  out:
948 	return;
949 }
950 
951 int
952 parse_ia_pd_options(uint8_t *p, size_t len, struct prefix *prefix)
953 {
954 	struct dhcp_option_hdr	 opt_hdr;
955 	struct dhcp_iaprefix	 iaprefix;
956 	struct in6_addr		 mask;
957 	int			 i;
958 	uint16_t		 status_code = DHCP_STATUS_SUCCESS;
959 	char			 ntopbuf[INET6_ADDRSTRLEN], *visbuf;
960 
961 	while (len >= sizeof(struct dhcp_option_hdr)) {
962 		memcpy(&opt_hdr, p, sizeof(struct dhcp_option_hdr));
963 		opt_hdr.code = ntohs(opt_hdr.code);
964 		opt_hdr.len = ntohs(opt_hdr.len);
965 		p += sizeof(struct dhcp_option_hdr);
966 		len -= sizeof(struct dhcp_option_hdr);
967 		if (log_getverbose() > 1)
968 			log_debug("%s: %s, len: %u", __func__,
969 			    dhcp_option_type2str(opt_hdr.code), opt_hdr.len);
970 		if (len < opt_hdr.len) {
971 			log_warnx("%s: malformed packet, ignoring", __func__);
972 			return DHCP_STATUS_UNSPECFAIL;
973 		}
974 
975 		switch (opt_hdr.code) {
976 		case DHO_IA_PREFIX:
977 			if (len < sizeof(struct dhcp_iaprefix)) {
978 				log_warnx("%s: malformed packet, ignoring",
979 				    __func__);
980 				return DHCP_STATUS_UNSPECFAIL;
981 			}
982 
983 			memcpy(&iaprefix, p, sizeof(struct dhcp_iaprefix));
984 			log_debug("%s: pltime: %u, vltime: %u, prefix: %s/%u",
985 			    __func__, ntohl(iaprefix.pltime),
986 			    ntohl(iaprefix.vltime), inet_ntop(AF_INET6,
987 			    &iaprefix.prefix, ntopbuf, INET6_ADDRSTRLEN),
988 			    iaprefix.prefix_len);
989 
990 			if (ntohl(iaprefix.vltime) < ntohl(iaprefix.pltime)) {
991 				log_warnx("%s: vltime < pltime, ignoring IA_PD",
992 				    __func__);
993 				break;
994 			}
995 
996 			if (ntohl(iaprefix.vltime) == 0) {
997 				log_debug("%s: vltime == 0, ignoring IA_PD",
998 				    __func__);
999 				break;
1000 			}
1001 
1002 			prefix->prefix = iaprefix.prefix;
1003 			prefix->prefix_len = iaprefix.prefix_len;
1004 			prefix->vltime = ntohl(iaprefix.vltime);
1005 			prefix->pltime = ntohl(iaprefix.pltime);
1006 
1007 			/* make sure prefix is masked correctly */
1008 			memset(&mask, 0, sizeof(mask));
1009 			in6_prefixlen2mask(&mask, prefix->prefix_len);
1010 			for (i = 0; i < 16; i++)
1011 				prefix->prefix.s6_addr[i] &= mask.s6_addr[i];
1012 
1013 			break;
1014 		case DHO_STATUS_CODE:
1015 			/* XXX STATUS_CODE can also appear outside of options */
1016 			if (len < 2) {
1017 				log_warnx("%s: malformed packet, ignoring",
1018 				    __func__);
1019 				return DHCP_STATUS_UNSPECFAIL;
1020 			}
1021 			memcpy(&status_code, p, sizeof(uint16_t));
1022 			status_code = ntohs(status_code);
1023 			/* must be at least 4 * srclen + 1 long */
1024 			visbuf = calloc(4, opt_hdr.len - 2 + 1);
1025 			if (visbuf == NULL) {
1026 				log_warn("%s", __func__);
1027 				break;
1028 			}
1029 			strvisx(visbuf, p + 2, opt_hdr.len - 2, VIS_SAFE);
1030 			log_debug("%s: %s - %s", __func__,
1031 			    dhcp_status2str(status_code), visbuf);
1032 			break;
1033 		default:
1034 			log_debug("unhandled option: %u", opt_hdr.code);
1035 		}
1036 		p += opt_hdr.len;
1037 		len -= opt_hdr.len;
1038 	}
1039 	return status_code;
1040 }
1041 
1042 /* XXX check valid transitions */
1043 void
1044 state_transition(struct dhcp6leased_iface *iface, enum if_state new_state)
1045 {
1046 	enum if_state	 old_state = iface->state;
1047 	char		 ifnamebuf[IF_NAMESIZE], *if_name;
1048 
1049 	iface->state = new_state;
1050 
1051 	switch (new_state) {
1052 	case IF_DOWN:
1053 		switch (old_state) {
1054 		case IF_RENEWING:
1055 		case IF_REBINDING:
1056 		case IF_REBOOTING:
1057 		case IF_BOUND:
1058 			deprecate_interfaces(iface);
1059 			break;
1060 		default:
1061 			break;
1062 		}
1063 		/*
1064 		 * Nothing else to do until iface comes up.
1065 		 * IP addresses will expire.
1066 		 */
1067 		iface->timo.tv_sec = -1;
1068 		break;
1069 	case IF_INIT:
1070 		switch (old_state) {
1071 		case IF_INIT:
1072 			if (iface->timo.tv_sec < MAX_EXP_BACKOFF_SLOW)
1073 				iface->timo.tv_sec *= 2;
1074 			break;
1075 		case IF_REQUESTING:
1076 		case IF_RENEWING:
1077 		case IF_REBINDING:
1078 		case IF_REBOOTING:
1079 			/* lease expired, got DHCPNAK or timeout: delete IP */
1080 			deconfigure_interfaces(iface);
1081 			/* fall through */
1082 		case IF_DOWN:
1083 			iface->timo.tv_sec = START_EXP_BACKOFF;
1084 			clock_gettime(CLOCK_MONOTONIC,
1085 			    &iface->elapsed_time_start);
1086 			break;
1087 		case IF_BOUND:
1088 			fatal("invalid transition Bound -> Init");
1089 			break;
1090 		}
1091 		request_dhcp_discover(iface);
1092 		break;
1093 	case IF_REBOOTING:
1094 		if (old_state == IF_REBOOTING)
1095 			iface->timo.tv_sec *= 2;
1096 		else {
1097 			iface->timo.tv_sec = START_EXP_BACKOFF;
1098 			arc4random_buf(iface->xid, sizeof(iface->xid));
1099 		}
1100 		request_dhcp_request(iface);
1101 		break;
1102 	case IF_REQUESTING:
1103 		if (old_state == IF_REQUESTING)
1104 			iface->timo.tv_sec *= 2;
1105 		else {
1106 			iface->timo.tv_sec = START_EXP_BACKOFF;
1107 			clock_gettime(CLOCK_MONOTONIC,
1108 			    &iface->elapsed_time_start);
1109 		}
1110 		request_dhcp_request(iface);
1111 		break;
1112 	case IF_BOUND:
1113 		iface->timo.tv_sec = iface->t1;
1114 		switch (old_state) {
1115 		case IF_REQUESTING:
1116 		case IF_RENEWING:
1117 		case IF_REBINDING:
1118 		case IF_REBOOTING:
1119 			configure_interfaces(iface);
1120 			break;
1121 		case IF_INIT:
1122 			if (engine_conf->rapid_commit)
1123 				configure_interfaces(iface);
1124 			else
1125 				fatal("invalid transition Init -> Bound");
1126 			break;
1127 		default:
1128 			break;
1129 		}
1130 		break;
1131 	case IF_RENEWING:
1132 		if (old_state == IF_BOUND) {
1133 			iface->timo.tv_sec = (iface->t2 -
1134 			    iface->t1) / 2; /* RFC 2131 4.4.5 */
1135 			arc4random_buf(iface->xid, sizeof(iface->xid));
1136 		} else
1137 			iface->timo.tv_sec /= 2;
1138 
1139 		if (iface->timo.tv_sec < 60)
1140 			iface->timo.tv_sec = 60;
1141 		request_dhcp_request(iface);
1142 		break;
1143 	case IF_REBINDING:
1144 		if (old_state == IF_RENEWING) {
1145 			iface->timo.tv_sec = (iface->lease_time -
1146 			    iface->t2) / 2; /* RFC 2131 4.4.5 */
1147 		} else
1148 			iface->timo.tv_sec /= 2;
1149 		request_dhcp_request(iface);
1150 		break;
1151 	}
1152 
1153 	if_name = if_indextoname(iface->if_index, ifnamebuf);
1154 	log_debug("%s[%s] %s -> %s, timo: %lld", __func__, if_name == NULL ?
1155 	    "?" : if_name, if_state_name[old_state], if_state_name[new_state],
1156 	    iface->timo.tv_sec);
1157 
1158 	if (iface->timo.tv_sec == -1) {
1159 		if (evtimer_pending(&iface->timer, NULL))
1160 			evtimer_del(&iface->timer);
1161 	} else
1162 		evtimer_add(&iface->timer, &iface->timo);
1163 }
1164 
1165 void
1166 iface_timeout(int fd, short events, void *arg)
1167 {
1168 	struct dhcp6leased_iface	*iface = (struct dhcp6leased_iface *)arg;
1169 	struct timespec		 now, res;
1170 
1171 	log_debug("%s[%d]: %s", __func__, iface->if_index,
1172 	    if_state_name[iface->state]);
1173 
1174 	switch (iface->state) {
1175 	case IF_DOWN:
1176 		state_transition(iface, IF_DOWN);
1177 		break;
1178 	case IF_INIT:
1179 		state_transition(iface, IF_INIT);
1180 		break;
1181 	case IF_REBOOTING:
1182 		if (iface->timo.tv_sec >= MAX_EXP_BACKOFF_FAST)
1183 			state_transition(iface, IF_INIT);
1184 		else
1185 			state_transition(iface, IF_REBOOTING);
1186 		break;
1187 	case IF_REQUESTING:
1188 		if (iface->timo.tv_sec >= MAX_EXP_BACKOFF_SLOW)
1189 			state_transition(iface, IF_INIT);
1190 		else
1191 			state_transition(iface, IF_REQUESTING);
1192 		break;
1193 	case IF_BOUND:
1194 		state_transition(iface, IF_RENEWING);
1195 		break;
1196 	case IF_RENEWING:
1197 		clock_gettime(CLOCK_MONOTONIC, &now);
1198 		timespecsub(&now, &iface->request_time, &res);
1199 		log_debug("%s: res.tv_sec: %lld, t2: %u", __func__,
1200 		    res.tv_sec, iface->t2);
1201 		if (res.tv_sec >= iface->t2)
1202 			state_transition(iface, IF_REBINDING);
1203 		else
1204 			state_transition(iface, IF_RENEWING);
1205 		break;
1206 	case IF_REBINDING:
1207 		clock_gettime(CLOCK_MONOTONIC, &now);
1208 		timespecsub(&now, &iface->request_time, &res);
1209 		log_debug("%s: res.tv_sec: %lld, lease_time: %u", __func__,
1210 		    res.tv_sec, iface->lease_time);
1211 		if (res.tv_sec > iface->lease_time)
1212 			state_transition(iface, IF_INIT);
1213 		else
1214 			state_transition(iface, IF_REBINDING);
1215 		break;
1216 	}
1217 }
1218 
1219 /* XXX can this be merged into dhcp_request()? */
1220 void
1221 request_dhcp_discover(struct dhcp6leased_iface *iface)
1222 {
1223 	struct imsg_req_dhcp	 imsg;
1224 	struct timespec		 now, res;
1225 
1226 	memset(&imsg, 0, sizeof(imsg));
1227 	imsg.if_index = iface->if_index;
1228 	memcpy(imsg.xid, iface->xid, sizeof(imsg.xid));
1229 	clock_gettime(CLOCK_MONOTONIC, &now);
1230 	timespecsub(&now, &iface->elapsed_time_start, &res);
1231 	if (res.tv_sec * 100 > 0xffff)
1232 		imsg.elapsed_time = 0xffff;
1233 	else
1234 		imsg.elapsed_time = res.tv_sec * 100;
1235 	engine_imsg_compose_frontend(IMSG_SEND_SOLICIT, 0, &imsg, sizeof(imsg));
1236 }
1237 
1238 void
1239 request_dhcp_request(struct dhcp6leased_iface *iface)
1240 {
1241 	struct imsg_req_dhcp	 imsg;
1242 	struct timespec		 now, res;
1243 
1244 	memset(&imsg, 0, sizeof(imsg));
1245 	imsg.if_index = iface->if_index;
1246 	memcpy(imsg.xid, iface->xid, sizeof(imsg.xid));
1247 
1248 	clock_gettime(CLOCK_MONOTONIC, &now);
1249 	timespecsub(&now, &iface->elapsed_time_start, &res);
1250 	if (res.tv_sec * 100 > 0xffff)
1251 		imsg.elapsed_time = 0xffff;
1252 	else
1253 		imsg.elapsed_time = res.tv_sec * 100;
1254 
1255 	switch (iface->state) {
1256 	case IF_DOWN:
1257 		fatalx("invalid state IF_DOWN in %s", __func__);
1258 		break;
1259 	case IF_INIT:
1260 		fatalx("invalid state IF_INIT in %s", __func__);
1261 		break;
1262 	case IF_BOUND:
1263 		fatalx("invalid state IF_BOUND in %s", __func__);
1264 		break;
1265 	case IF_REBOOTING:
1266 	case IF_REQUESTING:
1267 	case IF_RENEWING:
1268 	case IF_REBINDING:
1269 		imsg.serverid_len = iface->serverid_len;
1270 		memcpy(imsg.serverid, iface->serverid, SERVERID_SIZE);
1271 		memcpy(imsg.pds, iface->pds, sizeof(iface->pds));
1272 		break;
1273 	}
1274 	switch (iface->state) {
1275 	case IF_REQUESTING:
1276 		engine_imsg_compose_frontend(IMSG_SEND_REQUEST, 0, &imsg,
1277 		    sizeof(imsg));
1278 		break;
1279 	case IF_RENEWING:
1280 		engine_imsg_compose_frontend(IMSG_SEND_RENEW, 0, &imsg,
1281 		    sizeof(imsg));
1282 		break;
1283 	case IF_REBOOTING:
1284 	case IF_REBINDING:
1285 		engine_imsg_compose_frontend(IMSG_SEND_REBIND, 0, &imsg,
1286 		    sizeof(imsg));
1287 		break;
1288 	default:
1289 		fatalx("%s: wrong state", __func__);
1290 	}
1291 }
1292 
1293 /* XXX we need to install a reject route for the delegated prefix */
1294 void
1295 configure_interfaces(struct dhcp6leased_iface *iface)
1296 {
1297 	struct iface_conf	*iface_conf;
1298 	struct iface_ia_conf	*ia_conf;
1299 	struct iface_pd_conf	*pd_conf;
1300 	struct imsg_lease_info	 imsg_lease_info;
1301 	uint32_t	 	 i;
1302 	char		 	 ntopbuf[INET6_ADDRSTRLEN];
1303 	char			 ifnamebuf[IF_NAMESIZE], *if_name;
1304 
1305 	if ((if_name = if_indextoname(iface->if_index, ifnamebuf)) == NULL) {
1306 		log_debug("%s: unknown interface %d", __func__,
1307 		    iface->if_index);
1308 		return;
1309 	}
1310 	if ((iface_conf = find_iface_conf(&engine_conf->iface_list, if_name))
1311 	    == NULL) {
1312 		log_debug("%s: no interface configuration for %d", __func__,
1313 		    iface->if_index);
1314 		return;
1315 	}
1316 
1317 	for (i = 0; i < iface_conf->ia_count; i++) {
1318 		struct prefix	*pd = &iface->new_pds[i];
1319 
1320 		log_info("prefix delegation #%d %s/%d received on %s from "
1321 		    "server %s", i, inet_ntop(AF_INET6, &pd->prefix, ntopbuf,
1322 		    INET6_ADDRSTRLEN), pd->prefix_len, if_name,
1323 		    dhcp_duid2str(iface->serverid_len, iface->serverid));
1324 	}
1325 
1326 	SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) {
1327 		struct prefix	*pd = &iface->new_pds[ia_conf->id];
1328 
1329 		SIMPLEQ_FOREACH(pd_conf, &ia_conf->iface_pd_list, entry) {
1330 			send_reconfigure_interface(pd_conf, pd, CONFIGURE);
1331 		}
1332 	}
1333 
1334 	if (prefixcmp(iface->pds, iface->new_pds, iface_conf->ia_count) != 0) {
1335 		log_info("Prefix delegations on %s from server %s changed",
1336 		    if_name, dhcp_duid2str(iface->serverid_len,
1337 		    iface->serverid));
1338 		for (i = 0; i < iface_conf->ia_count; i++) {
1339 			log_debug("%s: iface->pds [%d]: %s/%d", __func__, i,
1340 			    inet_ntop(AF_INET6, &iface->pds[i].prefix, ntopbuf,
1341 			    INET6_ADDRSTRLEN), iface->pds[i].prefix_len);
1342 			log_debug("%s:        pds [%d]: %s/%d", __func__, i,
1343 			    inet_ntop(AF_INET6, &iface->new_pds[i].prefix,
1344 			    ntopbuf, INET6_ADDRSTRLEN),
1345 			    iface->new_pds[i].prefix_len);
1346 		}
1347 		deconfigure_interfaces(iface);
1348 	}
1349 
1350 	memcpy(iface->pds, iface->new_pds, sizeof(iface->pds));
1351 	memset(iface->new_pds, 0, sizeof(iface->new_pds));
1352 
1353 	memset(&imsg_lease_info, 0, sizeof(imsg_lease_info));
1354 	imsg_lease_info.if_index = iface->if_index;
1355 	memcpy(imsg_lease_info.pds, iface->pds, sizeof(iface->pds));
1356 	engine_imsg_compose_main(IMSG_WRITE_LEASE, 0, &imsg_lease_info,
1357 	    sizeof(imsg_lease_info));
1358 }
1359 
1360 void
1361 deconfigure_interfaces(struct dhcp6leased_iface *iface)
1362 {
1363 	struct iface_conf	*iface_conf;
1364 	struct iface_ia_conf	*ia_conf;
1365 	struct iface_pd_conf	*pd_conf;
1366 	uint32_t	 	 i;
1367 	char		 	 ntopbuf[INET6_ADDRSTRLEN];
1368 	char			 ifnamebuf[IF_NAMESIZE], *if_name;
1369 
1370 
1371 	if ((if_name = if_indextoname(iface->if_index, ifnamebuf)) == NULL) {
1372 		log_debug("%s: unknown interface %d", __func__,
1373 		    iface->if_index);
1374 		return;
1375 	}
1376 	if ((iface_conf = find_iface_conf(&engine_conf->iface_list, if_name))
1377 	    == NULL) {
1378 		log_debug("%s: no interface configuration for %d", __func__,
1379 		    iface->if_index);
1380 		return;
1381 	}
1382 
1383 	for (i = 0; i < iface_conf->ia_count; i++) {
1384 		struct prefix *pd = &iface->pds[i];
1385 
1386 		log_info("Prefix delegation #%d %s/%d expired on %s from "
1387 		    "server %s", i, inet_ntop(AF_INET6, &pd->prefix, ntopbuf,
1388 		    INET6_ADDRSTRLEN), pd->prefix_len, if_name,
1389 		    dhcp_duid2str(iface->serverid_len, iface->serverid));
1390 	}
1391 
1392 	SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) {
1393 		struct prefix	*pd = &iface->pds[ia_conf->id];
1394 
1395 		SIMPLEQ_FOREACH(pd_conf, &ia_conf->iface_pd_list, entry) {
1396 			send_reconfigure_interface(pd_conf, pd, DECONFIGURE);
1397 		}
1398 	}
1399 	memset(iface->pds, 0, sizeof(iface->pds));
1400 }
1401 
1402 void
1403 deprecate_interfaces(struct dhcp6leased_iface *iface)
1404 {
1405 	struct iface_conf	*iface_conf;
1406 	struct iface_ia_conf	*ia_conf;
1407 	struct iface_pd_conf	*pd_conf;
1408 	struct timespec		 now, diff;
1409 	uint32_t	 	 i;
1410 	char		 	 ntopbuf[INET6_ADDRSTRLEN];
1411 	char			 ifnamebuf[IF_NAMESIZE], *if_name;
1412 
1413 
1414 	if ((if_name = if_indextoname(iface->if_index, ifnamebuf)) == NULL) {
1415 		log_debug("%s: unknown interface %d", __func__,
1416 		    iface->if_index);
1417 		return;
1418 	}
1419 	if ((iface_conf = find_iface_conf(&engine_conf->iface_list, if_name))
1420 	    == NULL) {
1421 		log_debug("%s: no interface configuration for %d", __func__,
1422 		    iface->if_index);
1423 		return;
1424 	}
1425 
1426 	for (i = 0; i < iface_conf->ia_count; i++) {
1427 		struct prefix *pd = &iface->pds[i];
1428 
1429 		log_info("%s went down, deprecating prefix delegation #%d %s/%d"
1430 		    " from server %s", if_name, i, inet_ntop(AF_INET6,
1431 		    &pd->prefix, ntopbuf, INET6_ADDRSTRLEN), pd->prefix_len,
1432 		    dhcp_duid2str(iface->serverid_len, iface->serverid));
1433 	}
1434 
1435 	clock_gettime(CLOCK_MONOTONIC, &now);
1436 	timespecsub(&now, &iface->request_time, &diff);
1437 
1438 	SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) {
1439 		struct prefix	*pd = &iface->pds[ia_conf->id];
1440 
1441 		if (pd->vltime > diff.tv_sec)
1442 			pd->vltime -= diff.tv_sec;
1443 		else
1444 			pd->vltime = 0;
1445 
1446 		pd->pltime = 0;
1447 
1448 		SIMPLEQ_FOREACH(pd_conf, &ia_conf->iface_pd_list, entry) {
1449 			send_reconfigure_interface(pd_conf, pd, CONFIGURE);
1450 		}
1451 	}
1452 }
1453 
1454 int
1455 prefixcmp(struct prefix *a, struct prefix *b, int count)
1456 {
1457 	int i;
1458 
1459 	for (i = 0; i < count; i++) {
1460 		if (a[i].prefix_len != b[i].prefix_len)
1461 			return 1;
1462 		if (memcmp(&a[i].prefix, &b[i].prefix,
1463 		    sizeof(struct in6_addr)) != 0)
1464 			return 1;
1465 	}
1466 	return 0;
1467 }
1468 
1469 void
1470 send_reconfigure_interface(struct iface_pd_conf *pd_conf, struct prefix *pd,
1471     enum reconfigure_action action)
1472 {
1473 	struct imsg_configure_address	 address;
1474 	uint32_t			 if_index;
1475 	int				 i;
1476 	char				 ntopbuf[INET6_ADDRSTRLEN];
1477 
1478 	if (pd->prefix_len == 0)
1479 		return;
1480 
1481 	if (strcmp(pd_conf->name, "reserve") == 0)
1482 		return;
1483 
1484 	if ((if_index = if_nametoindex(pd_conf->name)) == 0)
1485 		return;
1486 
1487 	memset(&address, 0, sizeof(address));
1488 
1489 	address.if_index = if_index;
1490 	address.addr.sin6_family = AF_INET6;
1491 	address.addr.sin6_len = sizeof(address.addr);
1492 	address.addr.sin6_addr = pd->prefix;
1493 
1494 	for (i = 0; i < 16; i++)
1495 		address.addr.sin6_addr.s6_addr[i] |=
1496 		    pd_conf->prefix_mask.s6_addr[i];
1497 
1498 	/* XXX make this configurable & use SOII */
1499 	address.addr.sin6_addr.s6_addr[15] |= 1;
1500 
1501 	in6_prefixlen2mask(&address.mask, pd_conf->prefix_len);
1502 
1503 	log_debug("%s: %s %s: %s/%d", __func__, pd_conf->name,
1504 	    reconfigure_action_name[action], inet_ntop(AF_INET6,
1505 	    &address.addr.sin6_addr, ntopbuf, INET6_ADDRSTRLEN),
1506 	    pd_conf->prefix_len);
1507 
1508 	address.vltime = pd->vltime;
1509 	address.pltime = pd->pltime;
1510 
1511 	if (action == CONFIGURE)
1512 		engine_imsg_compose_main(IMSG_CONFIGURE_ADDRESS, 0, &address,
1513 		    sizeof(address));
1514 	else
1515 		engine_imsg_compose_main(IMSG_DECONFIGURE_ADDRESS, 0, &address,
1516 		    sizeof(address));
1517 }
1518 
1519 const char *
1520 dhcp_message_type2str(int type)
1521 {
1522 	static char buf[sizeof("Unknown [255]")];
1523 
1524 	switch (type) {
1525 	case DHCPSOLICIT:
1526 		return "DHCPSOLICIT";
1527 	case DHCPADVERTISE:
1528 		return "DHCPADVERTISE";
1529 	case DHCPREQUEST:
1530 		return "DHCPREQUEST";
1531 	case DHCPCONFIRM:
1532 		return "DHCPCONFIRM";
1533 	case DHCPRENEW:
1534 		return "DHCPRENEW";
1535 	case DHCPREBIND:
1536 		return "DHCPREBIND";
1537 	case DHCPREPLY:
1538 		return "DHCPREPLY";
1539 	case DHCPRELEASE:
1540 		return "DHCPRELEASE";
1541 	case DHCPDECLINE:
1542 		return "DHCPDECLINE";
1543 	case DHCPRECONFIGURE:
1544 		return "DHCPRECONFIGURE";
1545 	case DHCPINFORMATIONREQUEST:
1546 		return "DHCPINFORMATIONREQUEST";
1547 	case DHCPRELAYFORW:
1548 		return "DHCPRELAYFORW";
1549 	case DHCPRELAYREPL:
1550 		return "DHCPRELAYREPL";
1551 	default:
1552 		snprintf(buf, sizeof(buf), "Unknown [%u]", type & 0xff);
1553 		return buf;
1554 	}
1555 }
1556 
1557 const char *
1558 dhcp_option_type2str(int code)
1559 {
1560 	static char buf[sizeof("Unknown [65535]")];
1561 	switch (code) {
1562 	case DHO_CLIENTID:
1563 		return "DHO_CLIENTID";
1564 	case DHO_SERVERID:
1565 		return "DHO_SERVERID";
1566 	case DHO_ORO:
1567 		return "DHO_ORO";
1568 	case DHO_ELAPSED_TIME:
1569 		return "DHO_ELAPSED_TIME";
1570 	case DHO_STATUS_CODE:
1571 		return "DHO_STATUS_CODE";
1572 	case DHO_RAPID_COMMIT:
1573 		return "DHO_RAPID_COMMIT";
1574 	case DHO_VENDOR_CLASS:
1575 		return "DHO_VENDOR_CLASS";
1576 	case DHO_IA_PD:
1577 		return "DHO_IA_PD";
1578 	case DHO_IA_PREFIX:
1579 		return "DHO_IA_PREFIX";
1580 	case DHO_SOL_MAX_RT:
1581 		return "DHO_SOL_MAX_RT";
1582 	case DHO_INF_MAX_RT:
1583 		return "DHO_INF_MAX_RT";
1584 	default:
1585 		snprintf(buf, sizeof(buf), "Unknown [%u]", code &0xffff);
1586 		return buf;
1587 	}
1588 }
1589 
1590 const char*
1591 dhcp_duid2str(int len, uint8_t *p)
1592 {
1593 	static char	 buf[2 * 130];
1594 	int		 i, rem;
1595 	char		*pbuf;
1596 
1597 	if (len > 130)
1598 		return "invalid";
1599 
1600 	pbuf = buf;
1601 	rem = sizeof(buf);
1602 	for (i = 0; i < len && rem > 0; i++, pbuf += 2, rem -=2)
1603 		snprintf(pbuf, rem, "%02x", p[i]);
1604 
1605 	return buf;
1606 }
1607 
1608 const char*
1609 dhcp_status2str(int status)
1610 {
1611 	static char buf[sizeof("Unknown [255]")];
1612 
1613 	switch (status) {
1614 	case DHCP_STATUS_SUCCESS:
1615 		return "Success";
1616 	case DHCP_STATUS_UNSPECFAIL:
1617 		return "UnspecFail";
1618 	case DHCP_STATUS_NOADDRSAVAIL:
1619 		return "NoAddrsAvail";
1620 	case DHCP_STATUS_NOBINDING:
1621 		return "NoBinding";
1622 	case DHCP_STATUS_NOTONLINK:
1623 		return "NotOnLink";
1624 	case DHCP_STATUS_USEMULTICAST:
1625 		return "UseMulticast";
1626 	case DHCP_STATUS_NOPREFIXAVAIL:
1627 		return "NoPrefixAvail";
1628 	default:
1629 		snprintf(buf, sizeof(buf), "Unknown [%u]", status & 0xff);
1630 		return buf;
1631     }
1632 }
1633 
1634 /* from sys/netinet6/in6.c */
1635 /*
1636  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
1637  * All rights reserved.
1638  *
1639  * Redistribution and use in source and binary forms, with or without
1640  * modification, are permitted provided that the following conditions
1641  * are met:
1642  * 1. Redistributions of source code must retain the above copyright
1643  *    notice, this list of conditions and the following disclaimer.
1644  * 2. Redistributions in binary form must reproduce the above copyright
1645  *    notice, this list of conditions and the following disclaimer in the
1646  *    documentation and/or other materials provided with the distribution.
1647  * 3. Neither the name of the project nor the names of its contributors
1648  *    may be used to endorse or promote products derived from this software
1649  *    without specific prior written permission.
1650  *
1651  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
1652  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1653  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1654  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
1655  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1656  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1657  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1658  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1659  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1660  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1661  * SUCH DAMAGE.
1662  */
1663 void
1664 in6_prefixlen2mask(struct in6_addr *maskp, int len)
1665 {
1666 	u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff};
1667 	int bytelen, bitlen, i;
1668 
1669 	if (0 > len || len > 128)
1670 		fatalx("%s: invalid prefix length(%d)\n", __func__, len);
1671 
1672 	bzero(maskp, sizeof(*maskp));
1673 	bytelen = len / 8;
1674 	bitlen = len % 8;
1675 	for (i = 0; i < bytelen; i++)
1676 		maskp->s6_addr[i] = 0xff;
1677 	/* len == 128 is ok because bitlen == 0 then */
1678 	if (bitlen)
1679 		maskp->s6_addr[bytelen] = maskarray[bitlen - 1];
1680 }
1681