1/*	$OpenBSD: frontend.c,v 1.33 2024/01/26 21:14:08 jan 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
62struct bpf_ev {
63	struct event		 ev;
64	uint8_t			 buf[BPFLEN];
65};
66
67struct 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);
81void		 frontend_sig_handler(int, short, void *);
82void		 update_iface(struct if_msghdr *, struct sockaddr_dl *);
83void		 frontend_startup(void);
84void		 init_ifaces(void);
85void		 route_receive(int, short, void *);
86void		 handle_route_message(struct rt_msghdr *, struct sockaddr **);
87void		 get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
88void		 bpf_receive(int, short, void *);
89int		 get_flags(char *);
90int		 get_xflags(char *);
91struct iface	*get_iface_by_id(uint32_t);
92void		 remove_iface(uint32_t);
93void		 set_bpfsock(int, uint32_t);
94void		 iface_data_from_imsg(struct iface*, struct imsg_req_dhcp *);
95ssize_t		 build_packet(uint8_t, char *, uint32_t, struct ether_addr *,
96		     struct in_addr *, struct in_addr *, struct in_addr *);
97void		 send_packet(uint8_t, struct iface *);
98void		 bpf_send_packet(struct iface *, uint8_t *, ssize_t);
99int		 udp_send_packet(struct iface *, uint8_t *, ssize_t);
100#ifndef SMALL
101int		 iface_conf_cmp(struct iface_conf *, struct iface_conf *);
102#endif /* SMALL */
103
104LIST_HEAD(, iface)		 interfaces;
105struct dhcpleased_conf		*frontend_conf;
106static struct imsgev		*iev_main;
107static struct imsgev		*iev_engine;
108struct event			 ev_route;
109int				 ioctlsock;
110
111uint8_t				 dhcp_packet[1500];
112
113void
114frontend_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
130void
131frontend(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
194frontend_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
215int
216frontend_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
223int
224frontend_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
231void
232frontend_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
460void
461frontend_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
548int
549get_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
561int
562get_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
574void
575update_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
649void
650frontend_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
662void
663init_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
744void
745route_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
783void
784handle_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
834void
835get_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
849void
850bpf_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		imsg_dhcp.csumflags = hdr->bh_csumflags;
894		frontend_imsg_compose_engine(IMSG_DHCP, 0, 0, &imsg_dhcp,
895		    sizeof(imsg_dhcp));
896 cont:
897		p += BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
898		rem -= BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
899
900	}
901}
902
903void
904iface_data_from_imsg(struct iface* iface, struct imsg_req_dhcp *imsg)
905{
906	iface->xid = imsg->xid;
907	iface->ciaddr = imsg->ciaddr;
908	iface->requested_ip = imsg->requested_ip;
909	iface->server_identifier = imsg->server_identifier;
910	iface->dhcp_server = imsg->dhcp_server;
911}
912
913ssize_t
914build_packet(uint8_t message_type, char *if_name, uint32_t xid,
915    struct ether_addr *hw_address, struct in_addr *ciaddr, struct in_addr
916    *requested_ip, struct in_addr *server_identifier)
917{
918	static uint8_t	 dhcp_cookie[] = DHCP_COOKIE;
919	static uint8_t	 dhcp_message_type[] = {DHO_DHCP_MESSAGE_TYPE, 1,
920		DHCPDISCOVER};
921	static uint8_t	 dhcp_hostname[255 + 2] = {DHO_HOST_NAME, 0 /*, ... */};
922	static uint8_t	 dhcp_client_id[] = {DHO_DHCP_CLIENT_IDENTIFIER, 7,
923		HTYPE_ETHER, 0, 0, 0, 0, 0, 0};
924	static uint8_t	 dhcp_req_list[] = {DHO_DHCP_PARAMETER_REQUEST_LIST,
925		8, DHO_SUBNET_MASK, DHO_ROUTERS, DHO_DOMAIN_NAME_SERVERS,
926		DHO_HOST_NAME, DHO_DOMAIN_NAME, DHO_BROADCAST_ADDRESS,
927		DHO_DOMAIN_SEARCH, DHO_CLASSLESS_STATIC_ROUTES};
928	static uint8_t	 dhcp_req_list_v6[] = {DHO_DHCP_PARAMETER_REQUEST_LIST,
929		9, DHO_SUBNET_MASK, DHO_ROUTERS, DHO_DOMAIN_NAME_SERVERS,
930		DHO_HOST_NAME, DHO_DOMAIN_NAME, DHO_BROADCAST_ADDRESS,
931		DHO_DOMAIN_SEARCH, DHO_CLASSLESS_STATIC_ROUTES,
932		DHO_IPV6_ONLY_PREFERRED};
933	static uint8_t	 dhcp_requested_address[] = {DHO_DHCP_REQUESTED_ADDRESS,
934		4, 0, 0, 0, 0};
935	static uint8_t	 dhcp_server_identifier[] = {DHO_DHCP_SERVER_IDENTIFIER,
936		4, 0, 0, 0, 0};
937#ifndef SMALL
938	struct iface_conf	*iface_conf;
939#endif /* SMALL */
940	struct dhcp_hdr		*hdr;
941	ssize_t			 len;
942	uint8_t			*p;
943	char			*c;
944
945#ifndef SMALL
946	iface_conf = find_iface_conf(&frontend_conf->iface_list, if_name);
947#endif /* SMALL */
948
949	memset(dhcp_packet, 0, sizeof(dhcp_packet));
950	dhcp_message_type[2] = message_type;
951	p = dhcp_packet;
952	hdr = (struct dhcp_hdr *)p;
953	hdr->op = DHCP_BOOTREQUEST;
954	hdr->htype = HTYPE_ETHER;
955	hdr->hlen = 6;
956	hdr->hops = 0;
957	hdr->xid = htonl(xid);
958	hdr->secs = 0;
959	hdr->ciaddr = *ciaddr;
960	memcpy(hdr->chaddr, hw_address, sizeof(*hw_address));
961	p += sizeof(struct dhcp_hdr);
962	memcpy(p, dhcp_cookie, sizeof(dhcp_cookie));
963	p += sizeof(dhcp_cookie);
964	memcpy(p, dhcp_message_type, sizeof(dhcp_message_type));
965	p += sizeof(dhcp_message_type);
966
967#ifndef SMALL
968	if (iface_conf != NULL && iface_conf->h_name != NULL) {
969		if (iface_conf->h_name[0] != '\0') {
970			dhcp_hostname[1] = strlen(iface_conf->h_name);
971			memcpy(dhcp_hostname + 2, iface_conf->h_name,
972			    strlen(iface_conf->h_name));
973			memcpy(p, dhcp_hostname, dhcp_hostname[1] + 2);
974			p += dhcp_hostname[1] + 2;
975		}
976	} else
977#endif /* SMALL */
978	{
979		if (gethostname(dhcp_hostname + 2,
980		    sizeof(dhcp_hostname) - 2) == 0 &&
981		    dhcp_hostname[2] != '\0') {
982			if ((c = strchr(dhcp_hostname + 2, '.')) != NULL)
983				*c = '\0';
984			dhcp_hostname[1] = strlen(dhcp_hostname + 2);
985			memcpy(p, dhcp_hostname, dhcp_hostname[1] + 2);
986			p += dhcp_hostname[1] + 2;
987		}
988	}
989
990#ifndef SMALL
991	if (iface_conf != NULL) {
992		if (iface_conf->c_id_len > 0) {
993			/* XXX check space */
994			memcpy(p, iface_conf->c_id, iface_conf->c_id_len);
995			p += iface_conf->c_id_len;
996		} else {
997			memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address));
998			memcpy(p, dhcp_client_id, sizeof(dhcp_client_id));
999			p += sizeof(dhcp_client_id);
1000		}
1001		if (iface_conf->vc_id_len > 0) {
1002			/* XXX check space */
1003			memcpy(p, iface_conf->vc_id, iface_conf->vc_id_len);
1004			p += iface_conf->vc_id_len;
1005		}
1006		if (iface_conf->prefer_ipv6) {
1007			memcpy(p, dhcp_req_list_v6, sizeof(dhcp_req_list_v6));
1008			p += sizeof(dhcp_req_list_v6);
1009
1010		} else {
1011			memcpy(p, dhcp_req_list, sizeof(dhcp_req_list));
1012			p += sizeof(dhcp_req_list);
1013		}
1014	} else
1015#endif /* SMALL */
1016	{
1017		memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address));
1018		memcpy(p, dhcp_client_id, sizeof(dhcp_client_id));
1019		p += sizeof(dhcp_client_id);
1020		memcpy(p, dhcp_req_list, sizeof(dhcp_req_list));
1021		p += sizeof(dhcp_req_list);
1022	}
1023
1024	if (requested_ip->s_addr != INADDR_ANY) {
1025		memcpy(dhcp_requested_address + 2, requested_ip,
1026		    sizeof(*requested_ip));
1027		memcpy(p, dhcp_requested_address,
1028		    sizeof(dhcp_requested_address));
1029		p += sizeof(dhcp_requested_address);
1030	}
1031
1032	if (server_identifier->s_addr != INADDR_ANY) {
1033		memcpy(dhcp_server_identifier + 2, server_identifier,
1034		    sizeof(*server_identifier));
1035		memcpy(p, dhcp_server_identifier,
1036		    sizeof(dhcp_server_identifier));
1037		p += sizeof(dhcp_server_identifier);
1038	}
1039
1040	*p = DHO_END;
1041	p += 1;
1042
1043	len = p - dhcp_packet;
1044
1045	/* dhcp_packet is initialized with DHO_PADs */
1046	if (len < BOOTP_MIN_LEN)
1047		len = BOOTP_MIN_LEN;
1048
1049	return (len);
1050}
1051
1052void
1053send_packet(uint8_t message_type, struct iface *iface)
1054{
1055	ssize_t			 pkt_len;
1056	char			 ifnamebuf[IF_NAMESIZE], *if_name;
1057
1058	if (!event_initialized(&iface->bpfev.ev)) {
1059		iface->send_discover = 1;
1060		return;
1061	}
1062
1063	iface->send_discover = 0;
1064
1065	if ((if_name = if_indextoname(iface->ifinfo.if_index, ifnamebuf)) == NULL)
1066		return; /* iface went away, nothing to do */
1067
1068	log_debug("%s on %s", message_type == DHCPDISCOVER ? "DHCPDISCOVER" :
1069	    "DHCPREQUEST", if_name);
1070
1071	pkt_len = build_packet(message_type, if_name, iface->xid,
1072	    &iface->ifinfo.hw_address, &iface->ciaddr, &iface->requested_ip,
1073	    &iface->server_identifier);
1074	if (iface->dhcp_server.s_addr != INADDR_ANY) {
1075		if (udp_send_packet(iface, dhcp_packet, pkt_len) == -1)
1076			bpf_send_packet(iface, dhcp_packet, pkt_len);
1077	} else
1078		bpf_send_packet(iface, dhcp_packet, pkt_len);
1079}
1080
1081int
1082udp_send_packet(struct iface *iface, uint8_t *packet, ssize_t len)
1083{
1084	struct sockaddr_in	to;
1085
1086	memset(&to, 0, sizeof(to));
1087	to.sin_family = AF_INET;
1088	to.sin_len = sizeof(to);
1089	to.sin_addr = iface->dhcp_server;
1090	to.sin_port = ntohs(SERVER_PORT);
1091
1092	if (sendto(iface->udpsock, packet, len, 0, (struct sockaddr *)&to,
1093	    sizeof(to)) == -1) {
1094		log_warn("sendto");
1095		return -1;
1096	}
1097	return 0;
1098}
1099void
1100bpf_send_packet(struct iface *iface, uint8_t *packet, ssize_t len)
1101{
1102	struct iovec		 iov[4];
1103	struct ether_header	 eh;
1104	struct ip		 ip;
1105	struct udphdr		 udp;
1106	ssize_t			 total, result;
1107	int			 iovcnt = 0, i;
1108
1109	memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
1110	memcpy(eh.ether_shost, &iface->ifinfo.hw_address,
1111	    sizeof(eh.ether_dhost));
1112	eh.ether_type = htons(ETHERTYPE_IP);
1113	iov[0].iov_base = &eh;
1114	iov[0].iov_len = sizeof(eh);
1115	iovcnt++;
1116
1117	ip.ip_v = 4;
1118	ip.ip_hl = 5;
1119	ip.ip_tos = IPTOS_LOWDELAY;
1120	ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len);
1121	ip.ip_id = 0;
1122	ip.ip_off = 0;
1123	ip.ip_ttl = 128;
1124	ip.ip_p = IPPROTO_UDP;
1125	ip.ip_sum = 0;
1126	ip.ip_src.s_addr = INADDR_ANY;
1127	ip.ip_dst.s_addr = INADDR_BROADCAST;
1128	ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0));
1129	iov[iovcnt].iov_base = &ip;
1130	iov[iovcnt].iov_len = sizeof(ip);
1131	iovcnt++;
1132
1133	udp.uh_sport = htons(CLIENT_PORT);
1134	udp.uh_dport = htons(SERVER_PORT);
1135	udp.uh_ulen = htons(sizeof(udp) + len);
1136	udp.uh_sum = 0;
1137	udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
1138	    checksum((unsigned char *)packet, len,
1139	    checksum((unsigned char *)&ip.ip_src,
1140	    2 * sizeof(ip.ip_src),
1141	    IPPROTO_UDP + (uint32_t)ntohs(udp.uh_ulen)))));
1142	iov[iovcnt].iov_base = &udp;
1143	iov[iovcnt].iov_len = sizeof(udp);
1144	iovcnt++;
1145
1146	iov[iovcnt].iov_base = packet;
1147	iov[iovcnt].iov_len = len;
1148	iovcnt++;
1149
1150	total = 0;
1151	for (i = 0; i < iovcnt; i++)
1152		total += iov[i].iov_len;
1153
1154	result = writev(EVENT_FD(&iface->bpfev.ev), iov, iovcnt);
1155	if (result == -1)
1156		log_warn("%s: writev", __func__);
1157	else if (result < total) {
1158		log_warnx("%s, writev: %zd of %zd bytes", __func__, result,
1159		    total);
1160	}
1161}
1162
1163struct iface*
1164get_iface_by_id(uint32_t if_index)
1165{
1166	struct iface	*iface;
1167
1168	LIST_FOREACH (iface, &interfaces, entries) {
1169		if (iface->ifinfo.if_index == if_index)
1170			return (iface);
1171	}
1172
1173	return (NULL);
1174}
1175
1176void
1177remove_iface(uint32_t if_index)
1178{
1179	struct iface	*iface;
1180
1181	iface = get_iface_by_id(if_index);
1182
1183	if (iface == NULL)
1184		return;
1185
1186	LIST_REMOVE(iface, entries);
1187	if (event_initialized(&iface->bpfev.ev)) {
1188		event_del(&iface->bpfev.ev);
1189		close(EVENT_FD(&iface->bpfev.ev));
1190	}
1191	if (iface->udpsock != -1)
1192		close(iface->udpsock);
1193	free(iface);
1194}
1195
1196void
1197set_bpfsock(int bpfsock, uint32_t if_index)
1198{
1199	struct iface	*iface;
1200
1201	iface = get_iface_by_id(if_index);
1202
1203	if (iface == NULL) {
1204		/*
1205		 * The interface disappeared while we were waiting for the
1206		 * parent process to open the bpf socket.
1207		 */
1208		close(bpfsock);
1209	} else if (event_initialized(&iface->bpfev.ev)) {
1210		/*
1211		 * The autoconf flag is flapping and we have multiple bpf sockets in
1212		 * flight. We don't need this one because we already got one.
1213		 */
1214		close(bpfsock);
1215	} else {
1216		event_set(&iface->bpfev.ev, bpfsock, EV_READ |
1217		    EV_PERSIST, bpf_receive, iface);
1218		event_add(&iface->bpfev.ev, NULL);
1219		if (iface->send_discover)
1220			send_packet(DHCPDISCOVER, iface);
1221	}
1222}
1223
1224#ifndef SMALL
1225struct iface_conf*
1226find_iface_conf(struct iface_conf_head *head, char *if_name)
1227{
1228	struct iface_conf	*iface_conf;
1229
1230	if (if_name == NULL)
1231		return (NULL);
1232
1233	SIMPLEQ_FOREACH(iface_conf, head, entry) {
1234		if (strcmp(iface_conf->name, if_name) == 0)
1235			return iface_conf;
1236	}
1237	return (NULL);
1238}
1239
1240int*
1241changed_ifaces(struct dhcpleased_conf *oconf, struct dhcpleased_conf *nconf)
1242{
1243	struct iface_conf	*iface_conf, *oiface_conf;
1244	int			*ret, if_index, count = 0, i = 0;
1245
1246	/*
1247	 * Worst case: All old interfaces replaced with new interfaces.
1248	 * This should still be a small number
1249	 */
1250	SIMPLEQ_FOREACH(iface_conf, &oconf->iface_list, entry)
1251	    count++;
1252	SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry)
1253	    count++;
1254
1255	ret = calloc(count + 1, sizeof(int));
1256
1257	SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry) {
1258		if ((if_index = if_nametoindex(iface_conf->name)) == 0)
1259			continue;
1260		oiface_conf = find_iface_conf(&oconf->iface_list,
1261		    iface_conf->name);
1262		if (oiface_conf == NULL) {
1263			/* new interface added to config */
1264			ret[i++] = if_index;
1265		} else if (iface_conf_cmp(iface_conf, oiface_conf) != 0) {
1266			/* interface conf changed */
1267			ret[i++] = if_index;
1268		}
1269	}
1270	SIMPLEQ_FOREACH(oiface_conf, &oconf->iface_list, entry) {
1271		if ((if_index = if_nametoindex(oiface_conf->name)) == 0)
1272			continue;
1273		if (find_iface_conf(&nconf->iface_list, oiface_conf->name) ==
1274		    NULL) {
1275			/* interface removed from config */
1276			ret[i++] = if_index;
1277		}
1278	}
1279	return ret;
1280}
1281
1282int
1283iface_conf_cmp(struct iface_conf *a, struct iface_conf *b)
1284{
1285	if (a->vc_id_len != b->vc_id_len)
1286		return 1;
1287	if (memcmp(a->vc_id, b->vc_id, a->vc_id_len) != 0)
1288		return 1;
1289	if (a->c_id_len != b->c_id_len)
1290		return 1;
1291	if (memcmp(a->c_id, b->c_id, a->c_id_len) != 0)
1292		return 1;
1293	if (a->h_name == NULL ||  b->h_name == NULL)
1294		return 1;
1295	if (strcmp(a->h_name, b->h_name) != 0)
1296		return 1;
1297	if (a->ignore != b->ignore)
1298		return 1;
1299	if (a->ignore_servers_len != b->ignore_servers_len)
1300		return 1;
1301	if (memcmp(a->ignore_servers, b->ignore_servers,
1302	    a->ignore_servers_len * sizeof (struct in_addr)) != 0)
1303		return 1;
1304	return 0;
1305}
1306#endif /* SMALL */
1307