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