xref: /openbsd/sbin/dhcpleased/frontend.c (revision 4bdff4be)
1 /*	$OpenBSD: frontend.c,v 1.32 2023/12/14 09:58:37 claudio Exp $	*/
2 
3 /*
4  * Copyright (c) 2017, 2021 Florian Obser <florian@openbsd.org>
5  * Copyright (c) 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 #include <sys/types.h>
22 #include <sys/ioctl.h>
23 #include <sys/queue.h>
24 #include <sys/socket.h>
25 #include <sys/syslog.h>
26 #include <sys/uio.h>
27 
28 #include <net/bpf.h>
29 #include <net/if.h>
30 #include <net/if_dl.h>
31 #include <net/if_types.h>
32 #include <net/route.h>
33 
34 #include <netinet/in.h>
35 #include <netinet/if_ether.h>
36 #include <netinet/ip.h>
37 #include <netinet/udp.h>
38 
39 #include <arpa/inet.h>
40 
41 #include <errno.h>
42 #include <event.h>
43 #include <ifaddrs.h>
44 #include <imsg.h>
45 #include <pwd.h>
46 #include <signal.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 
52 #include "bpf.h"
53 #include "log.h"
54 #include "dhcpleased.h"
55 #include "frontend.h"
56 #include "control.h"
57 #include "checksum.h"
58 
59 #define	ROUTE_SOCKET_BUF_SIZE	16384
60 #define	BOOTP_MIN_LEN		300	/* fixed bootp packet adds up to 300 */
61 
62 struct bpf_ev {
63 	struct event		 ev;
64 	uint8_t			 buf[BPFLEN];
65 };
66 
67 struct iface {
68 	LIST_ENTRY(iface)	 entries;
69 	struct bpf_ev		 bpfev;
70 	struct imsg_ifinfo	 ifinfo;
71 	int			 send_discover;
72 	uint32_t		 xid;
73 	struct in_addr		 ciaddr;
74 	struct in_addr		 requested_ip;
75 	struct in_addr		 server_identifier;
76 	struct in_addr		 dhcp_server;
77 	int			 udpsock;
78 };
79 
80 __dead void	 frontend_shutdown(void);
81 void		 frontend_sig_handler(int, short, void *);
82 void		 update_iface(struct if_msghdr *, struct sockaddr_dl *);
83 void		 frontend_startup(void);
84 void		 init_ifaces(void);
85 void		 route_receive(int, short, void *);
86 void		 handle_route_message(struct rt_msghdr *, struct sockaddr **);
87 void		 get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
88 void		 bpf_receive(int, short, void *);
89 int		 get_flags(char *);
90 int		 get_xflags(char *);
91 struct iface	*get_iface_by_id(uint32_t);
92 void		 remove_iface(uint32_t);
93 void		 set_bpfsock(int, uint32_t);
94 void		 iface_data_from_imsg(struct iface*, struct imsg_req_dhcp *);
95 ssize_t		 build_packet(uint8_t, char *, uint32_t, struct ether_addr *,
96 		     struct in_addr *, struct in_addr *, struct in_addr *);
97 void		 send_packet(uint8_t, struct iface *);
98 void		 bpf_send_packet(struct iface *, uint8_t *, ssize_t);
99 int		 udp_send_packet(struct iface *, uint8_t *, ssize_t);
100 #ifndef SMALL
101 int		 iface_conf_cmp(struct iface_conf *, struct iface_conf *);
102 #endif /* SMALL */
103 
104 LIST_HEAD(, iface)		 interfaces;
105 struct dhcpleased_conf		*frontend_conf;
106 static struct imsgev		*iev_main;
107 static struct imsgev		*iev_engine;
108 struct event			 ev_route;
109 int				 ioctlsock;
110 
111 uint8_t				 dhcp_packet[1500];
112 
113 void
114 frontend_sig_handler(int sig, short event, void *bula)
115 {
116 	/*
117 	 * Normal signal handler rules don't apply because libevent
118 	 * decouples for us.
119 	 */
120 
121 	switch (sig) {
122 	case SIGINT:
123 	case SIGTERM:
124 		frontend_shutdown();
125 	default:
126 		fatalx("unexpected signal");
127 	}
128 }
129 
130 void
131 frontend(int debug, int verbose)
132 {
133 	struct event		 ev_sigint, ev_sigterm;
134 	struct passwd		*pw;
135 
136 #ifndef SMALL
137 	frontend_conf = config_new_empty();
138 #endif /* SMALL */
139 
140 	log_init(debug, LOG_DAEMON);
141 	log_setverbose(verbose);
142 
143 	if ((pw = getpwnam(DHCPLEASED_USER)) == NULL)
144 		fatal("getpwnam");
145 
146 	if (chdir("/") == -1)
147 		fatal("chdir(\"/\")");
148 
149 	if (unveil("/", "") == -1)
150 		fatal("unveil /");
151 	if (unveil(NULL, NULL) == -1)
152 		fatal("unveil");
153 
154 	setproctitle("%s", "frontend");
155 	log_procinit("frontend");
156 
157 	if ((ioctlsock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1)
158 		fatal("socket");
159 
160 	if (setgroups(1, &pw->pw_gid) ||
161 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
162 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
163 		fatal("can't drop privileges");
164 
165 	if (pledge("stdio unix recvfd route", NULL) == -1)
166 		fatal("pledge");
167 	event_init();
168 
169 	/* Setup signal handler. */
170 	signal_set(&ev_sigint, SIGINT, frontend_sig_handler, NULL);
171 	signal_set(&ev_sigterm, SIGTERM, frontend_sig_handler, NULL);
172 	signal_add(&ev_sigint, NULL);
173 	signal_add(&ev_sigterm, NULL);
174 	signal(SIGPIPE, SIG_IGN);
175 	signal(SIGHUP, SIG_IGN);
176 
177 	/* Setup pipe and event handler to the parent process. */
178 	if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
179 		fatal(NULL);
180 	imsg_init(&iev_main->ibuf, 3);
181 	iev_main->handler = frontend_dispatch_main;
182 	iev_main->events = EV_READ;
183 	event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
184 	    iev_main->handler, iev_main);
185 	event_add(&iev_main->ev, NULL);
186 
187 	LIST_INIT(&interfaces);
188 	event_dispatch();
189 
190 	frontend_shutdown();
191 }
192 
193 __dead void
194 frontend_shutdown(void)
195 {
196 	/* Close pipes. */
197 	msgbuf_write(&iev_engine->ibuf.w);
198 	msgbuf_clear(&iev_engine->ibuf.w);
199 	close(iev_engine->ibuf.fd);
200 	msgbuf_write(&iev_main->ibuf.w);
201 	msgbuf_clear(&iev_main->ibuf.w);
202 	close(iev_main->ibuf.fd);
203 
204 #ifndef SMALL
205 	config_clear(frontend_conf);
206 #endif /* SMALL */
207 
208 	free(iev_engine);
209 	free(iev_main);
210 
211 	log_info("frontend exiting");
212 	exit(0);
213 }
214 
215 int
216 frontend_imsg_compose_main(int type, pid_t pid, void *data,
217     uint16_t datalen)
218 {
219 	return (imsg_compose_event(iev_main, type, 0, pid, -1, data,
220 	    datalen));
221 }
222 
223 int
224 frontend_imsg_compose_engine(int type, uint32_t peerid, pid_t pid,
225     void *data, uint16_t datalen)
226 {
227 	return (imsg_compose_event(iev_engine, type, peerid, pid, -1,
228 	    data, datalen));
229 }
230 
231 void
232 frontend_dispatch_main(int fd, short event, void *bula)
233 {
234 	static struct dhcpleased_conf	*nconf;
235 	static struct iface_conf	*iface_conf;
236 	struct imsg			 imsg;
237 	struct imsgev			*iev = bula;
238 	struct imsgbuf			*ibuf = &iev->ibuf;
239 	struct iface			*iface;
240 	ssize_t				 n;
241 	int				 shut = 0, bpfsock, if_index, udpsock;
242 
243 	if (event & EV_READ) {
244 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
245 			fatal("imsg_read error");
246 		if (n == 0)	/* Connection closed. */
247 			shut = 1;
248 	}
249 	if (event & EV_WRITE) {
250 		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
251 			fatal("msgbuf_write");
252 		if (n == 0)	/* Connection closed. */
253 			shut = 1;
254 	}
255 
256 	for (;;) {
257 		if ((n = imsg_get(ibuf, &imsg)) == -1)
258 			fatal("%s: imsg_get error", __func__);
259 		if (n == 0)	/* No more messages. */
260 			break;
261 
262 		switch (imsg.hdr.type) {
263 		case IMSG_SOCKET_IPC:
264 			/*
265 			 * Setup pipe and event handler to the engine
266 			 * process.
267 			 */
268 			if (iev_engine)
269 				fatalx("%s: received unexpected imsg fd "
270 				    "to frontend", __func__);
271 
272 			if ((fd = imsg_get_fd(&imsg)) == -1)
273 				fatalx("%s: expected to receive imsg fd to "
274 				   "frontend but didn't receive any",
275 				   __func__);
276 
277 			iev_engine = malloc(sizeof(struct imsgev));
278 			if (iev_engine == NULL)
279 				fatal(NULL);
280 
281 			imsg_init(&iev_engine->ibuf, fd);
282 			iev_engine->handler = frontend_dispatch_engine;
283 			iev_engine->events = EV_READ;
284 
285 			event_set(&iev_engine->ev, iev_engine->ibuf.fd,
286 			iev_engine->events, iev_engine->handler, iev_engine);
287 			event_add(&iev_engine->ev, NULL);
288 			break;
289 		case IMSG_BPFSOCK:
290 			if ((bpfsock = imsg_get_fd(&imsg)) == -1)
291 				fatalx("%s: expected to receive imsg "
292 				    "bpf fd but didn't receive any",
293 				    __func__);
294 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
295 				fatalx("%s: IMSG_BPFSOCK wrong length: "
296 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
297 			memcpy(&if_index, imsg.data, sizeof(if_index));
298 			set_bpfsock(bpfsock, if_index);
299 			break;
300 		case IMSG_UDPSOCK:
301 			if ((udpsock = imsg_get_fd(&imsg)) == -1)
302 				fatalx("%s: expected to receive imsg "
303 				    "udpsocket fd but didn't receive any",
304 				    __func__);
305 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
306 				fatalx("%s: IMSG_UDPSOCK wrong length: "
307 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
308 			memcpy(&if_index, imsg.data, sizeof(if_index));
309 			if ((iface = get_iface_by_id(if_index)) == NULL) {
310 				close(fd);
311 				break;
312 			}
313 			if (iface->udpsock != -1)
314 				fatalx("%s: received unexpected udpsocket",
315 				    __func__);
316 			iface->udpsock = udpsock;
317 			break;
318 		case IMSG_CLOSE_UDPSOCK:
319 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
320 				fatalx("%s: IMSG_UDPSOCK wrong length: "
321 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
322 			memcpy(&if_index, imsg.data, sizeof(if_index));
323 			if ((iface = get_iface_by_id(if_index)) != NULL &&
324 			    iface->udpsock != -1) {
325 				close(iface->udpsock);
326 				iface->udpsock = -1;
327 			}
328 			break;
329 		case IMSG_ROUTESOCK:
330 			if ((fd = imsg_get_fd(&imsg)) == -1)
331 				fatalx("%s: expected to receive imsg "
332 				    "routesocket fd but didn't receive any",
333 				    __func__);
334 			event_set(&ev_route, fd, EV_READ | EV_PERSIST,
335 			    route_receive, NULL);
336 			break;
337 		case IMSG_STARTUP:
338 			frontend_startup();
339 			break;
340 #ifndef SMALL
341 		case IMSG_RECONF_CONF:
342 			if (nconf != NULL)
343 				fatalx("%s: IMSG_RECONF_CONF already in "
344 				    "progress", __func__);
345 			if ((nconf = malloc(sizeof(struct dhcpleased_conf))) ==
346 			    NULL)
347 				fatal(NULL);
348 			SIMPLEQ_INIT(&nconf->iface_list);
349 			break;
350 		case IMSG_RECONF_IFACE:
351 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
352 			    iface_conf))
353 				fatalx("%s: IMSG_RECONF_IFACE wrong length: "
354 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
355 			if ((iface_conf = malloc(sizeof(struct iface_conf)))
356 			    == NULL)
357 				fatal(NULL);
358 			memcpy(iface_conf, imsg.data, sizeof(struct
359 			    iface_conf));
360 			iface_conf->vc_id = NULL;
361 			iface_conf->vc_id_len = 0;
362 			iface_conf->c_id = NULL;
363 			iface_conf->c_id_len = 0;
364 			iface_conf->h_name = NULL;
365 			SIMPLEQ_INSERT_TAIL(&nconf->iface_list,
366 			    iface_conf, entry);
367 			break;
368 		case IMSG_RECONF_VC_ID:
369 			if (iface_conf == NULL)
370 				fatal("IMSG_RECONF_VC_ID without "
371 				    "IMSG_RECONF_IFACE");
372 			if (IMSG_DATA_SIZE(imsg) > 255 + 2)
373 				fatalx("%s: IMSG_RECONF_VC_ID wrong length: "
374 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
375 			if ((iface_conf->vc_id = malloc(IMSG_DATA_SIZE(imsg)))
376 			    == NULL)
377 				fatal(NULL);
378 			memcpy(iface_conf->vc_id, imsg.data,
379 			    IMSG_DATA_SIZE(imsg));
380 			iface_conf->vc_id_len = IMSG_DATA_SIZE(imsg);
381 			break;
382 		case IMSG_RECONF_C_ID:
383 			if (iface_conf == NULL)
384 				fatal("IMSG_RECONF_C_ID without "
385 				    "IMSG_RECONF_IFACE");
386 			if (IMSG_DATA_SIZE(imsg) > 255 + 2)
387 				fatalx("%s: IMSG_RECONF_C_ID wrong length: "
388 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
389 			if ((iface_conf->c_id = malloc(IMSG_DATA_SIZE(imsg)))
390 			    == NULL)
391 				fatal(NULL);
392 			memcpy(iface_conf->c_id, imsg.data,
393 			    IMSG_DATA_SIZE(imsg));
394 			iface_conf->c_id_len = IMSG_DATA_SIZE(imsg);
395 			break;
396 		case IMSG_RECONF_H_NAME:
397 			if (iface_conf == NULL)
398 				fatal("IMSG_RECONF_H_NAME without "
399 				    "IMSG_RECONF_IFACE");
400 			if (((char *)imsg.data)[IMSG_DATA_SIZE(imsg) - 1] !=
401 			    '\0')
402 				fatalx("Invalid hostname");
403 			if (IMSG_DATA_SIZE(imsg) > 256)
404 				fatalx("Invalid hostname");
405 			if ((iface_conf->h_name = strdup(imsg.data)) == NULL)
406 				fatal(NULL);
407 			break;
408 		case IMSG_RECONF_END: {
409 			int	 i;
410 			int	*ifaces;
411 			char	 ifnamebuf[IF_NAMESIZE], *if_name;
412 
413 			if (nconf == NULL)
414 				fatalx("%s: IMSG_RECONF_END without "
415 				    "IMSG_RECONF_CONF", __func__);
416 
417 			ifaces = changed_ifaces(frontend_conf, nconf);
418 			merge_config(frontend_conf, nconf);
419 			nconf = NULL;
420 			for (i = 0; ifaces[i] != 0; i++) {
421 				if_index = ifaces[i];
422 				if_name = if_indextoname(if_index, ifnamebuf);
423 				log_debug("changed iface: %s[%d]", if_name !=
424 				    NULL ? if_name : "<unknown>", if_index);
425 				frontend_imsg_compose_engine(
426 				    IMSG_REQUEST_REBOOT, 0, 0, &if_index,
427 				    sizeof(if_index));
428 			}
429 			free(ifaces);
430 			break;
431 		}
432 		case IMSG_CONTROLFD:
433 			if ((fd = imsg_get_fd(&imsg)) == -1)
434 				fatalx("%s: expected to receive imsg "
435 				    "control fd but didn't receive any",
436 				    __func__);
437 			/* Listen on control socket. */
438 			control_listen(fd);
439 			break;
440 		case IMSG_CTL_END:
441 			control_imsg_relay(&imsg);
442 			break;
443 #endif	/* SMALL */
444 		default:
445 			log_debug("%s: error handling imsg %d", __func__,
446 			    imsg.hdr.type);
447 			break;
448 		}
449 		imsg_free(&imsg);
450 	}
451 	if (!shut)
452 		imsg_event_add(iev);
453 	else {
454 		/* This pipe is dead. Remove its event handler. */
455 		event_del(&iev->ev);
456 		event_loopexit(NULL);
457 	}
458 }
459 
460 void
461 frontend_dispatch_engine(int fd, short event, void *bula)
462 {
463 	struct imsgev		*iev = bula;
464 	struct imsgbuf		*ibuf = &iev->ibuf;
465 	struct imsg		 imsg;
466 	struct iface		*iface;
467 	ssize_t			 n;
468 	int			 shut = 0;
469 
470 	if (event & EV_READ) {
471 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
472 			fatal("imsg_read error");
473 		if (n == 0)	/* Connection closed. */
474 			shut = 1;
475 	}
476 	if (event & EV_WRITE) {
477 		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
478 			fatal("msgbuf_write");
479 		if (n == 0)	/* Connection closed. */
480 			shut = 1;
481 	}
482 
483 	for (;;) {
484 		if ((n = imsg_get(ibuf, &imsg)) == -1)
485 			fatal("%s: imsg_get error", __func__);
486 		if (n == 0)	/* No more messages. */
487 			break;
488 
489 		switch (imsg.hdr.type) {
490 #ifndef	SMALL
491 		case IMSG_CTL_END:
492 		case IMSG_CTL_SHOW_INTERFACE_INFO:
493 			control_imsg_relay(&imsg);
494 			break;
495 #endif	/* SMALL */
496 		case IMSG_SEND_DISCOVER: {
497 			struct imsg_req_dhcp	 imsg_req_dhcp;
498 			if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_dhcp))
499 				fatalx("%s: IMSG_SEND_DISCOVER wrong "
500 				    "length: %lu", __func__,
501 				    IMSG_DATA_SIZE(imsg));
502 			memcpy(&imsg_req_dhcp, imsg.data,
503 			    sizeof(imsg_req_dhcp));
504 
505 			iface = get_iface_by_id(imsg_req_dhcp.if_index);
506 
507 			if (iface == NULL)
508 				break;
509 
510 			iface_data_from_imsg(iface, &imsg_req_dhcp);
511 			send_packet(DHCPDISCOVER, iface);
512 			break;
513 		}
514 		case IMSG_SEND_REQUEST: {
515 			struct imsg_req_dhcp	 imsg_req_dhcp;
516 			if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_dhcp))
517 				fatalx("%s: IMSG_SEND_REQUEST wrong "
518 				    "length: %lu", __func__,
519 				    IMSG_DATA_SIZE(imsg));
520 			memcpy(&imsg_req_dhcp, imsg.data,
521 			    sizeof(imsg_req_dhcp));
522 
523 			iface = get_iface_by_id(imsg_req_dhcp.if_index);
524 
525 			if (iface == NULL)
526 				break;
527 
528 			iface_data_from_imsg(iface, &imsg_req_dhcp);
529 			send_packet(DHCPREQUEST, iface);
530 			break;
531 		}
532 		default:
533 			log_debug("%s: error handling imsg %d", __func__,
534 			    imsg.hdr.type);
535 			break;
536 		}
537 		imsg_free(&imsg);
538 	}
539 	if (!shut)
540 		imsg_event_add(iev);
541 	else {
542 		/* This pipe is dead. Remove its event handler. */
543 		event_del(&iev->ev);
544 		event_loopexit(NULL);
545 	}
546 }
547 
548 int
549 get_flags(char *if_name)
550 {
551 	struct ifreq		 ifr;
552 
553 	strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
554 	if (ioctl(ioctlsock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) {
555 		log_warn("SIOCGIFFLAGS");
556 		return -1;
557 	}
558 	return ifr.ifr_flags;
559 }
560 
561 int
562 get_xflags(char *if_name)
563 {
564 	struct ifreq		 ifr;
565 
566 	strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
567 	if (ioctl(ioctlsock, SIOCGIFXFLAGS, (caddr_t)&ifr) == -1) {
568 		log_warn("SIOCGIFXFLAGS");
569 		return -1;
570 	}
571 	return ifr.ifr_flags;
572 }
573 
574 void
575 update_iface(struct if_msghdr *ifm, struct sockaddr_dl *sdl)
576 {
577 	struct iface		*iface;
578 	struct imsg_ifinfo	 ifinfo;
579 	uint32_t		 if_index;
580 	int			 flags, xflags;
581 	char			 ifnamebuf[IF_NAMESIZE], *if_name;
582 
583 	if_index = ifm->ifm_index;
584 
585 	flags = ifm->ifm_flags;
586 	xflags = ifm->ifm_xflags;
587 
588 	iface = get_iface_by_id(if_index);
589 	if_name = if_indextoname(if_index, ifnamebuf);
590 
591 	if (if_name == NULL) {
592 		if (iface != NULL) {
593 			log_debug("interface with idx %d removed", if_index);
594 			frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
595 			    &if_index, sizeof(if_index));
596 			remove_iface(if_index);
597 		}
598 		return;
599 	}
600 
601 	if (!(xflags & IFXF_AUTOCONF4)) {
602 		if (iface != NULL) {
603 			log_info("Removed autoconf flag from %s", if_name);
604 			frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
605 			    &if_index, sizeof(if_index));
606 			remove_iface(if_index);
607 		}
608 		return;
609 	}
610 
611 	memset(&ifinfo, 0, sizeof(ifinfo));
612 	ifinfo.if_index = if_index;
613 	ifinfo.link_state = ifm->ifm_data.ifi_link_state;
614 	ifinfo.rdomain = ifm->ifm_tableid;
615 	ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) ==
616 	    (IFF_UP | IFF_RUNNING);
617 
618 	if (sdl != NULL && (sdl->sdl_type == IFT_ETHER ||
619 	    sdl->sdl_type == IFT_CARP) && sdl->sdl_alen == ETHER_ADDR_LEN)
620 		memcpy(ifinfo.hw_address.ether_addr_octet, LLADDR(sdl),
621 		    ETHER_ADDR_LEN);
622 	else if (iface == NULL) {
623 		log_warnx("Could not find AF_LINK address for %s.", if_name);
624 		return;
625 	}
626 
627 	if (iface == NULL) {
628 		if ((iface = calloc(1, sizeof(*iface))) == NULL)
629 			fatal("calloc");
630 		iface->udpsock = -1;
631 		LIST_INSERT_HEAD(&interfaces, iface, entries);
632 		frontend_imsg_compose_main(IMSG_OPEN_BPFSOCK, 0,
633 		    &if_index, sizeof(if_index));
634 	} else {
635 		if (iface->ifinfo.rdomain != ifinfo.rdomain &&
636 		    iface->udpsock != -1) {
637 			close(iface->udpsock);
638 			iface->udpsock = -1;
639 		}
640 	}
641 
642 	if (memcmp(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo)) != 0) {
643 		memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo));
644 		frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &iface->ifinfo,
645 		    sizeof(iface->ifinfo));
646 	}
647 }
648 
649 void
650 frontend_startup(void)
651 {
652 	if (!event_initialized(&ev_route))
653 		fatalx("%s: did not receive a route socket from the main "
654 		    "process", __func__);
655 
656 	init_ifaces();
657 	if (pledge("stdio unix recvfd", NULL) == -1)
658 		fatal("pledge");
659 	event_add(&ev_route, NULL);
660 }
661 
662 void
663 init_ifaces(void)
664 {
665 	struct iface		*iface;
666 	struct imsg_ifinfo	 ifinfo;
667 	struct if_nameindex	*ifnidxp, *ifnidx;
668 	struct ifaddrs		*ifap, *ifa;
669 	uint32_t		 if_index;
670 	int			 flags, xflags;
671 	char			*if_name;
672 
673 	if ((ifnidxp = if_nameindex()) == NULL)
674 		fatalx("if_nameindex");
675 
676 	if (getifaddrs(&ifap) != 0)
677 		fatal("getifaddrs");
678 
679 	for (ifnidx = ifnidxp; ifnidx->if_index != 0 && ifnidx->if_name != NULL;
680 	    ifnidx++) {
681 		if_index = ifnidx->if_index;
682 		if_name = ifnidx->if_name;
683 		if ((flags = get_flags(if_name)) == -1)
684 			continue;
685 		if ((xflags = get_xflags(if_name)) == -1)
686 			continue;
687 		if (!(xflags & IFXF_AUTOCONF4))
688 			continue;
689 
690 		memset(&ifinfo, 0, sizeof(ifinfo));
691 		ifinfo.if_index = if_index;
692 		ifinfo.link_state = -1;
693 		ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) ==
694 		    (IFF_UP | IFF_RUNNING);
695 
696 		for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
697 			if (strcmp(if_name, ifa->ifa_name) != 0)
698 				continue;
699 			if (ifa->ifa_addr == NULL)
700 				continue;
701 
702 			switch (ifa->ifa_addr->sa_family) {
703 			case AF_LINK: {
704 				struct if_data		*if_data;
705 				struct sockaddr_dl	*sdl;
706 
707 				sdl = (struct sockaddr_dl *)ifa->ifa_addr;
708 				if ((sdl->sdl_type != IFT_ETHER &&
709 				    sdl->sdl_type != IFT_CARP) ||
710 				    sdl->sdl_alen != ETHER_ADDR_LEN)
711 					continue;
712 				memcpy(ifinfo.hw_address.ether_addr_octet,
713 				    LLADDR(sdl), ETHER_ADDR_LEN);
714 
715 				if_data = (struct if_data *)ifa->ifa_data;
716 				ifinfo.link_state = if_data->ifi_link_state;
717 				ifinfo.rdomain = if_data->ifi_rdomain;
718 				goto out;
719 			}
720 			default:
721 				break;
722 			}
723 		}
724  out:
725 		if (ifinfo.link_state == -1)
726 			/* no AF_LINK found */
727 			continue;
728 
729 		if ((iface = calloc(1, sizeof(*iface))) == NULL)
730 			fatal("calloc");
731 		iface->udpsock = -1;
732 		memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo));
733 		LIST_INSERT_HEAD(&interfaces, iface, entries);
734 		frontend_imsg_compose_main(IMSG_OPEN_BPFSOCK, 0,
735 		    &if_index, sizeof(if_index));
736 		frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &iface->ifinfo,
737 		    sizeof(iface->ifinfo));
738 	}
739 
740 	freeifaddrs(ifap);
741 	if_freenameindex(ifnidxp);
742 }
743 
744 void
745 route_receive(int fd, short events, void *arg)
746 {
747 	static uint8_t			 *buf;
748 
749 	struct rt_msghdr		*rtm;
750 	struct sockaddr			*sa, *rti_info[RTAX_MAX];
751 	ssize_t				 n;
752 
753 	if (buf == NULL) {
754 		buf = malloc(ROUTE_SOCKET_BUF_SIZE);
755 		if (buf == NULL)
756 			fatal("malloc");
757 	}
758 	rtm = (struct rt_msghdr *)buf;
759 	if ((n = read(fd, buf, ROUTE_SOCKET_BUF_SIZE)) == -1) {
760 		if (errno == EAGAIN || errno == EINTR)
761 			return;
762 		log_warn("dispatch_rtmsg: read error");
763 		return;
764 	}
765 
766 	if (n == 0)
767 		fatal("routing socket closed");
768 
769 	if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) {
770 		log_warnx("partial rtm of %zd in buffer", n);
771 		return;
772 	}
773 
774 	if (rtm->rtm_version != RTM_VERSION)
775 		return;
776 
777 	sa = (struct sockaddr *)(buf + rtm->rtm_hdrlen);
778 	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
779 
780 	handle_route_message(rtm, rti_info);
781 }
782 
783 void
784 handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info)
785 {
786 	struct sockaddr_dl		*sdl = NULL;
787 	struct if_announcemsghdr	*ifan;
788 	uint32_t			 if_index;
789 
790 	switch (rtm->rtm_type) {
791 	case RTM_IFINFO:
792 		if (rtm->rtm_addrs & RTA_IFP && rti_info[RTAX_IFP]->sa_family
793 		    == AF_LINK)
794 			sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP];
795 		update_iface((struct if_msghdr *)rtm, sdl);
796 		break;
797 	case RTM_IFANNOUNCE:
798 		ifan = (struct if_announcemsghdr *)rtm;
799 		if_index = ifan->ifan_index;
800                 if (ifan->ifan_what == IFAN_DEPARTURE) {
801 			frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
802 			    &if_index, sizeof(if_index));
803 			remove_iface(if_index);
804 		}
805 		break;
806 	case RTM_PROPOSAL:
807 		if (rtm->rtm_priority == RTP_PROPOSAL_SOLICIT) {
808 			log_debug("RTP_PROPOSAL_SOLICIT");
809 			frontend_imsg_compose_engine(IMSG_REPROPOSE_RDNS,
810 			    0, 0, NULL, 0);
811 		}
812 #ifndef SMALL
813 		else if (rtm->rtm_flags & RTF_PROTO3) {
814 			char		 ifnamebuf[IF_NAMESIZE], *if_name;
815 
816 			if_index = rtm->rtm_index;
817 			if_name = if_indextoname(if_index, ifnamebuf);
818 			log_warnx("\"dhclient %s\" ran, requesting new lease",
819 			    if_name != NULL ? if_name : "(unknown)");
820 			frontend_imsg_compose_engine(IMSG_REQUEST_REBOOT,
821 			    0, 0, &if_index, sizeof(if_index));
822 		}
823 #endif /* SMALL */
824 		break;
825 	default:
826 		log_debug("unexpected RTM: %d", rtm->rtm_type);
827 		break;
828 	}
829 }
830 
831 #define ROUNDUP(a) \
832 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
833 
834 void
835 get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
836 {
837 	int	i;
838 
839 	for (i = 0; i < RTAX_MAX; i++) {
840 		if (addrs & (1 << i)) {
841 			rti_info[i] = sa;
842 			sa = (struct sockaddr *)((char *)(sa) +
843 			    ROUNDUP(sa->sa_len));
844 		} else
845 			rti_info[i] = NULL;
846 	}
847 }
848 
849 void
850 bpf_receive(int fd, short events, void *arg)
851 {
852 	struct bpf_hdr		*hdr;
853 	struct imsg_dhcp	 imsg_dhcp;
854 	struct iface		*iface;
855 	ssize_t			 len, rem;
856 	uint8_t			*p;
857 
858 	iface = (struct iface *)arg;
859 
860 	if ((len = read(fd, iface->bpfev.buf, BPFLEN)) == -1) {
861 		log_warn("%s: read", __func__);
862 		return;
863 	}
864 
865 	if (len == 0)
866 		fatal("%s len == 0", __func__);
867 
868 	memset(&imsg_dhcp, 0, sizeof(imsg_dhcp));
869 	imsg_dhcp.if_index = iface->ifinfo.if_index;
870 
871 	rem = len;
872 	p = iface->bpfev.buf;
873 
874 	while (rem > 0) {
875 		if ((size_t)rem < sizeof(*hdr)) {
876 			log_warnx("packet too short");
877 			return;
878 		}
879 		hdr = (struct bpf_hdr *)p;
880 		if (hdr->bh_caplen != hdr->bh_datalen) {
881 			log_warnx("skipping truncated packet");
882 			goto cont;
883 		}
884 		if (rem < hdr->bh_hdrlen + hdr->bh_caplen)
885 			/* we are done */
886 			break;
887 		if (hdr->bh_caplen > sizeof(imsg_dhcp.packet)) {
888 			log_warn("packet too big");
889 			goto cont;
890 		}
891 		memcpy(&imsg_dhcp.packet, p + hdr->bh_hdrlen, hdr->bh_caplen);
892 		imsg_dhcp.len = hdr->bh_caplen;
893 		frontend_imsg_compose_engine(IMSG_DHCP, 0, 0, &imsg_dhcp,
894 		    sizeof(imsg_dhcp));
895  cont:
896 		p += BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
897 		rem -= BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
898 
899 	}
900 }
901 
902 void
903 iface_data_from_imsg(struct iface* iface, struct imsg_req_dhcp *imsg)
904 {
905 	iface->xid = imsg->xid;
906 	iface->ciaddr = imsg->ciaddr;
907 	iface->requested_ip = imsg->requested_ip;
908 	iface->server_identifier = imsg->server_identifier;
909 	iface->dhcp_server = imsg->dhcp_server;
910 }
911 
912 ssize_t
913 build_packet(uint8_t message_type, char *if_name, uint32_t xid,
914     struct ether_addr *hw_address, struct in_addr *ciaddr, struct in_addr
915     *requested_ip, struct in_addr *server_identifier)
916 {
917 	static uint8_t	 dhcp_cookie[] = DHCP_COOKIE;
918 	static uint8_t	 dhcp_message_type[] = {DHO_DHCP_MESSAGE_TYPE, 1,
919 		DHCPDISCOVER};
920 	static uint8_t	 dhcp_hostname[255 + 2] = {DHO_HOST_NAME, 0 /*, ... */};
921 	static uint8_t	 dhcp_client_id[] = {DHO_DHCP_CLIENT_IDENTIFIER, 7,
922 		HTYPE_ETHER, 0, 0, 0, 0, 0, 0};
923 	static uint8_t	 dhcp_req_list[] = {DHO_DHCP_PARAMETER_REQUEST_LIST,
924 		8, DHO_SUBNET_MASK, DHO_ROUTERS, DHO_DOMAIN_NAME_SERVERS,
925 		DHO_HOST_NAME, DHO_DOMAIN_NAME, DHO_BROADCAST_ADDRESS,
926 		DHO_DOMAIN_SEARCH, DHO_CLASSLESS_STATIC_ROUTES};
927 	static uint8_t	 dhcp_req_list_v6[] = {DHO_DHCP_PARAMETER_REQUEST_LIST,
928 		9, DHO_SUBNET_MASK, DHO_ROUTERS, DHO_DOMAIN_NAME_SERVERS,
929 		DHO_HOST_NAME, DHO_DOMAIN_NAME, DHO_BROADCAST_ADDRESS,
930 		DHO_DOMAIN_SEARCH, DHO_CLASSLESS_STATIC_ROUTES,
931 		DHO_IPV6_ONLY_PREFERRED};
932 	static uint8_t	 dhcp_requested_address[] = {DHO_DHCP_REQUESTED_ADDRESS,
933 		4, 0, 0, 0, 0};
934 	static uint8_t	 dhcp_server_identifier[] = {DHO_DHCP_SERVER_IDENTIFIER,
935 		4, 0, 0, 0, 0};
936 #ifndef SMALL
937 	struct iface_conf	*iface_conf;
938 #endif /* SMALL */
939 	struct dhcp_hdr		*hdr;
940 	ssize_t			 len;
941 	uint8_t			*p;
942 	char			*c;
943 
944 #ifndef SMALL
945 	iface_conf = find_iface_conf(&frontend_conf->iface_list, if_name);
946 #endif /* SMALL */
947 
948 	memset(dhcp_packet, 0, sizeof(dhcp_packet));
949 	dhcp_message_type[2] = message_type;
950 	p = dhcp_packet;
951 	hdr = (struct dhcp_hdr *)p;
952 	hdr->op = DHCP_BOOTREQUEST;
953 	hdr->htype = HTYPE_ETHER;
954 	hdr->hlen = 6;
955 	hdr->hops = 0;
956 	hdr->xid = htonl(xid);
957 	hdr->secs = 0;
958 	hdr->ciaddr = *ciaddr;
959 	memcpy(hdr->chaddr, hw_address, sizeof(*hw_address));
960 	p += sizeof(struct dhcp_hdr);
961 	memcpy(p, dhcp_cookie, sizeof(dhcp_cookie));
962 	p += sizeof(dhcp_cookie);
963 	memcpy(p, dhcp_message_type, sizeof(dhcp_message_type));
964 	p += sizeof(dhcp_message_type);
965 
966 #ifndef SMALL
967 	if (iface_conf != NULL && iface_conf->h_name != NULL) {
968 		if (iface_conf->h_name[0] != '\0') {
969 			dhcp_hostname[1] = strlen(iface_conf->h_name);
970 			memcpy(dhcp_hostname + 2, iface_conf->h_name,
971 			    strlen(iface_conf->h_name));
972 			memcpy(p, dhcp_hostname, dhcp_hostname[1] + 2);
973 			p += dhcp_hostname[1] + 2;
974 		}
975 	} else
976 #endif /* SMALL */
977 	{
978 		if (gethostname(dhcp_hostname + 2,
979 		    sizeof(dhcp_hostname) - 2) == 0 &&
980 		    dhcp_hostname[2] != '\0') {
981 			if ((c = strchr(dhcp_hostname + 2, '.')) != NULL)
982 				*c = '\0';
983 			dhcp_hostname[1] = strlen(dhcp_hostname + 2);
984 			memcpy(p, dhcp_hostname, dhcp_hostname[1] + 2);
985 			p += dhcp_hostname[1] + 2;
986 		}
987 	}
988 
989 #ifndef SMALL
990 	if (iface_conf != NULL) {
991 		if (iface_conf->c_id_len > 0) {
992 			/* XXX check space */
993 			memcpy(p, iface_conf->c_id, iface_conf->c_id_len);
994 			p += iface_conf->c_id_len;
995 		} else {
996 			memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address));
997 			memcpy(p, dhcp_client_id, sizeof(dhcp_client_id));
998 			p += sizeof(dhcp_client_id);
999 		}
1000 		if (iface_conf->vc_id_len > 0) {
1001 			/* XXX check space */
1002 			memcpy(p, iface_conf->vc_id, iface_conf->vc_id_len);
1003 			p += iface_conf->vc_id_len;
1004 		}
1005 		if (iface_conf->prefer_ipv6) {
1006 			memcpy(p, dhcp_req_list_v6, sizeof(dhcp_req_list_v6));
1007 			p += sizeof(dhcp_req_list_v6);
1008 
1009 		} else {
1010 			memcpy(p, dhcp_req_list, sizeof(dhcp_req_list));
1011 			p += sizeof(dhcp_req_list);
1012 		}
1013 	} else
1014 #endif /* SMALL */
1015 	{
1016 		memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address));
1017 		memcpy(p, dhcp_client_id, sizeof(dhcp_client_id));
1018 		p += sizeof(dhcp_client_id);
1019 		memcpy(p, dhcp_req_list, sizeof(dhcp_req_list));
1020 		p += sizeof(dhcp_req_list);
1021 	}
1022 
1023 	if (requested_ip->s_addr != INADDR_ANY) {
1024 		memcpy(dhcp_requested_address + 2, requested_ip,
1025 		    sizeof(*requested_ip));
1026 		memcpy(p, dhcp_requested_address,
1027 		    sizeof(dhcp_requested_address));
1028 		p += sizeof(dhcp_requested_address);
1029 	}
1030 
1031 	if (server_identifier->s_addr != INADDR_ANY) {
1032 		memcpy(dhcp_server_identifier + 2, server_identifier,
1033 		    sizeof(*server_identifier));
1034 		memcpy(p, dhcp_server_identifier,
1035 		    sizeof(dhcp_server_identifier));
1036 		p += sizeof(dhcp_server_identifier);
1037 	}
1038 
1039 	*p = DHO_END;
1040 	p += 1;
1041 
1042 	len = p - dhcp_packet;
1043 
1044 	/* dhcp_packet is initialized with DHO_PADs */
1045 	if (len < BOOTP_MIN_LEN)
1046 		len = BOOTP_MIN_LEN;
1047 
1048 	return (len);
1049 }
1050 
1051 void
1052 send_packet(uint8_t message_type, struct iface *iface)
1053 {
1054 	ssize_t			 pkt_len;
1055 	char			 ifnamebuf[IF_NAMESIZE], *if_name;
1056 
1057 	if (!event_initialized(&iface->bpfev.ev)) {
1058 		iface->send_discover = 1;
1059 		return;
1060 	}
1061 
1062 	iface->send_discover = 0;
1063 
1064 	if ((if_name = if_indextoname(iface->ifinfo.if_index, ifnamebuf)) == NULL)
1065 		return; /* iface went away, nothing to do */
1066 
1067 	log_debug("%s on %s", message_type == DHCPDISCOVER ? "DHCPDISCOVER" :
1068 	    "DHCPREQUEST", if_name);
1069 
1070 	pkt_len = build_packet(message_type, if_name, iface->xid,
1071 	    &iface->ifinfo.hw_address, &iface->ciaddr, &iface->requested_ip,
1072 	    &iface->server_identifier);
1073 	if (iface->dhcp_server.s_addr != INADDR_ANY) {
1074 		if (udp_send_packet(iface, dhcp_packet, pkt_len) == -1)
1075 			bpf_send_packet(iface, dhcp_packet, pkt_len);
1076 	} else
1077 		bpf_send_packet(iface, dhcp_packet, pkt_len);
1078 }
1079 
1080 int
1081 udp_send_packet(struct iface *iface, uint8_t *packet, ssize_t len)
1082 {
1083 	struct sockaddr_in	to;
1084 
1085 	memset(&to, 0, sizeof(to));
1086 	to.sin_family = AF_INET;
1087 	to.sin_len = sizeof(to);
1088 	to.sin_addr = iface->dhcp_server;
1089 	to.sin_port = ntohs(SERVER_PORT);
1090 
1091 	if (sendto(iface->udpsock, packet, len, 0, (struct sockaddr *)&to,
1092 	    sizeof(to)) == -1) {
1093 		log_warn("sendto");
1094 		return -1;
1095 	}
1096 	return 0;
1097 }
1098 void
1099 bpf_send_packet(struct iface *iface, uint8_t *packet, ssize_t len)
1100 {
1101 	struct iovec		 iov[4];
1102 	struct ether_header	 eh;
1103 	struct ip		 ip;
1104 	struct udphdr		 udp;
1105 	ssize_t			 total, result;
1106 	int			 iovcnt = 0, i;
1107 
1108 	memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
1109 	memcpy(eh.ether_shost, &iface->ifinfo.hw_address,
1110 	    sizeof(eh.ether_dhost));
1111 	eh.ether_type = htons(ETHERTYPE_IP);
1112 	iov[0].iov_base = &eh;
1113 	iov[0].iov_len = sizeof(eh);
1114 	iovcnt++;
1115 
1116 	ip.ip_v = 4;
1117 	ip.ip_hl = 5;
1118 	ip.ip_tos = IPTOS_LOWDELAY;
1119 	ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len);
1120 	ip.ip_id = 0;
1121 	ip.ip_off = 0;
1122 	ip.ip_ttl = 128;
1123 	ip.ip_p = IPPROTO_UDP;
1124 	ip.ip_sum = 0;
1125 	ip.ip_src.s_addr = INADDR_ANY;
1126 	ip.ip_dst.s_addr = INADDR_BROADCAST;
1127 	ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0));
1128 	iov[iovcnt].iov_base = &ip;
1129 	iov[iovcnt].iov_len = sizeof(ip);
1130 	iovcnt++;
1131 
1132 	udp.uh_sport = htons(CLIENT_PORT);
1133 	udp.uh_dport = htons(SERVER_PORT);
1134 	udp.uh_ulen = htons(sizeof(udp) + len);
1135 	udp.uh_sum = 0;
1136 	udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
1137 	    checksum((unsigned char *)packet, len,
1138 	    checksum((unsigned char *)&ip.ip_src,
1139 	    2 * sizeof(ip.ip_src),
1140 	    IPPROTO_UDP + (uint32_t)ntohs(udp.uh_ulen)))));
1141 	iov[iovcnt].iov_base = &udp;
1142 	iov[iovcnt].iov_len = sizeof(udp);
1143 	iovcnt++;
1144 
1145 	iov[iovcnt].iov_base = packet;
1146 	iov[iovcnt].iov_len = len;
1147 	iovcnt++;
1148 
1149 	total = 0;
1150 	for (i = 0; i < iovcnt; i++)
1151 		total += iov[i].iov_len;
1152 
1153 	result = writev(EVENT_FD(&iface->bpfev.ev), iov, iovcnt);
1154 	if (result == -1)
1155 		log_warn("%s: writev", __func__);
1156 	else if (result < total) {
1157 		log_warnx("%s, writev: %zd of %zd bytes", __func__, result,
1158 		    total);
1159 	}
1160 }
1161 
1162 struct iface*
1163 get_iface_by_id(uint32_t if_index)
1164 {
1165 	struct iface	*iface;
1166 
1167 	LIST_FOREACH (iface, &interfaces, entries) {
1168 		if (iface->ifinfo.if_index == if_index)
1169 			return (iface);
1170 	}
1171 
1172 	return (NULL);
1173 }
1174 
1175 void
1176 remove_iface(uint32_t if_index)
1177 {
1178 	struct iface	*iface;
1179 
1180 	iface = get_iface_by_id(if_index);
1181 
1182 	if (iface == NULL)
1183 		return;
1184 
1185 	LIST_REMOVE(iface, entries);
1186 	if (event_initialized(&iface->bpfev.ev)) {
1187 		event_del(&iface->bpfev.ev);
1188 		close(EVENT_FD(&iface->bpfev.ev));
1189 	}
1190 	if (iface->udpsock != -1)
1191 		close(iface->udpsock);
1192 	free(iface);
1193 }
1194 
1195 void
1196 set_bpfsock(int bpfsock, uint32_t if_index)
1197 {
1198 	struct iface	*iface;
1199 
1200 	iface = get_iface_by_id(if_index);
1201 
1202 	if (iface == NULL) {
1203 		/*
1204 		 * The interface disappeared while we were waiting for the
1205 		 * parent process to open the bpf socket.
1206 		 */
1207 		close(bpfsock);
1208 	} else if (event_initialized(&iface->bpfev.ev)) {
1209 		/*
1210 		 * The autoconf flag is flapping and we have multiple bpf sockets in
1211 		 * flight. We don't need this one because we already got one.
1212 		 */
1213 		close(bpfsock);
1214 	} else {
1215 		event_set(&iface->bpfev.ev, bpfsock, EV_READ |
1216 		    EV_PERSIST, bpf_receive, iface);
1217 		event_add(&iface->bpfev.ev, NULL);
1218 		if (iface->send_discover)
1219 			send_packet(DHCPDISCOVER, iface);
1220 	}
1221 }
1222 
1223 #ifndef SMALL
1224 struct iface_conf*
1225 find_iface_conf(struct iface_conf_head *head, char *if_name)
1226 {
1227 	struct iface_conf	*iface_conf;
1228 
1229 	if (if_name == NULL)
1230 		return (NULL);
1231 
1232 	SIMPLEQ_FOREACH(iface_conf, head, entry) {
1233 		if (strcmp(iface_conf->name, if_name) == 0)
1234 			return iface_conf;
1235 	}
1236 	return (NULL);
1237 }
1238 
1239 int*
1240 changed_ifaces(struct dhcpleased_conf *oconf, struct dhcpleased_conf *nconf)
1241 {
1242 	struct iface_conf	*iface_conf, *oiface_conf;
1243 	int			*ret, if_index, count = 0, i = 0;
1244 
1245 	/*
1246 	 * Worst case: All old interfaces replaced with new interfaces.
1247 	 * This should still be a small number
1248 	 */
1249 	SIMPLEQ_FOREACH(iface_conf, &oconf->iface_list, entry)
1250 	    count++;
1251 	SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry)
1252 	    count++;
1253 
1254 	ret = calloc(count + 1, sizeof(int));
1255 
1256 	SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry) {
1257 		if ((if_index = if_nametoindex(iface_conf->name)) == 0)
1258 			continue;
1259 		oiface_conf = find_iface_conf(&oconf->iface_list,
1260 		    iface_conf->name);
1261 		if (oiface_conf == NULL) {
1262 			/* new interface added to config */
1263 			ret[i++] = if_index;
1264 		} else if (iface_conf_cmp(iface_conf, oiface_conf) != 0) {
1265 			/* interface conf changed */
1266 			ret[i++] = if_index;
1267 		}
1268 	}
1269 	SIMPLEQ_FOREACH(oiface_conf, &oconf->iface_list, entry) {
1270 		if ((if_index = if_nametoindex(oiface_conf->name)) == 0)
1271 			continue;
1272 		if (find_iface_conf(&nconf->iface_list, oiface_conf->name) ==
1273 		    NULL) {
1274 			/* interface removed from config */
1275 			ret[i++] = if_index;
1276 		}
1277 	}
1278 	return ret;
1279 }
1280 
1281 int
1282 iface_conf_cmp(struct iface_conf *a, struct iface_conf *b)
1283 {
1284 	if (a->vc_id_len != b->vc_id_len)
1285 		return 1;
1286 	if (memcmp(a->vc_id, b->vc_id, a->vc_id_len) != 0)
1287 		return 1;
1288 	if (a->c_id_len != b->c_id_len)
1289 		return 1;
1290 	if (memcmp(a->c_id, b->c_id, a->c_id_len) != 0)
1291 		return 1;
1292 	if (a->h_name == NULL ||  b->h_name == NULL)
1293 		return 1;
1294 	if (strcmp(a->h_name, b->h_name) != 0)
1295 		return 1;
1296 	if (a->ignore != b->ignore)
1297 		return 1;
1298 	if (a->ignore_servers_len != b->ignore_servers_len)
1299 		return 1;
1300 	if (memcmp(a->ignore_servers, b->ignore_servers,
1301 	    a->ignore_servers_len * sizeof (struct in_addr)) != 0)
1302 		return 1;
1303 	return 0;
1304 }
1305 #endif /* SMALL */
1306