xref: /openbsd-src/sbin/dhcp6leased/engine.c (revision efb7adaa3d67e82ad4c56d3a17e32178919224b1)
1*efb7adaaSflorian /*	$OpenBSD: engine.c,v 1.31 2024/12/24 17:40:06 florian Exp $	*/
2ad7c548dSflorian 
3ad7c548dSflorian /*
4ad7c548dSflorian  * Copyright (c) 2017, 2021, 2024 Florian Obser <florian@openbsd.org>
5ad7c548dSflorian  * Copyright (c) 2004, 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 
22ad7c548dSflorian #include <sys/types.h>
23ad7c548dSflorian #include <sys/queue.h>
24ad7c548dSflorian #include <sys/socket.h>
25ad7c548dSflorian #include <sys/syslog.h>
26ad7c548dSflorian #include <sys/uio.h>
27ad7c548dSflorian #include <sys/mbuf.h>
28ad7c548dSflorian 
29ad7c548dSflorian #include <net/if.h>
30ad7c548dSflorian #include <net/route.h>
31ad7c548dSflorian #include <arpa/inet.h>
32ad7c548dSflorian #include <netinet/in.h>
33ad7c548dSflorian #include <netinet/ip.h>
34ad7c548dSflorian 
35ad7c548dSflorian #include <errno.h>
36ad7c548dSflorian #include <event.h>
37ad7c548dSflorian #include <imsg.h>
38ad7c548dSflorian #include <pwd.h>
39ad7c548dSflorian #include <signal.h>
40ad7c548dSflorian #include <stddef.h>
41ad7c548dSflorian #include <stdio.h>
42ad7c548dSflorian #include <stdlib.h>
43ad7c548dSflorian #include <string.h>
44ad7c548dSflorian #include <time.h>
45ad7c548dSflorian #include <unistd.h>
46ad7c548dSflorian #include <vis.h>
47ad7c548dSflorian 
48ad7c548dSflorian #include "log.h"
49ad7c548dSflorian #include "dhcp6leased.h"
50ad7c548dSflorian #include "engine.h"
51ad7c548dSflorian 
52ad7c548dSflorian /*
53ad7c548dSflorian  * RFC 2131 4.1 p23 has "SHOULD be 4 seconds", we are a bit more aggressive,
54ad7c548dSflorian  * networks are faster these days.
55ad7c548dSflorian  */
56ad7c548dSflorian #define	START_EXP_BACKOFF	 1
57ad7c548dSflorian #define	MAX_EXP_BACKOFF_SLOW	64 /* RFC 2131 4.1 p23 */
58ad7c548dSflorian #define	MAX_EXP_BACKOFF_FAST	 2
59ad7c548dSflorian #define	MINIMUM(a, b)		(((a) < (b)) ? (a) : (b))
60ad7c548dSflorian 
61ad7c548dSflorian enum if_state {
62ad7c548dSflorian 	IF_DOWN,
63ad7c548dSflorian 	IF_INIT,
64ad7c548dSflorian 	/* IF_SELECTING, */
65ad7c548dSflorian 	IF_REQUESTING,
66ad7c548dSflorian 	IF_BOUND,
67ad7c548dSflorian 	IF_RENEWING,
68ad7c548dSflorian 	IF_REBINDING,
69ad7c548dSflorian 	/* IF_INIT_REBOOT, */
70ad7c548dSflorian 	IF_REBOOTING,
71ad7c548dSflorian };
72ad7c548dSflorian 
73ad7c548dSflorian const char* if_state_name[] = {
74ad7c548dSflorian 	"Down",
75ad7c548dSflorian 	"Init",
76ad7c548dSflorian 	/* "Selecting", */
77ad7c548dSflorian 	"Requesting",
78ad7c548dSflorian 	"Bound",
79ad7c548dSflorian 	"Renewing",
80ad7c548dSflorian 	"Rebinding",
81ad7c548dSflorian 	/* "Init-Reboot", */
82ad7c548dSflorian 	"Rebooting",
83ad7c548dSflorian 	"IPv6 only",
84ad7c548dSflorian };
85ad7c548dSflorian 
8633c99573Sflorian enum reconfigure_action {
8733c99573Sflorian 	CONFIGURE,
8833c99573Sflorian 	DECONFIGURE,
8933c99573Sflorian };
9033c99573Sflorian 
9133c99573Sflorian const char* reconfigure_action_name[] = {
9233c99573Sflorian 	"configure",
9333c99573Sflorian 	"deconfigure",
9433c99573Sflorian };
9533c99573Sflorian 
96ad7c548dSflorian struct dhcp6leased_iface {
97ad7c548dSflorian 	LIST_ENTRY(dhcp6leased_iface)	 entries;
98ad7c548dSflorian 	enum if_state			 state;
99ad7c548dSflorian 	struct event			 timer;
100ad7c548dSflorian 	struct timeval			 timo;
101ad7c548dSflorian 	uint32_t			 if_index;
102ad7c548dSflorian 	int				 rdomain;
103ad7c548dSflorian 	int				 running;
104ad7c548dSflorian 	int				 link_state;
105ad7c548dSflorian 	uint8_t				 xid[XID_SIZE];
106ad7c548dSflorian 	int				 serverid_len;
107ad7c548dSflorian 	uint8_t				 serverid[SERVERID_SIZE];
108ad7c548dSflorian 	struct prefix			 pds[MAX_IA];
1090da4c31dSflorian 	struct prefix			 new_pds[MAX_IA];
110ad7c548dSflorian 	struct timespec			 request_time;
111ad7c548dSflorian 	struct timespec			 elapsed_time_start;
112ad7c548dSflorian 	uint32_t			 lease_time;
113ad7c548dSflorian 	uint32_t			 t1;
114ad7c548dSflorian 	uint32_t			 t2;
115ad7c548dSflorian };
116ad7c548dSflorian 
117ad7c548dSflorian LIST_HEAD(, dhcp6leased_iface) dhcp6leased_interfaces;
118ad7c548dSflorian 
119ad7c548dSflorian __dead void		 engine_shutdown(void);
120ad7c548dSflorian void			 engine_sig_handler(int sig, short, void *);
121ad7c548dSflorian void			 engine_dispatch_frontend(int, short, void *);
122ad7c548dSflorian void			 engine_dispatch_main(int, short, void *);
123ad7c548dSflorian void			 send_interface_info(struct dhcp6leased_iface *, pid_t);
124ad7c548dSflorian void			 engine_showinfo_ctl(struct imsg *, uint32_t);
125ad7c548dSflorian void			 engine_update_iface(struct imsg_ifinfo *);
126ad7c548dSflorian struct dhcp6leased_iface	*get_dhcp6leased_iface_by_id(uint32_t);
127ad7c548dSflorian void			 remove_dhcp6leased_iface(uint32_t);
128ad7c548dSflorian void			 parse_dhcp(struct dhcp6leased_iface *,
129ad7c548dSflorian 			     struct imsg_dhcp *);
13070eb162fSflorian int			 parse_ia_pd_options(uint8_t *, size_t, struct prefix *);
131ad7c548dSflorian void			 state_transition(struct dhcp6leased_iface *, enum
132ad7c548dSflorian 			     if_state);
133ad7c548dSflorian void			 iface_timeout(int, short, void *);
134ad7c548dSflorian void			 request_dhcp_discover(struct dhcp6leased_iface *);
135ad7c548dSflorian void			 request_dhcp_request(struct dhcp6leased_iface *);
136ad7c548dSflorian void			 configure_interfaces(struct dhcp6leased_iface *);
13733c99573Sflorian void			 deconfigure_interfaces(struct dhcp6leased_iface *);
138*efb7adaaSflorian void			 deprecate_interfaces(struct dhcp6leased_iface *);
1390da4c31dSflorian int			 prefixcmp(struct prefix *, struct prefix *, int);
14033c99573Sflorian void			 send_reconfigure_interface(struct iface_pd_conf *,
14133c99573Sflorian 			     struct prefix *, enum reconfigure_action);
142ad7c548dSflorian int			 engine_imsg_compose_main(int, pid_t, void *, uint16_t);
1437571100dSflorian const char		*dhcp_option_type2str(int);
144ad7c548dSflorian const char		*dhcp_duid2str(int, uint8_t *);
1457571100dSflorian const char		*dhcp_status2str(int);
146ad7c548dSflorian void			 in6_prefixlen2mask(struct in6_addr *, int len);
147ad7c548dSflorian 
148ad7c548dSflorian struct dhcp6leased_conf	*engine_conf;
149ad7c548dSflorian 
150ad7c548dSflorian static struct imsgev	*iev_frontend;
151ad7c548dSflorian static struct imsgev	*iev_main;
152ad7c548dSflorian int64_t			 proposal_id;
153ad7c548dSflorian static struct dhcp_duid	 duid;
154ad7c548dSflorian 
155ad7c548dSflorian void
156ad7c548dSflorian engine_sig_handler(int sig, short event, void *arg)
157ad7c548dSflorian {
158ad7c548dSflorian 	/*
159ad7c548dSflorian 	 * Normal signal handler rules don't apply because libevent
160ad7c548dSflorian 	 * decouples for us.
161ad7c548dSflorian 	 */
162ad7c548dSflorian 
163ad7c548dSflorian 	switch (sig) {
164ad7c548dSflorian 	case SIGINT:
165ad7c548dSflorian 	case SIGTERM:
166ad7c548dSflorian 		engine_shutdown();
167ad7c548dSflorian 	default:
168ad7c548dSflorian 		fatalx("unexpected signal");
169ad7c548dSflorian 	}
170ad7c548dSflorian }
171ad7c548dSflorian 
172ad7c548dSflorian void
173ad7c548dSflorian engine(int debug, int verbose)
174ad7c548dSflorian {
175ad7c548dSflorian 	struct event		 ev_sigint, ev_sigterm;
176ad7c548dSflorian 	struct passwd		*pw;
177ad7c548dSflorian 
178ad7c548dSflorian 	engine_conf = config_new_empty();
179ad7c548dSflorian 
180ad7c548dSflorian 	log_init(debug, LOG_DAEMON);
181ad7c548dSflorian 	log_setverbose(verbose);
182ad7c548dSflorian 
183ad7c548dSflorian 	if ((pw = getpwnam(DHCP6LEASED_USER)) == NULL)
184ad7c548dSflorian 		fatal("getpwnam");
185ad7c548dSflorian 
186ad7c548dSflorian 	if (chdir("/") == -1)
187ad7c548dSflorian 		fatal("chdir(\"/\")");
188ad7c548dSflorian 
189ad7c548dSflorian 	if (unveil("/", "") == -1)
190ad7c548dSflorian 		fatal("unveil /");
191ad7c548dSflorian 	if (unveil(NULL, NULL) == -1)
192ad7c548dSflorian 		fatal("unveil");
193ad7c548dSflorian 
194ad7c548dSflorian 	setproctitle("%s", "engine");
195ad7c548dSflorian 	log_procinit("engine");
196ad7c548dSflorian 
197ad7c548dSflorian 	if (setgroups(1, &pw->pw_gid) ||
198ad7c548dSflorian 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
199ad7c548dSflorian 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
200ad7c548dSflorian 		fatal("can't drop privileges");
201ad7c548dSflorian 
202ad7c548dSflorian 	if (pledge("stdio recvfd", NULL) == -1)
203ad7c548dSflorian 		fatal("pledge");
204ad7c548dSflorian 
205ad7c548dSflorian 	event_init();
206ad7c548dSflorian 
207ad7c548dSflorian 	/* Setup signal handler(s). */
208ad7c548dSflorian 	signal_set(&ev_sigint, SIGINT, engine_sig_handler, NULL);
209ad7c548dSflorian 	signal_set(&ev_sigterm, SIGTERM, engine_sig_handler, NULL);
210ad7c548dSflorian 	signal_add(&ev_sigint, NULL);
211ad7c548dSflorian 	signal_add(&ev_sigterm, NULL);
212ad7c548dSflorian 	signal(SIGPIPE, SIG_IGN);
213ad7c548dSflorian 	signal(SIGHUP, SIG_IGN);
214ad7c548dSflorian 
215ad7c548dSflorian 	/* Setup pipe and event handler to the main process. */
216ad7c548dSflorian 	if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
217ad7c548dSflorian 		fatal(NULL);
218ad7c548dSflorian 
2190e59d0d1Sclaudio 	if (imsgbuf_init(&iev_main->ibuf, 3) == -1)
2200e59d0d1Sclaudio 		fatal(NULL);
2210e59d0d1Sclaudio 	imsgbuf_allow_fdpass(&iev_main->ibuf);
222ad7c548dSflorian 	iev_main->handler = engine_dispatch_main;
223ad7c548dSflorian 
224ad7c548dSflorian 	/* Setup event handlers. */
225ad7c548dSflorian 	iev_main->events = EV_READ;
226ad7c548dSflorian 	event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
227ad7c548dSflorian 	    iev_main->handler, iev_main);
228ad7c548dSflorian 	event_add(&iev_main->ev, NULL);
229ad7c548dSflorian 
230ad7c548dSflorian 	LIST_INIT(&dhcp6leased_interfaces);
231ad7c548dSflorian 
232ad7c548dSflorian 	event_dispatch();
233ad7c548dSflorian 
234ad7c548dSflorian 	engine_shutdown();
235ad7c548dSflorian }
236ad7c548dSflorian 
237ad7c548dSflorian __dead void
238ad7c548dSflorian engine_shutdown(void)
239ad7c548dSflorian {
240ad7c548dSflorian 	/* Close pipes. */
2419cbf9e90Sclaudio 	imsgbuf_clear(&iev_frontend->ibuf);
242ad7c548dSflorian 	close(iev_frontend->ibuf.fd);
2439cbf9e90Sclaudio 	imsgbuf_clear(&iev_main->ibuf);
244ad7c548dSflorian 	close(iev_main->ibuf.fd);
245ad7c548dSflorian 
246ad7c548dSflorian 	free(iev_frontend);
247ad7c548dSflorian 	free(iev_main);
248ad7c548dSflorian 
249ad7c548dSflorian 	log_info("engine exiting");
250ad7c548dSflorian 	exit(0);
251ad7c548dSflorian }
252ad7c548dSflorian 
253ad7c548dSflorian int
254ad7c548dSflorian engine_imsg_compose_frontend(int type, pid_t pid, void *data,
255ad7c548dSflorian     uint16_t datalen)
256ad7c548dSflorian {
257ad7c548dSflorian 	return (imsg_compose_event(iev_frontend, type, 0, pid, -1,
258ad7c548dSflorian 	    data, datalen));
259ad7c548dSflorian }
260ad7c548dSflorian 
261ad7c548dSflorian int
262ad7c548dSflorian engine_imsg_compose_main(int type, pid_t pid, void *data,
263ad7c548dSflorian     uint16_t datalen)
264ad7c548dSflorian {
265ad7c548dSflorian 	return (imsg_compose_event(iev_main, type, 0, pid, -1,
266ad7c548dSflorian 	    data, datalen));
267ad7c548dSflorian }
268ad7c548dSflorian 
269ad7c548dSflorian void
270ad7c548dSflorian engine_dispatch_frontend(int fd, short event, void *bula)
271ad7c548dSflorian {
272ad7c548dSflorian 	struct imsgev			*iev = bula;
273ad7c548dSflorian 	struct imsgbuf			*ibuf = &iev->ibuf;
274ad7c548dSflorian 	struct imsg			 imsg;
275ad7c548dSflorian 	struct dhcp6leased_iface		*iface;
276ad7c548dSflorian 	ssize_t				 n;
277ad7c548dSflorian 	int				 shut = 0;
278ad7c548dSflorian 	int				 verbose;
279ad7c548dSflorian 	uint32_t			 if_index;
280ad7c548dSflorian 
281ad7c548dSflorian 	if (event & EV_READ) {
282668e5ba9Sclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
283dd7efffeSclaudio 			fatal("imsgbuf_read error");
284ad7c548dSflorian 		if (n == 0)	/* Connection closed. */
285ad7c548dSflorian 			shut = 1;
286ad7c548dSflorian 	}
287ad7c548dSflorian 	if (event & EV_WRITE) {
288dd7efffeSclaudio 		if (imsgbuf_write(ibuf) == -1) {
289e3b6409cSclaudio 			if (errno == EPIPE)	/* Connection closed. */
290ad7c548dSflorian 				shut = 1;
291e3b6409cSclaudio 			else
292dd7efffeSclaudio 				fatal("imsgbuf_write");
293e3b6409cSclaudio 		}
294ad7c548dSflorian 	}
295ad7c548dSflorian 
296ad7c548dSflorian 	for (;;) {
297ad7c548dSflorian 		if ((n = imsg_get(ibuf, &imsg)) == -1)
298ad7c548dSflorian 			fatal("%s: imsg_get error", __func__);
299ad7c548dSflorian 		if (n == 0)	/* No more messages. */
300ad7c548dSflorian 			break;
301ad7c548dSflorian 
302ad7c548dSflorian 		switch (imsg.hdr.type) {
303ad7c548dSflorian 		case IMSG_CTL_LOG_VERBOSE:
304ad7c548dSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(verbose))
305ad7c548dSflorian 				fatalx("%s: IMSG_CTL_LOG_VERBOSE wrong length: "
306ad7c548dSflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
307ad7c548dSflorian 			memcpy(&verbose, imsg.data, sizeof(verbose));
308ad7c548dSflorian 			log_setverbose(verbose);
309ad7c548dSflorian 			break;
310ad7c548dSflorian 		case IMSG_CTL_SHOW_INTERFACE_INFO:
311ad7c548dSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
312ad7c548dSflorian 				fatalx("%s: IMSG_CTL_SHOW_INTERFACE_INFO wrong "
313ad7c548dSflorian 				    "length: %lu", __func__,
314ad7c548dSflorian 				    IMSG_DATA_SIZE(imsg));
315ad7c548dSflorian 			memcpy(&if_index, imsg.data, sizeof(if_index));
316ad7c548dSflorian 			engine_showinfo_ctl(&imsg, if_index);
317ad7c548dSflorian 			break;
318ad7c548dSflorian 		case IMSG_REQUEST_REBOOT:
319ad7c548dSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
320ad7c548dSflorian 				fatalx("%s: IMSG_CTL_SEND_DISCOVER wrong "
321ad7c548dSflorian 				    "length: %lu", __func__,
322ad7c548dSflorian 				    IMSG_DATA_SIZE(imsg));
323ad7c548dSflorian 			memcpy(&if_index, imsg.data, sizeof(if_index));
324ad7c548dSflorian 			iface = get_dhcp6leased_iface_by_id(if_index);
325ad7c548dSflorian 			if (iface != NULL) {
326ad7c548dSflorian 				switch (iface->state) {
327ad7c548dSflorian 				case IF_DOWN:
328ad7c548dSflorian 					break;
329ad7c548dSflorian 				case IF_INIT:
330ad7c548dSflorian 				case IF_REQUESTING:
331ad7c548dSflorian 					state_transition(iface, iface->state);
332ad7c548dSflorian 					break;
333ad7c548dSflorian 				case IF_RENEWING:
334ad7c548dSflorian 				case IF_REBINDING:
335ad7c548dSflorian 				case IF_REBOOTING:
336ad7c548dSflorian 				case IF_BOUND:
337ad7c548dSflorian 					state_transition(iface, IF_REBOOTING);
338ad7c548dSflorian 					break;
339ad7c548dSflorian 				}
340ad7c548dSflorian 			}
341ad7c548dSflorian 			break;
342ad7c548dSflorian 		case IMSG_REMOVE_IF:
343ad7c548dSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
344ad7c548dSflorian 				fatalx("%s: IMSG_REMOVE_IF wrong length: %lu",
345ad7c548dSflorian 				    __func__, IMSG_DATA_SIZE(imsg));
346ad7c548dSflorian 			memcpy(&if_index, imsg.data, sizeof(if_index));
347ad7c548dSflorian 			remove_dhcp6leased_iface(if_index);
348ad7c548dSflorian 			break;
349ad7c548dSflorian 		case IMSG_DHCP: {
350ad7c548dSflorian 			struct imsg_dhcp	imsg_dhcp;
351ad7c548dSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_dhcp))
352ad7c548dSflorian 				fatalx("%s: IMSG_DHCP wrong length: %lu",
353ad7c548dSflorian 				    __func__, IMSG_DATA_SIZE(imsg));
354ad7c548dSflorian 			memcpy(&imsg_dhcp, imsg.data, sizeof(imsg_dhcp));
355ad7c548dSflorian 			iface = get_dhcp6leased_iface_by_id(imsg_dhcp.if_index);
356ad7c548dSflorian 			if (iface != NULL)
357ad7c548dSflorian 				parse_dhcp(iface, &imsg_dhcp);
358ad7c548dSflorian 			break;
359ad7c548dSflorian 		}
360ad7c548dSflorian 		default:
361ad7c548dSflorian 			log_debug("%s: unexpected imsg %d", __func__,
362ad7c548dSflorian 			    imsg.hdr.type);
363ad7c548dSflorian 			break;
364ad7c548dSflorian 		}
365ad7c548dSflorian 		imsg_free(&imsg);
366ad7c548dSflorian 	}
367ad7c548dSflorian 	if (!shut)
368ad7c548dSflorian 		imsg_event_add(iev);
369ad7c548dSflorian 	else {
370ad7c548dSflorian 		/* This pipe is dead. Remove its event handler. */
371ad7c548dSflorian 		event_del(&iev->ev);
372ad7c548dSflorian 		event_loopexit(NULL);
373ad7c548dSflorian 	}
374ad7c548dSflorian }
375ad7c548dSflorian 
376ad7c548dSflorian void
377ad7c548dSflorian engine_dispatch_main(int fd, short event, void *bula)
378ad7c548dSflorian {
379ad7c548dSflorian 	static struct dhcp6leased_conf	*nconf;
380ad7c548dSflorian 	static struct iface_conf	*iface_conf;
381ad7c548dSflorian 	static struct iface_ia_conf	*iface_ia_conf;
382ad7c548dSflorian 	struct iface_pd_conf		*iface_pd_conf;
383ad7c548dSflorian 	struct imsg			 imsg;
384ad7c548dSflorian 	struct imsgev			*iev = bula;
385ad7c548dSflorian 	struct imsgbuf			*ibuf = &iev->ibuf;
386ad7c548dSflorian 	struct imsg_ifinfo		 imsg_ifinfo;
387ad7c548dSflorian 	ssize_t				 n;
388ad7c548dSflorian 	int				 shut = 0;
389ad7c548dSflorian 
390ad7c548dSflorian 	if (event & EV_READ) {
391668e5ba9Sclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
392dd7efffeSclaudio 			fatal("imsgbuf_read error");
393ad7c548dSflorian 		if (n == 0)	/* Connection closed. */
394ad7c548dSflorian 			shut = 1;
395ad7c548dSflorian 	}
396ad7c548dSflorian 	if (event & EV_WRITE) {
397dd7efffeSclaudio 		if (imsgbuf_write(ibuf) == -1) {
398e3b6409cSclaudio 			if (errno == EPIPE)	/* Connection closed. */
399ad7c548dSflorian 				shut = 1;
400e3b6409cSclaudio 			else
401dd7efffeSclaudio 				fatal("imsgbuf_write");
402e3b6409cSclaudio 		}
403ad7c548dSflorian 	}
404ad7c548dSflorian 
405ad7c548dSflorian 	for (;;) {
406ad7c548dSflorian 		if ((n = imsg_get(ibuf, &imsg)) == -1)
407ad7c548dSflorian 			fatal("%s: imsg_get error", __func__);
408ad7c548dSflorian 		if (n == 0)	/* No more messages. */
409ad7c548dSflorian 			break;
410ad7c548dSflorian 
411ad7c548dSflorian 		switch (imsg.hdr.type) {
412ad7c548dSflorian 		case IMSG_SOCKET_IPC:
413ad7c548dSflorian 			/*
414ad7c548dSflorian 			 * Setup pipe and event handler to the frontend
415ad7c548dSflorian 			 * process.
416ad7c548dSflorian 			 */
417ad7c548dSflorian 			if (iev_frontend)
418ad7c548dSflorian 				fatalx("%s: received unexpected imsg fd "
419ad7c548dSflorian 				    "to engine", __func__);
420ad7c548dSflorian 
421ad7c548dSflorian 			if ((fd = imsg_get_fd(&imsg)) == -1)
422ad7c548dSflorian 				fatalx("%s: expected to receive imsg fd to "
423ad7c548dSflorian 				   "engine but didn't receive any", __func__);
424ad7c548dSflorian 
425ad7c548dSflorian 			iev_frontend = malloc(sizeof(struct imsgev));
426ad7c548dSflorian 			if (iev_frontend == NULL)
427ad7c548dSflorian 				fatal(NULL);
428ad7c548dSflorian 
4290e59d0d1Sclaudio 			if (imsgbuf_init(&iev_frontend->ibuf, fd) == -1)
4300e59d0d1Sclaudio 				fatal(NULL);
431ad7c548dSflorian 			iev_frontend->handler = engine_dispatch_frontend;
432ad7c548dSflorian 			iev_frontend->events = EV_READ;
433ad7c548dSflorian 
434ad7c548dSflorian 			event_set(&iev_frontend->ev, iev_frontend->ibuf.fd,
435ad7c548dSflorian 			iev_frontend->events, iev_frontend->handler,
436ad7c548dSflorian 			    iev_frontend);
437ad7c548dSflorian 			event_add(&iev_frontend->ev, NULL);
438ad7c548dSflorian 
439ad7c548dSflorian 			if (pledge("stdio", NULL) == -1)
440ad7c548dSflorian 				fatal("pledge");
441ad7c548dSflorian 
442ad7c548dSflorian 			break;
443ad7c548dSflorian 		case IMSG_UUID:
444ad7c548dSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(duid.uuid))
445ad7c548dSflorian 				fatalx("%s: IMSG_UUID wrong length: "
446ad7c548dSflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
447ad7c548dSflorian 			duid.type = htons(DUID_UUID_TYPE);
448ad7c548dSflorian 			memcpy(duid.uuid, imsg.data, sizeof(duid.uuid));
449ad7c548dSflorian 			break;
450ad7c548dSflorian 		case IMSG_UPDATE_IF:
451ad7c548dSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_ifinfo))
452ad7c548dSflorian 				fatalx("%s: IMSG_UPDATE_IF wrong length: %lu",
453ad7c548dSflorian 				    __func__, IMSG_DATA_SIZE(imsg));
454ad7c548dSflorian 			memcpy(&imsg_ifinfo, imsg.data, sizeof(imsg_ifinfo));
455ad7c548dSflorian 			engine_update_iface(&imsg_ifinfo);
456ad7c548dSflorian 			break;
457ad7c548dSflorian 		case IMSG_RECONF_CONF:
458ad7c548dSflorian 			if (nconf != NULL)
459ad7c548dSflorian 				fatalx("%s: IMSG_RECONF_CONF already in "
460ad7c548dSflorian 				    "progress", __func__);
4613ae4b9dfSflorian 			if (IMSG_DATA_SIZE(imsg) !=
4623ae4b9dfSflorian 			    sizeof(struct dhcp6leased_conf))
4633ae4b9dfSflorian 				fatalx("%s: IMSG_RECONF_CONF wrong length: %lu",
4643ae4b9dfSflorian 				    __func__, IMSG_DATA_SIZE(imsg));
465ad7c548dSflorian 			if ((nconf = malloc(sizeof(struct dhcp6leased_conf))) ==
466ad7c548dSflorian 			    NULL)
467ad7c548dSflorian 				fatal(NULL);
4683ae4b9dfSflorian 			memcpy(nconf, imsg.data,
4693ae4b9dfSflorian 			    sizeof(struct dhcp6leased_conf));
470ad7c548dSflorian 			SIMPLEQ_INIT(&nconf->iface_list);
471ad7c548dSflorian 			break;
472ad7c548dSflorian 		case IMSG_RECONF_IFACE:
473ad7c548dSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
474ad7c548dSflorian 			    iface_conf))
475ad7c548dSflorian 				fatalx("%s: IMSG_RECONF_IFACE wrong length: "
476ad7c548dSflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
477ad7c548dSflorian 			if ((iface_conf = malloc(sizeof(struct iface_conf)))
478ad7c548dSflorian 			    == NULL)
479ad7c548dSflorian 				fatal(NULL);
480ad7c548dSflorian 			memcpy(iface_conf, imsg.data, sizeof(struct
481ad7c548dSflorian 			    iface_conf));
482ad7c548dSflorian 			SIMPLEQ_INIT(&iface_conf->iface_ia_list);
483ad7c548dSflorian 			SIMPLEQ_INSERT_TAIL(&nconf->iface_list,
484ad7c548dSflorian 			    iface_conf, entry);
485ad7c548dSflorian 			iface_conf->ia_count = 0;
486ad7c548dSflorian 			break;
487ad7c548dSflorian 		case IMSG_RECONF_IFACE_IA:
488ad7c548dSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
489ad7c548dSflorian 			    iface_ia_conf))
490ad7c548dSflorian 				fatalx("%s: IMSG_RECONF_IFACE_IA wrong "
491ad7c548dSflorian 				    "length: %lu", __func__,
492ad7c548dSflorian 				    IMSG_DATA_SIZE(imsg));
493ad7c548dSflorian 			if ((iface_ia_conf =
494ad7c548dSflorian 			    malloc(sizeof(struct iface_ia_conf))) == NULL)
495ad7c548dSflorian 				fatal(NULL);
496ad7c548dSflorian 			memcpy(iface_ia_conf, imsg.data, sizeof(struct
497ad7c548dSflorian 			    iface_ia_conf));
498ad7c548dSflorian 			SIMPLEQ_INIT(&iface_ia_conf->iface_pd_list);
499ad7c548dSflorian 			SIMPLEQ_INSERT_TAIL(&iface_conf->iface_ia_list,
500ad7c548dSflorian 			    iface_ia_conf, entry);
50192119d76Sflorian 			iface_ia_conf->id = iface_conf->ia_count++;
502ad7c548dSflorian 			if (iface_conf->ia_count > MAX_IA)
503ad7c548dSflorian 				fatalx("Too many prefix delegation requests.");
504ad7c548dSflorian 			break;
505ad7c548dSflorian 		case IMSG_RECONF_IFACE_PD:
506ad7c548dSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
507ad7c548dSflorian 			    iface_pd_conf))
508ad7c548dSflorian 				fatalx("%s: IMSG_RECONF_IFACE_PD wrong length: "
509ad7c548dSflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
510ad7c548dSflorian 			if ((iface_pd_conf =
511ad7c548dSflorian 			    malloc(sizeof(struct iface_pd_conf))) == NULL)
512ad7c548dSflorian 				fatal(NULL);
513ad7c548dSflorian 			memcpy(iface_pd_conf, imsg.data, sizeof(struct
514ad7c548dSflorian 			    iface_pd_conf));
515ad7c548dSflorian 			SIMPLEQ_INSERT_TAIL(&iface_ia_conf->iface_pd_list,
516ad7c548dSflorian 			    iface_pd_conf, entry);
517ad7c548dSflorian 			break;
518ad7c548dSflorian 		case IMSG_RECONF_IFACE_IA_END:
519ad7c548dSflorian 			iface_ia_conf = NULL;
520ad7c548dSflorian 			break;
521ad7c548dSflorian 		case IMSG_RECONF_IFACE_END:
522ad7c548dSflorian 			iface_conf = NULL;
523ad7c548dSflorian 			break;
524ad7c548dSflorian 		case IMSG_RECONF_END: {
525ad7c548dSflorian 			struct dhcp6leased_iface	*iface;
526ad7c548dSflorian 			int			*ifaces;
527ad7c548dSflorian 			int			 i, if_index;
528ad7c548dSflorian 			char			*if_name;
529ad7c548dSflorian 			char			 ifnamebuf[IF_NAMESIZE];
530ad7c548dSflorian 
531ad7c548dSflorian 			if (nconf == NULL)
532ad7c548dSflorian 				fatalx("%s: IMSG_RECONF_END without "
533ad7c548dSflorian 				    "IMSG_RECONF_CONF", __func__);
534ad7c548dSflorian 			ifaces = changed_ifaces(engine_conf, nconf);
535ad7c548dSflorian 			merge_config(engine_conf, nconf);
536ad7c548dSflorian 			nconf = NULL;
537ad7c548dSflorian 			for (i = 0; ifaces[i] != 0; i++) {
538ad7c548dSflorian 				if_index = ifaces[i];
539ad7c548dSflorian 				if_name = if_indextoname(if_index, ifnamebuf);
540ad7c548dSflorian 				iface = get_dhcp6leased_iface_by_id(if_index);
541ad7c548dSflorian 				if (if_name == NULL || iface == NULL)
542ad7c548dSflorian 					continue;
543ad7c548dSflorian 				iface_conf = find_iface_conf(
544ad7c548dSflorian 				    &engine_conf->iface_list, if_name);
545ad7c548dSflorian 				if (iface_conf == NULL)
546ad7c548dSflorian 					continue;
547ad7c548dSflorian 			}
548ad7c548dSflorian 			free(ifaces);
549ad7c548dSflorian 			break;
550ad7c548dSflorian 		}
551ad7c548dSflorian 		default:
552ad7c548dSflorian 			log_debug("%s: unexpected imsg %d", __func__,
553ad7c548dSflorian 			    imsg.hdr.type);
554ad7c548dSflorian 			break;
555ad7c548dSflorian 		}
556ad7c548dSflorian 		imsg_free(&imsg);
557ad7c548dSflorian 	}
558ad7c548dSflorian 	if (!shut)
559ad7c548dSflorian 		imsg_event_add(iev);
560ad7c548dSflorian 	else {
561ad7c548dSflorian 		/* This pipe is dead. Remove its event handler. */
562ad7c548dSflorian 		event_del(&iev->ev);
563ad7c548dSflorian 		event_loopexit(NULL);
564ad7c548dSflorian 	}
565ad7c548dSflorian }
566ad7c548dSflorian 
567ad7c548dSflorian void
568ad7c548dSflorian send_interface_info(struct dhcp6leased_iface *iface, pid_t pid)
569ad7c548dSflorian {
570ad7c548dSflorian 	struct ctl_engine_info	 cei;
571ad7c548dSflorian 
572ad7c548dSflorian 	memset(&cei, 0, sizeof(cei));
573ad7c548dSflorian 	cei.if_index = iface->if_index;
574ad7c548dSflorian 	cei.running = iface->running;
575ad7c548dSflorian 	cei.link_state = iface->link_state;
576ad7c548dSflorian 	strlcpy(cei.state, if_state_name[iface->state], sizeof(cei.state));
577ad7c548dSflorian 	memcpy(&cei.request_time, &iface->request_time,
578ad7c548dSflorian 	    sizeof(cei.request_time));
579ad7c548dSflorian 	cei.lease_time = iface->lease_time;
580ad7c548dSflorian 	cei.t1 = iface->t1;
581ad7c548dSflorian 	cei.t2 = iface->t2;
582f35103ceSflorian 	memcpy(&cei.pds, &iface->pds, sizeof(cei.pds));
583ad7c548dSflorian 	engine_imsg_compose_frontend(IMSG_CTL_SHOW_INTERFACE_INFO, pid, &cei,
584ad7c548dSflorian 	    sizeof(cei));
585ad7c548dSflorian }
586ad7c548dSflorian 
587ad7c548dSflorian void
588ad7c548dSflorian engine_showinfo_ctl(struct imsg *imsg, uint32_t if_index)
589ad7c548dSflorian {
590ad7c548dSflorian 	struct dhcp6leased_iface			*iface;
591ad7c548dSflorian 
592ad7c548dSflorian 	switch (imsg->hdr.type) {
593ad7c548dSflorian 	case IMSG_CTL_SHOW_INTERFACE_INFO:
594ad7c548dSflorian 		if ((iface = get_dhcp6leased_iface_by_id(if_index)) != NULL)
595ad7c548dSflorian 			send_interface_info(iface, imsg->hdr.pid);
596ad7c548dSflorian 		else
597ad7c548dSflorian 			engine_imsg_compose_frontend(IMSG_CTL_END,
598ad7c548dSflorian 			    imsg->hdr.pid, NULL, 0);
599ad7c548dSflorian 		break;
600ad7c548dSflorian 	default:
601ad7c548dSflorian 		log_debug("%s: error handling imsg", __func__);
602ad7c548dSflorian 		break;
603ad7c548dSflorian 	}
604ad7c548dSflorian }
605ad7c548dSflorian 
606ad7c548dSflorian void
607ad7c548dSflorian engine_update_iface(struct imsg_ifinfo *imsg_ifinfo)
608ad7c548dSflorian {
609ad7c548dSflorian 	struct dhcp6leased_iface	*iface;
610763cddbdSflorian 	struct iface_conf		*iface_conf;
611ad7c548dSflorian 	int				 need_refresh = 0;
612763cddbdSflorian 	char				 ifnamebuf[IF_NAMESIZE], *if_name;
613ad7c548dSflorian 
614ad7c548dSflorian 	iface = get_dhcp6leased_iface_by_id(imsg_ifinfo->if_index);
615ad7c548dSflorian 
616ad7c548dSflorian 	if (iface == NULL) {
617ad7c548dSflorian 		if ((iface = calloc(1, sizeof(*iface))) == NULL)
618ad7c548dSflorian 			fatal("calloc");
619ad7c548dSflorian 		iface->state = IF_DOWN;
620ad7c548dSflorian 		arc4random_buf(iface->xid, sizeof(iface->xid));
621ad7c548dSflorian 		iface->timo.tv_usec = arc4random_uniform(1000000);
622ad7c548dSflorian 		evtimer_set(&iface->timer, iface_timeout, iface);
623ad7c548dSflorian 		iface->if_index = imsg_ifinfo->if_index;
624ad7c548dSflorian 		iface->rdomain = imsg_ifinfo->rdomain;
625ad7c548dSflorian 		iface->running = imsg_ifinfo->running;
626ad7c548dSflorian 		iface->link_state = imsg_ifinfo->link_state;
627ad7c548dSflorian 		LIST_INSERT_HEAD(&dhcp6leased_interfaces, iface, entries);
628ad7c548dSflorian 		need_refresh = 1;
629ad7c548dSflorian 	} else {
630ad7c548dSflorian 		if (imsg_ifinfo->rdomain != iface->rdomain) {
631ad7c548dSflorian 			iface->rdomain = imsg_ifinfo->rdomain;
632ad7c548dSflorian 			need_refresh = 1;
633ad7c548dSflorian 		}
634ad7c548dSflorian 		if (imsg_ifinfo->running != iface->running) {
635ad7c548dSflorian 			iface->running = imsg_ifinfo->running;
636ad7c548dSflorian 			need_refresh = 1;
637ad7c548dSflorian 		}
638ad7c548dSflorian 
639ad7c548dSflorian 		if (imsg_ifinfo->link_state != iface->link_state) {
640ad7c548dSflorian 			iface->link_state = imsg_ifinfo->link_state;
641ad7c548dSflorian 			need_refresh = 1;
642ad7c548dSflorian 		}
643ad7c548dSflorian 	}
644ad7c548dSflorian 
645ad7c548dSflorian 	if (!need_refresh)
646ad7c548dSflorian 		return;
647ad7c548dSflorian 
648763cddbdSflorian 	if ((if_name = if_indextoname(iface->if_index, ifnamebuf)) == NULL) {
649763cddbdSflorian 		log_debug("%s: unknown interface %d", __func__,
650763cddbdSflorian 		    iface->if_index);
651763cddbdSflorian 		return;
652763cddbdSflorian 	}
653ad7c548dSflorian 
654763cddbdSflorian 	if ((iface_conf = find_iface_conf(&engine_conf->iface_list, if_name))
655763cddbdSflorian 	    == NULL) {
656763cddbdSflorian 		log_debug("%s: no interface configuration for %d", __func__,
657763cddbdSflorian 		    iface->if_index);
658763cddbdSflorian 		return;
659763cddbdSflorian 	}
660763cddbdSflorian 
661763cddbdSflorian 	if (iface->running && LINK_STATE_IS_UP(iface->link_state)) {
662763cddbdSflorian 		uint32_t	 i;
663763cddbdSflorian 		int		 got_lease;
664763cddbdSflorian 
665763cddbdSflorian 		if (iface->pds[0].prefix_len == 0)
666763cddbdSflorian 			memcpy(iface->pds, imsg_ifinfo->pds,
667763cddbdSflorian 			    sizeof(iface->pds));
668763cddbdSflorian 
669763cddbdSflorian 		got_lease = 0;
670763cddbdSflorian 		for (i = 0; i < iface_conf->ia_count; i++) {
671763cddbdSflorian 			if (iface->pds[i].prefix_len > 0) {
672763cddbdSflorian 				got_lease = 1;
673763cddbdSflorian 				break;
674763cddbdSflorian 			}
675763cddbdSflorian 		}
676763cddbdSflorian 		if (got_lease)
677ad7c548dSflorian 			state_transition(iface, IF_REBOOTING);
678763cddbdSflorian 		else
679ad7c548dSflorian 			state_transition(iface, IF_INIT);
680ad7c548dSflorian 	} else
681ad7c548dSflorian 		state_transition(iface, IF_DOWN);
682ad7c548dSflorian }
683ad7c548dSflorian struct dhcp6leased_iface*
684ad7c548dSflorian get_dhcp6leased_iface_by_id(uint32_t if_index)
685ad7c548dSflorian {
686ad7c548dSflorian 	struct dhcp6leased_iface	*iface;
687ad7c548dSflorian 	LIST_FOREACH (iface, &dhcp6leased_interfaces, entries) {
688ad7c548dSflorian 		if (iface->if_index == if_index)
689ad7c548dSflorian 			return (iface);
690ad7c548dSflorian 	}
691ad7c548dSflorian 
692ad7c548dSflorian 	return (NULL);
693ad7c548dSflorian }
694ad7c548dSflorian 
695ad7c548dSflorian void
696ad7c548dSflorian remove_dhcp6leased_iface(uint32_t if_index)
697ad7c548dSflorian {
698ad7c548dSflorian 	struct dhcp6leased_iface	*iface;
699ad7c548dSflorian 
700ad7c548dSflorian 	iface = get_dhcp6leased_iface_by_id(if_index);
701ad7c548dSflorian 
702ad7c548dSflorian 	if (iface == NULL)
703ad7c548dSflorian 		return;
704ad7c548dSflorian 
70533c99573Sflorian 	deconfigure_interfaces(iface);
706ad7c548dSflorian 	LIST_REMOVE(iface, entries);
707ad7c548dSflorian 	evtimer_del(&iface->timer);
708ad7c548dSflorian 	free(iface);
709ad7c548dSflorian }
710ad7c548dSflorian 
711ad7c548dSflorian void
712ad7c548dSflorian parse_dhcp(struct dhcp6leased_iface *iface, struct imsg_dhcp *dhcp)
713ad7c548dSflorian {
714ad7c548dSflorian 	struct iface_conf	*iface_conf;
715ad7c548dSflorian 	struct iface_ia_conf	*ia_conf;
716ad7c548dSflorian 	struct dhcp_hdr		 hdr;
717ad7c548dSflorian 	struct dhcp_option_hdr	 opt_hdr;
718ad7c548dSflorian 	struct dhcp_iapd	 iapd;
719ad7c548dSflorian 	size_t			 rem;
7201c8c2e64Sflorian 	uint32_t		 t1, t2, lease_time;
7213ae4b9dfSflorian 	int			 serverid_len, rapid_commit = 0;
722ad7c548dSflorian 	uint8_t			 serverid[SERVERID_SIZE];
723ad7c548dSflorian 	uint8_t			*p;
724ad7c548dSflorian 	char			 ifnamebuf[IF_NAMESIZE], *if_name;
725ad7c548dSflorian 	char			 ntopbuf[INET6_ADDRSTRLEN];
726ad7c548dSflorian 
727ad7c548dSflorian 	if ((if_name = if_indextoname(iface->if_index, ifnamebuf)) == NULL) {
728ad7c548dSflorian 		log_debug("%s: unknown interface %d", __func__,
729ad7c548dSflorian 		    iface->if_index);
730ad7c548dSflorian 		goto out;
731ad7c548dSflorian 	}
732ad7c548dSflorian 	if ((iface_conf = find_iface_conf(&engine_conf->iface_list, if_name))
733ad7c548dSflorian 	    == NULL) {
734ad7c548dSflorian 		log_debug("%s: no interface configuration for %d", __func__,
735ad7c548dSflorian 		    iface->if_index);
736ad7c548dSflorian 		goto out;
737ad7c548dSflorian 	}
738ad7c548dSflorian 
739ad7c548dSflorian 	log_debug("%s: %s ia_count: %d", __func__, if_name,
740ad7c548dSflorian 	    iface_conf->ia_count);
741ad7c548dSflorian 
7421c8c2e64Sflorian 	serverid_len = t1 = t2 = lease_time = 0;
7430da4c31dSflorian 	memset(iface->new_pds, 0, sizeof(iface->new_pds));
744ad7c548dSflorian 
745ad7c548dSflorian 	p = dhcp->packet;
746ad7c548dSflorian 	rem = dhcp->len;
747ad7c548dSflorian 
748ad7c548dSflorian 	if (rem < sizeof(struct dhcp_hdr)) {
749ad7c548dSflorian 		log_warnx("%s: message too short", __func__);
750ad7c548dSflorian 		goto out;
751ad7c548dSflorian 	}
752ad7c548dSflorian 	memcpy(&hdr, p, sizeof(struct dhcp_hdr));
753ad7c548dSflorian 	p += sizeof(struct dhcp_hdr);
754ad7c548dSflorian 	rem -= sizeof(struct dhcp_hdr);
755ad7c548dSflorian 
756ad7c548dSflorian 	if (log_getverbose() > 1)
757ad7c548dSflorian 		log_debug("%s: %s, xid: 0x%02x%02x%02x", __func__,
758ad7c548dSflorian 		    dhcp_message_type2str(hdr.msg_type), hdr.xid[0], hdr.xid[1],
759ad7c548dSflorian 		    hdr.xid[2]);
760ad7c548dSflorian 
761ad7c548dSflorian 	while (rem >= sizeof(struct dhcp_option_hdr)) {
762ad7c548dSflorian 		memcpy(&opt_hdr, p, sizeof(struct dhcp_option_hdr));
763ad7c548dSflorian 		opt_hdr.code = ntohs(opt_hdr.code);
764ad7c548dSflorian 		opt_hdr.len = ntohs(opt_hdr.len);
765ad7c548dSflorian 		p += sizeof(struct dhcp_option_hdr);
766ad7c548dSflorian 		rem -= sizeof(struct dhcp_option_hdr);
767ad7c548dSflorian 		if (log_getverbose() > 1)
768ad7c548dSflorian 			log_debug("%s: %s, len: %u", __func__,
769ad7c548dSflorian 			    dhcp_option_type2str(opt_hdr.code), opt_hdr.len);
770ad7c548dSflorian 
771ad7c548dSflorian 		if (rem < opt_hdr.len) {
772ad7c548dSflorian 			log_warnx("%s: malformed packet, ignoring", __func__);
773ad7c548dSflorian 			goto out;
774ad7c548dSflorian 		}
775ad7c548dSflorian 
776ad7c548dSflorian 		switch (opt_hdr.code) {
777ad7c548dSflorian 		case DHO_CLIENTID:
778ad7c548dSflorian 			if (opt_hdr.len != sizeof(struct dhcp_duid) ||
779ad7c548dSflorian 			    memcmp(&duid, p, sizeof(struct dhcp_duid)) != 0) {
780ad7c548dSflorian 				log_debug("%s: message not for us", __func__);
781ad7c548dSflorian 				goto out;
782ad7c548dSflorian 			}
783ad7c548dSflorian 			break;
784ad7c548dSflorian 		case DHO_SERVERID:
785ad7c548dSflorian 			/*
786ad7c548dSflorian 			 * RFC 8415, 11.1:
787ad7c548dSflorian 			 * The length of the DUID (not including the type code)
788ad7c548dSflorian 			 * is at least 1 octet and at most 128 octets.
789ad7c548dSflorian 			 */
790ad7c548dSflorian 			if (opt_hdr.len < 2 + 1) {
791ad7c548dSflorian 				log_warnx("%s: SERVERID too short", __func__);
792ad7c548dSflorian 				goto out;
793ad7c548dSflorian 			}
794ad7c548dSflorian 			if (opt_hdr.len > SERVERID_SIZE) {
795ad7c548dSflorian 				log_warnx("%s: SERVERID too long", __func__);
796ad7c548dSflorian 				goto out;
797ad7c548dSflorian 			}
798ad7c548dSflorian 			log_debug("%s: SERVERID: %s", __func__,
799ad7c548dSflorian 			    dhcp_duid2str(opt_hdr.len, p));
800ad7c548dSflorian 			if (serverid_len != 0) {
801ad7c548dSflorian 				log_warnx("%s: duplicate SERVERID option",
802ad7c548dSflorian 				    __func__);
803ad7c548dSflorian 				goto out;
804ad7c548dSflorian 			}
805ad7c548dSflorian 			serverid_len = opt_hdr.len;
806ad7c548dSflorian 			memcpy(serverid, p, serverid_len);
807ad7c548dSflorian 			break;
808ad7c548dSflorian 		case DHO_IA_PD:
809ad7c548dSflorian 			if (opt_hdr.len < sizeof(struct dhcp_iapd)) {
810ad7c548dSflorian 				log_warnx("%s: IA_PD too short", __func__);
811ad7c548dSflorian 				goto out;
812ad7c548dSflorian 			}
813ad7c548dSflorian 			memcpy(&iapd, p, sizeof(struct dhcp_iapd));
814ad7c548dSflorian 
815ad7c548dSflorian 			if (t1 == 0 || t1 > ntohl(iapd.t1))
816ad7c548dSflorian 				t1 = ntohl(iapd.t1);
817ad7c548dSflorian 			if (t2 == 0 || t2 > ntohl(iapd.t2))
818ad7c548dSflorian 				t2 = ntohl(iapd.t2);
819ad7c548dSflorian 
820ad7c548dSflorian 			log_debug("%s: IA_PD, IAID: %08x, T1: %u, T2: %u",
821ad7c548dSflorian 			    __func__, ntohl(iapd.iaid), ntohl(iapd.t1),
822ad7c548dSflorian 			    ntohl(iapd.t2));
82370eb162fSflorian 			if (ntohl(iapd.iaid) < iface_conf->ia_count) {
82470eb162fSflorian 				int status_code;
82570eb162fSflorian 				status_code = parse_ia_pd_options(p +
826ad7c548dSflorian 				    sizeof(struct dhcp_iapd), opt_hdr.len -
827ad7c548dSflorian 				    sizeof(struct dhcp_iapd),
8280da4c31dSflorian 				    &iface->new_pds[ntohl(iapd.iaid)]);
82970eb162fSflorian 
83070eb162fSflorian 				if (status_code != DHCP_STATUS_SUCCESS &&
83170eb162fSflorian 				    iface->state == IF_RENEWING) {
83270eb162fSflorian 					state_transition(iface, IF_REBINDING);
83370eb162fSflorian 					goto out;
83470eb162fSflorian 				}
83570eb162fSflorian 			}
836ad7c548dSflorian 			break;
8373ae4b9dfSflorian 		case DHO_RAPID_COMMIT:
8383ae4b9dfSflorian 			if (opt_hdr.len != 0) {
8393ae4b9dfSflorian 				log_warnx("%s: invalid rapid commit option",
8403ae4b9dfSflorian 				    __func__);
8413ae4b9dfSflorian 				goto out;
8423ae4b9dfSflorian 			}
8433ae4b9dfSflorian 			rapid_commit = 1;
8443ae4b9dfSflorian 			break;
845ad7c548dSflorian 		default:
846ad7c548dSflorian 			log_debug("unhandled option: %u", opt_hdr.code);
847ad7c548dSflorian 			break;
848ad7c548dSflorian 		}
849ad7c548dSflorian 
850ad7c548dSflorian 		p += opt_hdr.len;
851ad7c548dSflorian 		rem -= opt_hdr.len;
852ad7c548dSflorian 	}
853ad7c548dSflorian 
854ad7c548dSflorian 	/* check that we got all the information we need */
855ad7c548dSflorian 	if (serverid_len == 0) {
856ad7c548dSflorian 		log_warnx("%s: Did not receive server identifier", __func__);
857ad7c548dSflorian 		goto out;
858ad7c548dSflorian 	}
859ad7c548dSflorian 
860ad7c548dSflorian 
861ad7c548dSflorian 	SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) {
8620da4c31dSflorian 		struct prefix	*pd = &iface->new_pds[ia_conf->id];
863ad7c548dSflorian 
864ad7c548dSflorian 		if (pd->prefix_len == 0) {
865ad7c548dSflorian 			log_warnx("%s: no IA for IAID %d found", __func__,
866ad7c548dSflorian 			    ia_conf->id);
867ad7c548dSflorian 			goto out;
868ad7c548dSflorian 		}
869ad7c548dSflorian 		if (pd->prefix_len > ia_conf->prefix_len) {
870ad7c548dSflorian 			log_warnx("%s: prefix for IAID %d too small: %d > %d",
871ad7c548dSflorian 			    __func__, ia_conf->id, pd->prefix_len,
872ad7c548dSflorian 			    ia_conf->prefix_len);
873ad7c548dSflorian 			goto out;
874ad7c548dSflorian 		}
8751c8c2e64Sflorian 
8761c8c2e64Sflorian 		if (lease_time < pd->vltime)
8771c8c2e64Sflorian 			lease_time = pd->vltime;
8781c8c2e64Sflorian 
879ad7c548dSflorian 		log_debug("%s: pltime: %u, vltime: %u, prefix: %s/%u",
880ad7c548dSflorian 		    __func__, pd->pltime, pd->vltime, inet_ntop(AF_INET6,
881ad7c548dSflorian 		    &pd->prefix, ntopbuf, INET6_ADDRSTRLEN), pd->prefix_len);
882ad7c548dSflorian 	}
883ad7c548dSflorian 
884ad7c548dSflorian 	switch (hdr.msg_type) {
885ad7c548dSflorian 	case DHCPSOLICIT:
886ad7c548dSflorian 	case DHCPREQUEST:
887ad7c548dSflorian 	case DHCPCONFIRM:
888ad7c548dSflorian 	case DHCPRENEW:
889ad7c548dSflorian 	case DHCPREBIND:
890ad7c548dSflorian 	case DHCPRELEASE:
891ad7c548dSflorian 	case DHCPDECLINE:
892ad7c548dSflorian 	case DHCPINFORMATIONREQUEST:
893ad7c548dSflorian 		log_warnx("%s: Ignoring client-only message (%s) from server",
894ad7c548dSflorian 		    __func__, dhcp_message_type2str(hdr.msg_type));
895ad7c548dSflorian 		goto out;
896ad7c548dSflorian 	case DHCPRELAYFORW:
897ad7c548dSflorian 	case DHCPRELAYREPL:
898ad7c548dSflorian 		log_warnx("%s: Ignoring relay-agent-only message (%s) from "
899ad7c548dSflorian 		    "server", __func__, dhcp_message_type2str(hdr.msg_type));
900ad7c548dSflorian 		goto out;
901ad7c548dSflorian 	case DHCPADVERTISE:
902ad7c548dSflorian 		if (iface->state != IF_INIT) {
903ad7c548dSflorian 			log_debug("%s: ignoring unexpected %s", __func__,
904ad7c548dSflorian 			    dhcp_message_type2str(hdr.msg_type));
905ad7c548dSflorian 			goto out;
906ad7c548dSflorian 		}
907ad7c548dSflorian 		iface->serverid_len = serverid_len;
908ad7c548dSflorian 		memcpy(iface->serverid, serverid, SERVERID_SIZE);
9090da4c31dSflorian 		memcpy(iface->pds, iface->new_pds, sizeof(iface->pds));
910ad7c548dSflorian 		state_transition(iface, IF_REQUESTING);
911ad7c548dSflorian 		break;
912ad7c548dSflorian 	case DHCPREPLY:
9131c8c2e64Sflorian 		switch (iface->state) {
9141c8c2e64Sflorian 		case IF_REQUESTING:
9151c8c2e64Sflorian 		case IF_RENEWING:
9161c8c2e64Sflorian 		case IF_REBINDING:
917763cddbdSflorian 		case IF_REBOOTING:
9181c8c2e64Sflorian 			break;
9193ae4b9dfSflorian 		case IF_INIT:
9203ae4b9dfSflorian 			if (rapid_commit && engine_conf->rapid_commit)
9213ae4b9dfSflorian 				break;
9223ae4b9dfSflorian 			/* fall through */
9231c8c2e64Sflorian 		default:
924ad7c548dSflorian 			log_debug("%s: ignoring unexpected %s", __func__,
925ad7c548dSflorian 			    dhcp_message_type2str(hdr.msg_type));
926ad7c548dSflorian 			goto out;
927ad7c548dSflorian 		}
928ad7c548dSflorian 		iface->serverid_len = serverid_len;
929ad7c548dSflorian 		memcpy(iface->serverid, serverid, SERVERID_SIZE);
9300da4c31dSflorian 
931ad7c548dSflorian 		/* XXX handle t1 = 0 or t2 = 0 */
932ad7c548dSflorian 		iface->t1 = t1;
933ad7c548dSflorian 		iface->t2 = t2;
9341c8c2e64Sflorian 		iface->lease_time = lease_time;
9351c8c2e64Sflorian 		clock_gettime(CLOCK_MONOTONIC, &iface->request_time);
936ad7c548dSflorian 		state_transition(iface, IF_BOUND);
937ad7c548dSflorian 		break;
938ad7c548dSflorian 	case DHCPRECONFIGURE:
939ad7c548dSflorian 		log_warnx("%s: Ignoring %s from server",
940ad7c548dSflorian 		    __func__, dhcp_message_type2str(hdr.msg_type));
941ad7c548dSflorian 		goto out;
942ad7c548dSflorian 	default:
943ad7c548dSflorian 		fatalx("%s: %s unhandled",
944ad7c548dSflorian 		    __func__, dhcp_message_type2str(hdr.msg_type));
945ad7c548dSflorian 		break;
946ad7c548dSflorian 	}
947ad7c548dSflorian  out:
9480da4c31dSflorian 	return;
949ad7c548dSflorian }
950ad7c548dSflorian 
95170eb162fSflorian int
952ad7c548dSflorian parse_ia_pd_options(uint8_t *p, size_t len, struct prefix *prefix)
953ad7c548dSflorian {
954ad7c548dSflorian 	struct dhcp_option_hdr	 opt_hdr;
955ad7c548dSflorian 	struct dhcp_iaprefix	 iaprefix;
956ad7c548dSflorian 	struct in6_addr		 mask;
957ad7c548dSflorian 	int			 i;
958363167f4Sflorian 	uint16_t		 status_code = DHCP_STATUS_SUCCESS;
959ad7c548dSflorian 	char			 ntopbuf[INET6_ADDRSTRLEN], *visbuf;
960ad7c548dSflorian 
961ad7c548dSflorian 	while (len >= sizeof(struct dhcp_option_hdr)) {
962ad7c548dSflorian 		memcpy(&opt_hdr, p, sizeof(struct dhcp_option_hdr));
963ad7c548dSflorian 		opt_hdr.code = ntohs(opt_hdr.code);
964ad7c548dSflorian 		opt_hdr.len = ntohs(opt_hdr.len);
965ad7c548dSflorian 		p += sizeof(struct dhcp_option_hdr);
966ad7c548dSflorian 		len -= sizeof(struct dhcp_option_hdr);
967ad7c548dSflorian 		if (log_getverbose() > 1)
968ad7c548dSflorian 			log_debug("%s: %s, len: %u", __func__,
969ad7c548dSflorian 			    dhcp_option_type2str(opt_hdr.code), opt_hdr.len);
970ad7c548dSflorian 		if (len < opt_hdr.len) {
971ad7c548dSflorian 			log_warnx("%s: malformed packet, ignoring", __func__);
97270eb162fSflorian 			return DHCP_STATUS_UNSPECFAIL;
973ad7c548dSflorian 		}
974ad7c548dSflorian 
975ad7c548dSflorian 		switch (opt_hdr.code) {
976ad7c548dSflorian 		case DHO_IA_PREFIX:
977ad7c548dSflorian 			if (len < sizeof(struct dhcp_iaprefix)) {
978ad7c548dSflorian 				log_warnx("%s: malformed packet, ignoring",
979ad7c548dSflorian 				    __func__);
98070eb162fSflorian 				return DHCP_STATUS_UNSPECFAIL;
981ad7c548dSflorian 			}
982ad7c548dSflorian 
983ad7c548dSflorian 			memcpy(&iaprefix, p, sizeof(struct dhcp_iaprefix));
984ad7c548dSflorian 			log_debug("%s: pltime: %u, vltime: %u, prefix: %s/%u",
985ad7c548dSflorian 			    __func__, ntohl(iaprefix.pltime),
986ad7c548dSflorian 			    ntohl(iaprefix.vltime), inet_ntop(AF_INET6,
987ad7c548dSflorian 			    &iaprefix.prefix, ntopbuf, INET6_ADDRSTRLEN),
988ad7c548dSflorian 			    iaprefix.prefix_len);
98910da7d5dSflorian 
990ad7c548dSflorian 			if (ntohl(iaprefix.vltime) < ntohl(iaprefix.pltime)) {
991ad7c548dSflorian 				log_warnx("%s: vltime < pltime, ignoring IA_PD",
992ad7c548dSflorian 				    __func__);
993ad7c548dSflorian 				break;
994ad7c548dSflorian 			}
995ad7c548dSflorian 
99610da7d5dSflorian 			if (ntohl(iaprefix.vltime) == 0) {
99710da7d5dSflorian 				log_debug("%s: vltime == 0, ignoring IA_PD",
99810da7d5dSflorian 				    __func__);
99910da7d5dSflorian 				break;
100010da7d5dSflorian 			}
100110da7d5dSflorian 
1002ad7c548dSflorian 			prefix->prefix = iaprefix.prefix;
1003ad7c548dSflorian 			prefix->prefix_len = iaprefix.prefix_len;
1004ad7c548dSflorian 			prefix->vltime = ntohl(iaprefix.vltime);
1005ad7c548dSflorian 			prefix->pltime = ntohl(iaprefix.pltime);
1006ad7c548dSflorian 
100710da7d5dSflorian 			/* make sure prefix is masked correctly */
1008ad7c548dSflorian 			memset(&mask, 0, sizeof(mask));
1009ad7c548dSflorian 			in6_prefixlen2mask(&mask, prefix->prefix_len);
1010ad7c548dSflorian 			for (i = 0; i < 16; i++)
1011ad7c548dSflorian 				prefix->prefix.s6_addr[i] &= mask.s6_addr[i];
1012ad7c548dSflorian 
1013ad7c548dSflorian 			break;
1014ad7c548dSflorian 		case DHO_STATUS_CODE:
101570eb162fSflorian 			/* XXX STATUS_CODE can also appear outside of options */
1016ad7c548dSflorian 			if (len < 2) {
1017ad7c548dSflorian 				log_warnx("%s: malformed packet, ignoring",
1018ad7c548dSflorian 				    __func__);
101970eb162fSflorian 				return DHCP_STATUS_UNSPECFAIL;
1020ad7c548dSflorian 			}
1021ad7c548dSflorian 			memcpy(&status_code, p, sizeof(uint16_t));
1022ad7c548dSflorian 			status_code = ntohs(status_code);
10237580ae52Sflorian 			/* must be at least 4 * srclen + 1 long */
10247580ae52Sflorian 			visbuf = calloc(4, opt_hdr.len - 2 + 1);
10257580ae52Sflorian 			if (visbuf == NULL) {
10267580ae52Sflorian 				log_warn("%s", __func__);
10277580ae52Sflorian 				break;
10287580ae52Sflorian 			}
10297580ae52Sflorian 			strvisx(visbuf, p + 2, opt_hdr.len - 2, VIS_SAFE);
1030ad7c548dSflorian 			log_debug("%s: %s - %s", __func__,
1031ad7c548dSflorian 			    dhcp_status2str(status_code), visbuf);
1032ad7c548dSflorian 			break;
1033ad7c548dSflorian 		default:
1034ad7c548dSflorian 			log_debug("unhandled option: %u", opt_hdr.code);
1035ad7c548dSflorian 		}
1036ad7c548dSflorian 		p += opt_hdr.len;
1037ad7c548dSflorian 		len -= opt_hdr.len;
1038ad7c548dSflorian 	}
103970eb162fSflorian 	return status_code;
1040ad7c548dSflorian }
1041ad7c548dSflorian 
1042ad7c548dSflorian /* XXX check valid transitions */
1043ad7c548dSflorian void
1044ad7c548dSflorian state_transition(struct dhcp6leased_iface *iface, enum if_state new_state)
1045ad7c548dSflorian {
1046ad7c548dSflorian 	enum if_state	 old_state = iface->state;
1047ad7c548dSflorian 	char		 ifnamebuf[IF_NAMESIZE], *if_name;
1048ad7c548dSflorian 
1049ad7c548dSflorian 	iface->state = new_state;
1050ad7c548dSflorian 
1051ad7c548dSflorian 	switch (new_state) {
1052ad7c548dSflorian 	case IF_DOWN:
1053*efb7adaaSflorian 		switch (old_state) {
1054*efb7adaaSflorian 		case IF_RENEWING:
1055*efb7adaaSflorian 		case IF_REBINDING:
1056*efb7adaaSflorian 		case IF_REBOOTING:
1057*efb7adaaSflorian 		case IF_BOUND:
1058*efb7adaaSflorian 			deprecate_interfaces(iface);
1059*efb7adaaSflorian 			break;
1060*efb7adaaSflorian 		default:
1061*efb7adaaSflorian 			break;
1062*efb7adaaSflorian 		}
10636912c931Sflorian 		/*
1064*efb7adaaSflorian 		 * Nothing else to do until iface comes up.
1065*efb7adaaSflorian 		 * IP addresses will expire.
10666912c931Sflorian 		 */
1067ad7c548dSflorian 		iface->timo.tv_sec = -1;
1068ad7c548dSflorian 		break;
1069ad7c548dSflorian 	case IF_INIT:
1070ad7c548dSflorian 		switch (old_state) {
1071ad7c548dSflorian 		case IF_INIT:
1072ad7c548dSflorian 			if (iface->timo.tv_sec < MAX_EXP_BACKOFF_SLOW)
1073ad7c548dSflorian 				iface->timo.tv_sec *= 2;
1074ad7c548dSflorian 			break;
1075ad7c548dSflorian 		case IF_REQUESTING:
1076ad7c548dSflorian 		case IF_RENEWING:
1077ad7c548dSflorian 		case IF_REBINDING:
1078ad7c548dSflorian 		case IF_REBOOTING:
1079ad7c548dSflorian 			/* lease expired, got DHCPNAK or timeout: delete IP */
108033c99573Sflorian 			deconfigure_interfaces(iface);
1081ad7c548dSflorian 			/* fall through */
1082ad7c548dSflorian 		case IF_DOWN:
1083ad7c548dSflorian 			iface->timo.tv_sec = START_EXP_BACKOFF;
1084ad7c548dSflorian 			clock_gettime(CLOCK_MONOTONIC,
1085ad7c548dSflorian 			    &iface->elapsed_time_start);
1086ad7c548dSflorian 			break;
1087ad7c548dSflorian 		case IF_BOUND:
1088ad7c548dSflorian 			fatal("invalid transition Bound -> Init");
1089ad7c548dSflorian 			break;
1090ad7c548dSflorian 		}
1091ad7c548dSflorian 		request_dhcp_discover(iface);
1092ad7c548dSflorian 		break;
1093ad7c548dSflorian 	case IF_REBOOTING:
1094ad7c548dSflorian 		if (old_state == IF_REBOOTING)
1095ad7c548dSflorian 			iface->timo.tv_sec *= 2;
1096ad7c548dSflorian 		else {
1097ad7c548dSflorian 			iface->timo.tv_sec = START_EXP_BACKOFF;
1098ad7c548dSflorian 			arc4random_buf(iface->xid, sizeof(iface->xid));
1099ad7c548dSflorian 		}
1100ad7c548dSflorian 		request_dhcp_request(iface);
1101ad7c548dSflorian 		break;
1102ad7c548dSflorian 	case IF_REQUESTING:
1103ad7c548dSflorian 		if (old_state == IF_REQUESTING)
1104ad7c548dSflorian 			iface->timo.tv_sec *= 2;
1105ad7c548dSflorian 		else {
1106ad7c548dSflorian 			iface->timo.tv_sec = START_EXP_BACKOFF;
1107ad7c548dSflorian 			clock_gettime(CLOCK_MONOTONIC,
1108ad7c548dSflorian 			    &iface->elapsed_time_start);
1109ad7c548dSflorian 		}
1110ad7c548dSflorian 		request_dhcp_request(iface);
1111ad7c548dSflorian 		break;
1112ad7c548dSflorian 	case IF_BOUND:
1113ad7c548dSflorian 		iface->timo.tv_sec = iface->t1;
11141c8c2e64Sflorian 		switch (old_state) {
11151c8c2e64Sflorian 		case IF_REQUESTING:
11161c8c2e64Sflorian 		case IF_RENEWING:
11171c8c2e64Sflorian 		case IF_REBINDING:
11181c8c2e64Sflorian 		case IF_REBOOTING:
1119ad7c548dSflorian 			configure_interfaces(iface);
11201c8c2e64Sflorian 			break;
11213ae4b9dfSflorian 		case IF_INIT:
11223ae4b9dfSflorian 			if (engine_conf->rapid_commit)
11233ae4b9dfSflorian 				configure_interfaces(iface);
11243ae4b9dfSflorian 			else
11253ae4b9dfSflorian 				fatal("invalid transition Init -> Bound");
11263ae4b9dfSflorian 			break;
11271c8c2e64Sflorian 		default:
11281c8c2e64Sflorian 			break;
1129ad7c548dSflorian 		}
1130ad7c548dSflorian 		break;
1131ad7c548dSflorian 	case IF_RENEWING:
1132ad7c548dSflorian 		if (old_state == IF_BOUND) {
1133ad7c548dSflorian 			iface->timo.tv_sec = (iface->t2 -
1134ad7c548dSflorian 			    iface->t1) / 2; /* RFC 2131 4.4.5 */
1135ad7c548dSflorian 			arc4random_buf(iface->xid, sizeof(iface->xid));
1136ad7c548dSflorian 		} else
1137ad7c548dSflorian 			iface->timo.tv_sec /= 2;
1138ad7c548dSflorian 
1139ad7c548dSflorian 		if (iface->timo.tv_sec < 60)
1140ad7c548dSflorian 			iface->timo.tv_sec = 60;
1141ad7c548dSflorian 		request_dhcp_request(iface);
1142ad7c548dSflorian 		break;
1143ad7c548dSflorian 	case IF_REBINDING:
1144ad7c548dSflorian 		if (old_state == IF_RENEWING) {
1145ad7c548dSflorian 			iface->timo.tv_sec = (iface->lease_time -
1146ad7c548dSflorian 			    iface->t2) / 2; /* RFC 2131 4.4.5 */
1147ad7c548dSflorian 		} else
1148ad7c548dSflorian 			iface->timo.tv_sec /= 2;
1149ad7c548dSflorian 		request_dhcp_request(iface);
1150ad7c548dSflorian 		break;
1151ad7c548dSflorian 	}
1152ad7c548dSflorian 
1153ad7c548dSflorian 	if_name = if_indextoname(iface->if_index, ifnamebuf);
1154ad7c548dSflorian 	log_debug("%s[%s] %s -> %s, timo: %lld", __func__, if_name == NULL ?
1155ad7c548dSflorian 	    "?" : if_name, if_state_name[old_state], if_state_name[new_state],
1156ad7c548dSflorian 	    iface->timo.tv_sec);
1157ad7c548dSflorian 
1158ad7c548dSflorian 	if (iface->timo.tv_sec == -1) {
1159ad7c548dSflorian 		if (evtimer_pending(&iface->timer, NULL))
1160ad7c548dSflorian 			evtimer_del(&iface->timer);
1161ad7c548dSflorian 	} else
1162ad7c548dSflorian 		evtimer_add(&iface->timer, &iface->timo);
1163ad7c548dSflorian }
1164ad7c548dSflorian 
1165ad7c548dSflorian void
1166ad7c548dSflorian iface_timeout(int fd, short events, void *arg)
1167ad7c548dSflorian {
1168ad7c548dSflorian 	struct dhcp6leased_iface	*iface = (struct dhcp6leased_iface *)arg;
1169ad7c548dSflorian 	struct timespec		 now, res;
1170ad7c548dSflorian 
1171ad7c548dSflorian 	log_debug("%s[%d]: %s", __func__, iface->if_index,
1172ad7c548dSflorian 	    if_state_name[iface->state]);
1173ad7c548dSflorian 
1174ad7c548dSflorian 	switch (iface->state) {
1175ad7c548dSflorian 	case IF_DOWN:
1176ad7c548dSflorian 		state_transition(iface, IF_DOWN);
1177ad7c548dSflorian 		break;
1178ad7c548dSflorian 	case IF_INIT:
1179ad7c548dSflorian 		state_transition(iface, IF_INIT);
1180ad7c548dSflorian 		break;
1181ad7c548dSflorian 	case IF_REBOOTING:
1182ad7c548dSflorian 		if (iface->timo.tv_sec >= MAX_EXP_BACKOFF_FAST)
1183ad7c548dSflorian 			state_transition(iface, IF_INIT);
1184ad7c548dSflorian 		else
1185ad7c548dSflorian 			state_transition(iface, IF_REBOOTING);
1186ad7c548dSflorian 		break;
1187ad7c548dSflorian 	case IF_REQUESTING:
1188ad7c548dSflorian 		if (iface->timo.tv_sec >= MAX_EXP_BACKOFF_SLOW)
1189ad7c548dSflorian 			state_transition(iface, IF_INIT);
1190ad7c548dSflorian 		else
1191ad7c548dSflorian 			state_transition(iface, IF_REQUESTING);
1192ad7c548dSflorian 		break;
1193ad7c548dSflorian 	case IF_BOUND:
1194ad7c548dSflorian 		state_transition(iface, IF_RENEWING);
1195ad7c548dSflorian 		break;
1196ad7c548dSflorian 	case IF_RENEWING:
1197ad7c548dSflorian 		clock_gettime(CLOCK_MONOTONIC, &now);
1198ad7c548dSflorian 		timespecsub(&now, &iface->request_time, &res);
1199ad7c548dSflorian 		log_debug("%s: res.tv_sec: %lld, t2: %u", __func__,
1200ad7c548dSflorian 		    res.tv_sec, iface->t2);
12011c8c2e64Sflorian 		if (res.tv_sec >= iface->t2)
1202ad7c548dSflorian 			state_transition(iface, IF_REBINDING);
1203ad7c548dSflorian 		else
1204ad7c548dSflorian 			state_transition(iface, IF_RENEWING);
1205ad7c548dSflorian 		break;
1206ad7c548dSflorian 	case IF_REBINDING:
1207ad7c548dSflorian 		clock_gettime(CLOCK_MONOTONIC, &now);
1208ad7c548dSflorian 		timespecsub(&now, &iface->request_time, &res);
1209ad7c548dSflorian 		log_debug("%s: res.tv_sec: %lld, lease_time: %u", __func__,
1210ad7c548dSflorian 		    res.tv_sec, iface->lease_time);
1211ad7c548dSflorian 		if (res.tv_sec > iface->lease_time)
1212ad7c548dSflorian 			state_transition(iface, IF_INIT);
1213ad7c548dSflorian 		else
1214ad7c548dSflorian 			state_transition(iface, IF_REBINDING);
1215ad7c548dSflorian 		break;
1216ad7c548dSflorian 	}
1217ad7c548dSflorian }
1218ad7c548dSflorian 
12191c8c2e64Sflorian /* XXX can this be merged into dhcp_request()? */
1220ad7c548dSflorian void
1221ad7c548dSflorian request_dhcp_discover(struct dhcp6leased_iface *iface)
1222ad7c548dSflorian {
1223ad7c548dSflorian 	struct imsg_req_dhcp	 imsg;
1224ad7c548dSflorian 	struct timespec		 now, res;
1225ad7c548dSflorian 
1226ad7c548dSflorian 	memset(&imsg, 0, sizeof(imsg));
1227ad7c548dSflorian 	imsg.if_index = iface->if_index;
1228ad7c548dSflorian 	memcpy(imsg.xid, iface->xid, sizeof(imsg.xid));
1229ad7c548dSflorian 	clock_gettime(CLOCK_MONOTONIC, &now);
1230ad7c548dSflorian 	timespecsub(&now, &iface->elapsed_time_start, &res);
1231ad7c548dSflorian 	if (res.tv_sec * 100 > 0xffff)
1232ad7c548dSflorian 		imsg.elapsed_time = 0xffff;
1233ad7c548dSflorian 	else
1234ad7c548dSflorian 		imsg.elapsed_time = res.tv_sec * 100;
12351c8c2e64Sflorian 	engine_imsg_compose_frontend(IMSG_SEND_SOLICIT, 0, &imsg, sizeof(imsg));
1236ad7c548dSflorian }
1237ad7c548dSflorian 
1238ad7c548dSflorian void
1239ad7c548dSflorian request_dhcp_request(struct dhcp6leased_iface *iface)
1240ad7c548dSflorian {
1241ad7c548dSflorian 	struct imsg_req_dhcp	 imsg;
1242ad7c548dSflorian 	struct timespec		 now, res;
1243ad7c548dSflorian 
1244ad7c548dSflorian 	memset(&imsg, 0, sizeof(imsg));
1245ad7c548dSflorian 	imsg.if_index = iface->if_index;
1246ad7c548dSflorian 	memcpy(imsg.xid, iface->xid, sizeof(imsg.xid));
1247ad7c548dSflorian 
1248ad7c548dSflorian 	clock_gettime(CLOCK_MONOTONIC, &now);
1249ad7c548dSflorian 	timespecsub(&now, &iface->elapsed_time_start, &res);
1250ad7c548dSflorian 	if (res.tv_sec * 100 > 0xffff)
1251ad7c548dSflorian 		imsg.elapsed_time = 0xffff;
1252ad7c548dSflorian 	else
1253ad7c548dSflorian 		imsg.elapsed_time = res.tv_sec * 100;
1254ad7c548dSflorian 
1255ad7c548dSflorian 	switch (iface->state) {
1256ad7c548dSflorian 	case IF_DOWN:
1257ad7c548dSflorian 		fatalx("invalid state IF_DOWN in %s", __func__);
1258ad7c548dSflorian 		break;
1259ad7c548dSflorian 	case IF_INIT:
1260ad7c548dSflorian 		fatalx("invalid state IF_INIT in %s", __func__);
1261ad7c548dSflorian 		break;
1262ad7c548dSflorian 	case IF_BOUND:
1263ad7c548dSflorian 		fatalx("invalid state IF_BOUND in %s", __func__);
1264ad7c548dSflorian 		break;
1265ad7c548dSflorian 	case IF_REBOOTING:
1266ad7c548dSflorian 	case IF_REQUESTING:
12671c8c2e64Sflorian 	case IF_RENEWING:
12681c8c2e64Sflorian 	case IF_REBINDING:
1269ad7c548dSflorian 		imsg.serverid_len = iface->serverid_len;
1270ad7c548dSflorian 		memcpy(imsg.serverid, iface->serverid, SERVERID_SIZE);
1271ad7c548dSflorian 		memcpy(imsg.pds, iface->pds, sizeof(iface->pds));
1272ad7c548dSflorian 		break;
12731c8c2e64Sflorian 	}
12741c8c2e64Sflorian 	switch (iface->state) {
12751c8c2e64Sflorian 	case IF_REQUESTING:
12761c8c2e64Sflorian 		engine_imsg_compose_frontend(IMSG_SEND_REQUEST, 0, &imsg,
12771c8c2e64Sflorian 		    sizeof(imsg));
12781c8c2e64Sflorian 		break;
1279ad7c548dSflorian 	case IF_RENEWING:
12801c8c2e64Sflorian 		engine_imsg_compose_frontend(IMSG_SEND_RENEW, 0, &imsg,
12811c8c2e64Sflorian 		    sizeof(imsg));
1282ad7c548dSflorian 		break;
1283763cddbdSflorian 	case IF_REBOOTING:
1284ad7c548dSflorian 	case IF_REBINDING:
12851c8c2e64Sflorian 		engine_imsg_compose_frontend(IMSG_SEND_REBIND, 0, &imsg,
12861c8c2e64Sflorian 		    sizeof(imsg));
1287ad7c548dSflorian 		break;
12881c8c2e64Sflorian 	default:
12891c8c2e64Sflorian 		fatalx("%s: wrong state", __func__);
1290ad7c548dSflorian 	}
1291ad7c548dSflorian }
1292ad7c548dSflorian 
1293ad7c548dSflorian /* XXX we need to install a reject route for the delegated prefix */
1294ad7c548dSflorian void
1295ad7c548dSflorian configure_interfaces(struct dhcp6leased_iface *iface)
1296ad7c548dSflorian {
1297ad7c548dSflorian 	struct iface_conf	*iface_conf;
1298ad7c548dSflorian 	struct iface_ia_conf	*ia_conf;
1299ad7c548dSflorian 	struct iface_pd_conf	*pd_conf;
1300763cddbdSflorian 	struct imsg_lease_info	 imsg_lease_info;
13010498f896Sflorian 	uint32_t	 	 i;
13020498f896Sflorian 	char		 	 ntopbuf[INET6_ADDRSTRLEN];
1303ad7c548dSflorian 	char			 ifnamebuf[IF_NAMESIZE], *if_name;
1304ad7c548dSflorian 
1305ad7c548dSflorian 	if ((if_name = if_indextoname(iface->if_index, ifnamebuf)) == NULL) {
1306ad7c548dSflorian 		log_debug("%s: unknown interface %d", __func__,
1307ad7c548dSflorian 		    iface->if_index);
1308ad7c548dSflorian 		return;
1309ad7c548dSflorian 	}
1310ad7c548dSflorian 	if ((iface_conf = find_iface_conf(&engine_conf->iface_list, if_name))
1311ad7c548dSflorian 	    == NULL) {
1312ad7c548dSflorian 		log_debug("%s: no interface configuration for %d", __func__,
1313ad7c548dSflorian 		    iface->if_index);
1314ad7c548dSflorian 		return;
1315ad7c548dSflorian 	}
1316ad7c548dSflorian 
13170498f896Sflorian 	for (i = 0; i < iface_conf->ia_count; i++) {
13180498f896Sflorian 		struct prefix	*pd = &iface->new_pds[i];
13190498f896Sflorian 
13200498f896Sflorian 		log_info("prefix delegation #%d %s/%d received on %s from "
13210498f896Sflorian 		    "server %s", i, inet_ntop(AF_INET6, &pd->prefix, ntopbuf,
13220498f896Sflorian 		    INET6_ADDRSTRLEN), pd->prefix_len, if_name,
13230498f896Sflorian 		    dhcp_duid2str(iface->serverid_len, iface->serverid));
13240498f896Sflorian 	}
1325763cddbdSflorian 
1326ad7c548dSflorian 	SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) {
13270da4c31dSflorian 		struct prefix	*pd = &iface->new_pds[ia_conf->id];
1328ad7c548dSflorian 
1329ad7c548dSflorian 		SIMPLEQ_FOREACH(pd_conf, &ia_conf->iface_pd_list, entry) {
133033c99573Sflorian 			send_reconfigure_interface(pd_conf, pd, CONFIGURE);
1331ad7c548dSflorian 		}
1332ad7c548dSflorian 	}
13330da4c31dSflorian 
13340da4c31dSflorian 	if (prefixcmp(iface->pds, iface->new_pds, iface_conf->ia_count) != 0) {
13350498f896Sflorian 		log_info("Prefix delegations on %s from server %s changed",
13360498f896Sflorian 		    if_name, dhcp_duid2str(iface->serverid_len,
13370498f896Sflorian 		    iface->serverid));
13380da4c31dSflorian 		for (i = 0; i < iface_conf->ia_count; i++) {
13390da4c31dSflorian 			log_debug("%s: iface->pds [%d]: %s/%d", __func__, i,
13400da4c31dSflorian 			    inet_ntop(AF_INET6, &iface->pds[i].prefix, ntopbuf,
13410da4c31dSflorian 			    INET6_ADDRSTRLEN), iface->pds[i].prefix_len);
13420da4c31dSflorian 			log_debug("%s:        pds [%d]: %s/%d", __func__, i,
13430da4c31dSflorian 			    inet_ntop(AF_INET6, &iface->new_pds[i].prefix,
13440da4c31dSflorian 			    ntopbuf, INET6_ADDRSTRLEN),
13450da4c31dSflorian 			    iface->new_pds[i].prefix_len);
13460da4c31dSflorian 		}
13470da4c31dSflorian 		deconfigure_interfaces(iface);
13480da4c31dSflorian 	}
13490da4c31dSflorian 
13500da4c31dSflorian 	memcpy(iface->pds, iface->new_pds, sizeof(iface->pds));
13510da4c31dSflorian 	memset(iface->new_pds, 0, sizeof(iface->new_pds));
1352cc3e93c2Sflorian 
1353cc3e93c2Sflorian 	memset(&imsg_lease_info, 0, sizeof(imsg_lease_info));
1354cc3e93c2Sflorian 	imsg_lease_info.if_index = iface->if_index;
1355cc3e93c2Sflorian 	memcpy(imsg_lease_info.pds, iface->pds, sizeof(iface->pds));
1356cc3e93c2Sflorian 	engine_imsg_compose_main(IMSG_WRITE_LEASE, 0, &imsg_lease_info,
1357cc3e93c2Sflorian 	    sizeof(imsg_lease_info));
1358ad7c548dSflorian }
1359ad7c548dSflorian 
1360ad7c548dSflorian void
136133c99573Sflorian deconfigure_interfaces(struct dhcp6leased_iface *iface)
136233c99573Sflorian {
136333c99573Sflorian 	struct iface_conf	*iface_conf;
136433c99573Sflorian 	struct iface_ia_conf	*ia_conf;
136533c99573Sflorian 	struct iface_pd_conf	*pd_conf;
13660498f896Sflorian 	uint32_t	 	 i;
13670498f896Sflorian 	char		 	 ntopbuf[INET6_ADDRSTRLEN];
136833c99573Sflorian 	char			 ifnamebuf[IF_NAMESIZE], *if_name;
136933c99573Sflorian 
137033c99573Sflorian 
137133c99573Sflorian 	if ((if_name = if_indextoname(iface->if_index, ifnamebuf)) == NULL) {
137233c99573Sflorian 		log_debug("%s: unknown interface %d", __func__,
137333c99573Sflorian 		    iface->if_index);
137433c99573Sflorian 		return;
137533c99573Sflorian 	}
137633c99573Sflorian 	if ((iface_conf = find_iface_conf(&engine_conf->iface_list, if_name))
137733c99573Sflorian 	    == NULL) {
137833c99573Sflorian 		log_debug("%s: no interface configuration for %d", __func__,
137933c99573Sflorian 		    iface->if_index);
138033c99573Sflorian 		return;
138133c99573Sflorian 	}
138233c99573Sflorian 
13830498f896Sflorian 	for (i = 0; i < iface_conf->ia_count; i++) {
13840498f896Sflorian 		struct prefix *pd = &iface->pds[i];
13850498f896Sflorian 
13860498f896Sflorian 		log_info("Prefix delegation #%d %s/%d expired on %s from "
13870498f896Sflorian 		    "server %s", i, inet_ntop(AF_INET6, &pd->prefix, ntopbuf,
13880498f896Sflorian 		    INET6_ADDRSTRLEN), pd->prefix_len, if_name,
13890498f896Sflorian 		    dhcp_duid2str(iface->serverid_len, iface->serverid));
13900498f896Sflorian 	}
13910498f896Sflorian 
139233c99573Sflorian 	SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) {
139333c99573Sflorian 		struct prefix	*pd = &iface->pds[ia_conf->id];
139433c99573Sflorian 
139533c99573Sflorian 		SIMPLEQ_FOREACH(pd_conf, &ia_conf->iface_pd_list, entry) {
139633c99573Sflorian 			send_reconfigure_interface(pd_conf, pd, DECONFIGURE);
139733c99573Sflorian 		}
139833c99573Sflorian 	}
13998e130894Sflorian 	memset(iface->pds, 0, sizeof(iface->pds));
140033c99573Sflorian }
140133c99573Sflorian 
1402*efb7adaaSflorian void
1403*efb7adaaSflorian deprecate_interfaces(struct dhcp6leased_iface *iface)
1404*efb7adaaSflorian {
1405*efb7adaaSflorian 	struct iface_conf	*iface_conf;
1406*efb7adaaSflorian 	struct iface_ia_conf	*ia_conf;
1407*efb7adaaSflorian 	struct iface_pd_conf	*pd_conf;
1408*efb7adaaSflorian 	struct timespec		 now, diff;
1409*efb7adaaSflorian 	uint32_t	 	 i;
1410*efb7adaaSflorian 	char		 	 ntopbuf[INET6_ADDRSTRLEN];
1411*efb7adaaSflorian 	char			 ifnamebuf[IF_NAMESIZE], *if_name;
1412*efb7adaaSflorian 
1413*efb7adaaSflorian 
1414*efb7adaaSflorian 	if ((if_name = if_indextoname(iface->if_index, ifnamebuf)) == NULL) {
1415*efb7adaaSflorian 		log_debug("%s: unknown interface %d", __func__,
1416*efb7adaaSflorian 		    iface->if_index);
1417*efb7adaaSflorian 		return;
1418*efb7adaaSflorian 	}
1419*efb7adaaSflorian 	if ((iface_conf = find_iface_conf(&engine_conf->iface_list, if_name))
1420*efb7adaaSflorian 	    == NULL) {
1421*efb7adaaSflorian 		log_debug("%s: no interface configuration for %d", __func__,
1422*efb7adaaSflorian 		    iface->if_index);
1423*efb7adaaSflorian 		return;
1424*efb7adaaSflorian 	}
1425*efb7adaaSflorian 
1426*efb7adaaSflorian 	for (i = 0; i < iface_conf->ia_count; i++) {
1427*efb7adaaSflorian 		struct prefix *pd = &iface->pds[i];
1428*efb7adaaSflorian 
1429*efb7adaaSflorian 		log_info("%s went down, deprecating prefix delegation #%d %s/%d"
1430*efb7adaaSflorian 		    " from server %s", if_name, i, inet_ntop(AF_INET6,
1431*efb7adaaSflorian 		    &pd->prefix, ntopbuf, INET6_ADDRSTRLEN), pd->prefix_len,
1432*efb7adaaSflorian 		    dhcp_duid2str(iface->serverid_len, iface->serverid));
1433*efb7adaaSflorian 	}
1434*efb7adaaSflorian 
1435*efb7adaaSflorian 	clock_gettime(CLOCK_MONOTONIC, &now);
1436*efb7adaaSflorian 	timespecsub(&now, &iface->request_time, &diff);
1437*efb7adaaSflorian 
1438*efb7adaaSflorian 	SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) {
1439*efb7adaaSflorian 		struct prefix	*pd = &iface->pds[ia_conf->id];
1440*efb7adaaSflorian 
1441*efb7adaaSflorian 		if (pd->vltime > diff.tv_sec)
1442*efb7adaaSflorian 			pd->vltime -= diff.tv_sec;
1443*efb7adaaSflorian 		else
1444*efb7adaaSflorian 			pd->vltime = 0;
1445*efb7adaaSflorian 
1446*efb7adaaSflorian 		pd->pltime = 0;
1447*efb7adaaSflorian 
1448*efb7adaaSflorian 		SIMPLEQ_FOREACH(pd_conf, &ia_conf->iface_pd_list, entry) {
1449*efb7adaaSflorian 			send_reconfigure_interface(pd_conf, pd, CONFIGURE);
1450*efb7adaaSflorian 		}
1451*efb7adaaSflorian 	}
1452*efb7adaaSflorian }
1453*efb7adaaSflorian 
14540da4c31dSflorian int
14550da4c31dSflorian prefixcmp(struct prefix *a, struct prefix *b, int count)
14560da4c31dSflorian {
14570da4c31dSflorian 	int i;
14580da4c31dSflorian 
14590da4c31dSflorian 	for (i = 0; i < count; i++) {
14600da4c31dSflorian 		if (a[i].prefix_len != b[i].prefix_len)
14610da4c31dSflorian 			return 1;
14620da4c31dSflorian 		if (memcmp(&a[i].prefix, &b[i].prefix,
14630da4c31dSflorian 		    sizeof(struct in6_addr)) != 0)
14640da4c31dSflorian 			return 1;
14650da4c31dSflorian 	}
14660da4c31dSflorian 	return 0;
14670da4c31dSflorian }
14680da4c31dSflorian 
146933c99573Sflorian void
147033c99573Sflorian send_reconfigure_interface(struct iface_pd_conf *pd_conf, struct prefix *pd,
147133c99573Sflorian     enum reconfigure_action action)
1472ad7c548dSflorian {
1473ad7c548dSflorian 	struct imsg_configure_address	 address;
1474ad7c548dSflorian 	uint32_t			 if_index;
1475ad7c548dSflorian 	int				 i;
1476ad7c548dSflorian 	char				 ntopbuf[INET6_ADDRSTRLEN];
1477ad7c548dSflorian 
14787e484d93Sflorian 	if (pd->prefix_len == 0)
14797e484d93Sflorian 		return;
14807e484d93Sflorian 
1481ad7c548dSflorian 	if (strcmp(pd_conf->name, "reserve") == 0)
1482ad7c548dSflorian 		return;
1483ad7c548dSflorian 
1484ad7c548dSflorian 	if ((if_index = if_nametoindex(pd_conf->name)) == 0)
1485ad7c548dSflorian 		return;
1486ad7c548dSflorian 
1487ad7c548dSflorian 	memset(&address, 0, sizeof(address));
1488ad7c548dSflorian 
1489ad7c548dSflorian 	address.if_index = if_index;
1490ad7c548dSflorian 	address.addr.sin6_family = AF_INET6;
1491ad7c548dSflorian 	address.addr.sin6_len = sizeof(address.addr);
1492ad7c548dSflorian 	address.addr.sin6_addr = pd->prefix;
1493ad7c548dSflorian 
1494ad7c548dSflorian 	for (i = 0; i < 16; i++)
1495ad7c548dSflorian 		address.addr.sin6_addr.s6_addr[i] |=
1496ad7c548dSflorian 		    pd_conf->prefix_mask.s6_addr[i];
1497ad7c548dSflorian 
1498ad7c548dSflorian 	/* XXX make this configurable & use SOII */
1499ad7c548dSflorian 	address.addr.sin6_addr.s6_addr[15] |= 1;
1500ad7c548dSflorian 
1501ad7c548dSflorian 	in6_prefixlen2mask(&address.mask, pd_conf->prefix_len);
1502ad7c548dSflorian 
150333c99573Sflorian 	log_debug("%s: %s %s: %s/%d", __func__, pd_conf->name,
150433c99573Sflorian 	    reconfigure_action_name[action], inet_ntop(AF_INET6,
150533c99573Sflorian 	    &address.addr.sin6_addr, ntopbuf, INET6_ADDRSTRLEN),
150633c99573Sflorian 	    pd_conf->prefix_len);
1507ad7c548dSflorian 
1508ad7c548dSflorian 	address.vltime = pd->vltime;
1509ad7c548dSflorian 	address.pltime = pd->pltime;
1510ad7c548dSflorian 
151133c99573Sflorian 	if (action == CONFIGURE)
1512ad7c548dSflorian 		engine_imsg_compose_main(IMSG_CONFIGURE_ADDRESS, 0, &address,
1513ad7c548dSflorian 		    sizeof(address));
151433c99573Sflorian 	else
151533c99573Sflorian 		engine_imsg_compose_main(IMSG_DECONFIGURE_ADDRESS, 0, &address,
151633c99573Sflorian 		    sizeof(address));
1517ad7c548dSflorian }
1518ad7c548dSflorian 
1519ad7c548dSflorian const char *
15207571100dSflorian dhcp_message_type2str(int type)
1521ad7c548dSflorian {
1522ad7c548dSflorian 	static char buf[sizeof("Unknown [255]")];
1523ad7c548dSflorian 
1524ad7c548dSflorian 	switch (type) {
1525ad7c548dSflorian 	case DHCPSOLICIT:
1526ad7c548dSflorian 		return "DHCPSOLICIT";
1527ad7c548dSflorian 	case DHCPADVERTISE:
1528ad7c548dSflorian 		return "DHCPADVERTISE";
1529ad7c548dSflorian 	case DHCPREQUEST:
1530ad7c548dSflorian 		return "DHCPREQUEST";
1531ad7c548dSflorian 	case DHCPCONFIRM:
1532ad7c548dSflorian 		return "DHCPCONFIRM";
1533ad7c548dSflorian 	case DHCPRENEW:
1534ad7c548dSflorian 		return "DHCPRENEW";
1535ad7c548dSflorian 	case DHCPREBIND:
1536ad7c548dSflorian 		return "DHCPREBIND";
1537ad7c548dSflorian 	case DHCPREPLY:
1538ad7c548dSflorian 		return "DHCPREPLY";
1539ad7c548dSflorian 	case DHCPRELEASE:
1540ad7c548dSflorian 		return "DHCPRELEASE";
1541ad7c548dSflorian 	case DHCPDECLINE:
1542ad7c548dSflorian 		return "DHCPDECLINE";
1543ad7c548dSflorian 	case DHCPRECONFIGURE:
1544ad7c548dSflorian 		return "DHCPRECONFIGURE";
1545ad7c548dSflorian 	case DHCPINFORMATIONREQUEST:
1546ad7c548dSflorian 		return "DHCPINFORMATIONREQUEST";
1547ad7c548dSflorian 	case DHCPRELAYFORW:
1548ad7c548dSflorian 		return "DHCPRELAYFORW";
1549ad7c548dSflorian 	case DHCPRELAYREPL:
1550ad7c548dSflorian 		return "DHCPRELAYREPL";
1551ad7c548dSflorian 	default:
15527571100dSflorian 		snprintf(buf, sizeof(buf), "Unknown [%u]", type & 0xff);
1553ad7c548dSflorian 		return buf;
1554ad7c548dSflorian 	}
1555ad7c548dSflorian }
1556ad7c548dSflorian 
1557ad7c548dSflorian const char *
15587571100dSflorian dhcp_option_type2str(int code)
1559ad7c548dSflorian {
1560ad7c548dSflorian 	static char buf[sizeof("Unknown [65535]")];
1561ad7c548dSflorian 	switch (code) {
1562ad7c548dSflorian 	case DHO_CLIENTID:
1563ad7c548dSflorian 		return "DHO_CLIENTID";
1564ad7c548dSflorian 	case DHO_SERVERID:
1565ad7c548dSflorian 		return "DHO_SERVERID";
1566ad7c548dSflorian 	case DHO_ORO:
1567ad7c548dSflorian 		return "DHO_ORO";
1568ad7c548dSflorian 	case DHO_ELAPSED_TIME:
1569ad7c548dSflorian 		return "DHO_ELAPSED_TIME";
1570ad7c548dSflorian 	case DHO_STATUS_CODE:
1571ad7c548dSflorian 		return "DHO_STATUS_CODE";
1572ad7c548dSflorian 	case DHO_RAPID_COMMIT:
1573ad7c548dSflorian 		return "DHO_RAPID_COMMIT";
1574ad7c548dSflorian 	case DHO_VENDOR_CLASS:
1575ad7c548dSflorian 		return "DHO_VENDOR_CLASS";
1576ad7c548dSflorian 	case DHO_IA_PD:
1577ad7c548dSflorian 		return "DHO_IA_PD";
1578ad7c548dSflorian 	case DHO_IA_PREFIX:
1579ad7c548dSflorian 		return "DHO_IA_PREFIX";
1580ad7c548dSflorian 	case DHO_SOL_MAX_RT:
1581ad7c548dSflorian 		return "DHO_SOL_MAX_RT";
1582ad7c548dSflorian 	case DHO_INF_MAX_RT:
1583ad7c548dSflorian 		return "DHO_INF_MAX_RT";
1584ad7c548dSflorian 	default:
15857571100dSflorian 		snprintf(buf, sizeof(buf), "Unknown [%u]", code &0xffff);
1586ad7c548dSflorian 		return buf;
1587ad7c548dSflorian 	}
1588ad7c548dSflorian }
1589ad7c548dSflorian 
1590ad7c548dSflorian const char*
1591ad7c548dSflorian dhcp_duid2str(int len, uint8_t *p)
1592ad7c548dSflorian {
1593ad7c548dSflorian 	static char	 buf[2 * 130];
1594ad7c548dSflorian 	int		 i, rem;
1595ad7c548dSflorian 	char		*pbuf;
1596ad7c548dSflorian 
1597ad7c548dSflorian 	if (len > 130)
1598ad7c548dSflorian 		return "invalid";
1599ad7c548dSflorian 
1600ad7c548dSflorian 	pbuf = buf;
1601ad7c548dSflorian 	rem = sizeof(buf);
1602ad7c548dSflorian 	for (i = 0; i < len && rem > 0; i++, pbuf += 2, rem -=2)
1603ad7c548dSflorian 		snprintf(pbuf, rem, "%02x", p[i]);
1604ad7c548dSflorian 
1605ad7c548dSflorian 	return buf;
1606ad7c548dSflorian }
1607ad7c548dSflorian 
1608ad7c548dSflorian const char*
16097571100dSflorian dhcp_status2str(int status)
1610ad7c548dSflorian {
1611ad7c548dSflorian 	static char buf[sizeof("Unknown [255]")];
1612ad7c548dSflorian 
1613ad7c548dSflorian 	switch (status) {
1614ad7c548dSflorian 	case DHCP_STATUS_SUCCESS:
1615ad7c548dSflorian 		return "Success";
1616ad7c548dSflorian 	case DHCP_STATUS_UNSPECFAIL:
1617ad7c548dSflorian 		return "UnspecFail";
1618ad7c548dSflorian 	case DHCP_STATUS_NOADDRSAVAIL:
1619ad7c548dSflorian 		return "NoAddrsAvail";
1620ad7c548dSflorian 	case DHCP_STATUS_NOBINDING:
1621ad7c548dSflorian 		return "NoBinding";
1622ad7c548dSflorian 	case DHCP_STATUS_NOTONLINK:
1623ad7c548dSflorian 		return "NotOnLink";
1624ad7c548dSflorian 	case DHCP_STATUS_USEMULTICAST:
1625ad7c548dSflorian 		return "UseMulticast";
1626ad7c548dSflorian 	case DHCP_STATUS_NOPREFIXAVAIL:
1627ad7c548dSflorian 		return "NoPrefixAvail";
1628ad7c548dSflorian 	default:
16297571100dSflorian 		snprintf(buf, sizeof(buf), "Unknown [%u]", status & 0xff);
1630ad7c548dSflorian 		return buf;
1631ad7c548dSflorian     }
1632ad7c548dSflorian }
1633ad7c548dSflorian 
1634ad7c548dSflorian /* from sys/netinet6/in6.c */
1635ad7c548dSflorian /*
1636ad7c548dSflorian  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
1637ad7c548dSflorian  * All rights reserved.
1638ad7c548dSflorian  *
1639ad7c548dSflorian  * Redistribution and use in source and binary forms, with or without
1640ad7c548dSflorian  * modification, are permitted provided that the following conditions
1641ad7c548dSflorian  * are met:
1642ad7c548dSflorian  * 1. Redistributions of source code must retain the above copyright
1643ad7c548dSflorian  *    notice, this list of conditions and the following disclaimer.
1644ad7c548dSflorian  * 2. Redistributions in binary form must reproduce the above copyright
1645ad7c548dSflorian  *    notice, this list of conditions and the following disclaimer in the
1646ad7c548dSflorian  *    documentation and/or other materials provided with the distribution.
1647ad7c548dSflorian  * 3. Neither the name of the project nor the names of its contributors
1648ad7c548dSflorian  *    may be used to endorse or promote products derived from this software
1649ad7c548dSflorian  *    without specific prior written permission.
1650ad7c548dSflorian  *
1651ad7c548dSflorian  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
1652ad7c548dSflorian  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1653ad7c548dSflorian  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1654ad7c548dSflorian  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
1655ad7c548dSflorian  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1656ad7c548dSflorian  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1657ad7c548dSflorian  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1658ad7c548dSflorian  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1659ad7c548dSflorian  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1660ad7c548dSflorian  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1661ad7c548dSflorian  * SUCH DAMAGE.
1662ad7c548dSflorian  */
1663ad7c548dSflorian void
1664ad7c548dSflorian in6_prefixlen2mask(struct in6_addr *maskp, int len)
1665ad7c548dSflorian {
1666ad7c548dSflorian 	u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff};
1667ad7c548dSflorian 	int bytelen, bitlen, i;
1668ad7c548dSflorian 
1669ad7c548dSflorian 	if (0 > len || len > 128)
1670ad7c548dSflorian 		fatalx("%s: invalid prefix length(%d)\n", __func__, len);
1671ad7c548dSflorian 
1672ad7c548dSflorian 	bzero(maskp, sizeof(*maskp));
1673ad7c548dSflorian 	bytelen = len / 8;
1674ad7c548dSflorian 	bitlen = len % 8;
1675ad7c548dSflorian 	for (i = 0; i < bytelen; i++)
1676ad7c548dSflorian 		maskp->s6_addr[i] = 0xff;
1677ad7c548dSflorian 	/* len == 128 is ok because bitlen == 0 then */
1678ad7c548dSflorian 	if (bitlen)
1679ad7c548dSflorian 		maskp->s6_addr[bytelen] = maskarray[bitlen - 1];
1680ad7c548dSflorian }
1681