xref: /openbsd/sbin/dhcpleased/frontend.c (revision e998cdbe)
1*e998cdbeSflorian /*	$OpenBSD: frontend.c,v 1.3 2021/03/07 18:39:11 florian 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
6057419a7fSflorian 
6157419a7fSflorian struct bpf_ev {
6257419a7fSflorian 	struct event		 ev;
6357419a7fSflorian 	uint8_t			 buf[BPFLEN];
6457419a7fSflorian };
6557419a7fSflorian 
6657419a7fSflorian struct iface           {
6757419a7fSflorian 	LIST_ENTRY(iface)	 entries;
6857419a7fSflorian 	struct bpf_ev		 bpfev;
6957419a7fSflorian 	struct ether_addr	 hw_address;
7057419a7fSflorian 	uint32_t		 if_index;
7157419a7fSflorian 	int			 rdomain;
7257419a7fSflorian 	int			 send_discover;
7357419a7fSflorian 	uint32_t		 xid;
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 *);
8257419a7fSflorian void		 update_iface(uint32_t, char*);
8357419a7fSflorian void		 frontend_startup(void);
8457419a7fSflorian void		 route_receive(int, short, void *);
8557419a7fSflorian void		 handle_route_message(struct rt_msghdr *, struct sockaddr **);
8657419a7fSflorian void		 get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
8757419a7fSflorian void		 bpf_receive(int, short, void *);
8857419a7fSflorian int		 get_flags(char *);
8957419a7fSflorian int		 get_xflags(char *);
9057419a7fSflorian int		 get_ifrdomain(char *);
9157419a7fSflorian struct iface	*get_iface_by_id(uint32_t);
9257419a7fSflorian void		 remove_iface(uint32_t);
9357419a7fSflorian void		 set_bpfsock(int, uint32_t);
9457419a7fSflorian ssize_t		 build_packet(uint8_t, uint32_t, struct ether_addr *, struct
9557419a7fSflorian 		     in_addr *, struct in_addr *);
9657419a7fSflorian void		 send_discover(struct iface *);
9757419a7fSflorian void		 send_request(struct iface *);
9857419a7fSflorian void		 bpf_send_packet(struct iface *, uint8_t *, ssize_t);
9957419a7fSflorian void		 udp_send_packet(struct iface *, uint8_t *, ssize_t);
10057419a7fSflorian 
10157419a7fSflorian LIST_HEAD(, iface)		 interfaces;
10257419a7fSflorian static struct imsgev		*iev_main;
10357419a7fSflorian static struct imsgev		*iev_engine;
10457419a7fSflorian struct event			 ev_route;
10557419a7fSflorian int				 ioctlsock;
10657419a7fSflorian 
10757419a7fSflorian uint8_t				 dhcp_packet[1500];
10857419a7fSflorian 
10957419a7fSflorian void
11057419a7fSflorian frontend_sig_handler(int sig, short event, void *bula)
11157419a7fSflorian {
11257419a7fSflorian 	/*
11357419a7fSflorian 	 * Normal signal handler rules don't apply because libevent
11457419a7fSflorian 	 * decouples for us.
11557419a7fSflorian 	 */
11657419a7fSflorian 
11757419a7fSflorian 	switch (sig) {
11857419a7fSflorian 	case SIGINT:
11957419a7fSflorian 	case SIGTERM:
12057419a7fSflorian 		frontend_shutdown();
12157419a7fSflorian 	default:
12257419a7fSflorian 		fatalx("unexpected signal");
12357419a7fSflorian 	}
12457419a7fSflorian }
12557419a7fSflorian 
12657419a7fSflorian void
12757419a7fSflorian frontend(int debug, int verbose)
12857419a7fSflorian {
12957419a7fSflorian 	struct event		 ev_sigint, ev_sigterm;
13057419a7fSflorian 	struct passwd		*pw;
13157419a7fSflorian 
13257419a7fSflorian 	log_init(debug, LOG_DAEMON);
13357419a7fSflorian 	log_setverbose(verbose);
13457419a7fSflorian 
13557419a7fSflorian 	if ((pw = getpwnam(DHCPLEASED_USER)) == NULL)
13657419a7fSflorian 		fatal("getpwnam");
13757419a7fSflorian 
13857419a7fSflorian 	if (chroot(pw->pw_dir) == -1)
13957419a7fSflorian 		fatal("chroot");
14057419a7fSflorian 	if (chdir("/") == -1)
14157419a7fSflorian 		fatal("chdir(\"/\")");
14257419a7fSflorian 
14357419a7fSflorian 	setproctitle("%s", "frontend");
14457419a7fSflorian 	log_procinit("frontend");
14557419a7fSflorian 
14657419a7fSflorian 	if ((ioctlsock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1)
14757419a7fSflorian 		fatal("socket");
14857419a7fSflorian 
14957419a7fSflorian 	if (setgroups(1, &pw->pw_gid) ||
15057419a7fSflorian 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
15157419a7fSflorian 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
15257419a7fSflorian 		fatal("can't drop privileges");
15357419a7fSflorian 
15457419a7fSflorian 	if (pledge("stdio unix recvfd route", NULL) == -1)
15557419a7fSflorian 		fatal("pledge");
15657419a7fSflorian 	event_init();
15757419a7fSflorian 
15857419a7fSflorian 	/* Setup signal handler. */
15957419a7fSflorian 	signal_set(&ev_sigint, SIGINT, frontend_sig_handler, NULL);
16057419a7fSflorian 	signal_set(&ev_sigterm, SIGTERM, frontend_sig_handler, NULL);
16157419a7fSflorian 	signal_add(&ev_sigint, NULL);
16257419a7fSflorian 	signal_add(&ev_sigterm, NULL);
16357419a7fSflorian 	signal(SIGPIPE, SIG_IGN);
16457419a7fSflorian 	signal(SIGHUP, SIG_IGN);
16557419a7fSflorian 
16657419a7fSflorian 	/* Setup pipe and event handler to the parent process. */
16757419a7fSflorian 	if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
16857419a7fSflorian 		fatal(NULL);
16957419a7fSflorian 	imsg_init(&iev_main->ibuf, 3);
17057419a7fSflorian 	iev_main->handler = frontend_dispatch_main;
17157419a7fSflorian 	iev_main->events = EV_READ;
17257419a7fSflorian 	event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
17357419a7fSflorian 	    iev_main->handler, iev_main);
17457419a7fSflorian 	event_add(&iev_main->ev, NULL);
17557419a7fSflorian 
17657419a7fSflorian 	LIST_INIT(&interfaces);
17757419a7fSflorian 	event_dispatch();
17857419a7fSflorian 
17957419a7fSflorian 	frontend_shutdown();
18057419a7fSflorian }
18157419a7fSflorian 
18257419a7fSflorian __dead void
18357419a7fSflorian frontend_shutdown(void)
18457419a7fSflorian {
18557419a7fSflorian 	/* Close pipes. */
18657419a7fSflorian 	msgbuf_write(&iev_engine->ibuf.w);
18757419a7fSflorian 	msgbuf_clear(&iev_engine->ibuf.w);
18857419a7fSflorian 	close(iev_engine->ibuf.fd);
18957419a7fSflorian 	msgbuf_write(&iev_main->ibuf.w);
19057419a7fSflorian 	msgbuf_clear(&iev_main->ibuf.w);
19157419a7fSflorian 	close(iev_main->ibuf.fd);
19257419a7fSflorian 
19357419a7fSflorian 	free(iev_engine);
19457419a7fSflorian 	free(iev_main);
19557419a7fSflorian 
19657419a7fSflorian 	log_info("frontend exiting");
19757419a7fSflorian 	exit(0);
19857419a7fSflorian }
19957419a7fSflorian 
20057419a7fSflorian int
20157419a7fSflorian frontend_imsg_compose_main(int type, pid_t pid, void *data,
20257419a7fSflorian     uint16_t datalen)
20357419a7fSflorian {
20457419a7fSflorian 	return (imsg_compose_event(iev_main, type, 0, pid, -1, data,
20557419a7fSflorian 	    datalen));
20657419a7fSflorian }
20757419a7fSflorian 
20857419a7fSflorian int
20957419a7fSflorian frontend_imsg_compose_engine(int type, uint32_t peerid, pid_t pid,
21057419a7fSflorian     void *data, uint16_t datalen)
21157419a7fSflorian {
21257419a7fSflorian 	return (imsg_compose_event(iev_engine, type, peerid, pid, -1,
21357419a7fSflorian 	    data, datalen));
21457419a7fSflorian }
21557419a7fSflorian 
21657419a7fSflorian void
21757419a7fSflorian frontend_dispatch_main(int fd, short event, void *bula)
21857419a7fSflorian {
21957419a7fSflorian 	struct imsg		 imsg;
22057419a7fSflorian 	struct imsgev		*iev = bula;
22157419a7fSflorian 	struct imsgbuf		*ibuf = &iev->ibuf;
22257419a7fSflorian 	struct iface		*iface;
22357419a7fSflorian 	ssize_t			 n;
22457419a7fSflorian 	int			 shut = 0, bpfsock, if_index, udpsock;
22557419a7fSflorian 
22657419a7fSflorian 	if (event & EV_READ) {
22757419a7fSflorian 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
22857419a7fSflorian 			fatal("imsg_read error");
22957419a7fSflorian 		if (n == 0)	/* Connection closed. */
23057419a7fSflorian 			shut = 1;
23157419a7fSflorian 	}
23257419a7fSflorian 	if (event & EV_WRITE) {
23357419a7fSflorian 		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
23457419a7fSflorian 			fatal("msgbuf_write");
23557419a7fSflorian 		if (n == 0)	/* Connection closed. */
23657419a7fSflorian 			shut = 1;
23757419a7fSflorian 	}
23857419a7fSflorian 
23957419a7fSflorian 	for (;;) {
24057419a7fSflorian 		if ((n = imsg_get(ibuf, &imsg)) == -1)
24157419a7fSflorian 			fatal("%s: imsg_get error", __func__);
24257419a7fSflorian 		if (n == 0)	/* No more messages. */
24357419a7fSflorian 			break;
24457419a7fSflorian 
24557419a7fSflorian 		switch (imsg.hdr.type) {
24657419a7fSflorian 		case IMSG_SOCKET_IPC:
24757419a7fSflorian 			/*
24857419a7fSflorian 			 * Setup pipe and event handler to the engine
24957419a7fSflorian 			 * process.
25057419a7fSflorian 			 */
25157419a7fSflorian 			if (iev_engine)
25257419a7fSflorian 				fatalx("%s: received unexpected imsg fd "
25357419a7fSflorian 				    "to frontend", __func__);
25457419a7fSflorian 
25557419a7fSflorian 			if ((fd = imsg.fd) == -1)
25657419a7fSflorian 				fatalx("%s: expected to receive imsg fd to "
25757419a7fSflorian 				   "frontend but didn't receive any",
25857419a7fSflorian 				   __func__);
25957419a7fSflorian 
26057419a7fSflorian 			iev_engine = malloc(sizeof(struct imsgev));
26157419a7fSflorian 			if (iev_engine == NULL)
26257419a7fSflorian 				fatal(NULL);
26357419a7fSflorian 
26457419a7fSflorian 			imsg_init(&iev_engine->ibuf, fd);
26557419a7fSflorian 			iev_engine->handler = frontend_dispatch_engine;
26657419a7fSflorian 			iev_engine->events = EV_READ;
26757419a7fSflorian 
26857419a7fSflorian 			event_set(&iev_engine->ev, iev_engine->ibuf.fd,
26957419a7fSflorian 			iev_engine->events, iev_engine->handler, iev_engine);
27057419a7fSflorian 			event_add(&iev_engine->ev, NULL);
27157419a7fSflorian 			break;
27257419a7fSflorian 		case IMSG_BPFSOCK:
27357419a7fSflorian 			if ((bpfsock = imsg.fd) == -1)
27457419a7fSflorian 				fatalx("%s: expected to receive imsg "
27557419a7fSflorian 				    "bpf fd but didn't receive any",
27657419a7fSflorian 				    __func__);
27757419a7fSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
27857419a7fSflorian 				fatalx("%s: IMSG_BPFSOCK wrong length: "
27957419a7fSflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
28057419a7fSflorian 			memcpy(&if_index, imsg.data, sizeof(if_index));
28157419a7fSflorian 			set_bpfsock(bpfsock, if_index);
28257419a7fSflorian 			break;
28357419a7fSflorian 		case IMSG_UDPSOCK:
28457419a7fSflorian 			if ((udpsock = imsg.fd) == -1)
28557419a7fSflorian 				fatalx("%s: expected to receive imsg "
28657419a7fSflorian 				    "udpsocket fd but didn't receive any",
28757419a7fSflorian 				    __func__);
28857419a7fSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
28957419a7fSflorian 				fatalx("%s: IMSG_UDPSOCK wrong length: "
29057419a7fSflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
29157419a7fSflorian 			memcpy(&if_index, imsg.data, sizeof(if_index));
29257419a7fSflorian 			if ((iface = get_iface_by_id(if_index)) == NULL) {
29357419a7fSflorian 				close(fd);
29457419a7fSflorian 				break;
29557419a7fSflorian 			}
29657419a7fSflorian 			if (iface->udpsock != -1)
29757419a7fSflorian 				fatalx("%s: received unexpected udpsocket",
29857419a7fSflorian 				    __func__);
29957419a7fSflorian 			iface->udpsock = udpsock;
30057419a7fSflorian 			break;
30157419a7fSflorian 		case IMSG_CLOSE_UDPSOCK:
30257419a7fSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
30357419a7fSflorian 				fatalx("%s: IMSG_UDPSOCK wrong length: "
30457419a7fSflorian 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
30557419a7fSflorian 			memcpy(&if_index, imsg.data, sizeof(if_index));
30657419a7fSflorian 			if ((iface = get_iface_by_id(if_index)) != NULL &&
30757419a7fSflorian 			    iface->udpsock != -1) {
30857419a7fSflorian 				close(iface->udpsock);
30957419a7fSflorian 				iface->udpsock = -1;
31057419a7fSflorian 			}
31157419a7fSflorian 			break;
31257419a7fSflorian 		case IMSG_ROUTESOCK:
31357419a7fSflorian 			if ((fd = imsg.fd) == -1)
31457419a7fSflorian 				fatalx("%s: expected to receive imsg "
31557419a7fSflorian 				    "routesocket fd but didn't receive any",
31657419a7fSflorian 				    __func__);
31757419a7fSflorian 			event_set(&ev_route, fd, EV_READ | EV_PERSIST,
31857419a7fSflorian 			    route_receive, NULL);
31957419a7fSflorian 			break;
32057419a7fSflorian 		case IMSG_STARTUP:
32157419a7fSflorian 			frontend_startup();
32257419a7fSflorian 			break;
32357419a7fSflorian #ifndef	SMALL
32457419a7fSflorian 		case IMSG_CONTROLFD:
32557419a7fSflorian 			if ((fd = imsg.fd) == -1)
32657419a7fSflorian 				fatalx("%s: expected to receive imsg "
32757419a7fSflorian 				    "control fd but didn't receive any",
32857419a7fSflorian 				    __func__);
32957419a7fSflorian 			/* Listen on control socket. */
33057419a7fSflorian 			control_listen(fd);
33157419a7fSflorian 			break;
33257419a7fSflorian 		case IMSG_CTL_END:
33357419a7fSflorian 			control_imsg_relay(&imsg);
33457419a7fSflorian 			break;
33557419a7fSflorian #endif	/* SMALL */
33657419a7fSflorian 		default:
33757419a7fSflorian 			log_debug("%s: error handling imsg %d", __func__,
33857419a7fSflorian 			    imsg.hdr.type);
33957419a7fSflorian 			break;
34057419a7fSflorian 		}
34157419a7fSflorian 		imsg_free(&imsg);
34257419a7fSflorian 	}
34357419a7fSflorian 	if (!shut)
34457419a7fSflorian 		imsg_event_add(iev);
34557419a7fSflorian 	else {
34657419a7fSflorian 		/* This pipe is dead. Remove its event handler. */
34757419a7fSflorian 		event_del(&iev->ev);
34857419a7fSflorian 		event_loopexit(NULL);
34957419a7fSflorian 	}
35057419a7fSflorian }
35157419a7fSflorian 
35257419a7fSflorian void
35357419a7fSflorian frontend_dispatch_engine(int fd, short event, void *bula)
35457419a7fSflorian {
35557419a7fSflorian 	struct imsgev		*iev = bula;
35657419a7fSflorian 	struct imsgbuf		*ibuf = &iev->ibuf;
35757419a7fSflorian 	struct imsg		 imsg;
35857419a7fSflorian 	struct iface		*iface;
35957419a7fSflorian 	ssize_t			 n;
36057419a7fSflorian 	int			 shut = 0;
36157419a7fSflorian 
36257419a7fSflorian 	if (event & EV_READ) {
36357419a7fSflorian 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
36457419a7fSflorian 			fatal("imsg_read error");
36557419a7fSflorian 		if (n == 0)	/* Connection closed. */
36657419a7fSflorian 			shut = 1;
36757419a7fSflorian 	}
36857419a7fSflorian 	if (event & EV_WRITE) {
36957419a7fSflorian 		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
37057419a7fSflorian 			fatal("msgbuf_write");
37157419a7fSflorian 		if (n == 0)	/* Connection closed. */
37257419a7fSflorian 			shut = 1;
37357419a7fSflorian 	}
37457419a7fSflorian 
37557419a7fSflorian 	for (;;) {
37657419a7fSflorian 		if ((n = imsg_get(ibuf, &imsg)) == -1)
37757419a7fSflorian 			fatal("%s: imsg_get error", __func__);
37857419a7fSflorian 		if (n == 0)	/* No more messages. */
37957419a7fSflorian 			break;
38057419a7fSflorian 
38157419a7fSflorian 		switch (imsg.hdr.type) {
38257419a7fSflorian #ifndef	SMALL
38357419a7fSflorian 		case IMSG_CTL_END:
38457419a7fSflorian 		case IMSG_CTL_SHOW_INTERFACE_INFO:
38557419a7fSflorian 			control_imsg_relay(&imsg);
38657419a7fSflorian 			break;
38757419a7fSflorian #endif	/* SMALL */
38857419a7fSflorian 		case IMSG_SEND_DISCOVER: {
38957419a7fSflorian 			struct imsg_req_discover	 imsg_req_discover;
39057419a7fSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_discover))
39157419a7fSflorian 				fatalx("%s: IMSG_SEND_DISCOVER wrong "
39257419a7fSflorian 				    "length: %lu", __func__,
39357419a7fSflorian 				    IMSG_DATA_SIZE(imsg));
39457419a7fSflorian 			memcpy(&imsg_req_discover, imsg.data,
39557419a7fSflorian 			    sizeof(imsg_req_discover));
39657419a7fSflorian 			iface = get_iface_by_id(imsg_req_discover.if_index);
39757419a7fSflorian 			if (iface != NULL) {
39857419a7fSflorian 				iface->xid = imsg_req_discover.xid;
39957419a7fSflorian 				send_discover(iface);
40057419a7fSflorian 			}
40157419a7fSflorian 			break;
40257419a7fSflorian 		}
40357419a7fSflorian 		case IMSG_SEND_REQUEST: {
40457419a7fSflorian 			struct imsg_req_request	 imsg_req_request;
40557419a7fSflorian 			if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_request))
40657419a7fSflorian 				fatalx("%s: IMSG_SEND_REQUEST wrong "
40757419a7fSflorian 				    "length: %lu", __func__,
40857419a7fSflorian 				    IMSG_DATA_SIZE(imsg));
40957419a7fSflorian 			memcpy(&imsg_req_request, imsg.data,
41057419a7fSflorian 			    sizeof(imsg_req_request));
41157419a7fSflorian 			iface = get_iface_by_id(imsg_req_request.if_index);
41257419a7fSflorian 			if (iface != NULL) {
41357419a7fSflorian 				iface->xid = imsg_req_request.xid;
41457419a7fSflorian 				iface->requested_ip.s_addr =
41557419a7fSflorian 				    imsg_req_request.requested_ip.s_addr;
41657419a7fSflorian 				iface->server_identifier.s_addr =
41757419a7fSflorian 				    imsg_req_request.server_identifier.s_addr;
41857419a7fSflorian 				iface->dhcp_server.s_addr =
41957419a7fSflorian 				    imsg_req_request.dhcp_server.s_addr;
42057419a7fSflorian 				send_request(iface);
42157419a7fSflorian 			}
42257419a7fSflorian 			break;
42357419a7fSflorian 		}
42457419a7fSflorian 		default:
42557419a7fSflorian 			log_debug("%s: error handling imsg %d", __func__,
42657419a7fSflorian 			    imsg.hdr.type);
42757419a7fSflorian 			break;
42857419a7fSflorian 		}
42957419a7fSflorian 		imsg_free(&imsg);
43057419a7fSflorian 	}
43157419a7fSflorian 	if (!shut)
43257419a7fSflorian 		imsg_event_add(iev);
43357419a7fSflorian 	else {
43457419a7fSflorian 		/* This pipe is dead. Remove its event handler. */
43557419a7fSflorian 		event_del(&iev->ev);
43657419a7fSflorian 		event_loopexit(NULL);
43757419a7fSflorian 	}
43857419a7fSflorian }
43957419a7fSflorian 
44057419a7fSflorian int
44157419a7fSflorian get_flags(char *if_name)
44257419a7fSflorian {
44357419a7fSflorian 	struct ifreq		 ifr;
44457419a7fSflorian 
44557419a7fSflorian 	strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
44657419a7fSflorian 	if (ioctl(ioctlsock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) {
44757419a7fSflorian 		log_warn("SIOCGIFFLAGS");
44857419a7fSflorian 		return -1;
44957419a7fSflorian 	}
45057419a7fSflorian 	return ifr.ifr_flags;
45157419a7fSflorian }
45257419a7fSflorian 
45357419a7fSflorian int
45457419a7fSflorian get_xflags(char *if_name)
45557419a7fSflorian {
45657419a7fSflorian 	struct ifreq		 ifr;
45757419a7fSflorian 
45857419a7fSflorian 	strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
45957419a7fSflorian 	if (ioctl(ioctlsock, SIOCGIFXFLAGS, (caddr_t)&ifr) == -1) {
46057419a7fSflorian 		log_warn("SIOCGIFXFLAGS");
46157419a7fSflorian 		return -1;
46257419a7fSflorian 	}
46357419a7fSflorian 	return ifr.ifr_flags;
46457419a7fSflorian }
46557419a7fSflorian 
46657419a7fSflorian int
46757419a7fSflorian get_ifrdomain(char *if_name)
46857419a7fSflorian {
46957419a7fSflorian 	struct ifreq		 ifr;
47057419a7fSflorian 
47157419a7fSflorian 	strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
47257419a7fSflorian 	if (ioctl(ioctlsock, SIOCGIFRDOMAIN, (caddr_t)&ifr) == -1) {
47357419a7fSflorian 		log_warn("SIOCGIFRDOMAIN");
47457419a7fSflorian 		return -1;
47557419a7fSflorian 	}
47657419a7fSflorian 	return ifr.ifr_rdomainid;
47757419a7fSflorian }
47857419a7fSflorian 
47957419a7fSflorian void
48057419a7fSflorian update_iface(uint32_t if_index, char* if_name)
48157419a7fSflorian {
48257419a7fSflorian 	struct iface		*iface;
48357419a7fSflorian 	struct imsg_ifinfo	 imsg_ifinfo;
48457419a7fSflorian 	struct ifaddrs		*ifap, *ifa;
48557419a7fSflorian 	struct sockaddr_dl	*sdl;
48657419a7fSflorian 	int			 flags, xflags, ifrdomain;
48757419a7fSflorian 
48857419a7fSflorian 	if ((flags = get_flags(if_name)) == -1 || (xflags =
48957419a7fSflorian 	    get_xflags(if_name)) == -1)
49057419a7fSflorian 		return;
49157419a7fSflorian 
49257419a7fSflorian 	if (!(xflags & IFXF_AUTOCONF4))
49357419a7fSflorian 		return;
49457419a7fSflorian 
49557419a7fSflorian 	if((ifrdomain = get_ifrdomain(if_name)) == -1)
49657419a7fSflorian 		return;
49757419a7fSflorian 
49857419a7fSflorian 	iface = get_iface_by_id(if_index);
49957419a7fSflorian 
50057419a7fSflorian 	if (iface != NULL) {
50157419a7fSflorian 		if (iface->rdomain != ifrdomain) {
50257419a7fSflorian 			iface->rdomain = ifrdomain;
50357419a7fSflorian 			if (iface->udpsock != -1) {
50457419a7fSflorian 				close(iface->udpsock);
50557419a7fSflorian 				iface->udpsock = -1;
50657419a7fSflorian 			}
50757419a7fSflorian 		}
50857419a7fSflorian 	} else {
50957419a7fSflorian 		if ((iface = calloc(1, sizeof(*iface))) == NULL)
51057419a7fSflorian 			fatal("calloc");
51157419a7fSflorian 		iface->if_index = if_index;
51257419a7fSflorian 		iface->rdomain = ifrdomain;
51357419a7fSflorian 		iface->udpsock = -1;
51457419a7fSflorian 		LIST_INSERT_HEAD(&interfaces, iface, entries);
51557419a7fSflorian 		frontend_imsg_compose_main(IMSG_OPEN_BPFSOCK, 0,
51657419a7fSflorian 		    &if_index, sizeof(if_index));
51757419a7fSflorian 	}
51857419a7fSflorian 
51957419a7fSflorian 	memset(&imsg_ifinfo, 0, sizeof(imsg_ifinfo));
52057419a7fSflorian 
52157419a7fSflorian 	imsg_ifinfo.if_index = if_index;
52257419a7fSflorian 	imsg_ifinfo.rdomain = ifrdomain;
52357419a7fSflorian 
52457419a7fSflorian 	imsg_ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP |
52557419a7fSflorian 	    IFF_RUNNING);
52657419a7fSflorian 
52757419a7fSflorian 
52857419a7fSflorian 	if (getifaddrs(&ifap) != 0)
52957419a7fSflorian 		fatal("getifaddrs");
53057419a7fSflorian 
53157419a7fSflorian 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
53257419a7fSflorian 		if (strcmp(if_name, ifa->ifa_name) != 0)
53357419a7fSflorian 			continue;
53457419a7fSflorian 		if (ifa->ifa_addr == NULL)
53557419a7fSflorian 			continue;
53657419a7fSflorian 
53757419a7fSflorian 		switch(ifa->ifa_addr->sa_family) {
53857419a7fSflorian 		case AF_LINK:
53957419a7fSflorian 			imsg_ifinfo.link_state =
54057419a7fSflorian 			    ((struct if_data *)ifa->ifa_data)->ifi_link_state;
54157419a7fSflorian 			sdl = (struct sockaddr_dl *)ifa->ifa_addr;
54257419a7fSflorian 			if (sdl->sdl_type != IFT_ETHER ||
54357419a7fSflorian 			    sdl->sdl_alen != ETHER_ADDR_LEN)
54457419a7fSflorian 				continue;
54557419a7fSflorian 			memcpy(iface->hw_address.ether_addr_octet,
54657419a7fSflorian 			    LLADDR(sdl), ETHER_ADDR_LEN);
54757419a7fSflorian 			goto out;
54857419a7fSflorian 		default:
54957419a7fSflorian 			break;
55057419a7fSflorian 		}
55157419a7fSflorian 	}
55257419a7fSflorian  out:
55357419a7fSflorian 	freeifaddrs(ifap);
55457419a7fSflorian 
55557419a7fSflorian 	memcpy(&imsg_ifinfo.hw_address, &iface->hw_address,
55657419a7fSflorian 	    sizeof(imsg_ifinfo.hw_address));
55757419a7fSflorian 
55857419a7fSflorian 	frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &imsg_ifinfo,
55957419a7fSflorian 	    sizeof(imsg_ifinfo));
56057419a7fSflorian }
56157419a7fSflorian 
56257419a7fSflorian void
56357419a7fSflorian frontend_startup(void)
56457419a7fSflorian {
56557419a7fSflorian 	struct if_nameindex	*ifnidxp, *ifnidx;
56657419a7fSflorian 
56757419a7fSflorian 	if (!event_initialized(&ev_route))
56857419a7fSflorian 		fatalx("%s: did not receive a route socket from the main "
56957419a7fSflorian 		    "process", __func__);
57057419a7fSflorian 
57157419a7fSflorian 	event_add(&ev_route, NULL);
57257419a7fSflorian 
57357419a7fSflorian 	if ((ifnidxp = if_nameindex()) == NULL)
57457419a7fSflorian 		fatalx("if_nameindex");
57557419a7fSflorian 
57657419a7fSflorian 	for(ifnidx = ifnidxp; ifnidx->if_index !=0 && ifnidx->if_name != NULL;
57757419a7fSflorian 	    ifnidx++)
57857419a7fSflorian 		update_iface(ifnidx->if_index, ifnidx->if_name);
57957419a7fSflorian 
58057419a7fSflorian 	if_freenameindex(ifnidxp);
58157419a7fSflorian }
58257419a7fSflorian 
58357419a7fSflorian void
58457419a7fSflorian route_receive(int fd, short events, void *arg)
58557419a7fSflorian {
58657419a7fSflorian 	static uint8_t			 *buf;
58757419a7fSflorian 
58857419a7fSflorian 	struct rt_msghdr		*rtm;
58957419a7fSflorian 	struct sockaddr			*sa, *rti_info[RTAX_MAX];
59057419a7fSflorian 	ssize_t				 n;
59157419a7fSflorian 
59257419a7fSflorian 	if (buf == NULL) {
59357419a7fSflorian 		buf = malloc(ROUTE_SOCKET_BUF_SIZE);
59457419a7fSflorian 		if (buf == NULL)
59557419a7fSflorian 			fatal("malloc");
59657419a7fSflorian 	}
59757419a7fSflorian 	rtm = (struct rt_msghdr *)buf;
59857419a7fSflorian 	if ((n = read(fd, buf, ROUTE_SOCKET_BUF_SIZE)) == -1) {
59957419a7fSflorian 		if (errno == EAGAIN || errno == EINTR)
60057419a7fSflorian 			return;
60157419a7fSflorian 		log_warn("dispatch_rtmsg: read error");
60257419a7fSflorian 		return;
60357419a7fSflorian 	}
60457419a7fSflorian 
60557419a7fSflorian 	if (n == 0)
60657419a7fSflorian 		fatal("routing socket closed");
60757419a7fSflorian 
60857419a7fSflorian 	if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) {
60957419a7fSflorian 		log_warnx("partial rtm of %zd in buffer", n);
61057419a7fSflorian 		return;
61157419a7fSflorian 	}
61257419a7fSflorian 
61357419a7fSflorian 	if (rtm->rtm_version != RTM_VERSION)
61457419a7fSflorian 		return;
61557419a7fSflorian 
61657419a7fSflorian 	sa = (struct sockaddr *)(buf + rtm->rtm_hdrlen);
61757419a7fSflorian 	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
61857419a7fSflorian 
61957419a7fSflorian 	handle_route_message(rtm, rti_info);
62057419a7fSflorian }
62157419a7fSflorian 
62257419a7fSflorian void
62357419a7fSflorian handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info)
62457419a7fSflorian {
62557419a7fSflorian 	struct if_msghdr		*ifm;
62657419a7fSflorian 	int				 xflags, if_index;
627*e998cdbeSflorian 	char				 ifnamebuf[IF_NAMESIZE], *if_name;
62857419a7fSflorian 
62957419a7fSflorian 	switch (rtm->rtm_type) {
63057419a7fSflorian 	case RTM_IFINFO:
63157419a7fSflorian 		ifm = (struct if_msghdr *)rtm;
63257419a7fSflorian 		if_name = if_indextoname(ifm->ifm_index, ifnamebuf);
63357419a7fSflorian 		if (if_name == NULL) {
63457419a7fSflorian 			log_debug("RTM_IFINFO: lost if %d", ifm->ifm_index);
63557419a7fSflorian 			if_index = ifm->ifm_index;
63657419a7fSflorian 			frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
63757419a7fSflorian 			    &if_index, sizeof(if_index));
63857419a7fSflorian 			remove_iface(if_index);
63957419a7fSflorian 		} else {
64057419a7fSflorian 			xflags = get_xflags(if_name);
64157419a7fSflorian 			if (xflags == -1 || !(xflags & IFXF_AUTOCONF4)) {
64257419a7fSflorian 				log_debug("RTM_IFINFO: %s(%d) no(longer) "
64357419a7fSflorian 				   "autoconf4", if_name, ifm->ifm_index);
64457419a7fSflorian 				if_index = ifm->ifm_index;
64557419a7fSflorian 				frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0,
64657419a7fSflorian 				    0, &if_index, sizeof(if_index));
64757419a7fSflorian 			} else {
64857419a7fSflorian 				update_iface(ifm->ifm_index, if_name);
64957419a7fSflorian 			}
65057419a7fSflorian 		}
65157419a7fSflorian 		break;
65257419a7fSflorian 	case RTM_NEWADDR:
65357419a7fSflorian 		ifm = (struct if_msghdr *)rtm;
65457419a7fSflorian 		if_name = if_indextoname(ifm->ifm_index, ifnamebuf);
655*e998cdbeSflorian 		log_debug("RTM_NEWADDR: %s[%u]", if_name == NULL ?
656*e998cdbeSflorian 		    "?" : if_name, ifm->ifm_index);
65757419a7fSflorian 		update_iface(ifm->ifm_index, if_name);
65857419a7fSflorian 		break;
65957419a7fSflorian 	case RTM_PROPOSAL:
66057419a7fSflorian 		if (rtm->rtm_priority == RTP_PROPOSAL_SOLICIT) {
66157419a7fSflorian 			log_debug("RTP_PROPOSAL_SOLICIT");
66257419a7fSflorian 			frontend_imsg_compose_engine(IMSG_REPROPOSE_RDNS,
66357419a7fSflorian 			    0, 0, NULL, 0);
66457419a7fSflorian 		}
66557419a7fSflorian 		break;
66657419a7fSflorian 	default:
66757419a7fSflorian 		log_debug("unexpected RTM: %d", rtm->rtm_type);
66857419a7fSflorian 		break;
66957419a7fSflorian 	}
67057419a7fSflorian 
67157419a7fSflorian }
67257419a7fSflorian 
67357419a7fSflorian #define ROUNDUP(a) \
67457419a7fSflorian 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
67557419a7fSflorian 
67657419a7fSflorian void
67757419a7fSflorian get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
67857419a7fSflorian {
67957419a7fSflorian 	int	i;
68057419a7fSflorian 
68157419a7fSflorian 	for (i = 0; i < RTAX_MAX; i++) {
68257419a7fSflorian 		if (addrs & (1 << i)) {
68357419a7fSflorian 			rti_info[i] = sa;
68457419a7fSflorian 			sa = (struct sockaddr *)((char *)(sa) +
68557419a7fSflorian 			    ROUNDUP(sa->sa_len));
68657419a7fSflorian 		} else
68757419a7fSflorian 			rti_info[i] = NULL;
68857419a7fSflorian 	}
68957419a7fSflorian }
69057419a7fSflorian 
69157419a7fSflorian void
69257419a7fSflorian bpf_receive(int fd, short events, void *arg)
69357419a7fSflorian {
69457419a7fSflorian 	struct bpf_hdr		*hdr;
69557419a7fSflorian 	struct imsg_dhcp	 imsg_dhcp;
69657419a7fSflorian 	struct iface		*iface;
69757419a7fSflorian 	ssize_t			 len, rem;
69857419a7fSflorian 	uint8_t			*p;
69957419a7fSflorian 
70057419a7fSflorian 	iface = (struct iface *)arg;
70157419a7fSflorian 
70257419a7fSflorian 	if ((len = read(fd, iface->bpfev.buf, BPFLEN)) == -1) {
70357419a7fSflorian 		log_warn("read");
70457419a7fSflorian 		return;
70557419a7fSflorian 	}
706*e998cdbeSflorian 
707*e998cdbeSflorian 	if (len == 0)
708*e998cdbeSflorian 		fatal("%s len == 0", __func__);
70957419a7fSflorian 
71057419a7fSflorian 	memset(&imsg_dhcp, 0, sizeof(imsg_dhcp));
71157419a7fSflorian 	imsg_dhcp.if_index = iface->if_index;
71257419a7fSflorian 
71357419a7fSflorian 	rem = len;
71457419a7fSflorian 	p = iface->bpfev.buf;
71557419a7fSflorian 
71657419a7fSflorian 	while (rem > 0) {
71757419a7fSflorian 		if ((size_t)rem < sizeof(*hdr)) {
71857419a7fSflorian 			log_warnx("packet too short");
71957419a7fSflorian 			return;
72057419a7fSflorian 		}
72157419a7fSflorian 		hdr = (struct bpf_hdr *)p;
72257419a7fSflorian 		if (hdr->bh_caplen != hdr->bh_datalen) {
72357419a7fSflorian 			log_warnx("skipping truncated packet");
72457419a7fSflorian 			goto cont;
72557419a7fSflorian 		}
72657419a7fSflorian 		if (rem < hdr->bh_hdrlen + hdr->bh_caplen)
72757419a7fSflorian 			/* we are done */
72857419a7fSflorian 			break;
72957419a7fSflorian 		if (hdr->bh_caplen > sizeof(imsg_dhcp.packet)) {
73057419a7fSflorian 			log_warn("packet too big");
73157419a7fSflorian 			goto cont;
73257419a7fSflorian 		}
73357419a7fSflorian 		memcpy(&imsg_dhcp.packet, p + hdr->bh_hdrlen, hdr->bh_caplen);
73457419a7fSflorian 		imsg_dhcp.len = hdr->bh_caplen;
73557419a7fSflorian 		frontend_imsg_compose_engine(IMSG_DHCP, 0, 0, &imsg_dhcp,
73657419a7fSflorian 		    sizeof(imsg_dhcp));
73757419a7fSflorian  cont:
73857419a7fSflorian 		p += BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
73957419a7fSflorian 		rem -= BPF_WORDALIGN(hdr->bh_hdrlen + hdr->bh_caplen);
74057419a7fSflorian 
74157419a7fSflorian 	}
74257419a7fSflorian }
74357419a7fSflorian 
74457419a7fSflorian ssize_t
74557419a7fSflorian build_packet(uint8_t message_type, uint32_t xid, struct ether_addr *hw_address,
74657419a7fSflorian     struct in_addr *requested_ip, struct in_addr *server_identifier)
74757419a7fSflorian {
74857419a7fSflorian 	static uint8_t	 dhcp_cookie[] = DHCP_COOKIE;
74957419a7fSflorian 	static uint8_t	 dhcp_message_type[] = {DHO_DHCP_MESSAGE_TYPE, 1,
75057419a7fSflorian 		DHCPDISCOVER};
75157419a7fSflorian 	static uint8_t	 dhcp_hostname[255] = {DHO_HOST_NAME, 0 /*, ... */};
75257419a7fSflorian 	static uint8_t	 dhcp_client_id[] = {DHO_DHCP_CLIENT_IDENTIFIER, 7,
75357419a7fSflorian 		HTYPE_ETHER, 0, 0, 0, 0, 0, 0};
75457419a7fSflorian 	static uint8_t	 dhcp_req_list[] = {DHO_DHCP_PARAMETER_REQUEST_LIST,
75557419a7fSflorian 		8, DHO_SUBNET_MASK, DHO_ROUTERS, DHO_DOMAIN_NAME_SERVERS,
75657419a7fSflorian 		DHO_HOST_NAME, DHO_DOMAIN_NAME, DHO_BROADCAST_ADDRESS,
75757419a7fSflorian 		DHO_DOMAIN_SEARCH, DHO_CLASSLESS_STATIC_ROUTES};
75857419a7fSflorian 	static uint8_t	 dhcp_requested_address[] = {DHO_DHCP_REQUESTED_ADDRESS,
75957419a7fSflorian 		4, 0, 0, 0, 0};
76057419a7fSflorian 	static uint8_t	 dhcp_server_identifier[] = {DHO_DHCP_SERVER_IDENTIFIER,
76157419a7fSflorian 		4, 0, 0, 0, 0};
76257419a7fSflorian 	struct dhcp_hdr	*hdr;
76357419a7fSflorian 	uint8_t		*p;
76457419a7fSflorian 	char		*c;
76557419a7fSflorian 
76657419a7fSflorian 	memset(dhcp_packet, 0, sizeof(dhcp_packet));
76757419a7fSflorian 	dhcp_message_type[2] = message_type;
76857419a7fSflorian 	p = dhcp_packet;
76957419a7fSflorian 	hdr = (struct dhcp_hdr *)p;
77057419a7fSflorian 	hdr->op = DHCP_BOOTREQUEST;
77157419a7fSflorian 	hdr->htype = HTYPE_ETHER;
77257419a7fSflorian 	hdr->hlen = 6;
77357419a7fSflorian 	hdr->hops = 0;
77457419a7fSflorian 	hdr->xid = xid;
77557419a7fSflorian 	hdr->secs = 0;
77657419a7fSflorian 	memcpy(hdr->chaddr, hw_address, sizeof(*hw_address));
77757419a7fSflorian 	p += sizeof(struct dhcp_hdr);
77857419a7fSflorian 	memcpy(p, dhcp_cookie, sizeof(dhcp_cookie));
77957419a7fSflorian 	p += sizeof(dhcp_cookie);
78057419a7fSflorian 	memcpy(p, dhcp_message_type, sizeof(dhcp_message_type));
78157419a7fSflorian 	p += sizeof(dhcp_message_type);
78257419a7fSflorian 	if (gethostname(dhcp_hostname + 2, sizeof(dhcp_hostname) - 2) == 0) {
78357419a7fSflorian 		if ((c = strchr(dhcp_hostname + 2, '.')) != NULL)
78457419a7fSflorian 			*c = '\0';
78557419a7fSflorian 		dhcp_hostname[1] = strlen(dhcp_hostname + 2);
78657419a7fSflorian 		memcpy(p, dhcp_hostname, dhcp_hostname[1] + 2);
78757419a7fSflorian 		p += dhcp_hostname[1] + 2;
78857419a7fSflorian 	}
78957419a7fSflorian 	memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address));
79057419a7fSflorian 	memcpy(p, dhcp_client_id, sizeof(dhcp_client_id));
79157419a7fSflorian 	p += sizeof(dhcp_client_id);
79257419a7fSflorian 	memcpy(p, dhcp_req_list, sizeof(dhcp_req_list));
79357419a7fSflorian 	p += sizeof(dhcp_req_list);
79457419a7fSflorian 
79557419a7fSflorian 	if (message_type == DHCPREQUEST) {
79657419a7fSflorian 		memcpy(dhcp_requested_address + 2, requested_ip,
79757419a7fSflorian 		    sizeof(*requested_ip));
79857419a7fSflorian 		memcpy(p, dhcp_requested_address,
79957419a7fSflorian 		    sizeof(dhcp_requested_address));
80057419a7fSflorian 		p += sizeof(dhcp_requested_address);
80157419a7fSflorian 
80257419a7fSflorian 		if (server_identifier->s_addr != INADDR_ANY) {
80357419a7fSflorian 			memcpy(dhcp_server_identifier + 2, server_identifier,
80457419a7fSflorian 			    sizeof(*server_identifier));
80557419a7fSflorian 			memcpy(p, dhcp_server_identifier,
80657419a7fSflorian 			    sizeof(dhcp_server_identifier));
80757419a7fSflorian 			p += sizeof(dhcp_server_identifier);
80857419a7fSflorian 		}
80957419a7fSflorian 	}
81057419a7fSflorian 
81157419a7fSflorian 	*p = DHO_END;
81257419a7fSflorian 	p += 1;
81357419a7fSflorian 
81457419a7fSflorian 	return (p - dhcp_packet);
81557419a7fSflorian }
81657419a7fSflorian 
81757419a7fSflorian void
81857419a7fSflorian send_discover(struct iface *iface)
81957419a7fSflorian {
82057419a7fSflorian 	ssize_t	 pkt_len;
821*e998cdbeSflorian 	char	 ifnamebuf[IF_NAMESIZE], *if_name;
82257419a7fSflorian 
82357419a7fSflorian 	if (!event_initialized(&iface->bpfev.ev)) {
82457419a7fSflorian 		iface->send_discover = 1;
82557419a7fSflorian 		return;
82657419a7fSflorian 	}
82757419a7fSflorian 	iface->send_discover = 0;
828*e998cdbeSflorian 
829*e998cdbeSflorian 	if_name = if_indextoname(iface->if_index, ifnamebuf);
830*e998cdbeSflorian 	log_debug("DHCPDISCOVER on %s", if_name == NULL ? "?" : if_name);
831*e998cdbeSflorian 
83257419a7fSflorian 	pkt_len = build_packet(DHCPDISCOVER, iface->xid, &iface->hw_address,
83357419a7fSflorian 	    &iface->requested_ip, NULL);
83457419a7fSflorian 	bpf_send_packet(iface, dhcp_packet, pkt_len);
83557419a7fSflorian }
83657419a7fSflorian 
83757419a7fSflorian void
83857419a7fSflorian send_request(struct iface *iface)
83957419a7fSflorian {
84057419a7fSflorian 	ssize_t	 pkt_len;
841*e998cdbeSflorian 	char	 ifnamebuf[IF_NAMESIZE], *if_name;
842*e998cdbeSflorian 
843*e998cdbeSflorian 	if_name = if_indextoname(iface->if_index, ifnamebuf);
844*e998cdbeSflorian 	log_debug("DHCPREQUEST on %s", if_name == NULL ? "?" : if_name);
84557419a7fSflorian 
84657419a7fSflorian 	pkt_len = build_packet(DHCPREQUEST, iface->xid, &iface->hw_address,
84757419a7fSflorian 	    &iface->requested_ip, &iface->server_identifier);
84857419a7fSflorian 	if (iface->dhcp_server.s_addr != INADDR_ANY)
84957419a7fSflorian 		udp_send_packet(iface, dhcp_packet, pkt_len);
85057419a7fSflorian 	else
85157419a7fSflorian 		bpf_send_packet(iface, dhcp_packet, pkt_len);
85257419a7fSflorian }
85357419a7fSflorian 
85457419a7fSflorian void
85557419a7fSflorian udp_send_packet(struct iface *iface, uint8_t *packet, ssize_t len)
85657419a7fSflorian {
85757419a7fSflorian 	struct sockaddr_in	to;
85857419a7fSflorian 
85957419a7fSflorian 	memset(&to, 0, sizeof(to));
86057419a7fSflorian 	to.sin_family = AF_INET;
86157419a7fSflorian 	to.sin_len = sizeof(to);
86257419a7fSflorian 	to.sin_addr.s_addr = iface->dhcp_server.s_addr;
86357419a7fSflorian 	to.sin_port = ntohs(SERVER_PORT);
86457419a7fSflorian 
86557419a7fSflorian 	if (sendto(iface->udpsock, packet, len, 0, (struct sockaddr *)&to,
86657419a7fSflorian 	    sizeof(to)) == -1)
86757419a7fSflorian 		log_warn("sendto");
86857419a7fSflorian }
86957419a7fSflorian void
87057419a7fSflorian bpf_send_packet(struct iface *iface, uint8_t *packet, ssize_t len)
87157419a7fSflorian {
87257419a7fSflorian 	struct iovec		 iov[4];
87357419a7fSflorian 	struct ether_header	 eh;
87457419a7fSflorian 	struct ip		 ip;
87557419a7fSflorian 	struct udphdr		 udp;
87657419a7fSflorian 	ssize_t			 total, result;
87757419a7fSflorian 	int			 iovcnt = 0, i;
87857419a7fSflorian 
87957419a7fSflorian 	memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
88057419a7fSflorian 	memcpy(eh.ether_shost, &iface->hw_address, sizeof(eh.ether_dhost));
88157419a7fSflorian 	eh.ether_type = htons(ETHERTYPE_IP);
88257419a7fSflorian 	iov[0].iov_base = &eh;
88357419a7fSflorian 	iov[0].iov_len = sizeof(eh);
88457419a7fSflorian 	iovcnt++;
88557419a7fSflorian 
88657419a7fSflorian 	ip.ip_v = 4;
88757419a7fSflorian 	ip.ip_hl = 5;
88857419a7fSflorian 	ip.ip_tos = IPTOS_LOWDELAY;
88957419a7fSflorian 	ip.ip_len = htons(sizeof(ip) + sizeof(udp) + len);
89057419a7fSflorian 	ip.ip_id = 0;
89157419a7fSflorian 	ip.ip_off = 0;
89257419a7fSflorian 	ip.ip_ttl = 128;
89357419a7fSflorian 	ip.ip_p = IPPROTO_UDP;
89457419a7fSflorian 	ip.ip_sum = 0;
89557419a7fSflorian 	ip.ip_src.s_addr = 0;
89657419a7fSflorian 	ip.ip_dst.s_addr = INADDR_BROADCAST;
89757419a7fSflorian 	ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0));
89857419a7fSflorian 	iov[iovcnt].iov_base = &ip;
89957419a7fSflorian 	iov[iovcnt].iov_len = sizeof(ip);
90057419a7fSflorian 	iovcnt++;
90157419a7fSflorian 
90257419a7fSflorian 	udp.uh_sport = htons(CLIENT_PORT);
90357419a7fSflorian 	udp.uh_dport = htons(SERVER_PORT);
90457419a7fSflorian 	udp.uh_ulen = htons(sizeof(udp) + len);
90557419a7fSflorian 	udp.uh_sum = 0;
90657419a7fSflorian 	udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
90757419a7fSflorian 	    checksum((unsigned char *)packet, len,
90857419a7fSflorian 	    checksum((unsigned char *)&ip.ip_src,
90957419a7fSflorian 	    2 * sizeof(ip.ip_src),
91057419a7fSflorian 	    IPPROTO_UDP + (uint32_t)ntohs(udp.uh_ulen)))));
91157419a7fSflorian 	iov[iovcnt].iov_base = &udp;
91257419a7fSflorian 	iov[iovcnt].iov_len = sizeof(udp);
91357419a7fSflorian 	iovcnt++;
91457419a7fSflorian 
91557419a7fSflorian 	iov[iovcnt].iov_base = packet;
91657419a7fSflorian 	iov[iovcnt].iov_len = len;
91757419a7fSflorian 	iovcnt++;
91857419a7fSflorian 
91957419a7fSflorian 	total = 0;
92057419a7fSflorian 	for (i = 0; i < iovcnt; i++)
92157419a7fSflorian 		total += iov[i].iov_len;
92257419a7fSflorian 
92357419a7fSflorian 	result = writev(EVENT_FD(&iface->bpfev.ev), iov, iovcnt);
92457419a7fSflorian 	if (result == -1)
92557419a7fSflorian 		log_warn("%s: writev", __func__);
92657419a7fSflorian 	else if (result < total) {
92757419a7fSflorian 		log_warnx("%s, writev: %zd of %zd bytes", __func__, result,
92857419a7fSflorian 		    total);
92957419a7fSflorian 	}
93057419a7fSflorian }
93157419a7fSflorian 
93257419a7fSflorian struct iface*
93357419a7fSflorian get_iface_by_id(uint32_t if_index)
93457419a7fSflorian {
93557419a7fSflorian 	struct iface	*iface;
93657419a7fSflorian 
93757419a7fSflorian 	LIST_FOREACH (iface, &interfaces, entries) {
93857419a7fSflorian 		if (iface->if_index == if_index)
93957419a7fSflorian 			return (iface);
94057419a7fSflorian 	}
94157419a7fSflorian 
94257419a7fSflorian 	return (NULL);
94357419a7fSflorian }
94457419a7fSflorian 
94557419a7fSflorian void
94657419a7fSflorian remove_iface(uint32_t if_index)
94757419a7fSflorian {
94857419a7fSflorian 	struct iface	*iface;
94957419a7fSflorian 
95057419a7fSflorian 	iface = get_iface_by_id(if_index);
95157419a7fSflorian 
95257419a7fSflorian 	if (iface == NULL)
95357419a7fSflorian 		return;
95457419a7fSflorian 
95557419a7fSflorian 	LIST_REMOVE(iface, entries);
95657419a7fSflorian 	event_del(&iface->bpfev.ev);
95757419a7fSflorian 	close(EVENT_FD(&iface->bpfev.ev));
95857419a7fSflorian 	if (iface->udpsock != -1)
95957419a7fSflorian 		close(iface->udpsock);
96057419a7fSflorian 	free(iface);
96157419a7fSflorian }
96257419a7fSflorian 
96357419a7fSflorian void
96457419a7fSflorian set_bpfsock(int bpfsock, uint32_t if_index)
96557419a7fSflorian {
96657419a7fSflorian 	struct iface	*iface;
96757419a7fSflorian 
96857419a7fSflorian 	if ((iface = get_iface_by_id(if_index)) == NULL) {
96957419a7fSflorian 		/*
97057419a7fSflorian 		 * The interface disappeared while we were waiting for the
97157419a7fSflorian 		 * parent process to open the raw socket.
97257419a7fSflorian 		 */
97357419a7fSflorian 		close(bpfsock);
97457419a7fSflorian 	} else {
97557419a7fSflorian 		event_set(&iface->bpfev.ev, bpfsock, EV_READ |
97657419a7fSflorian 		    EV_PERSIST, bpf_receive, iface);
97757419a7fSflorian 		event_add(&iface->bpfev.ev, NULL);
97857419a7fSflorian 		if (iface->send_discover)
97957419a7fSflorian 			send_discover(iface);
98057419a7fSflorian 	}
98157419a7fSflorian }
982