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