xref: /openbsd/sbin/dhcpleased/frontend.c (revision 2b86dc95)
1*2b86dc95Sjan /*	$OpenBSD: frontend.c,v 1.33 2024/01/26 21:14:08 jan Exp $	*/
257419a7fSflorian 
357419a7fSflorian /*
457419a7fSflorian  * Copyright (c) 2017, 2021 Florian Obser <florian@openbsd.org>
557419a7fSflorian  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
657419a7fSflorian  * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
757419a7fSflorian  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
857419a7fSflorian  *
957419a7fSflorian  * Permission to use, copy, modify, and distribute this software for any
1057419a7fSflorian  * purpose with or without fee is hereby granted, provided that the above
1157419a7fSflorian  * copyright notice and this permission notice appear in all copies.
1257419a7fSflorian  *
1357419a7fSflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1457419a7fSflorian  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1557419a7fSflorian  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1657419a7fSflorian  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1757419a7fSflorian  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1857419a7fSflorian  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1957419a7fSflorian  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2057419a7fSflorian  */
2157419a7fSflorian #include <sys/types.h>
2257419a7fSflorian #include <sys/ioctl.h>
2357419a7fSflorian #include <sys/queue.h>
2457419a7fSflorian #include <sys/socket.h>
2557419a7fSflorian #include <sys/syslog.h>
2657419a7fSflorian #include <sys/uio.h>
2757419a7fSflorian 
2857419a7fSflorian #include <net/bpf.h>
2957419a7fSflorian #include <net/if.h>
3057419a7fSflorian #include <net/if_dl.h>
3157419a7fSflorian #include <net/if_types.h>
3257419a7fSflorian #include <net/route.h>
3357419a7fSflorian 
3498b9f859Sclaudio #include <netinet/in.h>
3598b9f859Sclaudio #include <netinet/if_ether.h>
3657419a7fSflorian #include <netinet/ip.h>
3757419a7fSflorian #include <netinet/udp.h>
3857419a7fSflorian 
3957419a7fSflorian #include <arpa/inet.h>
4057419a7fSflorian 
4157419a7fSflorian #include <errno.h>
4257419a7fSflorian #include <event.h>
4357419a7fSflorian #include <ifaddrs.h>
4457419a7fSflorian #include <imsg.h>
4557419a7fSflorian #include <pwd.h>
4657419a7fSflorian #include <signal.h>
4757419a7fSflorian #include <stdio.h>
4857419a7fSflorian #include <stdlib.h>
4957419a7fSflorian #include <string.h>
5057419a7fSflorian #include <unistd.h>
5157419a7fSflorian 
5257419a7fSflorian #include "bpf.h"
5357419a7fSflorian #include "log.h"
5457419a7fSflorian #include "dhcpleased.h"
5557419a7fSflorian #include "frontend.h"
5657419a7fSflorian #include "control.h"
5757419a7fSflorian #include "checksum.h"
5857419a7fSflorian 
5957419a7fSflorian #define	ROUTE_SOCKET_BUF_SIZE	16384
602d2edb0eSflorian #define	BOOTP_MIN_LEN		300	/* fixed bootp packet adds up to 300 */
6157419a7fSflorian 
6257419a7fSflorian struct bpf_ev {
6357419a7fSflorian 	struct event		 ev;
6457419a7fSflorian 	uint8_t			 buf[BPFLEN];
6557419a7fSflorian };
6657419a7fSflorian 
6757419a7fSflorian struct iface {
6857419a7fSflorian 	LIST_ENTRY(iface)	 entries;
6957419a7fSflorian 	struct bpf_ev		 bpfev;
701bbf741cSflorian 	struct imsg_ifinfo	 ifinfo;
7157419a7fSflorian 	int			 send_discover;
7257419a7fSflorian 	uint32_t		 xid;
7382a5f3d7Sflorian 	struct in_addr		 ciaddr;
7457419a7fSflorian 	struct in_addr		 requested_ip;
7557419a7fSflorian 	struct in_addr		 server_identifier;
7657419a7fSflorian 	struct in_addr		 dhcp_server;
7757419a7fSflorian 	int			 udpsock;
7857419a7fSflorian };
7957419a7fSflorian 
8057419a7fSflorian __dead void	 frontend_shutdown(void);
8157419a7fSflorian void		 frontend_sig_handler(int, short, void *);
8248e1b614Sflorian void		 update_iface(struct if_msghdr *, struct sockaddr_dl *);
8357419a7fSflorian void		 frontend_startup(void);
841bbf741cSflorian void		 init_ifaces(void);
8557419a7fSflorian void		 route_receive(int, short, void *);
8657419a7fSflorian void		 handle_route_message(struct rt_msghdr *, struct sockaddr **);
8757419a7fSflorian void		 get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
8857419a7fSflorian void		 bpf_receive(int, short, void *);
8957419a7fSflorian int		 get_flags(char *);
9057419a7fSflorian int		 get_xflags(char *);
9157419a7fSflorian struct iface	*get_iface_by_id(uint32_t);
9257419a7fSflorian void		 remove_iface(uint32_t);
9357419a7fSflorian void		 set_bpfsock(int, uint32_t);
9482a5f3d7Sflorian void		 iface_data_from_imsg(struct iface*, struct imsg_req_dhcp *);
95a41cc082Sflorian ssize_t		 build_packet(uint8_t, char *, uint32_t, struct ether_addr *,
9682a5f3d7Sflorian 		     struct in_addr *, struct in_addr *, struct in_addr *);
9782a5f3d7Sflorian void		 send_packet(uint8_t, struct iface *);
9857419a7fSflorian void		 bpf_send_packet(struct iface *, uint8_t *, ssize_t);
99c645071eSflorian int		 udp_send_packet(struct iface *, uint8_t *, ssize_t);
1004eb9756fSflorian #ifndef SMALL
101580befd2Sflorian int		 iface_conf_cmp(struct iface_conf *, struct iface_conf *);
1024eb9756fSflorian #endif /* SMALL */
10357419a7fSflorian 
10457419a7fSflorian LIST_HEAD(, iface)		 interfaces;
105a41cc082Sflorian struct dhcpleased_conf		*frontend_conf;
10657419a7fSflorian static struct imsgev		*iev_main;
10757419a7fSflorian static struct imsgev		*iev_engine;
10857419a7fSflorian struct event			 ev_route;
10957419a7fSflorian int				 ioctlsock;
11057419a7fSflorian 
11157419a7fSflorian uint8_t				 dhcp_packet[1500];
11257419a7fSflorian 
11357419a7fSflorian void
frontend_sig_handler(int sig,short event,void * bula)11457419a7fSflorian frontend_sig_handler(int sig, short event, void *bula)
11557419a7fSflorian {
11657419a7fSflorian 	/*
11757419a7fSflorian 	 * Normal signal handler rules don't apply because libevent
11857419a7fSflorian 	 * decouples for us.
11957419a7fSflorian 	 */
12057419a7fSflorian 
12157419a7fSflorian 	switch (sig) {
12257419a7fSflorian 	case SIGINT:
12357419a7fSflorian 	case SIGTERM:
12457419a7fSflorian 		frontend_shutdown();
12557419a7fSflorian 	default:
12657419a7fSflorian 		fatalx("unexpected signal");
12757419a7fSflorian 	}
12857419a7fSflorian }
12957419a7fSflorian 
13057419a7fSflorian void
frontend(int debug,int verbose)13157419a7fSflorian frontend(int debug, int verbose)
13257419a7fSflorian {
13357419a7fSflorian 	struct event		 ev_sigint, ev_sigterm;
13457419a7fSflorian 	struct passwd		*pw;
13557419a7fSflorian 
136a41cc082Sflorian #ifndef SMALL
137a41cc082Sflorian 	frontend_conf = config_new_empty();
138a41cc082Sflorian #endif /* SMALL */
139a41cc082Sflorian 
14057419a7fSflorian 	log_init(debug, LOG_DAEMON);
14157419a7fSflorian 	log_setverbose(verbose);
14257419a7fSflorian 
14357419a7fSflorian 	if ((pw = getpwnam(DHCPLEASED_USER)) == NULL)
14457419a7fSflorian 		fatal("getpwnam");
14557419a7fSflorian 
14657419a7fSflorian 	if (chdir("/") == -1)
14757419a7fSflorian 		fatal("chdir(\"/\")");
14857419a7fSflorian 
149c7d84514Sflorian 	if (unveil("/", "") == -1)
150bc5a8259Sbeck 		fatal("unveil /");
151c7d84514Sflorian 	if (unveil(NULL, NULL) == -1)
152bc5a8259Sbeck 		fatal("unveil");
153c7d84514Sflorian 
15457419a7fSflorian 	setproctitle("%s", "frontend");
15557419a7fSflorian 	log_procinit("frontend");
15657419a7fSflorian 
15757419a7fSflorian 	if ((ioctlsock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1)
15857419a7fSflorian 		fatal("socket");
15957419a7fSflorian 
16057419a7fSflorian 	if (setgroups(1, &pw->pw_gid) ||
16157419a7fSflorian 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
16257419a7fSflorian 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
16357419a7fSflorian 		fatal("can't drop privileges");
16457419a7fSflorian 
16557419a7fSflorian 	if (pledge("stdio unix recvfd route", NULL) == -1)
16657419a7fSflorian 		fatal("pledge");
16757419a7fSflorian 	event_init();
16857419a7fSflorian 
16957419a7fSflorian 	/* Setup signal handler. */
17057419a7fSflorian 	signal_set(&ev_sigint, SIGINT, frontend_sig_handler, NULL);
17157419a7fSflorian 	signal_set(&ev_sigterm, SIGTERM, frontend_sig_handler, NULL);
17257419a7fSflorian 	signal_add(&ev_sigint, NULL);
17357419a7fSflorian 	signal_add(&ev_sigterm, NULL);
17457419a7fSflorian 	signal(SIGPIPE, SIG_IGN);
17557419a7fSflorian 	signal(SIGHUP, SIG_IGN);
17657419a7fSflorian 
17757419a7fSflorian 	/* Setup pipe and event handler to the parent process. */
17857419a7fSflorian 	if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
17957419a7fSflorian 		fatal(NULL);
18057419a7fSflorian 	imsg_init(&iev_main->ibuf, 3);
18157419a7fSflorian 	iev_main->handler = frontend_dispatch_main;
18257419a7fSflorian 	iev_main->events = EV_READ;
18357419a7fSflorian 	event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
18457419a7fSflorian 	    iev_main->handler, iev_main);
18557419a7fSflorian 	event_add(&iev_main->ev, NULL);
18657419a7fSflorian 
18757419a7fSflorian 	LIST_INIT(&interfaces);
18857419a7fSflorian 	event_dispatch();
18957419a7fSflorian 
19057419a7fSflorian 	frontend_shutdown();
19157419a7fSflorian }
19257419a7fSflorian 
19357419a7fSflorian __dead void
frontend_shutdown(void)19457419a7fSflorian frontend_shutdown(void)
19557419a7fSflorian {
19657419a7fSflorian 	/* Close pipes. */
19757419a7fSflorian 	msgbuf_write(&iev_engine->ibuf.w);
19857419a7fSflorian 	msgbuf_clear(&iev_engine->ibuf.w);
19957419a7fSflorian 	close(iev_engine->ibuf.fd);
20057419a7fSflorian 	msgbuf_write(&iev_main->ibuf.w);
20157419a7fSflorian 	msgbuf_clear(&iev_main->ibuf.w);
20257419a7fSflorian 	close(iev_main->ibuf.fd);
20357419a7fSflorian 
204a41cc082Sflorian #ifndef SMALL
205a41cc082Sflorian 	config_clear(frontend_conf);
206a41cc082Sflorian #endif /* SMALL */
207a41cc082Sflorian 
20857419a7fSflorian 	free(iev_engine);
20957419a7fSflorian 	free(iev_main);
21057419a7fSflorian 
21157419a7fSflorian 	log_info("frontend exiting");
21257419a7fSflorian 	exit(0);
21357419a7fSflorian }
21457419a7fSflorian 
21557419a7fSflorian int
frontend_imsg_compose_main(int type,pid_t pid,void * data,uint16_t datalen)21657419a7fSflorian frontend_imsg_compose_main(int type, pid_t pid, void *data,
21757419a7fSflorian     uint16_t datalen)
21857419a7fSflorian {
21957419a7fSflorian 	return (imsg_compose_event(iev_main, type, 0, pid, -1, data,
22057419a7fSflorian 	    datalen));
22157419a7fSflorian }
22257419a7fSflorian 
22357419a7fSflorian int
frontend_imsg_compose_engine(int type,uint32_t peerid,pid_t pid,void * data,uint16_t datalen)22457419a7fSflorian frontend_imsg_compose_engine(int type, uint32_t peerid, pid_t pid,
22557419a7fSflorian     void *data, uint16_t datalen)
22657419a7fSflorian {
22757419a7fSflorian 	return (imsg_compose_event(iev_engine, type, peerid, pid, -1,
22857419a7fSflorian 	    data, datalen));
22957419a7fSflorian }
23057419a7fSflorian 
23157419a7fSflorian void
frontend_dispatch_main(int fd,short event,void * bula)23257419a7fSflorian frontend_dispatch_main(int fd, short event, void *bula)
23357419a7fSflorian {
234a41cc082Sflorian 	static struct dhcpleased_conf	*nconf;
235a41cc082Sflorian 	static struct iface_conf	*iface_conf;
23657419a7fSflorian 	struct imsg			 imsg;
23757419a7fSflorian 	struct imsgev			*iev = bula;
23857419a7fSflorian 	struct imsgbuf			*ibuf = &iev->ibuf;
23957419a7fSflorian 	struct iface			*iface;
24057419a7fSflorian 	ssize_t				 n;
24157419a7fSflorian 	int				 shut = 0, bpfsock, if_index, udpsock;
24257419a7fSflorian 
24357419a7fSflorian 	if (event & EV_READ) {
24457419a7fSflorian 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
24557419a7fSflorian 			fatal("imsg_read error");
24657419a7fSflorian 		if (n == 0)	/* Connection closed. */
24757419a7fSflorian 			shut = 1;
24857419a7fSflorian 	}
24957419a7fSflorian 	if (event & EV_WRITE) {
25057419a7fSflorian 		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
25157419a7fSflorian 			fatal("msgbuf_write");
25257419a7fSflorian 		if (n == 0)	/* Connection closed. */
25357419a7fSflorian 			shut = 1;
25457419a7fSflorian 	}
25557419a7fSflorian 
25657419a7fSflorian 	for (;;) {
25757419a7fSflorian 		if ((n = imsg_get(ibuf, &imsg)) == -1)
25857419a7fSflorian 			fatal("%s: imsg_get error", __func__);
25957419a7fSflorian 		if (n == 0)	/* No more messages. */
26057419a7fSflorian 			break;
26157419a7fSflorian 
26257419a7fSflorian 		switch (imsg.hdr.type) {
26357419a7fSflorian 		case IMSG_SOCKET_IPC:
26457419a7fSflorian 			/*
26557419a7fSflorian 			 * Setup pipe and event handler to the engine
26657419a7fSflorian 			 * process.
26757419a7fSflorian 			 */
26857419a7fSflorian 			if (iev_engine)
26957419a7fSflorian 				fatalx("%s: received unexpected imsg fd "
27057419a7fSflorian 				    "to frontend", __func__);
27157419a7fSflorian 
272dcedc8acSclaudio 			if ((fd = imsg_get_fd(&imsg)) == -1)
27357419a7fSflorian 				fatalx("%s: expected to receive imsg fd to "
27457419a7fSflorian 				   "frontend but didn't receive any",
27557419a7fSflorian 				   __func__);
27657419a7fSflorian 
27757419a7fSflorian 			iev_engine = malloc(sizeof(struct imsgev));
27857419a7fSflorian 			if (iev_engine == NULL)
27957419a7fSflorian 				fatal(NULL);
28057419a7fSflorian 
28157419a7fSflorian 			imsg_init(&iev_engine->ibuf, fd);
28257419a7fSflorian 			iev_engine->handler = frontend_dispatch_engine;
28357419a7fSflorian 			iev_engine->events = EV_READ;
28457419a7fSflorian 
28557419a7fSflorian 			event_set(&iev_engine->ev, iev_engine->ibuf.fd,
28657419a7fSflorian 			iev_engine->events, iev_engine->handler, iev_engine);
28757419a7fSflorian 			event_add(&iev_engine->ev, NULL);
28857419a7fSflorian 			break;
28957419a7fSflorian 		case IMSG_BPFSOCK:
290dcedc8acSclaudio 			if ((bpfsock = imsg_get_fd(&imsg)) == -1)
29157419a7fSflorian 				fatalx("%s: expected to receive imsg "
29257419a7fSflorian 				    "bpf fd but didn't receive any",
29357419a7fSflorian 				    __func__);
29457419a7fSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
29557419a7fSflorian 				fatalx("%s: IMSG_BPFSOCK wrong length: "
29657419a7fSflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
29757419a7fSflorian 			memcpy(&if_index, imsg.data, sizeof(if_index));
29857419a7fSflorian 			set_bpfsock(bpfsock, if_index);
29957419a7fSflorian 			break;
30057419a7fSflorian 		case IMSG_UDPSOCK:
301dcedc8acSclaudio 			if ((udpsock = imsg_get_fd(&imsg)) == -1)
30257419a7fSflorian 				fatalx("%s: expected to receive imsg "
30357419a7fSflorian 				    "udpsocket fd but didn't receive any",
30457419a7fSflorian 				    __func__);
30557419a7fSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
30657419a7fSflorian 				fatalx("%s: IMSG_UDPSOCK wrong length: "
30757419a7fSflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
30857419a7fSflorian 			memcpy(&if_index, imsg.data, sizeof(if_index));
30957419a7fSflorian 			if ((iface = get_iface_by_id(if_index)) == NULL) {
31057419a7fSflorian 				close(fd);
31157419a7fSflorian 				break;
31257419a7fSflorian 			}
31357419a7fSflorian 			if (iface->udpsock != -1)
31457419a7fSflorian 				fatalx("%s: received unexpected udpsocket",
31557419a7fSflorian 				    __func__);
31657419a7fSflorian 			iface->udpsock = udpsock;
31757419a7fSflorian 			break;
31857419a7fSflorian 		case IMSG_CLOSE_UDPSOCK:
31957419a7fSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
32057419a7fSflorian 				fatalx("%s: IMSG_UDPSOCK wrong length: "
32157419a7fSflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
32257419a7fSflorian 			memcpy(&if_index, imsg.data, sizeof(if_index));
32357419a7fSflorian 			if ((iface = get_iface_by_id(if_index)) != NULL &&
32457419a7fSflorian 			    iface->udpsock != -1) {
32557419a7fSflorian 				close(iface->udpsock);
32657419a7fSflorian 				iface->udpsock = -1;
32757419a7fSflorian 			}
32857419a7fSflorian 			break;
32957419a7fSflorian 		case IMSG_ROUTESOCK:
330dcedc8acSclaudio 			if ((fd = imsg_get_fd(&imsg)) == -1)
33157419a7fSflorian 				fatalx("%s: expected to receive imsg "
33257419a7fSflorian 				    "routesocket fd but didn't receive any",
33357419a7fSflorian 				    __func__);
33457419a7fSflorian 			event_set(&ev_route, fd, EV_READ | EV_PERSIST,
33557419a7fSflorian 			    route_receive, NULL);
33657419a7fSflorian 			break;
33757419a7fSflorian 		case IMSG_STARTUP:
33857419a7fSflorian 			frontend_startup();
33957419a7fSflorian 			break;
34057419a7fSflorian #ifndef SMALL
341a41cc082Sflorian 		case IMSG_RECONF_CONF:
342a41cc082Sflorian 			if (nconf != NULL)
343a41cc082Sflorian 				fatalx("%s: IMSG_RECONF_CONF already in "
344a41cc082Sflorian 				    "progress", __func__);
345a41cc082Sflorian 			if ((nconf = malloc(sizeof(struct dhcpleased_conf))) ==
346a41cc082Sflorian 			    NULL)
347a41cc082Sflorian 				fatal(NULL);
348a41cc082Sflorian 			SIMPLEQ_INIT(&nconf->iface_list);
349a41cc082Sflorian 			break;
350a41cc082Sflorian 		case IMSG_RECONF_IFACE:
351a41cc082Sflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
352a41cc082Sflorian 			    iface_conf))
353a41cc082Sflorian 				fatalx("%s: IMSG_RECONF_IFACE wrong length: "
354a41cc082Sflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
355a41cc082Sflorian 			if ((iface_conf = malloc(sizeof(struct iface_conf)))
356a41cc082Sflorian 			    == NULL)
357a41cc082Sflorian 				fatal(NULL);
358a41cc082Sflorian 			memcpy(iface_conf, imsg.data, sizeof(struct
359a41cc082Sflorian 			    iface_conf));
360a41cc082Sflorian 			iface_conf->vc_id = NULL;
361a41cc082Sflorian 			iface_conf->vc_id_len = 0;
362a41cc082Sflorian 			iface_conf->c_id = NULL;
363a41cc082Sflorian 			iface_conf->c_id_len = 0;
364b3441518Sflorian 			iface_conf->h_name = NULL;
365a41cc082Sflorian 			SIMPLEQ_INSERT_TAIL(&nconf->iface_list,
366a41cc082Sflorian 			    iface_conf, entry);
367a41cc082Sflorian 			break;
368a41cc082Sflorian 		case IMSG_RECONF_VC_ID:
369a41cc082Sflorian 			if (iface_conf == NULL)
370a41cc082Sflorian 				fatal("IMSG_RECONF_VC_ID without "
371a41cc082Sflorian 				    "IMSG_RECONF_IFACE");
372a41cc082Sflorian 			if (IMSG_DATA_SIZE(imsg) > 255 + 2)
373a41cc082Sflorian 				fatalx("%s: IMSG_RECONF_VC_ID wrong length: "
374a41cc082Sflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
375a41cc082Sflorian 			if ((iface_conf->vc_id = malloc(IMSG_DATA_SIZE(imsg)))
376a41cc082Sflorian 			    == NULL)
377a41cc082Sflorian 				fatal(NULL);
378a41cc082Sflorian 			memcpy(iface_conf->vc_id, imsg.data,
379a41cc082Sflorian 			    IMSG_DATA_SIZE(imsg));
380a41cc082Sflorian 			iface_conf->vc_id_len = IMSG_DATA_SIZE(imsg);
381a41cc082Sflorian 			break;
382a41cc082Sflorian 		case IMSG_RECONF_C_ID:
383a41cc082Sflorian 			if (iface_conf == NULL)
384a41cc082Sflorian 				fatal("IMSG_RECONF_C_ID without "
385a41cc082Sflorian 				    "IMSG_RECONF_IFACE");
386a41cc082Sflorian 			if (IMSG_DATA_SIZE(imsg) > 255 + 2)
387a41cc082Sflorian 				fatalx("%s: IMSG_RECONF_C_ID wrong length: "
388a41cc082Sflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
389a41cc082Sflorian 			if ((iface_conf->c_id = malloc(IMSG_DATA_SIZE(imsg)))
390a41cc082Sflorian 			    == NULL)
391a41cc082Sflorian 				fatal(NULL);
392a41cc082Sflorian 			memcpy(iface_conf->c_id, imsg.data,
393a41cc082Sflorian 			    IMSG_DATA_SIZE(imsg));
394a41cc082Sflorian 			iface_conf->c_id_len = IMSG_DATA_SIZE(imsg);
395a41cc082Sflorian 			break;
396b3441518Sflorian 		case IMSG_RECONF_H_NAME:
397b3441518Sflorian 			if (iface_conf == NULL)
398b3441518Sflorian 				fatal("IMSG_RECONF_H_NAME without "
399b3441518Sflorian 				    "IMSG_RECONF_IFACE");
400b3441518Sflorian 			if (((char *)imsg.data)[IMSG_DATA_SIZE(imsg) - 1] !=
401b3441518Sflorian 			    '\0')
402b3441518Sflorian 				fatalx("Invalid hostname");
403b3441518Sflorian 			if (IMSG_DATA_SIZE(imsg) > 256)
404b3441518Sflorian 				fatalx("Invalid hostname");
405b3441518Sflorian 			if ((iface_conf->h_name = strdup(imsg.data)) == NULL)
406b3441518Sflorian 				fatal(NULL);
407b3441518Sflorian 			break;
408580befd2Sflorian 		case IMSG_RECONF_END: {
409580befd2Sflorian 			int	 i;
410580befd2Sflorian 			int	*ifaces;
411580befd2Sflorian 			char	 ifnamebuf[IF_NAMESIZE], *if_name;
412580befd2Sflorian 
413a41cc082Sflorian 			if (nconf == NULL)
414a41cc082Sflorian 				fatalx("%s: IMSG_RECONF_END without "
415a41cc082Sflorian 				    "IMSG_RECONF_CONF", __func__);
416580befd2Sflorian 
417580befd2Sflorian 			ifaces = changed_ifaces(frontend_conf, nconf);
418a41cc082Sflorian 			merge_config(frontend_conf, nconf);
419a41cc082Sflorian 			nconf = NULL;
420580befd2Sflorian 			for (i = 0; ifaces[i] != 0; i++) {
421580befd2Sflorian 				if_index = ifaces[i];
422580befd2Sflorian 				if_name = if_indextoname(if_index, ifnamebuf);
423580befd2Sflorian 				log_debug("changed iface: %s[%d]", if_name !=
424580befd2Sflorian 				    NULL ? if_name : "<unknown>", if_index);
425580befd2Sflorian 				frontend_imsg_compose_engine(
426580befd2Sflorian 				    IMSG_REQUEST_REBOOT, 0, 0, &if_index,
427580befd2Sflorian 				    sizeof(if_index));
428580befd2Sflorian 			}
429580befd2Sflorian 			free(ifaces);
430a41cc082Sflorian 			break;
431580befd2Sflorian 		}
43257419a7fSflorian 		case IMSG_CONTROLFD:
433dcedc8acSclaudio 			if ((fd = imsg_get_fd(&imsg)) == -1)
43457419a7fSflorian 				fatalx("%s: expected to receive imsg "
43557419a7fSflorian 				    "control fd but didn't receive any",
43657419a7fSflorian 				    __func__);
43757419a7fSflorian 			/* Listen on control socket. */
43857419a7fSflorian 			control_listen(fd);
43957419a7fSflorian 			break;
44057419a7fSflorian 		case IMSG_CTL_END:
44157419a7fSflorian 			control_imsg_relay(&imsg);
44257419a7fSflorian 			break;
44357419a7fSflorian #endif	/* SMALL */
44457419a7fSflorian 		default:
44557419a7fSflorian 			log_debug("%s: error handling imsg %d", __func__,
44657419a7fSflorian 			    imsg.hdr.type);
44757419a7fSflorian 			break;
44857419a7fSflorian 		}
44957419a7fSflorian 		imsg_free(&imsg);
45057419a7fSflorian 	}
45157419a7fSflorian 	if (!shut)
45257419a7fSflorian 		imsg_event_add(iev);
45357419a7fSflorian 	else {
45457419a7fSflorian 		/* This pipe is dead. Remove its event handler. */
45557419a7fSflorian 		event_del(&iev->ev);
45657419a7fSflorian 		event_loopexit(NULL);
45757419a7fSflorian 	}
45857419a7fSflorian }
45957419a7fSflorian 
46057419a7fSflorian void
frontend_dispatch_engine(int fd,short event,void * bula)46157419a7fSflorian frontend_dispatch_engine(int fd, short event, void *bula)
46257419a7fSflorian {
46357419a7fSflorian 	struct imsgev		*iev = bula;
46457419a7fSflorian 	struct imsgbuf		*ibuf = &iev->ibuf;
46557419a7fSflorian 	struct imsg		 imsg;
46657419a7fSflorian 	struct iface		*iface;
46757419a7fSflorian 	ssize_t			 n;
46857419a7fSflorian 	int			 shut = 0;
46957419a7fSflorian 
47057419a7fSflorian 	if (event & EV_READ) {
47157419a7fSflorian 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
47257419a7fSflorian 			fatal("imsg_read error");
47357419a7fSflorian 		if (n == 0)	/* Connection closed. */
47457419a7fSflorian 			shut = 1;
47557419a7fSflorian 	}
47657419a7fSflorian 	if (event & EV_WRITE) {
47757419a7fSflorian 		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
47857419a7fSflorian 			fatal("msgbuf_write");
47957419a7fSflorian 		if (n == 0)	/* Connection closed. */
48057419a7fSflorian 			shut = 1;
48157419a7fSflorian 	}
48257419a7fSflorian 
48357419a7fSflorian 	for (;;) {
48457419a7fSflorian 		if ((n = imsg_get(ibuf, &imsg)) == -1)
48557419a7fSflorian 			fatal("%s: imsg_get error", __func__);
48657419a7fSflorian 		if (n == 0)	/* No more messages. */
48757419a7fSflorian 			break;
48857419a7fSflorian 
48957419a7fSflorian 		switch (imsg.hdr.type) {
49057419a7fSflorian #ifndef	SMALL
49157419a7fSflorian 		case IMSG_CTL_END:
49257419a7fSflorian 		case IMSG_CTL_SHOW_INTERFACE_INFO:
49357419a7fSflorian 			control_imsg_relay(&imsg);
49457419a7fSflorian 			break;
49557419a7fSflorian #endif	/* SMALL */
49657419a7fSflorian 		case IMSG_SEND_DISCOVER: {
49782a5f3d7Sflorian 			struct imsg_req_dhcp	 imsg_req_dhcp;
49882a5f3d7Sflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_dhcp))
49957419a7fSflorian 				fatalx("%s: IMSG_SEND_DISCOVER wrong "
50057419a7fSflorian 				    "length: %lu", __func__,
50157419a7fSflorian 				    IMSG_DATA_SIZE(imsg));
50282a5f3d7Sflorian 			memcpy(&imsg_req_dhcp, imsg.data,
50382a5f3d7Sflorian 			    sizeof(imsg_req_dhcp));
50482a5f3d7Sflorian 
50582a5f3d7Sflorian 			iface = get_iface_by_id(imsg_req_dhcp.if_index);
50682a5f3d7Sflorian 
50782a5f3d7Sflorian 			if (iface == NULL)
50882a5f3d7Sflorian 				break;
50982a5f3d7Sflorian 
51082a5f3d7Sflorian 			iface_data_from_imsg(iface, &imsg_req_dhcp);
51182a5f3d7Sflorian 			send_packet(DHCPDISCOVER, iface);
51257419a7fSflorian 			break;
51357419a7fSflorian 		}
51457419a7fSflorian 		case IMSG_SEND_REQUEST: {
51582a5f3d7Sflorian 			struct imsg_req_dhcp	 imsg_req_dhcp;
51682a5f3d7Sflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_dhcp))
51757419a7fSflorian 				fatalx("%s: IMSG_SEND_REQUEST wrong "
51857419a7fSflorian 				    "length: %lu", __func__,
51957419a7fSflorian 				    IMSG_DATA_SIZE(imsg));
52082a5f3d7Sflorian 			memcpy(&imsg_req_dhcp, imsg.data,
52182a5f3d7Sflorian 			    sizeof(imsg_req_dhcp));
52282a5f3d7Sflorian 
52382a5f3d7Sflorian 			iface = get_iface_by_id(imsg_req_dhcp.if_index);
52482a5f3d7Sflorian 
52582a5f3d7Sflorian 			if (iface == NULL)
52682a5f3d7Sflorian 				break;
52782a5f3d7Sflorian 
52882a5f3d7Sflorian 			iface_data_from_imsg(iface, &imsg_req_dhcp);
52982a5f3d7Sflorian 			send_packet(DHCPREQUEST, iface);
53057419a7fSflorian 			break;
53157419a7fSflorian 		}
53257419a7fSflorian 		default:
53357419a7fSflorian 			log_debug("%s: error handling imsg %d", __func__,
53457419a7fSflorian 			    imsg.hdr.type);
53557419a7fSflorian 			break;
53657419a7fSflorian 		}
53757419a7fSflorian 		imsg_free(&imsg);
53857419a7fSflorian 	}
53957419a7fSflorian 	if (!shut)
54057419a7fSflorian 		imsg_event_add(iev);
54157419a7fSflorian 	else {
54257419a7fSflorian 		/* This pipe is dead. Remove its event handler. */
54357419a7fSflorian 		event_del(&iev->ev);
54457419a7fSflorian 		event_loopexit(NULL);
54557419a7fSflorian 	}
54657419a7fSflorian }
54757419a7fSflorian 
54857419a7fSflorian int
get_flags(char * if_name)54957419a7fSflorian get_flags(char *if_name)
55057419a7fSflorian {
55157419a7fSflorian 	struct ifreq		 ifr;
55257419a7fSflorian 
55357419a7fSflorian 	strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
55457419a7fSflorian 	if (ioctl(ioctlsock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) {
55557419a7fSflorian 		log_warn("SIOCGIFFLAGS");
55657419a7fSflorian 		return -1;
55757419a7fSflorian 	}
55857419a7fSflorian 	return ifr.ifr_flags;
55957419a7fSflorian }
56057419a7fSflorian 
56157419a7fSflorian int
get_xflags(char * if_name)56257419a7fSflorian get_xflags(char *if_name)
56357419a7fSflorian {
56457419a7fSflorian 	struct ifreq		 ifr;
56557419a7fSflorian 
56657419a7fSflorian 	strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
56757419a7fSflorian 	if (ioctl(ioctlsock, SIOCGIFXFLAGS, (caddr_t)&ifr) == -1) {
56857419a7fSflorian 		log_warn("SIOCGIFXFLAGS");
56957419a7fSflorian 		return -1;
57057419a7fSflorian 	}
57157419a7fSflorian 	return ifr.ifr_flags;
57257419a7fSflorian }
57357419a7fSflorian 
57457419a7fSflorian void
update_iface(struct if_msghdr * ifm,struct sockaddr_dl * sdl)57548e1b614Sflorian update_iface(struct if_msghdr *ifm, struct sockaddr_dl *sdl)
57657419a7fSflorian {
57757419a7fSflorian 	struct iface		*iface;
5781bbf741cSflorian 	struct imsg_ifinfo	 ifinfo;
5791bbf741cSflorian 	uint32_t		 if_index;
58048e1b614Sflorian 	int			 flags, xflags;
5811bbf741cSflorian 	char			 ifnamebuf[IF_NAMESIZE], *if_name;
58257419a7fSflorian 
5831bbf741cSflorian 	if_index = ifm->ifm_index;
5841bbf741cSflorian 
5851bbf741cSflorian 	flags = ifm->ifm_flags;
5861bbf741cSflorian 	xflags = ifm->ifm_xflags;
5871bbf741cSflorian 
5881bbf741cSflorian 	iface = get_iface_by_id(if_index);
5891bbf741cSflorian 	if_name = if_indextoname(if_index, ifnamebuf);
5901bbf741cSflorian 
5911bbf741cSflorian 	if (if_name == NULL) {
5921bbf741cSflorian 		if (iface != NULL) {
5931bbf741cSflorian 			log_debug("interface with idx %d removed", if_index);
5941bbf741cSflorian 			frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
5951bbf741cSflorian 			    &if_index, sizeof(if_index));
5961bbf741cSflorian 			remove_iface(if_index);
5971bbf741cSflorian 		}
59857419a7fSflorian 		return;
5991bbf741cSflorian 	}
60057419a7fSflorian 
6011bbf741cSflorian 	if (!(xflags & IFXF_AUTOCONF4)) {
6021bbf741cSflorian 		if (iface != NULL) {
6031bbf741cSflorian 			log_info("Removed autoconf flag from %s", if_name);
6041bbf741cSflorian 			frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
6051bbf741cSflorian 			    &if_index, sizeof(if_index));
6061bbf741cSflorian 			remove_iface(if_index);
6071bbf741cSflorian 		}
60857419a7fSflorian 		return;
6091bbf741cSflorian 	}
61057419a7fSflorian 
6111bbf741cSflorian 	memset(&ifinfo, 0, sizeof(ifinfo));
6121bbf741cSflorian 	ifinfo.if_index = if_index;
6131bbf741cSflorian 	ifinfo.link_state = ifm->ifm_data.ifi_link_state;
6141bbf741cSflorian 	ifinfo.rdomain = ifm->ifm_tableid;
6151bbf741cSflorian 	ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) ==
6161bbf741cSflorian 	    (IFF_UP | IFF_RUNNING);
61757419a7fSflorian 
618a13fc78dSflorian 	if (sdl != NULL && (sdl->sdl_type == IFT_ETHER ||
619a13fc78dSflorian 	    sdl->sdl_type == IFT_CARP) && sdl->sdl_alen == ETHER_ADDR_LEN)
6201bbf741cSflorian 		memcpy(ifinfo.hw_address.ether_addr_octet, LLADDR(sdl),
6211bbf741cSflorian 		    ETHER_ADDR_LEN);
62248e1b614Sflorian 	else if (iface == NULL) {
6231bbf741cSflorian 		log_warnx("Could not find AF_LINK address for %s.", if_name);
6241bbf741cSflorian 		return;
6254fd2b0e0Sflorian 	}
6261bbf741cSflorian 
6271bbf741cSflorian 	if (iface == NULL) {
6284fd2b0e0Sflorian 		if ((iface = calloc(1, sizeof(*iface))) == NULL)
6294fd2b0e0Sflorian 			fatal("calloc");
6304fd2b0e0Sflorian 		iface->udpsock = -1;
6314fd2b0e0Sflorian 		LIST_INSERT_HEAD(&interfaces, iface, entries);
6324fd2b0e0Sflorian 		frontend_imsg_compose_main(IMSG_OPEN_BPFSOCK, 0,
6334fd2b0e0Sflorian 		    &if_index, sizeof(if_index));
6341bbf741cSflorian 	} else {
6351bbf741cSflorian 		if (iface->ifinfo.rdomain != ifinfo.rdomain &&
6361bbf741cSflorian 		    iface->udpsock != -1) {
6371bbf741cSflorian 			close(iface->udpsock);
6381bbf741cSflorian 			iface->udpsock = -1;
6391bbf741cSflorian 		}
6404fd2b0e0Sflorian 	}
6414fd2b0e0Sflorian 
6421bbf741cSflorian 	if (memcmp(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo)) != 0) {
6431bbf741cSflorian 		memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo));
6441bbf741cSflorian 		frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &iface->ifinfo,
6451bbf741cSflorian 		    sizeof(iface->ifinfo));
6461bbf741cSflorian 	}
64757419a7fSflorian }
64857419a7fSflorian 
64957419a7fSflorian void
frontend_startup(void)65057419a7fSflorian frontend_startup(void)
65157419a7fSflorian {
65257419a7fSflorian 	if (!event_initialized(&ev_route))
65357419a7fSflorian 		fatalx("%s: did not receive a route socket from the main "
65457419a7fSflorian 		    "process", __func__);
65557419a7fSflorian 
6561bbf741cSflorian 	init_ifaces();
65748e1b614Sflorian 	if (pledge("stdio unix recvfd", NULL) == -1)
65848e1b614Sflorian 		fatal("pledge");
65957419a7fSflorian 	event_add(&ev_route, NULL);
6601bbf741cSflorian }
6611bbf741cSflorian 
6621bbf741cSflorian void
init_ifaces(void)6631bbf741cSflorian init_ifaces(void)
6641bbf741cSflorian {
6651bbf741cSflorian 	struct iface		*iface;
6661bbf741cSflorian 	struct imsg_ifinfo	 ifinfo;
6671bbf741cSflorian 	struct if_nameindex	*ifnidxp, *ifnidx;
6681bbf741cSflorian 	struct ifaddrs		*ifap, *ifa;
6691bbf741cSflorian 	uint32_t		 if_index;
6701bbf741cSflorian 	int			 flags, xflags;
6711bbf741cSflorian 	char			*if_name;
67257419a7fSflorian 
67357419a7fSflorian 	if ((ifnidxp = if_nameindex()) == NULL)
67457419a7fSflorian 		fatalx("if_nameindex");
67557419a7fSflorian 
6761bbf741cSflorian 	if (getifaddrs(&ifap) != 0)
6771bbf741cSflorian 		fatal("getifaddrs");
67857419a7fSflorian 
6791bbf741cSflorian 	for (ifnidx = ifnidxp; ifnidx->if_index != 0 && ifnidx->if_name != NULL;
6801bbf741cSflorian 	    ifnidx++) {
6811bbf741cSflorian 		if_index = ifnidx->if_index;
6821bbf741cSflorian 		if_name = ifnidx->if_name;
6831bbf741cSflorian 		if ((flags = get_flags(if_name)) == -1)
6841bbf741cSflorian 			continue;
6851bbf741cSflorian 		if ((xflags = get_xflags(if_name)) == -1)
6861bbf741cSflorian 			continue;
6871bbf741cSflorian 		if (!(xflags & IFXF_AUTOCONF4))
6881bbf741cSflorian 			continue;
6891bbf741cSflorian 
6901bbf741cSflorian 		memset(&ifinfo, 0, sizeof(ifinfo));
6911bbf741cSflorian 		ifinfo.if_index = if_index;
6921bbf741cSflorian 		ifinfo.link_state = -1;
6931bbf741cSflorian 		ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) ==
6941bbf741cSflorian 		    (IFF_UP | IFF_RUNNING);
6951bbf741cSflorian 
6961bbf741cSflorian 		for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
6971bbf741cSflorian 			if (strcmp(if_name, ifa->ifa_name) != 0)
6981bbf741cSflorian 				continue;
6991bbf741cSflorian 			if (ifa->ifa_addr == NULL)
7001bbf741cSflorian 				continue;
7011bbf741cSflorian 
7021bbf741cSflorian 			switch (ifa->ifa_addr->sa_family) {
7031bbf741cSflorian 			case AF_LINK: {
7041bbf741cSflorian 				struct if_data		*if_data;
7051bbf741cSflorian 				struct sockaddr_dl	*sdl;
7061bbf741cSflorian 
7071bbf741cSflorian 				sdl = (struct sockaddr_dl *)ifa->ifa_addr;
708a13fc78dSflorian 				if ((sdl->sdl_type != IFT_ETHER &&
709a13fc78dSflorian 				    sdl->sdl_type != IFT_CARP) ||
7101bbf741cSflorian 				    sdl->sdl_alen != ETHER_ADDR_LEN)
7111bbf741cSflorian 					continue;
7121bbf741cSflorian 				memcpy(ifinfo.hw_address.ether_addr_octet,
7131bbf741cSflorian 				    LLADDR(sdl), ETHER_ADDR_LEN);
7141bbf741cSflorian 
7151bbf741cSflorian 				if_data = (struct if_data *)ifa->ifa_data;
7161bbf741cSflorian 				ifinfo.link_state = if_data->ifi_link_state;
7171bbf741cSflorian 				ifinfo.rdomain = if_data->ifi_rdomain;
7181bbf741cSflorian 				goto out;
7191bbf741cSflorian 			}
7201bbf741cSflorian 			default:
7211bbf741cSflorian 				break;
7221bbf741cSflorian 			}
7231bbf741cSflorian 		}
7241bbf741cSflorian  out:
7251bbf741cSflorian 		if (ifinfo.link_state == -1)
7261bbf741cSflorian 			/* no AF_LINK found */
7271bbf741cSflorian 			continue;
7281bbf741cSflorian 
7291bbf741cSflorian 		if ((iface = calloc(1, sizeof(*iface))) == NULL)
7301bbf741cSflorian 			fatal("calloc");
7311bbf741cSflorian 		iface->udpsock = -1;
7321bbf741cSflorian 		memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo));
7331bbf741cSflorian 		LIST_INSERT_HEAD(&interfaces, iface, entries);
7341bbf741cSflorian 		frontend_imsg_compose_main(IMSG_OPEN_BPFSOCK, 0,
7351bbf741cSflorian 		    &if_index, sizeof(if_index));
7361bbf741cSflorian 		frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &iface->ifinfo,
7371bbf741cSflorian 		    sizeof(iface->ifinfo));
7381bbf741cSflorian 	}
7391bbf741cSflorian 
7401bbf741cSflorian 	freeifaddrs(ifap);
74157419a7fSflorian 	if_freenameindex(ifnidxp);
74257419a7fSflorian }
74357419a7fSflorian 
74457419a7fSflorian void
route_receive(int fd,short events,void * arg)74557419a7fSflorian route_receive(int fd, short events, void *arg)
74657419a7fSflorian {
74757419a7fSflorian 	static uint8_t			 *buf;
74857419a7fSflorian 
74957419a7fSflorian 	struct rt_msghdr		*rtm;
75057419a7fSflorian 	struct sockaddr			*sa, *rti_info[RTAX_MAX];
75157419a7fSflorian 	ssize_t				 n;
75257419a7fSflorian 
75357419a7fSflorian 	if (buf == NULL) {
75457419a7fSflorian 		buf = malloc(ROUTE_SOCKET_BUF_SIZE);
75557419a7fSflorian 		if (buf == NULL)
75657419a7fSflorian 			fatal("malloc");
75757419a7fSflorian 	}
75857419a7fSflorian 	rtm = (struct rt_msghdr *)buf;
75957419a7fSflorian 	if ((n = read(fd, buf, ROUTE_SOCKET_BUF_SIZE)) == -1) {
76057419a7fSflorian 		if (errno == EAGAIN || errno == EINTR)
76157419a7fSflorian 			return;
76257419a7fSflorian 		log_warn("dispatch_rtmsg: read error");
76357419a7fSflorian 		return;
76457419a7fSflorian 	}
76557419a7fSflorian 
76657419a7fSflorian 	if (n == 0)
76757419a7fSflorian 		fatal("routing socket closed");
76857419a7fSflorian 
76957419a7fSflorian 	if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) {
77057419a7fSflorian 		log_warnx("partial rtm of %zd in buffer", n);
77157419a7fSflorian 		return;
77257419a7fSflorian 	}
77357419a7fSflorian 
77457419a7fSflorian 	if (rtm->rtm_version != RTM_VERSION)
77557419a7fSflorian 		return;
77657419a7fSflorian 
77757419a7fSflorian 	sa = (struct sockaddr *)(buf + rtm->rtm_hdrlen);
77857419a7fSflorian 	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
77957419a7fSflorian 
78057419a7fSflorian 	handle_route_message(rtm, rti_info);
78157419a7fSflorian }
78257419a7fSflorian 
78357419a7fSflorian void
handle_route_message(struct rt_msghdr * rtm,struct sockaddr ** rti_info)78457419a7fSflorian handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info)
78557419a7fSflorian {
78648e1b614Sflorian 	struct sockaddr_dl		*sdl = NULL;
787d3097762Sflorian 	struct if_announcemsghdr	*ifan;
788d3097762Sflorian 	uint32_t			 if_index;
789d3097762Sflorian 
79057419a7fSflorian 	switch (rtm->rtm_type) {
79157419a7fSflorian 	case RTM_IFINFO:
79248e1b614Sflorian 		if (rtm->rtm_addrs & RTA_IFP && rti_info[RTAX_IFP]->sa_family
79348e1b614Sflorian 		    == AF_LINK)
79448e1b614Sflorian 			sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP];
79548e1b614Sflorian 		update_iface((struct if_msghdr *)rtm, sdl);
79657419a7fSflorian 		break;
797d3097762Sflorian 	case RTM_IFANNOUNCE:
798d3097762Sflorian 		ifan = (struct if_announcemsghdr *)rtm;
799d3097762Sflorian 		if_index = ifan->ifan_index;
800d3097762Sflorian                 if (ifan->ifan_what == IFAN_DEPARTURE) {
801d3097762Sflorian 			frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
802d3097762Sflorian 			    &if_index, sizeof(if_index));
803d3097762Sflorian 			remove_iface(if_index);
804d3097762Sflorian 		}
805d3097762Sflorian 		break;
80657419a7fSflorian 	case RTM_PROPOSAL:
80757419a7fSflorian 		if (rtm->rtm_priority == RTP_PROPOSAL_SOLICIT) {
80857419a7fSflorian 			log_debug("RTP_PROPOSAL_SOLICIT");
80957419a7fSflorian 			frontend_imsg_compose_engine(IMSG_REPROPOSE_RDNS,
81057419a7fSflorian 			    0, 0, NULL, 0);
811c7313d44Sflorian 		}
812c7313d44Sflorian #ifndef SMALL
813c7313d44Sflorian 		else if (rtm->rtm_flags & RTF_PROTO3) {
814bc837866Sflorian 			char		 ifnamebuf[IF_NAMESIZE], *if_name;
815bc837866Sflorian 
816bc837866Sflorian 			if_index = rtm->rtm_index;
817bc837866Sflorian 			if_name = if_indextoname(if_index, ifnamebuf);
818bc837866Sflorian 			log_warnx("\"dhclient %s\" ran, requesting new lease",
819bc837866Sflorian 			    if_name != NULL ? if_name : "(unknown)");
820c7313d44Sflorian 			frontend_imsg_compose_engine(IMSG_REQUEST_REBOOT,
821bc837866Sflorian 			    0, 0, &if_index, sizeof(if_index));
82257419a7fSflorian 		}
823c7313d44Sflorian #endif /* SMALL */
82457419a7fSflorian 		break;
82557419a7fSflorian 	default:
82657419a7fSflorian 		log_debug("unexpected RTM: %d", rtm->rtm_type);
82757419a7fSflorian 		break;
82857419a7fSflorian 	}
82957419a7fSflorian }
83057419a7fSflorian 
83157419a7fSflorian #define ROUNDUP(a) \
83257419a7fSflorian 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
83357419a7fSflorian 
83457419a7fSflorian void
get_rtaddrs(int addrs,struct sockaddr * sa,struct sockaddr ** rti_info)83557419a7fSflorian get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
83657419a7fSflorian {
83757419a7fSflorian 	int	i;
83857419a7fSflorian 
83957419a7fSflorian 	for (i = 0; i < RTAX_MAX; i++) {
84057419a7fSflorian 		if (addrs & (1 << i)) {
84157419a7fSflorian 			rti_info[i] = sa;
84257419a7fSflorian 			sa = (struct sockaddr *)((char *)(sa) +
84357419a7fSflorian 			    ROUNDUP(sa->sa_len));
84457419a7fSflorian 		} else
84557419a7fSflorian 			rti_info[i] = NULL;
84657419a7fSflorian 	}
84757419a7fSflorian }
84857419a7fSflorian 
84957419a7fSflorian void
bpf_receive(int fd,short events,void * arg)85057419a7fSflorian bpf_receive(int fd, short events, void *arg)
85157419a7fSflorian {
85257419a7fSflorian 	struct bpf_hdr		*hdr;
85357419a7fSflorian 	struct imsg_dhcp	 imsg_dhcp;
85457419a7fSflorian 	struct iface		*iface;
85557419a7fSflorian 	ssize_t			 len, rem;
85657419a7fSflorian 	uint8_t			*p;
85757419a7fSflorian 
85857419a7fSflorian 	iface = (struct iface *)arg;
85957419a7fSflorian 
86057419a7fSflorian 	if ((len = read(fd, iface->bpfev.buf, BPFLEN)) == -1) {
86137fb58f6Sflorian 		log_warn("%s: read", __func__);
86257419a7fSflorian 		return;
86357419a7fSflorian 	}
864e998cdbeSflorian 
865e998cdbeSflorian 	if (len == 0)
866e998cdbeSflorian 		fatal("%s len == 0", __func__);
86757419a7fSflorian 
86857419a7fSflorian 	memset(&imsg_dhcp, 0, sizeof(imsg_dhcp));
8691bbf741cSflorian 	imsg_dhcp.if_index = iface->ifinfo.if_index;
87057419a7fSflorian 
87157419a7fSflorian 	rem = len;
87257419a7fSflorian 	p = iface->bpfev.buf;
87357419a7fSflorian 
87457419a7fSflorian 	while (rem > 0) {
87557419a7fSflorian 		if ((size_t)rem < sizeof(*hdr)) {
87657419a7fSflorian 			log_warnx("packet too short");
87757419a7fSflorian 			return;
87857419a7fSflorian 		}
87957419a7fSflorian 		hdr = (struct bpf_hdr *)p;
88057419a7fSflorian 		if (hdr->bh_caplen != hdr->bh_datalen) {
88157419a7fSflorian 			log_warnx("skipping truncated packet");
88257419a7fSflorian 			goto cont;
88357419a7fSflorian 		}
88457419a7fSflorian 		if (rem < hdr->bh_hdrlen + hdr->bh_caplen)
88557419a7fSflorian 			/* we are done */
88657419a7fSflorian 			break;
88757419a7fSflorian 		if (hdr->bh_caplen > sizeof(imsg_dhcp.packet)) {
88857419a7fSflorian 			log_warn("packet too big");
88957419a7fSflorian 			goto cont;
89057419a7fSflorian 		}
89157419a7fSflorian 		memcpy(&imsg_dhcp.packet, p + hdr->bh_hdrlen, hdr->bh_caplen);
89257419a7fSflorian 		imsg_dhcp.len = hdr->bh_caplen;
893*2b86dc95Sjan 		imsg_dhcp.csumflags = hdr->bh_csumflags;
89457419a7fSflorian 		frontend_imsg_compose_engine(IMSG_DHCP, 0, 0, &imsg_dhcp,
89557419a7fSflorian 		    sizeof(imsg_dhcp));
89657419a7fSflorian  cont:
89757419a7fSflorian 		p += BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
89857419a7fSflorian 		rem -= BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
89957419a7fSflorian 
90057419a7fSflorian 	}
90157419a7fSflorian }
90257419a7fSflorian 
90382a5f3d7Sflorian void
iface_data_from_imsg(struct iface * iface,struct imsg_req_dhcp * imsg)90482a5f3d7Sflorian iface_data_from_imsg(struct iface* iface, struct imsg_req_dhcp *imsg)
90582a5f3d7Sflorian {
90682a5f3d7Sflorian 	iface->xid = imsg->xid;
907b2b40540Sflorian 	iface->ciaddr = imsg->ciaddr;
908b2b40540Sflorian 	iface->requested_ip = imsg->requested_ip;
909b2b40540Sflorian 	iface->server_identifier = imsg->server_identifier;
910b2b40540Sflorian 	iface->dhcp_server = imsg->dhcp_server;
91182a5f3d7Sflorian }
91282a5f3d7Sflorian 
91357419a7fSflorian 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)914a41cc082Sflorian build_packet(uint8_t message_type, char *if_name, uint32_t xid,
91582a5f3d7Sflorian     struct ether_addr *hw_address, struct in_addr *ciaddr, struct in_addr
91682a5f3d7Sflorian     *requested_ip, struct in_addr *server_identifier)
91757419a7fSflorian {
91857419a7fSflorian 	static uint8_t	 dhcp_cookie[] = DHCP_COOKIE;
91957419a7fSflorian 	static uint8_t	 dhcp_message_type[] = {DHO_DHCP_MESSAGE_TYPE, 1,
92057419a7fSflorian 		DHCPDISCOVER};
921b3441518Sflorian 	static uint8_t	 dhcp_hostname[255 + 2] = {DHO_HOST_NAME, 0 /*, ... */};
92257419a7fSflorian 	static uint8_t	 dhcp_client_id[] = {DHO_DHCP_CLIENT_IDENTIFIER, 7,
92357419a7fSflorian 		HTYPE_ETHER, 0, 0, 0, 0, 0, 0};
92457419a7fSflorian 	static uint8_t	 dhcp_req_list[] = {DHO_DHCP_PARAMETER_REQUEST_LIST,
925ea86c963Sflorian 		8, DHO_SUBNET_MASK, DHO_ROUTERS, DHO_DOMAIN_NAME_SERVERS,
92657419a7fSflorian 		DHO_HOST_NAME, DHO_DOMAIN_NAME, DHO_BROADCAST_ADDRESS,
927ea86c963Sflorian 		DHO_DOMAIN_SEARCH, DHO_CLASSLESS_STATIC_ROUTES};
9286d7478f0Sflorian 	static uint8_t	 dhcp_req_list_v6[] = {DHO_DHCP_PARAMETER_REQUEST_LIST,
9296d7478f0Sflorian 		9, DHO_SUBNET_MASK, DHO_ROUTERS, DHO_DOMAIN_NAME_SERVERS,
9306d7478f0Sflorian 		DHO_HOST_NAME, DHO_DOMAIN_NAME, DHO_BROADCAST_ADDRESS,
9316d7478f0Sflorian 		DHO_DOMAIN_SEARCH, DHO_CLASSLESS_STATIC_ROUTES,
9326d7478f0Sflorian 		DHO_IPV6_ONLY_PREFERRED};
93357419a7fSflorian 	static uint8_t	 dhcp_requested_address[] = {DHO_DHCP_REQUESTED_ADDRESS,
93457419a7fSflorian 		4, 0, 0, 0, 0};
93557419a7fSflorian 	static uint8_t	 dhcp_server_identifier[] = {DHO_DHCP_SERVER_IDENTIFIER,
93657419a7fSflorian 		4, 0, 0, 0, 0};
937a41cc082Sflorian #ifndef SMALL
938a41cc082Sflorian 	struct iface_conf	*iface_conf;
939a41cc082Sflorian #endif /* SMALL */
94057419a7fSflorian 	struct dhcp_hdr		*hdr;
9412d2edb0eSflorian 	ssize_t			 len;
94257419a7fSflorian 	uint8_t			*p;
94357419a7fSflorian 	char			*c;
94457419a7fSflorian 
945a41cc082Sflorian #ifndef SMALL
946a41cc082Sflorian 	iface_conf = find_iface_conf(&frontend_conf->iface_list, if_name);
947a41cc082Sflorian #endif /* SMALL */
948a41cc082Sflorian 
94957419a7fSflorian 	memset(dhcp_packet, 0, sizeof(dhcp_packet));
95057419a7fSflorian 	dhcp_message_type[2] = message_type;
95157419a7fSflorian 	p = dhcp_packet;
95257419a7fSflorian 	hdr = (struct dhcp_hdr *)p;
95357419a7fSflorian 	hdr->op = DHCP_BOOTREQUEST;
95457419a7fSflorian 	hdr->htype = HTYPE_ETHER;
95557419a7fSflorian 	hdr->hlen = 6;
95657419a7fSflorian 	hdr->hops = 0;
957c2da98a6Sflorian 	hdr->xid = htonl(xid);
95857419a7fSflorian 	hdr->secs = 0;
959b2b40540Sflorian 	hdr->ciaddr = *ciaddr;
96057419a7fSflorian 	memcpy(hdr->chaddr, hw_address, sizeof(*hw_address));
96157419a7fSflorian 	p += sizeof(struct dhcp_hdr);
96257419a7fSflorian 	memcpy(p, dhcp_cookie, sizeof(dhcp_cookie));
96357419a7fSflorian 	p += sizeof(dhcp_cookie);
96457419a7fSflorian 	memcpy(p, dhcp_message_type, sizeof(dhcp_message_type));
96557419a7fSflorian 	p += sizeof(dhcp_message_type);
966b3441518Sflorian 
967b3441518Sflorian #ifndef SMALL
968b3441518Sflorian 	if (iface_conf != NULL && iface_conf->h_name != NULL) {
969b3441518Sflorian 		if (iface_conf->h_name[0] != '\0') {
970b3441518Sflorian 			dhcp_hostname[1] = strlen(iface_conf->h_name);
971b3441518Sflorian 			memcpy(dhcp_hostname + 2, iface_conf->h_name,
972b3441518Sflorian 			    strlen(iface_conf->h_name));
973b3441518Sflorian 			memcpy(p, dhcp_hostname, dhcp_hostname[1] + 2);
974b3441518Sflorian 			p += dhcp_hostname[1] + 2;
975b3441518Sflorian 		}
976b3441518Sflorian 	} else
977b3441518Sflorian #endif /* SMALL */
978b3441518Sflorian 	{
979b3441518Sflorian 		if (gethostname(dhcp_hostname + 2,
9808491c0abSflorian 		    sizeof(dhcp_hostname) - 2) == 0 &&
9818491c0abSflorian 		    dhcp_hostname[2] != '\0') {
98257419a7fSflorian 			if ((c = strchr(dhcp_hostname + 2, '.')) != NULL)
98357419a7fSflorian 				*c = '\0';
98457419a7fSflorian 			dhcp_hostname[1] = strlen(dhcp_hostname + 2);
98557419a7fSflorian 			memcpy(p, dhcp_hostname, dhcp_hostname[1] + 2);
98657419a7fSflorian 			p += dhcp_hostname[1] + 2;
98757419a7fSflorian 		}
988b3441518Sflorian 	}
989a41cc082Sflorian 
990a41cc082Sflorian #ifndef SMALL
991a41cc082Sflorian 	if (iface_conf != NULL) {
992a41cc082Sflorian 		if (iface_conf->c_id_len > 0) {
993a41cc082Sflorian 			/* XXX check space */
994a41cc082Sflorian 			memcpy(p, iface_conf->c_id, iface_conf->c_id_len);
995a41cc082Sflorian 			p += iface_conf->c_id_len;
99690c58abaSflorian 		} else {
99790c58abaSflorian 			memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address));
99890c58abaSflorian 			memcpy(p, dhcp_client_id, sizeof(dhcp_client_id));
99990c58abaSflorian 			p += sizeof(dhcp_client_id);
1000a41cc082Sflorian 		}
1001a41cc082Sflorian 		if (iface_conf->vc_id_len > 0) {
1002a41cc082Sflorian 			/* XXX check space */
1003a41cc082Sflorian 			memcpy(p, iface_conf->vc_id, iface_conf->vc_id_len);
1004a41cc082Sflorian 			p += iface_conf->vc_id_len;
1005a41cc082Sflorian 		}
10066d7478f0Sflorian 		if (iface_conf->prefer_ipv6) {
10076d7478f0Sflorian 			memcpy(p, dhcp_req_list_v6, sizeof(dhcp_req_list_v6));
10086d7478f0Sflorian 			p += sizeof(dhcp_req_list_v6);
10096d7478f0Sflorian 
10106d7478f0Sflorian 		} else {
10116d7478f0Sflorian 			memcpy(p, dhcp_req_list, sizeof(dhcp_req_list));
10126d7478f0Sflorian 			p += sizeof(dhcp_req_list);
10136d7478f0Sflorian 		}
1014a41cc082Sflorian 	} else
1015a41cc082Sflorian #endif /* SMALL */
1016a41cc082Sflorian 	{
101757419a7fSflorian 		memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address));
101857419a7fSflorian 		memcpy(p, dhcp_client_id, sizeof(dhcp_client_id));
101957419a7fSflorian 		p += sizeof(dhcp_client_id);
102057419a7fSflorian 		memcpy(p, dhcp_req_list, sizeof(dhcp_req_list));
102157419a7fSflorian 		p += sizeof(dhcp_req_list);
10226d7478f0Sflorian 	}
102357419a7fSflorian 
102482a5f3d7Sflorian 	if (requested_ip->s_addr != INADDR_ANY) {
102557419a7fSflorian 		memcpy(dhcp_requested_address + 2, requested_ip,
102657419a7fSflorian 		    sizeof(*requested_ip));
102757419a7fSflorian 		memcpy(p, dhcp_requested_address,
102857419a7fSflorian 		    sizeof(dhcp_requested_address));
102957419a7fSflorian 		p += sizeof(dhcp_requested_address);
103082a5f3d7Sflorian 	}
103157419a7fSflorian 
103257419a7fSflorian 	if (server_identifier->s_addr != INADDR_ANY) {
103357419a7fSflorian 		memcpy(dhcp_server_identifier + 2, server_identifier,
103457419a7fSflorian 		    sizeof(*server_identifier));
103557419a7fSflorian 		memcpy(p, dhcp_server_identifier,
103657419a7fSflorian 		    sizeof(dhcp_server_identifier));
103757419a7fSflorian 		p += sizeof(dhcp_server_identifier);
103857419a7fSflorian 	}
103957419a7fSflorian 
104057419a7fSflorian 	*p = DHO_END;
104157419a7fSflorian 	p += 1;
104257419a7fSflorian 
10432d2edb0eSflorian 	len = p - dhcp_packet;
10442d2edb0eSflorian 
10452d2edb0eSflorian 	/* dhcp_packet is initialized with DHO_PADs */
10462d2edb0eSflorian 	if (len < BOOTP_MIN_LEN)
10472d2edb0eSflorian 		len = BOOTP_MIN_LEN;
10482d2edb0eSflorian 
10492d2edb0eSflorian 	return (len);
105057419a7fSflorian }
105157419a7fSflorian 
105257419a7fSflorian void
send_packet(uint8_t message_type,struct iface * iface)105382a5f3d7Sflorian send_packet(uint8_t message_type, struct iface *iface)
105457419a7fSflorian {
105557419a7fSflorian 	ssize_t			 pkt_len;
1056e998cdbeSflorian 	char			 ifnamebuf[IF_NAMESIZE], *if_name;
105757419a7fSflorian 
105857419a7fSflorian 	if (!event_initialized(&iface->bpfev.ev)) {
105957419a7fSflorian 		iface->send_discover = 1;
106057419a7fSflorian 		return;
106157419a7fSflorian 	}
106282a5f3d7Sflorian 
106357419a7fSflorian 	iface->send_discover = 0;
1064e998cdbeSflorian 
106582a5f3d7Sflorian 	if ((if_name = if_indextoname(iface->ifinfo.if_index, ifnamebuf)) == NULL)
106682a5f3d7Sflorian 		return; /* iface went away, nothing to do */
1067e998cdbeSflorian 
106882a5f3d7Sflorian 	log_debug("%s on %s", message_type == DHCPDISCOVER ? "DHCPDISCOVER" :
106982a5f3d7Sflorian 	    "DHCPREQUEST", if_name);
107057419a7fSflorian 
107182a5f3d7Sflorian 	pkt_len = build_packet(message_type, if_name, iface->xid,
107282a5f3d7Sflorian 	    &iface->ifinfo.hw_address, &iface->ciaddr, &iface->requested_ip,
10731bbf741cSflorian 	    &iface->server_identifier);
1074c645071eSflorian 	if (iface->dhcp_server.s_addr != INADDR_ANY) {
1075c645071eSflorian 		if (udp_send_packet(iface, dhcp_packet, pkt_len) == -1)
1076c645071eSflorian 			bpf_send_packet(iface, dhcp_packet, pkt_len);
1077c645071eSflorian 	} else
107857419a7fSflorian 		bpf_send_packet(iface, dhcp_packet, pkt_len);
107957419a7fSflorian }
108057419a7fSflorian 
1081c645071eSflorian int
udp_send_packet(struct iface * iface,uint8_t * packet,ssize_t len)108257419a7fSflorian udp_send_packet(struct iface *iface, uint8_t *packet, ssize_t len)
108357419a7fSflorian {
108457419a7fSflorian 	struct sockaddr_in	to;
108557419a7fSflorian 
108657419a7fSflorian 	memset(&to, 0, sizeof(to));
108757419a7fSflorian 	to.sin_family = AF_INET;
108857419a7fSflorian 	to.sin_len = sizeof(to);
1089b2b40540Sflorian 	to.sin_addr = iface->dhcp_server;
109057419a7fSflorian 	to.sin_port = ntohs(SERVER_PORT);
109157419a7fSflorian 
109257419a7fSflorian 	if (sendto(iface->udpsock, packet, len, 0, (struct sockaddr *)&to,
1093c645071eSflorian 	    sizeof(to)) == -1) {
109457419a7fSflorian 		log_warn("sendto");
1095c645071eSflorian 		return -1;
1096c645071eSflorian 	}
1097c645071eSflorian 	return 0;
109857419a7fSflorian }
109957419a7fSflorian void
bpf_send_packet(struct iface * iface,uint8_t * packet,ssize_t len)110057419a7fSflorian bpf_send_packet(struct iface *iface, uint8_t *packet, ssize_t len)
110157419a7fSflorian {
110257419a7fSflorian 	struct iovec		 iov[4];
110357419a7fSflorian 	struct ether_header	 eh;
110457419a7fSflorian 	struct ip		 ip;
110557419a7fSflorian 	struct udphdr		 udp;
110657419a7fSflorian 	ssize_t			 total, result;
110757419a7fSflorian 	int			 iovcnt = 0, i;
110857419a7fSflorian 
110957419a7fSflorian 	memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
11101bbf741cSflorian 	memcpy(eh.ether_shost, &iface->ifinfo.hw_address,
11111bbf741cSflorian 	    sizeof(eh.ether_dhost));
111257419a7fSflorian 	eh.ether_type = htons(ETHERTYPE_IP);
111357419a7fSflorian 	iov[0].iov_base = &eh;
111457419a7fSflorian 	iov[0].iov_len = sizeof(eh);
111557419a7fSflorian 	iovcnt++;
111657419a7fSflorian 
111757419a7fSflorian 	ip.ip_v = 4;
111857419a7fSflorian 	ip.ip_hl = 5;
111957419a7fSflorian 	ip.ip_tos = IPTOS_LOWDELAY;
112057419a7fSflorian 	ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len);
112157419a7fSflorian 	ip.ip_id = 0;
112257419a7fSflorian 	ip.ip_off = 0;
112357419a7fSflorian 	ip.ip_ttl = 128;
112457419a7fSflorian 	ip.ip_p = IPPROTO_UDP;
112557419a7fSflorian 	ip.ip_sum = 0;
1126b2b40540Sflorian 	ip.ip_src.s_addr = INADDR_ANY;
112757419a7fSflorian 	ip.ip_dst.s_addr = INADDR_BROADCAST;
112857419a7fSflorian 	ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0));
112957419a7fSflorian 	iov[iovcnt].iov_base = &ip;
113057419a7fSflorian 	iov[iovcnt].iov_len = sizeof(ip);
113157419a7fSflorian 	iovcnt++;
113257419a7fSflorian 
113357419a7fSflorian 	udp.uh_sport = htons(CLIENT_PORT);
113457419a7fSflorian 	udp.uh_dport = htons(SERVER_PORT);
113557419a7fSflorian 	udp.uh_ulen = htons(sizeof(udp) + len);
113657419a7fSflorian 	udp.uh_sum = 0;
113757419a7fSflorian 	udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
113857419a7fSflorian 	    checksum((unsigned char *)packet, len,
113957419a7fSflorian 	    checksum((unsigned char *)&ip.ip_src,
114057419a7fSflorian 	    2 * sizeof(ip.ip_src),
114157419a7fSflorian 	    IPPROTO_UDP + (uint32_t)ntohs(udp.uh_ulen)))));
114257419a7fSflorian 	iov[iovcnt].iov_base = &udp;
114357419a7fSflorian 	iov[iovcnt].iov_len = sizeof(udp);
114457419a7fSflorian 	iovcnt++;
114557419a7fSflorian 
114657419a7fSflorian 	iov[iovcnt].iov_base = packet;
114757419a7fSflorian 	iov[iovcnt].iov_len = len;
114857419a7fSflorian 	iovcnt++;
114957419a7fSflorian 
115057419a7fSflorian 	total = 0;
115157419a7fSflorian 	for (i = 0; i < iovcnt; i++)
115257419a7fSflorian 		total += iov[i].iov_len;
115357419a7fSflorian 
115457419a7fSflorian 	result = writev(EVENT_FD(&iface->bpfev.ev), iov, iovcnt);
115557419a7fSflorian 	if (result == -1)
115657419a7fSflorian 		log_warn("%s: writev", __func__);
115757419a7fSflorian 	else if (result < total) {
115857419a7fSflorian 		log_warnx("%s, writev: %zd of %zd bytes", __func__, result,
115957419a7fSflorian 		    total);
116057419a7fSflorian 	}
116157419a7fSflorian }
116257419a7fSflorian 
116357419a7fSflorian struct iface*
get_iface_by_id(uint32_t if_index)116457419a7fSflorian get_iface_by_id(uint32_t if_index)
116557419a7fSflorian {
116657419a7fSflorian 	struct iface	*iface;
116757419a7fSflorian 
116857419a7fSflorian 	LIST_FOREACH (iface, &interfaces, entries) {
11691bbf741cSflorian 		if (iface->ifinfo.if_index == if_index)
117057419a7fSflorian 			return (iface);
117157419a7fSflorian 	}
117257419a7fSflorian 
117357419a7fSflorian 	return (NULL);
117457419a7fSflorian }
117557419a7fSflorian 
117657419a7fSflorian void
remove_iface(uint32_t if_index)117757419a7fSflorian remove_iface(uint32_t if_index)
117857419a7fSflorian {
117957419a7fSflorian 	struct iface	*iface;
118057419a7fSflorian 
118157419a7fSflorian 	iface = get_iface_by_id(if_index);
118257419a7fSflorian 
118357419a7fSflorian 	if (iface == NULL)
118457419a7fSflorian 		return;
118557419a7fSflorian 
118657419a7fSflorian 	LIST_REMOVE(iface, entries);
1187c4c62a46Sflorian 	if (event_initialized(&iface->bpfev.ev)) {
118857419a7fSflorian 		event_del(&iface->bpfev.ev);
118957419a7fSflorian 		close(EVENT_FD(&iface->bpfev.ev));
1190c4c62a46Sflorian 	}
119157419a7fSflorian 	if (iface->udpsock != -1)
119257419a7fSflorian 		close(iface->udpsock);
119357419a7fSflorian 	free(iface);
119457419a7fSflorian }
119557419a7fSflorian 
119657419a7fSflorian void
set_bpfsock(int bpfsock,uint32_t if_index)119757419a7fSflorian set_bpfsock(int bpfsock, uint32_t if_index)
119857419a7fSflorian {
119957419a7fSflorian 	struct iface	*iface;
120057419a7fSflorian 
1201c4c62a46Sflorian 	iface = get_iface_by_id(if_index);
1202c4c62a46Sflorian 
1203c4c62a46Sflorian 	if (iface == NULL) {
120457419a7fSflorian 		/*
120557419a7fSflorian 		 * The interface disappeared while we were waiting for the
1206c4c62a46Sflorian 		 * parent process to open the bpf socket.
1207c4c62a46Sflorian 		 */
1208c4c62a46Sflorian 		close(bpfsock);
1209c4c62a46Sflorian 	} else if (event_initialized(&iface->bpfev.ev)) {
1210c4c62a46Sflorian 		/*
1211c4c62a46Sflorian 		 * The autoconf flag is flapping and we have multiple bpf sockets in
1212c4c62a46Sflorian 		 * flight. We don't need this one because we already got one.
121357419a7fSflorian 		 */
121457419a7fSflorian 		close(bpfsock);
121557419a7fSflorian 	} else {
121657419a7fSflorian 		event_set(&iface->bpfev.ev, bpfsock, EV_READ |
121757419a7fSflorian 		    EV_PERSIST, bpf_receive, iface);
121857419a7fSflorian 		event_add(&iface->bpfev.ev, NULL);
121957419a7fSflorian 		if (iface->send_discover)
122082a5f3d7Sflorian 			send_packet(DHCPDISCOVER, iface);
122157419a7fSflorian 	}
122257419a7fSflorian }
1223a41cc082Sflorian 
1224a41cc082Sflorian #ifndef SMALL
1225a41cc082Sflorian struct iface_conf*
find_iface_conf(struct iface_conf_head * head,char * if_name)1226a41cc082Sflorian find_iface_conf(struct iface_conf_head *head, char *if_name)
1227a41cc082Sflorian {
1228a41cc082Sflorian 	struct iface_conf	*iface_conf;
1229a41cc082Sflorian 
1230a41cc082Sflorian 	if (if_name == NULL)
1231a41cc082Sflorian 		return (NULL);
1232a41cc082Sflorian 
1233a41cc082Sflorian 	SIMPLEQ_FOREACH(iface_conf, head, entry) {
1234a41cc082Sflorian 		if (strcmp(iface_conf->name, if_name) == 0)
1235a41cc082Sflorian 			return iface_conf;
1236a41cc082Sflorian 	}
1237a41cc082Sflorian 	return (NULL);
1238a41cc082Sflorian }
1239580befd2Sflorian 
1240580befd2Sflorian int*
changed_ifaces(struct dhcpleased_conf * oconf,struct dhcpleased_conf * nconf)1241580befd2Sflorian changed_ifaces(struct dhcpleased_conf *oconf, struct dhcpleased_conf *nconf)
1242580befd2Sflorian {
1243580befd2Sflorian 	struct iface_conf	*iface_conf, *oiface_conf;
1244580befd2Sflorian 	int			*ret, if_index, count = 0, i = 0;
1245580befd2Sflorian 
1246580befd2Sflorian 	/*
1247580befd2Sflorian 	 * Worst case: All old interfaces replaced with new interfaces.
1248580befd2Sflorian 	 * This should still be a small number
1249580befd2Sflorian 	 */
1250580befd2Sflorian 	SIMPLEQ_FOREACH(iface_conf, &oconf->iface_list, entry)
1251580befd2Sflorian 	    count++;
1252580befd2Sflorian 	SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry)
1253580befd2Sflorian 	    count++;
1254580befd2Sflorian 
1255580befd2Sflorian 	ret = calloc(count + 1, sizeof(int));
1256580befd2Sflorian 
1257580befd2Sflorian 	SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry) {
1258580befd2Sflorian 		if ((if_index = if_nametoindex(iface_conf->name)) == 0)
1259580befd2Sflorian 			continue;
1260580befd2Sflorian 		oiface_conf = find_iface_conf(&oconf->iface_list,
1261580befd2Sflorian 		    iface_conf->name);
1262580befd2Sflorian 		if (oiface_conf == NULL) {
1263580befd2Sflorian 			/* new interface added to config */
1264580befd2Sflorian 			ret[i++] = if_index;
1265580befd2Sflorian 		} else if (iface_conf_cmp(iface_conf, oiface_conf) != 0) {
1266580befd2Sflorian 			/* interface conf changed */
1267580befd2Sflorian 			ret[i++] = if_index;
1268580befd2Sflorian 		}
1269580befd2Sflorian 	}
1270580befd2Sflorian 	SIMPLEQ_FOREACH(oiface_conf, &oconf->iface_list, entry) {
1271580befd2Sflorian 		if ((if_index = if_nametoindex(oiface_conf->name)) == 0)
1272580befd2Sflorian 			continue;
1273580befd2Sflorian 		if (find_iface_conf(&nconf->iface_list, oiface_conf->name) ==
1274580befd2Sflorian 		    NULL) {
1275580befd2Sflorian 			/* interface removed from config */
1276580befd2Sflorian 			ret[i++] = if_index;
1277580befd2Sflorian 		}
1278580befd2Sflorian 	}
1279580befd2Sflorian 	return ret;
1280580befd2Sflorian }
1281580befd2Sflorian 
1282580befd2Sflorian int
iface_conf_cmp(struct iface_conf * a,struct iface_conf * b)1283580befd2Sflorian iface_conf_cmp(struct iface_conf *a, struct iface_conf *b)
1284580befd2Sflorian {
1285580befd2Sflorian 	if (a->vc_id_len != b->vc_id_len)
1286580befd2Sflorian 		return 1;
1287580befd2Sflorian 	if (memcmp(a->vc_id, b->vc_id, a->vc_id_len) != 0)
1288580befd2Sflorian 		return 1;
1289580befd2Sflorian 	if (a->c_id_len != b->c_id_len)
1290580befd2Sflorian 		return 1;
1291580befd2Sflorian 	if (memcmp(a->c_id, b->c_id, a->c_id_len) != 0)
1292580befd2Sflorian 		return 1;
1293b3441518Sflorian 	if (a->h_name == NULL ||  b->h_name == NULL)
1294b3441518Sflorian 		return 1;
1295b3441518Sflorian 	if (strcmp(a->h_name, b->h_name) != 0)
1296b3441518Sflorian 		return 1;
1297c2bc6c6dSflorian 	if (a->ignore != b->ignore)
1298c2bc6c6dSflorian 		return 1;
1299c2bc6c6dSflorian 	if (a->ignore_servers_len != b->ignore_servers_len)
1300c2bc6c6dSflorian 		return 1;
1301c2bc6c6dSflorian 	if (memcmp(a->ignore_servers, b->ignore_servers,
1302c2bc6c6dSflorian 	    a->ignore_servers_len * sizeof (struct in_addr)) != 0)
1303c2bc6c6dSflorian 		return 1;
1304580befd2Sflorian 	return 0;
1305580befd2Sflorian }
1306a41cc082Sflorian #endif /* SMALL */
1307