xref: /openbsd-src/sbin/dhcpleased/engine.c (revision 0e59d0d19ca6a10a17663d531bcea1b99c1bfe09)
1*0e59d0d1Sclaudio /*	$OpenBSD: engine.c,v 1.55 2024/11/21 13:35:20 claudio Exp $	*/
257419a7fSflorian 
357419a7fSflorian /*
457419a7fSflorian  * Copyright (c) 2017, 2021 Florian Obser <florian@openbsd.org>
557419a7fSflorian  * Copyright (c) 2004, 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 
2257419a7fSflorian #include <sys/types.h>
2357419a7fSflorian #include <sys/queue.h>
2457419a7fSflorian #include <sys/socket.h>
2557419a7fSflorian #include <sys/syslog.h>
2657419a7fSflorian #include <sys/uio.h>
272b86dc95Sjan #include <sys/mbuf.h>
2857419a7fSflorian 
2957419a7fSflorian #include <net/if.h>
3057419a7fSflorian #include <net/route.h>
3157419a7fSflorian #include <arpa/inet.h>
3257419a7fSflorian #include <netinet/in.h>
3357419a7fSflorian #include <netinet/if_ether.h>
3457419a7fSflorian #include <netinet/ip.h>
3557419a7fSflorian #include <netinet/udp.h>
3657419a7fSflorian 
3757419a7fSflorian #include <errno.h>
3857419a7fSflorian #include <event.h>
3957419a7fSflorian #include <imsg.h>
4057419a7fSflorian #include <pwd.h>
4157419a7fSflorian #include <signal.h>
4257419a7fSflorian #include <stddef.h>
430bc729e2Sflorian #include <stdio.h>
4457419a7fSflorian #include <stdlib.h>
4557419a7fSflorian #include <string.h>
4657419a7fSflorian #include <time.h>
4757419a7fSflorian #include <unistd.h>
4857419a7fSflorian #include <vis.h>
4957419a7fSflorian 
5057419a7fSflorian #include "checksum.h"
5157419a7fSflorian #include "log.h"
5257419a7fSflorian #include "dhcpleased.h"
5357419a7fSflorian #include "engine.h"
5457419a7fSflorian 
55acf27097Sflorian /*
56acf27097Sflorian  * RFC 2131 4.1 p23 has "SHOULD be 4 seconds", we are a bit more aggressive,
57acf27097Sflorian  * networks are faster these days.
58acf27097Sflorian  */
59acf27097Sflorian #define	START_EXP_BACKOFF	 1
608baf56acSflorian #define	MAX_EXP_BACKOFF_SLOW	64 /* RFC 2131 4.1 p23 */
618baf56acSflorian #define	MAX_EXP_BACKOFF_FAST	 2
6257419a7fSflorian #define	MINIMUM(a, b)		(((a) < (b)) ? (a) : (b))
6357419a7fSflorian 
6457419a7fSflorian enum if_state {
6557419a7fSflorian 	IF_DOWN,
6657419a7fSflorian 	IF_INIT,
6757419a7fSflorian 	/* IF_SELECTING, */
6857419a7fSflorian 	IF_REQUESTING,
6957419a7fSflorian 	IF_BOUND,
7057419a7fSflorian 	IF_RENEWING,
7157419a7fSflorian 	IF_REBINDING,
7257419a7fSflorian 	/* IF_INIT_REBOOT, */
7357419a7fSflorian 	IF_REBOOTING,
746d7478f0Sflorian 	IF_IPV6_ONLY,
7557419a7fSflorian };
7657419a7fSflorian 
7757419a7fSflorian const char* if_state_name[] = {
7857419a7fSflorian 	"Down",
7957419a7fSflorian 	"Init",
8057419a7fSflorian 	/* "Selecting", */
8157419a7fSflorian 	"Requesting",
8257419a7fSflorian 	"Bound",
8357419a7fSflorian 	"Renewing",
8457419a7fSflorian 	"Rebinding",
8557419a7fSflorian 	/* "Init-Reboot", */
8657419a7fSflorian 	"Rebooting",
876d7478f0Sflorian 	"IPv6 only",
8857419a7fSflorian };
8957419a7fSflorian 
9057419a7fSflorian struct dhcpleased_iface {
9157419a7fSflorian 	LIST_ENTRY(dhcpleased_iface)	 entries;
9257419a7fSflorian 	enum if_state			 state;
9357419a7fSflorian 	struct event			 timer;
9457419a7fSflorian 	struct timeval			 timo;
9557419a7fSflorian 	uint32_t			 if_index;
9657419a7fSflorian 	int				 rdomain;
9757419a7fSflorian 	int				 running;
9857419a7fSflorian 	struct ether_addr		 hw_address;
9957419a7fSflorian 	int				 link_state;
10057419a7fSflorian 	uint32_t			 cur_mtu;
10157419a7fSflorian 	uint32_t			 xid;
10257419a7fSflorian 	struct timespec			 request_time;
10357419a7fSflorian 	struct in_addr			 server_identifier;
10457419a7fSflorian 	struct in_addr			 dhcp_server; /* for unicast */
10557419a7fSflorian 	struct in_addr			 requested_ip;
10657419a7fSflorian 	struct in_addr			 mask;
107ebace80cSflorian 	struct in_addr			 siaddr;
108ebace80cSflorian 	char				 file[4 * DHCP_FILE_LEN + 1];
109ebace80cSflorian 	char				 hostname[4 * 255 + 1];
110ebace80cSflorian 	char				 domainname[4 * 255 + 1];
1116aeacae8Sflorian 	struct dhcp_route		 prev_routes[MAX_DHCP_ROUTES];
1126aeacae8Sflorian 	int				 prev_routes_len;
113351dd593Sflorian 	struct dhcp_route		 routes[MAX_DHCP_ROUTES];
114351dd593Sflorian 	int				 routes_len;
11557419a7fSflorian 	struct in_addr			 nameservers[MAX_RDNS_COUNT];
11657419a7fSflorian 	uint32_t			 lease_time;
11757419a7fSflorian 	uint32_t			 renewal_time;
11857419a7fSflorian 	uint32_t			 rebinding_time;
1196d7478f0Sflorian 	uint32_t			 ipv6_only_time;
12057419a7fSflorian };
12157419a7fSflorian 
12257419a7fSflorian LIST_HEAD(, dhcpleased_iface) dhcpleased_interfaces;
12357419a7fSflorian 
12457419a7fSflorian __dead void		 engine_shutdown(void);
12557419a7fSflorian void			 engine_sig_handler(int sig, short, void *);
12657419a7fSflorian void			 engine_dispatch_frontend(int, short, void *);
12757419a7fSflorian void			 engine_dispatch_main(int, short, void *);
12857419a7fSflorian #ifndef	SMALL
12957419a7fSflorian void			 send_interface_info(struct dhcpleased_iface *, pid_t);
13045c5e5adSflorian void			 engine_showinfo_ctl(pid_t, uint32_t);
13157419a7fSflorian #endif	/* SMALL */
1326e93e3e9Sflorian void			 engine_update_iface(struct imsg_ifinfo *);
13357419a7fSflorian struct dhcpleased_iface	*get_dhcpleased_iface_by_id(uint32_t);
13457419a7fSflorian void			 remove_dhcpleased_iface(uint32_t);
13557419a7fSflorian void			 parse_dhcp(struct dhcpleased_iface *,
13657419a7fSflorian 			     struct imsg_dhcp *);
13757419a7fSflorian void			 state_transition(struct dhcpleased_iface *, enum
13857419a7fSflorian 			     if_state);
13957419a7fSflorian void			 iface_timeout(int, short, void *);
14057419a7fSflorian void			 request_dhcp_discover(struct dhcpleased_iface *);
14157419a7fSflorian void			 request_dhcp_request(struct dhcpleased_iface *);
1420bc729e2Sflorian void			 log_lease(struct dhcpleased_iface *, int);
1430bc729e2Sflorian void			 log_rdns(struct dhcpleased_iface *, int);
14457419a7fSflorian void			 send_configure_interface(struct dhcpleased_iface *);
14557419a7fSflorian void			 send_rdns_proposal(struct dhcpleased_iface *);
14657419a7fSflorian void			 send_deconfigure_interface(struct dhcpleased_iface *);
14757419a7fSflorian void			 send_rdns_withdraw(struct dhcpleased_iface *);
148c2bc6c6dSflorian void			 send_routes_withdraw(struct dhcpleased_iface *);
1496e93e3e9Sflorian void			 parse_lease(struct dhcpleased_iface *,
1506e93e3e9Sflorian 			     struct imsg_ifinfo *);
15157419a7fSflorian int			 engine_imsg_compose_main(int, pid_t, void *, uint16_t);
152e998cdbeSflorian void			 log_dhcp_hdr(struct dhcp_hdr *);
153e998cdbeSflorian const char		*dhcp_message_type2str(uint8_t);
15457419a7fSflorian 
155a41cc082Sflorian #ifndef SMALL
156a41cc082Sflorian struct dhcpleased_conf	*engine_conf;
157a41cc082Sflorian #endif /* SMALL */
158a41cc082Sflorian 
15957419a7fSflorian static struct imsgev	*iev_frontend;
16057419a7fSflorian static struct imsgev	*iev_main;
16157419a7fSflorian int64_t			 proposal_id;
16257419a7fSflorian 
16357419a7fSflorian void
16457419a7fSflorian engine_sig_handler(int sig, short event, void *arg)
16557419a7fSflorian {
16657419a7fSflorian 	/*
16757419a7fSflorian 	 * Normal signal handler rules don't apply because libevent
16857419a7fSflorian 	 * decouples for us.
16957419a7fSflorian 	 */
17057419a7fSflorian 
17157419a7fSflorian 	switch (sig) {
17257419a7fSflorian 	case SIGINT:
17357419a7fSflorian 	case SIGTERM:
17457419a7fSflorian 		engine_shutdown();
17557419a7fSflorian 	default:
17657419a7fSflorian 		fatalx("unexpected signal");
17757419a7fSflorian 	}
17857419a7fSflorian }
17957419a7fSflorian 
18057419a7fSflorian void
18157419a7fSflorian engine(int debug, int verbose)
18257419a7fSflorian {
18357419a7fSflorian 	struct event		 ev_sigint, ev_sigterm;
18457419a7fSflorian 	struct passwd		*pw;
18557419a7fSflorian 
186a41cc082Sflorian #ifndef SMALL
187a41cc082Sflorian 	engine_conf = config_new_empty();
188a41cc082Sflorian #endif /* SMALL */
189a41cc082Sflorian 
19057419a7fSflorian 	log_init(debug, LOG_DAEMON);
19157419a7fSflorian 	log_setverbose(verbose);
19257419a7fSflorian 
19357419a7fSflorian 	if ((pw = getpwnam(DHCPLEASED_USER)) == NULL)
19457419a7fSflorian 		fatal("getpwnam");
19557419a7fSflorian 
19657419a7fSflorian 	if (chdir("/") == -1)
19757419a7fSflorian 		fatal("chdir(\"/\")");
19857419a7fSflorian 
199c7d84514Sflorian 	if (unveil("/", "") == -1)
200bc5a8259Sbeck 		fatal("unveil /");
201c7d84514Sflorian 	if (unveil(NULL, NULL) == -1)
202bc5a8259Sbeck 		fatal("unveil");
203c7d84514Sflorian 
20457419a7fSflorian 	setproctitle("%s", "engine");
20557419a7fSflorian 	log_procinit("engine");
20657419a7fSflorian 
20757419a7fSflorian 	if (setgroups(1, &pw->pw_gid) ||
20857419a7fSflorian 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
20957419a7fSflorian 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
21057419a7fSflorian 		fatal("can't drop privileges");
21157419a7fSflorian 
21257419a7fSflorian 	if (pledge("stdio recvfd", NULL) == -1)
21357419a7fSflorian 		fatal("pledge");
21457419a7fSflorian 
21557419a7fSflorian 	event_init();
21657419a7fSflorian 
21757419a7fSflorian 	/* Setup signal handler(s). */
21857419a7fSflorian 	signal_set(&ev_sigint, SIGINT, engine_sig_handler, NULL);
21957419a7fSflorian 	signal_set(&ev_sigterm, SIGTERM, engine_sig_handler, NULL);
22057419a7fSflorian 	signal_add(&ev_sigint, NULL);
22157419a7fSflorian 	signal_add(&ev_sigterm, NULL);
22257419a7fSflorian 	signal(SIGPIPE, SIG_IGN);
22357419a7fSflorian 	signal(SIGHUP, SIG_IGN);
22457419a7fSflorian 
22557419a7fSflorian 	/* Setup pipe and event handler to the main process. */
22657419a7fSflorian 	if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
22757419a7fSflorian 		fatal(NULL);
22857419a7fSflorian 
229*0e59d0d1Sclaudio 	if (imsgbuf_init(&iev_main->ibuf, 3) == -1)
230*0e59d0d1Sclaudio 		fatal(NULL);
231*0e59d0d1Sclaudio 	imsgbuf_allow_fdpass(&iev_main->ibuf);
23257419a7fSflorian 	iev_main->handler = engine_dispatch_main;
23357419a7fSflorian 
23457419a7fSflorian 	/* Setup event handlers. */
23557419a7fSflorian 	iev_main->events = EV_READ;
23657419a7fSflorian 	event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
23757419a7fSflorian 	    iev_main->handler, iev_main);
23857419a7fSflorian 	event_add(&iev_main->ev, NULL);
23957419a7fSflorian 
24057419a7fSflorian 	LIST_INIT(&dhcpleased_interfaces);
24157419a7fSflorian 
24257419a7fSflorian 	event_dispatch();
24357419a7fSflorian 
24457419a7fSflorian 	engine_shutdown();
24557419a7fSflorian }
24657419a7fSflorian 
24757419a7fSflorian __dead void
24857419a7fSflorian engine_shutdown(void)
24957419a7fSflorian {
25057419a7fSflorian 	/* Close pipes. */
2519cbf9e90Sclaudio 	imsgbuf_clear(&iev_frontend->ibuf);
25257419a7fSflorian 	close(iev_frontend->ibuf.fd);
2539cbf9e90Sclaudio 	imsgbuf_clear(&iev_main->ibuf);
25457419a7fSflorian 	close(iev_main->ibuf.fd);
25557419a7fSflorian 
25657419a7fSflorian 	free(iev_frontend);
25757419a7fSflorian 	free(iev_main);
25857419a7fSflorian 
25957419a7fSflorian 	log_info("engine exiting");
26057419a7fSflorian 	exit(0);
26157419a7fSflorian }
26257419a7fSflorian 
26357419a7fSflorian int
26457419a7fSflorian engine_imsg_compose_frontend(int type, pid_t pid, void *data,
26557419a7fSflorian     uint16_t datalen)
26657419a7fSflorian {
26757419a7fSflorian 	return (imsg_compose_event(iev_frontend, type, 0, pid, -1,
26857419a7fSflorian 	    data, datalen));
26957419a7fSflorian }
27057419a7fSflorian 
27157419a7fSflorian int
27257419a7fSflorian engine_imsg_compose_main(int type, pid_t pid, void *data,
27357419a7fSflorian     uint16_t datalen)
27457419a7fSflorian {
27557419a7fSflorian 	return (imsg_compose_event(iev_main, type, 0, pid, -1,
27657419a7fSflorian 	    data, datalen));
27757419a7fSflorian }
27857419a7fSflorian 
27957419a7fSflorian void
28057419a7fSflorian engine_dispatch_frontend(int fd, short event, void *bula)
28157419a7fSflorian {
28257419a7fSflorian 	struct imsgev			*iev = bula;
28357419a7fSflorian 	struct imsgbuf			*ibuf = &iev->ibuf;
28457419a7fSflorian 	struct imsg			 imsg;
28557419a7fSflorian 	struct dhcpleased_iface		*iface;
28657419a7fSflorian 	ssize_t				 n;
28757419a7fSflorian 	int				 shut = 0;
28857419a7fSflorian #ifndef	SMALL
28957419a7fSflorian 	int				 verbose;
29057419a7fSflorian #endif	/* SMALL */
29145c5e5adSflorian 	uint32_t			 if_index, type;
29257419a7fSflorian 
29357419a7fSflorian 	if (event & EV_READ) {
294668e5ba9Sclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
295dd7efffeSclaudio 			fatal("imsgbuf_read error");
29657419a7fSflorian 		if (n == 0)	/* Connection closed. */
29757419a7fSflorian 			shut = 1;
29857419a7fSflorian 	}
29957419a7fSflorian 	if (event & EV_WRITE) {
300dd7efffeSclaudio 		if (imsgbuf_write(ibuf) == -1) {
301e3b6409cSclaudio 			if (errno == EPIPE)	/* Connection closed. */
30257419a7fSflorian 				shut = 1;
303e3b6409cSclaudio 			else
304dd7efffeSclaudio 				fatal("imsgbuf_write");
305e3b6409cSclaudio 		}
30657419a7fSflorian 	}
30757419a7fSflorian 
30857419a7fSflorian 	for (;;) {
30957419a7fSflorian 		if ((n = imsg_get(ibuf, &imsg)) == -1)
31057419a7fSflorian 			fatal("%s: imsg_get error", __func__);
31157419a7fSflorian 		if (n == 0)	/* No more messages. */
31257419a7fSflorian 			break;
31357419a7fSflorian 
31445c5e5adSflorian 		type = imsg_get_type(&imsg);
31545c5e5adSflorian 
31645c5e5adSflorian 		switch (type) {
31757419a7fSflorian #ifndef	SMALL
31857419a7fSflorian 		case IMSG_CTL_LOG_VERBOSE:
31945c5e5adSflorian 			if (imsg_get_data(&imsg, &verbose,
32045c5e5adSflorian 			    sizeof(verbose)) == -1)
32145c5e5adSflorian 				fatalx("%s: invalid %s", __func__, i2s(type));
32245c5e5adSflorian 
32357419a7fSflorian 			log_setverbose(verbose);
32457419a7fSflorian 			break;
32557419a7fSflorian 		case IMSG_CTL_SHOW_INTERFACE_INFO:
32645c5e5adSflorian 			if (imsg_get_data(&imsg, &if_index,
32745c5e5adSflorian 			    sizeof(if_index)) == -1)
32845c5e5adSflorian 				fatalx("%s: invalid %s", __func__, i2s(type));
32945c5e5adSflorian 
33045c5e5adSflorian 			engine_showinfo_ctl(imsg_get_pid(&imsg), if_index);
33157419a7fSflorian 			break;
332c7313d44Sflorian 		case IMSG_REQUEST_REBOOT:
33345c5e5adSflorian 			if (imsg_get_data(&imsg, &if_index,
33445c5e5adSflorian 			    sizeof(if_index)) == -1)
33545c5e5adSflorian 				fatalx("%s: invalid %s", __func__, i2s(type));
33645c5e5adSflorian 
33757419a7fSflorian 			iface = get_dhcpleased_iface_by_id(if_index);
33857419a7fSflorian 			if (iface != NULL) {
33957419a7fSflorian 				switch (iface->state) {
34057419a7fSflorian 				case IF_DOWN:
34157419a7fSflorian 					break;
34257419a7fSflorian 				case IF_INIT:
34357419a7fSflorian 				case IF_REQUESTING:
3441c0be873Sflorian 					state_transition(iface, iface->state);
3451c0be873Sflorian 					break;
34657419a7fSflorian 				case IF_RENEWING:
34757419a7fSflorian 				case IF_REBINDING:
34857419a7fSflorian 				case IF_REBOOTING:
34957419a7fSflorian 				case IF_BOUND:
3506d7478f0Sflorian 				case IF_IPV6_ONLY:
3511c0be873Sflorian 					state_transition(iface, IF_REBOOTING);
35257419a7fSflorian 					break;
35357419a7fSflorian 				}
35457419a7fSflorian 			}
35557419a7fSflorian 			break;
35657419a7fSflorian #endif	/* SMALL */
35757419a7fSflorian 		case IMSG_REMOVE_IF:
35845c5e5adSflorian 			if (imsg_get_data(&imsg, &if_index,
35945c5e5adSflorian 			    sizeof(if_index)) == -1)
36045c5e5adSflorian 				fatalx("%s: invalid %s", __func__, i2s(type));
36145c5e5adSflorian 
36257419a7fSflorian 			remove_dhcpleased_iface(if_index);
36357419a7fSflorian 			break;
36457419a7fSflorian 		case IMSG_DHCP: {
36557419a7fSflorian 			struct imsg_dhcp	imsg_dhcp;
36645c5e5adSflorian 
36745c5e5adSflorian 			if (imsg_get_data(&imsg, &imsg_dhcp,
36845c5e5adSflorian 			    sizeof(imsg_dhcp)) == -1)
36945c5e5adSflorian 				fatalx("%s: invalid %s", __func__, i2s(type));
37045c5e5adSflorian 
37157419a7fSflorian 			iface = get_dhcpleased_iface_by_id(imsg_dhcp.if_index);
37257419a7fSflorian 			if (iface != NULL)
37357419a7fSflorian 				parse_dhcp(iface, &imsg_dhcp);
37457419a7fSflorian 			break;
37557419a7fSflorian 		}
37657419a7fSflorian 		case IMSG_REPROPOSE_RDNS:
37757419a7fSflorian 			LIST_FOREACH (iface, &dhcpleased_interfaces, entries)
37857419a7fSflorian 				send_rdns_proposal(iface);
37957419a7fSflorian 			break;
38057419a7fSflorian 		default:
38145c5e5adSflorian 			log_debug("%s: unexpected imsg %d", __func__, type);
38257419a7fSflorian 			break;
38357419a7fSflorian 		}
38457419a7fSflorian 		imsg_free(&imsg);
38557419a7fSflorian 	}
38657419a7fSflorian 	if (!shut)
38757419a7fSflorian 		imsg_event_add(iev);
38857419a7fSflorian 	else {
38957419a7fSflorian 		/* This pipe is dead. Remove its event handler. */
39057419a7fSflorian 		event_del(&iev->ev);
39157419a7fSflorian 		event_loopexit(NULL);
39257419a7fSflorian 	}
39357419a7fSflorian }
39457419a7fSflorian 
39557419a7fSflorian void
39657419a7fSflorian engine_dispatch_main(int fd, short event, void *bula)
39757419a7fSflorian {
398a41cc082Sflorian #ifndef SMALL
399a41cc082Sflorian 	static struct dhcpleased_conf	*nconf;
400a41cc082Sflorian 	static struct iface_conf	*iface_conf;
401a41cc082Sflorian #endif /* SMALL */
40257419a7fSflorian 	struct imsg			 imsg;
40357419a7fSflorian 	struct imsgev			*iev = bula;
40457419a7fSflorian 	struct imsgbuf			*ibuf = &iev->ibuf;
40557419a7fSflorian 	struct imsg_ifinfo		 imsg_ifinfo;
40657419a7fSflorian 	ssize_t				 n;
40745c5e5adSflorian 	uint32_t			 type;
40857419a7fSflorian 	int				 shut = 0;
40957419a7fSflorian 
41057419a7fSflorian 	if (event & EV_READ) {
411668e5ba9Sclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
412dd7efffeSclaudio 			fatal("imsgbuf_read error");
41357419a7fSflorian 		if (n == 0)	/* Connection closed. */
41457419a7fSflorian 			shut = 1;
41557419a7fSflorian 	}
41657419a7fSflorian 	if (event & EV_WRITE) {
417dd7efffeSclaudio 		if (imsgbuf_write(ibuf) == -1) {
418e3b6409cSclaudio 			if (errno == EPIPE)	/* Connection closed. */
41957419a7fSflorian 				shut = 1;
420e3b6409cSclaudio 			else
421dd7efffeSclaudio 				fatal("imsgbuf_write");
422e3b6409cSclaudio 		}
42357419a7fSflorian 	}
42457419a7fSflorian 
42557419a7fSflorian 	for (;;) {
42657419a7fSflorian 		if ((n = imsg_get(ibuf, &imsg)) == -1)
42757419a7fSflorian 			fatal("%s: imsg_get error", __func__);
42857419a7fSflorian 		if (n == 0)	/* No more messages. */
42957419a7fSflorian 			break;
43057419a7fSflorian 
43145c5e5adSflorian 		type = imsg_get_type(&imsg);
43245c5e5adSflorian 
43345c5e5adSflorian 		switch (type) {
43457419a7fSflorian 		case IMSG_SOCKET_IPC:
43557419a7fSflorian 			/*
43657419a7fSflorian 			 * Setup pipe and event handler to the frontend
43757419a7fSflorian 			 * process.
43857419a7fSflorian 			 */
43957419a7fSflorian 			if (iev_frontend)
44057419a7fSflorian 				fatalx("%s: received unexpected imsg fd "
44157419a7fSflorian 				    "to engine", __func__);
44257419a7fSflorian 
443dcedc8acSclaudio 			if ((fd = imsg_get_fd(&imsg)) == -1)
44457419a7fSflorian 				fatalx("%s: expected to receive imsg fd to "
44557419a7fSflorian 				   "engine but didn't receive any", __func__);
44657419a7fSflorian 
44757419a7fSflorian 			iev_frontend = malloc(sizeof(struct imsgev));
44857419a7fSflorian 			if (iev_frontend == NULL)
44957419a7fSflorian 				fatal(NULL);
45057419a7fSflorian 
451*0e59d0d1Sclaudio 			if (imsgbuf_init(&iev_frontend->ibuf, fd) == -1)
452*0e59d0d1Sclaudio 				fatal(NULL);
45357419a7fSflorian 			iev_frontend->handler = engine_dispatch_frontend;
45457419a7fSflorian 			iev_frontend->events = EV_READ;
45557419a7fSflorian 
45657419a7fSflorian 			event_set(&iev_frontend->ev, iev_frontend->ibuf.fd,
45757419a7fSflorian 			iev_frontend->events, iev_frontend->handler,
45857419a7fSflorian 			    iev_frontend);
45957419a7fSflorian 			event_add(&iev_frontend->ev, NULL);
4606e93e3e9Sflorian 
4616e93e3e9Sflorian 			if (pledge("stdio", NULL) == -1)
4626e93e3e9Sflorian 				fatal("pledge");
4636e93e3e9Sflorian 
46457419a7fSflorian 			break;
46557419a7fSflorian 		case IMSG_UPDATE_IF:
46645c5e5adSflorian 			if (imsg_get_data(&imsg, &imsg_ifinfo,
46745c5e5adSflorian 			    sizeof(imsg_ifinfo)) == -1)
46845c5e5adSflorian 				fatalx("%s: invalid %s", __func__, i2s(type));
469159ce6f4Sflorian 			if (imsg_ifinfo.lease[LEASE_SIZE - 1] != '\0')
470159ce6f4Sflorian 				fatalx("Invalid lease");
47145c5e5adSflorian 
4726e93e3e9Sflorian 			engine_update_iface(&imsg_ifinfo);
47357419a7fSflorian 			break;
474a41cc082Sflorian #ifndef SMALL
475a41cc082Sflorian 		case IMSG_RECONF_CONF:
476a41cc082Sflorian 			if (nconf != NULL)
477a41cc082Sflorian 				fatalx("%s: IMSG_RECONF_CONF already in "
478a41cc082Sflorian 				    "progress", __func__);
479a41cc082Sflorian 			if ((nconf = malloc(sizeof(struct dhcpleased_conf))) ==
480a41cc082Sflorian 			    NULL)
481a41cc082Sflorian 				fatal(NULL);
482a41cc082Sflorian 			SIMPLEQ_INIT(&nconf->iface_list);
483a41cc082Sflorian 			break;
484a41cc082Sflorian 		case IMSG_RECONF_IFACE:
485a41cc082Sflorian 			if ((iface_conf = malloc(sizeof(struct iface_conf)))
486a41cc082Sflorian 			    == NULL)
487a41cc082Sflorian 				fatal(NULL);
48845c5e5adSflorian 
48945c5e5adSflorian 			if (imsg_get_data(&imsg, iface_conf,
49045c5e5adSflorian 			    sizeof(struct iface_conf)) == -1)
49145c5e5adSflorian 				fatalx("%s: invalid %s", __func__, i2s(type));
49245c5e5adSflorian 
493a41cc082Sflorian 			iface_conf->vc_id = NULL;
494a41cc082Sflorian 			iface_conf->vc_id_len = 0;
495a41cc082Sflorian 			iface_conf->c_id = NULL;
496a41cc082Sflorian 			iface_conf->c_id_len = 0;
497b3441518Sflorian 			iface_conf->h_name = NULL;
498a41cc082Sflorian 			SIMPLEQ_INSERT_TAIL(&nconf->iface_list,
499a41cc082Sflorian 			    iface_conf, entry);
500a41cc082Sflorian 			break;
501a41cc082Sflorian 		case IMSG_RECONF_VC_ID:
502a41cc082Sflorian 			if (iface_conf == NULL)
503f46577a8Sflorian 				fatalx("%s: %s without IMSG_RECONF_IFACE",
504f46577a8Sflorian 				    __func__, i2s(type));
505f46577a8Sflorian 			if (iface_conf->vc_id != NULL)
506f46577a8Sflorian 				fatalx("%s: multiple %s for the same interface",
507f46577a8Sflorian 				    __func__, i2s(type));
50845c5e5adSflorian 			if ((iface_conf->vc_id_len = imsg_get_len(&imsg))
50945c5e5adSflorian 			    > 255 + 2 || iface_conf->vc_id_len == 0)
51045c5e5adSflorian 				fatalx("%s: invalid %s", __func__, i2s(type));
51145c5e5adSflorian 			if ((iface_conf->vc_id = malloc(iface_conf->vc_id_len))
512a41cc082Sflorian 			    == NULL)
513a41cc082Sflorian 				fatal(NULL);
51445c5e5adSflorian 			if (imsg_get_data(&imsg, iface_conf->vc_id,
51545c5e5adSflorian 			    iface_conf->vc_id_len) == -1)
51645c5e5adSflorian 				fatalx("%s: invalid %s", __func__, i2s(type));
517a41cc082Sflorian 			break;
518a41cc082Sflorian 		case IMSG_RECONF_C_ID:
519a41cc082Sflorian 			if (iface_conf == NULL)
520f46577a8Sflorian 				fatalx("%s: %s without IMSG_RECONF_IFACE",
521f46577a8Sflorian 				    __func__, i2s(type));
522f46577a8Sflorian 			if (iface_conf->c_id != NULL)
523f46577a8Sflorian 				fatalx("%s: multiple %s for the same interface",
524f46577a8Sflorian 				    __func__, i2s(type));
52545c5e5adSflorian 			if ((iface_conf->c_id_len = imsg_get_len(&imsg))
52645c5e5adSflorian 			    > 255 + 2 || iface_conf->c_id_len == 0)
52745c5e5adSflorian 				fatalx("%s: invalid %s", __func__, i2s(type));
52845c5e5adSflorian 			if ((iface_conf->c_id = malloc(iface_conf->c_id_len))
529a41cc082Sflorian 			    == NULL)
530a41cc082Sflorian 				fatal(NULL);
53145c5e5adSflorian 			if (imsg_get_data(&imsg, iface_conf->c_id,
53245c5e5adSflorian 			    iface_conf->c_id_len) == -1)
53345c5e5adSflorian 				fatalx("%s: invalid %s", __func__, i2s(type));
534a41cc082Sflorian 			break;
53545c5e5adSflorian 		case IMSG_RECONF_H_NAME: {
53645c5e5adSflorian 			size_t	len;
53745c5e5adSflorian 
538b3441518Sflorian 			if (iface_conf == NULL)
539f46577a8Sflorian 				fatalx("%s: %s without IMSG_RECONF_IFACE",
540f46577a8Sflorian 				    __func__, i2s(type));
541f46577a8Sflorian 			if (iface_conf->h_name != NULL)
542f46577a8Sflorian 				fatalx("%s: multiple %s for the same interface",
543f46577a8Sflorian 				    __func__, i2s(type));
54445c5e5adSflorian 			if ((len = imsg_get_len(&imsg)) > 256 || len == 0)
54545c5e5adSflorian 				fatalx("%s: invalid %s", __func__, i2s(type));
54645c5e5adSflorian 			if ((iface_conf->h_name = malloc(len)) == NULL)
547b3441518Sflorian 				fatal(NULL);
54845c5e5adSflorian 			if (imsg_get_data(&imsg, iface_conf->h_name, len) == -1)
54945c5e5adSflorian 				fatalx("%s: invalid %s", __func__, i2s(type));
55045c5e5adSflorian 			if (iface_conf->h_name[len - 1] != '\0')
55145c5e5adSflorian 				fatalx("Invalid hostname");
552b3441518Sflorian 			break;
55345c5e5adSflorian 		}
554c2bc6c6dSflorian 		case IMSG_RECONF_END: {
555c2bc6c6dSflorian 			struct dhcpleased_iface	*iface;
556c2bc6c6dSflorian 			int			*ifaces;
557c2bc6c6dSflorian 			int			 i, if_index;
558c2bc6c6dSflorian 			char			*if_name;
559c2bc6c6dSflorian 			char			 ifnamebuf[IF_NAMESIZE];
560c2bc6c6dSflorian 
561a41cc082Sflorian 			if (nconf == NULL)
5627e5648d1Sflorian 				fatalx("%s: %s without IMSG_RECONF_CONF",
5637e5648d1Sflorian 				    __func__, i2s(type));
5647e5648d1Sflorian 
565c2bc6c6dSflorian 			ifaces = changed_ifaces(engine_conf, nconf);
566a41cc082Sflorian 			merge_config(engine_conf, nconf);
567a41cc082Sflorian 			nconf = NULL;
568c2bc6c6dSflorian 			for (i = 0; ifaces[i] != 0; i++) {
569c2bc6c6dSflorian 				if_index = ifaces[i];
570c2bc6c6dSflorian 				if_name = if_indextoname(if_index, ifnamebuf);
571c2bc6c6dSflorian 				iface = get_dhcpleased_iface_by_id(if_index);
572c2bc6c6dSflorian 				if (if_name == NULL || iface == NULL)
573c2bc6c6dSflorian 					continue;
574c2bc6c6dSflorian 				iface_conf = find_iface_conf(
575c2bc6c6dSflorian 				    &engine_conf->iface_list, if_name);
576c2bc6c6dSflorian 				if (iface_conf == NULL)
577c2bc6c6dSflorian 					continue;
578c2bc6c6dSflorian 				if (iface_conf->ignore & IGN_DNS)
579c2bc6c6dSflorian 					send_rdns_withdraw(iface);
580c2bc6c6dSflorian 				if (iface_conf->ignore & IGN_ROUTES)
581c2bc6c6dSflorian 					send_routes_withdraw(iface);
582c2bc6c6dSflorian 			}
583c2bc6c6dSflorian 			free(ifaces);
584a41cc082Sflorian 			break;
585c2bc6c6dSflorian 		}
586a41cc082Sflorian #endif /* SMALL */
58757419a7fSflorian 		default:
58845c5e5adSflorian 			log_debug("%s: unexpected imsg %d", __func__, type);
58957419a7fSflorian 			break;
59057419a7fSflorian 		}
59157419a7fSflorian 		imsg_free(&imsg);
59257419a7fSflorian 	}
59357419a7fSflorian 	if (!shut)
59457419a7fSflorian 		imsg_event_add(iev);
59557419a7fSflorian 	else {
59657419a7fSflorian 		/* This pipe is dead. Remove its event handler. */
59757419a7fSflorian 		event_del(&iev->ev);
59857419a7fSflorian 		event_loopexit(NULL);
59957419a7fSflorian 	}
60057419a7fSflorian }
60157419a7fSflorian 
60257419a7fSflorian #ifndef	SMALL
60357419a7fSflorian void
60457419a7fSflorian send_interface_info(struct dhcpleased_iface *iface, pid_t pid)
60557419a7fSflorian {
60657419a7fSflorian 	struct ctl_engine_info	 cei;
60757419a7fSflorian 
60857419a7fSflorian 	memset(&cei, 0, sizeof(cei));
60957419a7fSflorian 	cei.if_index = iface->if_index;
61057419a7fSflorian 	cei.running = iface->running;
61157419a7fSflorian 	cei.link_state = iface->link_state;
61257419a7fSflorian 	strlcpy(cei.state, if_state_name[iface->state], sizeof(cei.state));
61357419a7fSflorian 	memcpy(&cei.request_time, &iface->request_time,
61457419a7fSflorian 	    sizeof(cei.request_time));
615b2b40540Sflorian 	cei.server_identifier = iface->server_identifier;
616b2b40540Sflorian 	cei.dhcp_server = iface->dhcp_server;
617b2b40540Sflorian 	cei.requested_ip = iface->requested_ip;
618b2b40540Sflorian 	cei.mask = iface->mask;
619351dd593Sflorian 	cei.routes_len = iface->routes_len;
620351dd593Sflorian 	memcpy(cei.routes, iface->routes, sizeof(cei.routes));
62157419a7fSflorian 	memcpy(cei.nameservers, iface->nameservers, sizeof(cei.nameservers));
62257419a7fSflorian 	cei.lease_time = iface->lease_time;
62357419a7fSflorian 	cei.renewal_time = iface->renewal_time;
62457419a7fSflorian 	cei.rebinding_time = iface->rebinding_time;
62557419a7fSflorian 	engine_imsg_compose_frontend(IMSG_CTL_SHOW_INTERFACE_INFO, pid, &cei,
62657419a7fSflorian 	    sizeof(cei));
62757419a7fSflorian }
62857419a7fSflorian 
62957419a7fSflorian void
63045c5e5adSflorian engine_showinfo_ctl(pid_t pid, uint32_t if_index)
63157419a7fSflorian {
63257419a7fSflorian 	struct dhcpleased_iface			*iface;
63357419a7fSflorian 
634a86df30dSflorian 	if ((iface = get_dhcpleased_iface_by_id(if_index)) != NULL)
63545c5e5adSflorian 		send_interface_info(iface, pid);
636a86df30dSflorian 	else
63745c5e5adSflorian 		engine_imsg_compose_frontend(IMSG_CTL_END, pid, NULL, 0);
63857419a7fSflorian }
63957419a7fSflorian #endif	/* SMALL */
640e998cdbeSflorian 
64157419a7fSflorian void
6426e93e3e9Sflorian engine_update_iface(struct imsg_ifinfo *imsg_ifinfo)
64357419a7fSflorian {
64457419a7fSflorian 	struct dhcpleased_iface	*iface;
64557419a7fSflorian 	int			 need_refresh = 0;
64657419a7fSflorian 
64757419a7fSflorian 	iface = get_dhcpleased_iface_by_id(imsg_ifinfo->if_index);
64857419a7fSflorian 
64957419a7fSflorian 	if (iface == NULL) {
65057419a7fSflorian 		if ((iface = calloc(1, sizeof(*iface))) == NULL)
65157419a7fSflorian 			fatal("calloc");
65257419a7fSflorian 		iface->state = IF_DOWN;
6539e257558Sflorian 		iface->xid = arc4random();
65457419a7fSflorian 		iface->timo.tv_usec = arc4random_uniform(1000000);
65557419a7fSflorian 		evtimer_set(&iface->timer, iface_timeout, iface);
65657419a7fSflorian 		iface->if_index = imsg_ifinfo->if_index;
65757419a7fSflorian 		iface->rdomain = imsg_ifinfo->rdomain;
65857419a7fSflorian 		iface->running = imsg_ifinfo->running;
65957419a7fSflorian 		iface->link_state = imsg_ifinfo->link_state;
66057419a7fSflorian 		iface->requested_ip.s_addr = INADDR_ANY;
66157419a7fSflorian 		memcpy(&iface->hw_address, &imsg_ifinfo->hw_address,
66257419a7fSflorian 		    sizeof(struct ether_addr));
66357419a7fSflorian 		LIST_INSERT_HEAD(&dhcpleased_interfaces, iface, entries);
66457419a7fSflorian 		need_refresh = 1;
66557419a7fSflorian 	} else {
66657419a7fSflorian 		if (memcmp(&iface->hw_address, &imsg_ifinfo->hw_address,
66757419a7fSflorian 		    sizeof(struct ether_addr)) != 0) {
66857419a7fSflorian 			memcpy(&iface->hw_address, &imsg_ifinfo->hw_address,
66957419a7fSflorian 			    sizeof(struct ether_addr));
67057419a7fSflorian 			need_refresh = 1;
67157419a7fSflorian 		}
67257419a7fSflorian 		if (imsg_ifinfo->rdomain != iface->rdomain) {
67357419a7fSflorian 			iface->rdomain = imsg_ifinfo->rdomain;
67457419a7fSflorian 			need_refresh = 1;
67557419a7fSflorian 		}
67657419a7fSflorian 		if (imsg_ifinfo->running != iface->running) {
67757419a7fSflorian 			iface->running = imsg_ifinfo->running;
67857419a7fSflorian 			need_refresh = 1;
67957419a7fSflorian 		}
68057419a7fSflorian 
68157419a7fSflorian 		if (imsg_ifinfo->link_state != iface->link_state) {
68257419a7fSflorian 			iface->link_state = imsg_ifinfo->link_state;
68357419a7fSflorian 			need_refresh = 1;
68457419a7fSflorian 		}
68557419a7fSflorian 	}
68657419a7fSflorian 
68757419a7fSflorian 	if (!need_refresh)
68857419a7fSflorian 		return;
68957419a7fSflorian 
69057419a7fSflorian 	if (iface->running && LINK_STATE_IS_UP(iface->link_state)) {
69157419a7fSflorian 		if (iface->requested_ip.s_addr == INADDR_ANY)
692b7f83d9aSflorian 			parse_lease(iface, imsg_ifinfo);
693b7f83d9aSflorian 
694b7f83d9aSflorian 		if (iface->requested_ip.s_addr == INADDR_ANY)
69557419a7fSflorian 			state_transition(iface, IF_INIT);
69657419a7fSflorian 		else
69757419a7fSflorian 			state_transition(iface, IF_REBOOTING);
69857419a7fSflorian 	} else
69957419a7fSflorian 		state_transition(iface, IF_DOWN);
70057419a7fSflorian }
70157419a7fSflorian struct dhcpleased_iface*
70257419a7fSflorian get_dhcpleased_iface_by_id(uint32_t if_index)
70357419a7fSflorian {
70457419a7fSflorian 	struct dhcpleased_iface	*iface;
70557419a7fSflorian 	LIST_FOREACH (iface, &dhcpleased_interfaces, entries) {
70657419a7fSflorian 		if (iface->if_index == if_index)
70757419a7fSflorian 			return (iface);
70857419a7fSflorian 	}
70957419a7fSflorian 
71057419a7fSflorian 	return (NULL);
71157419a7fSflorian }
71257419a7fSflorian 
71357419a7fSflorian void
71457419a7fSflorian remove_dhcpleased_iface(uint32_t if_index)
71557419a7fSflorian {
71657419a7fSflorian 	struct dhcpleased_iface	*iface;
71757419a7fSflorian 
71857419a7fSflorian 	iface = get_dhcpleased_iface_by_id(if_index);
71957419a7fSflorian 
72057419a7fSflorian 	if (iface == NULL)
72157419a7fSflorian 		return;
72257419a7fSflorian 
72357419a7fSflorian 	send_rdns_withdraw(iface);
724b7f83d9aSflorian 	send_deconfigure_interface(iface);
72557419a7fSflorian 	LIST_REMOVE(iface, entries);
72657419a7fSflorian 	evtimer_del(&iface->timer);
72757419a7fSflorian 	free(iface);
72857419a7fSflorian }
72957419a7fSflorian 
73057419a7fSflorian void
73157419a7fSflorian parse_dhcp(struct dhcpleased_iface *iface, struct imsg_dhcp *dhcp)
73257419a7fSflorian {
73357419a7fSflorian 	static uint8_t		 cookie[] = DHCP_COOKIE;
73457419a7fSflorian 	static struct ether_addr bcast_mac;
735a41cc082Sflorian #ifndef SMALL
736a41cc082Sflorian 	struct iface_conf	*iface_conf;
737a41cc082Sflorian #endif /* SMALL */
73857419a7fSflorian 	struct ether_header	*eh;
73957419a7fSflorian 	struct ether_addr	 ether_src, ether_dst;
74057419a7fSflorian 	struct ip		*ip;
74157419a7fSflorian 	struct udphdr		*udp;
74257419a7fSflorian 	struct dhcp_hdr		*dhcp_hdr;
743351dd593Sflorian 	struct in_addr		 server_identifier, subnet_mask;
74457419a7fSflorian 	struct in_addr		 nameservers[MAX_RDNS_COUNT];
745351dd593Sflorian 	struct dhcp_route	 routes[MAX_DHCP_ROUTES];
74657419a7fSflorian 	size_t			 rem, i;
74757419a7fSflorian 	uint32_t		 sum, usum, lease_time = 0, renewal_time = 0;
74857419a7fSflorian 	uint32_t		 rebinding_time = 0;
7496d7478f0Sflorian 	uint32_t		 ipv6_only_time = 0;
750945a7accSflorian 	uint8_t			*p, dho = DHO_PAD, dho_len, slen;
75157419a7fSflorian 	uint8_t			 dhcp_message_type = 0;
752bef7dfb5Sflorian 	int			 routes_len = 0, routers = 0, csr = 0;
75357419a7fSflorian 	char			 from[sizeof("xx:xx:xx:xx:xx:xx")];
75457419a7fSflorian 	char			 to[sizeof("xx:xx:xx:xx:xx:xx")];
75557419a7fSflorian 	char			 hbuf_src[INET_ADDRSTRLEN];
75657419a7fSflorian 	char			 hbuf_dst[INET_ADDRSTRLEN];
75757419a7fSflorian 	char			 hbuf[INET_ADDRSTRLEN];
758ebace80cSflorian 	char			 domainname[4 * 255 + 1];
759ebace80cSflorian 	char			 hostname[4 * 255 + 1];
760e998cdbeSflorian 	char			 ifnamebuf[IF_NAMESIZE], *if_name;
76157419a7fSflorian 
76257419a7fSflorian 	if (bcast_mac.ether_addr_octet[0] == 0)
76357419a7fSflorian 		memset(bcast_mac.ether_addr_octet, 0xff, ETHER_ADDR_LEN);
76457419a7fSflorian 
765a41cc082Sflorian 	if_name = if_indextoname(iface->if_index, ifnamebuf);
766a41cc082Sflorian 
767a41cc082Sflorian #ifndef SMALL
768a41cc082Sflorian 	iface_conf = find_iface_conf(&engine_conf->iface_list, if_name);
769a41cc082Sflorian #endif /* SMALL*/
770a41cc082Sflorian 
77157419a7fSflorian 	memset(hbuf_src, 0, sizeof(hbuf_src));
77257419a7fSflorian 	memset(hbuf_dst, 0, sizeof(hbuf_dst));
77357419a7fSflorian 
77457419a7fSflorian 	p = dhcp->packet;
77557419a7fSflorian 	rem = dhcp->len;
77657419a7fSflorian 
77757419a7fSflorian 	if (rem < sizeof(*eh)) {
77857419a7fSflorian 		log_warnx("%s: message too short", __func__);
77957419a7fSflorian 		return;
78057419a7fSflorian 	}
78157419a7fSflorian 	eh = (struct ether_header *)p;
78257419a7fSflorian 	memcpy(ether_src.ether_addr_octet, eh->ether_shost,
78357419a7fSflorian 	    sizeof(ether_src.ether_addr_octet));
78457419a7fSflorian 	strlcpy(from, ether_ntoa(&ether_src), sizeof(from));
78557419a7fSflorian 	memcpy(ether_dst.ether_addr_octet, eh->ether_dhost,
78657419a7fSflorian 	    sizeof(ether_dst.ether_addr_octet));
78757419a7fSflorian 	strlcpy(to, ether_ntoa(&ether_dst), sizeof(to));
78857419a7fSflorian 	p += sizeof(*eh);
78957419a7fSflorian 	rem -= sizeof(*eh);
79057419a7fSflorian 
79157419a7fSflorian 	if (memcmp(&ether_dst, &iface->hw_address, sizeof(ether_dst)) != 0 &&
79257419a7fSflorian 	    memcmp(&ether_dst, &bcast_mac, sizeof(ether_dst)) != 0)
79357419a7fSflorian 		return ; /* silently ignore packet not for us */
79457419a7fSflorian 
79557419a7fSflorian 	if (rem < sizeof(*ip))
79657419a7fSflorian 		goto too_short;
79757419a7fSflorian 
798e998cdbeSflorian 	if (log_getverbose() > 1)
79957419a7fSflorian 		log_debug("%s, from: %s, to: %s", __func__, from, to);
80057419a7fSflorian 
80157419a7fSflorian 	ip = (struct ip *)p;
80257419a7fSflorian 
80357419a7fSflorian 	if (rem < (size_t)ip->ip_hl << 2)
80457419a7fSflorian 		goto too_short;
80557419a7fSflorian 
8062b86dc95Sjan 	if ((dhcp->csumflags & M_IPV4_CSUM_IN_OK) == 0 &&
8072b86dc95Sjan 	    wrapsum(checksum((uint8_t *)ip, ip->ip_hl << 2, 0)) != 0) {
80857419a7fSflorian 		log_warnx("%s: bad IP checksum", __func__);
80957419a7fSflorian 		return;
81057419a7fSflorian 	}
81157419a7fSflorian 	if (rem < ntohs(ip->ip_len))
81257419a7fSflorian 		goto too_short;
81357419a7fSflorian 
81457419a7fSflorian 	p += ip->ip_hl << 2;
81557419a7fSflorian 	rem -= ip->ip_hl << 2;
81657419a7fSflorian 
81757419a7fSflorian 	if (inet_ntop(AF_INET, &ip->ip_src, hbuf_src, sizeof(hbuf_src)) == NULL)
81857419a7fSflorian 		hbuf_src[0] = '\0';
81957419a7fSflorian 	if (inet_ntop(AF_INET, &ip->ip_dst, hbuf_dst, sizeof(hbuf_dst)) == NULL)
820e998cdbeSflorian 		hbuf_dst[0] = '\0';
82157419a7fSflorian 
822c2bc6c6dSflorian #ifndef SMALL
823c2bc6c6dSflorian 	if (iface_conf != NULL) {
824c2bc6c6dSflorian 		for (i = 0; (int)i < iface_conf->ignore_servers_len; i++) {
825c2bc6c6dSflorian 			if (iface_conf->ignore_servers[i].s_addr ==
826c2bc6c6dSflorian 			    ip->ip_src.s_addr) {
827c2bc6c6dSflorian 				log_debug("ignoring server %s", hbuf_src);
828c2bc6c6dSflorian 				return;
829c2bc6c6dSflorian 			}
830c2bc6c6dSflorian 		}
831c2bc6c6dSflorian 	}
832c2bc6c6dSflorian #endif /* SMALL */
833c2bc6c6dSflorian 
83457419a7fSflorian 	if (rem < sizeof(*udp))
83557419a7fSflorian 		goto too_short;
83657419a7fSflorian 
83757419a7fSflorian 	udp = (struct udphdr *)p;
83857419a7fSflorian 	if (rem < ntohs(udp->uh_ulen))
83957419a7fSflorian 		goto too_short;
84057419a7fSflorian 
84157419a7fSflorian 	if (rem > ntohs(udp->uh_ulen)) {
842e998cdbeSflorian 		if (log_getverbose() > 1) {
843e998cdbeSflorian 			log_debug("%s: accepting packet with %lu bytes of data"
844e998cdbeSflorian 			    " after udp payload", __func__, rem -
845e998cdbeSflorian 			    ntohs(udp->uh_ulen));
846e998cdbeSflorian 		}
84757419a7fSflorian 		rem = ntohs(udp->uh_ulen);
84857419a7fSflorian 	}
84957419a7fSflorian 
85057419a7fSflorian 	p += sizeof(*udp);
85157419a7fSflorian 	rem -= sizeof(*udp);
85257419a7fSflorian 
8532b86dc95Sjan 	if ((dhcp->csumflags & M_UDP_CSUM_IN_OK) == 0) {
85457419a7fSflorian 		usum = udp->uh_sum;
85557419a7fSflorian 		udp->uh_sum = 0;
85657419a7fSflorian 
8572b86dc95Sjan 		sum = wrapsum(checksum((uint8_t *)udp, sizeof(*udp),
8582b86dc95Sjan 		    checksum(p, rem,
85957419a7fSflorian 		    checksum((uint8_t *)&ip->ip_src, 2 * sizeof(ip->ip_src),
86057419a7fSflorian 		    IPPROTO_UDP + ntohs(udp->uh_ulen)))));
86157419a7fSflorian 
86257419a7fSflorian 		if (usum != 0 && usum != sum) {
86357419a7fSflorian 			log_warnx("%s: bad UDP checksum", __func__);
86457419a7fSflorian 			return;
86557419a7fSflorian 		}
8662b86dc95Sjan 	}
86757419a7fSflorian 
868e998cdbeSflorian 	if (log_getverbose() > 1) {
869e998cdbeSflorian 		log_debug("%s: %s:%d -> %s:%d", __func__, hbuf_src,
870e998cdbeSflorian 		    ntohs(udp->uh_sport), hbuf_dst, ntohs(udp->uh_dport));
871e998cdbeSflorian 	}
87257419a7fSflorian 
87357419a7fSflorian 	if (rem < sizeof(*dhcp_hdr))
87457419a7fSflorian 		goto too_short;
87557419a7fSflorian 
87657419a7fSflorian 	dhcp_hdr = (struct dhcp_hdr *)p;
87757419a7fSflorian 	p += sizeof(*dhcp_hdr);
87857419a7fSflorian 	rem -= sizeof(*dhcp_hdr);
87957419a7fSflorian 
88057419a7fSflorian 	dhcp_hdr->sname[DHCP_SNAME_LEN -1 ] = '\0'; /* ensure it's a string */
88157419a7fSflorian 	dhcp_hdr->file[DHCP_FILE_LEN -1 ] = '\0'; /* ensure it's a string */
882e998cdbeSflorian 
883e998cdbeSflorian 	if (log_getverbose() > 1)
884e998cdbeSflorian 		log_dhcp_hdr(dhcp_hdr);
88557419a7fSflorian 
88657419a7fSflorian 	if (dhcp_hdr->op != DHCP_BOOTREPLY) {
8876d06d422Stb 		log_warnx("%s: ignoring non-reply packet", __func__);
88857419a7fSflorian 		return;
88957419a7fSflorian 	}
89057419a7fSflorian 
891c2da98a6Sflorian 	if (ntohl(dhcp_hdr->xid) != iface->xid)
89257419a7fSflorian 		return; /* silently ignore wrong xid */
89357419a7fSflorian 
89457419a7fSflorian 	if (rem < sizeof(cookie))
89557419a7fSflorian 		goto too_short;
89657419a7fSflorian 
89757419a7fSflorian 	if (memcmp(p, cookie, sizeof(cookie)) != 0) {
89857419a7fSflorian 		log_warnx("%s: no dhcp cookie in packet from %s", __func__,
89957419a7fSflorian 		    from);
90057419a7fSflorian 		return;
90157419a7fSflorian 	}
90257419a7fSflorian 	p += sizeof(cookie);
90357419a7fSflorian 	rem -= sizeof(cookie);
90457419a7fSflorian 
90557419a7fSflorian 	memset(&server_identifier, 0, sizeof(server_identifier));
90657419a7fSflorian 	memset(&subnet_mask, 0, sizeof(subnet_mask));
907351dd593Sflorian 	memset(&routes, 0, sizeof(routes));
90857419a7fSflorian 	memset(&nameservers, 0, sizeof(nameservers));
909ef603d85Sflorian 	memset(hostname, 0, sizeof(hostname));
910ef603d85Sflorian 	memset(domainname, 0, sizeof(domainname));
91157419a7fSflorian 
91257419a7fSflorian 	while (rem > 0 && dho != DHO_END) {
91357419a7fSflorian 		dho = *p;
91457419a7fSflorian 		p += 1;
91557419a7fSflorian 		rem -= 1;
91657419a7fSflorian 		/* only DHO_END and DHO_PAD are 1 byte long without length */
91757419a7fSflorian 		if (dho == DHO_PAD || dho == DHO_END)
91857419a7fSflorian 			dho_len = 0;
91957419a7fSflorian 		else {
92057419a7fSflorian 			if (rem == 0)
92157419a7fSflorian 				goto too_short; /* missing option length */
92257419a7fSflorian 			dho_len = *p;
92357419a7fSflorian 			p += 1;
92457419a7fSflorian 			rem -= 1;
92557419a7fSflorian 			if (rem < dho_len)
92657419a7fSflorian 				goto too_short;
92757419a7fSflorian 		}
92857419a7fSflorian 
92957419a7fSflorian 		switch (dho) {
93057419a7fSflorian 		case DHO_PAD:
931e998cdbeSflorian 			if (log_getverbose() > 1)
93257419a7fSflorian 				log_debug("DHO_PAD");
93357419a7fSflorian 			break;
93457419a7fSflorian 		case DHO_END:
935e998cdbeSflorian 			if (log_getverbose() > 1)
93657419a7fSflorian 				log_debug("DHO_END");
93757419a7fSflorian 			break;
93857419a7fSflorian 		case DHO_DHCP_MESSAGE_TYPE:
93957419a7fSflorian 			if (dho_len != 1)
94057419a7fSflorian 				goto wrong_length;
94157419a7fSflorian 			dhcp_message_type = *p;
942e998cdbeSflorian 			if (log_getverbose() > 1) {
943e998cdbeSflorian 				log_debug("DHO_DHCP_MESSAGE_TYPE: %s",
944e998cdbeSflorian 				    dhcp_message_type2str(dhcp_message_type));
94557419a7fSflorian 			}
94657419a7fSflorian 			p += dho_len;
94757419a7fSflorian 			rem -= dho_len;
94857419a7fSflorian 			break;
94957419a7fSflorian 		case DHO_DHCP_SERVER_IDENTIFIER:
95057419a7fSflorian 			if (dho_len != sizeof(server_identifier))
95157419a7fSflorian 				goto wrong_length;
95257419a7fSflorian 			memcpy(&server_identifier, p,
95357419a7fSflorian 			    sizeof(server_identifier));
954e998cdbeSflorian 			if (log_getverbose() > 1) {
95557419a7fSflorian 				log_debug("DHO_DHCP_SERVER_IDENTIFIER: %s",
95657419a7fSflorian 				    inet_ntop(AF_INET, &server_identifier,
95757419a7fSflorian 				    hbuf, sizeof(hbuf)));
958e998cdbeSflorian 			}
95957419a7fSflorian 			p += dho_len;
96057419a7fSflorian 			rem -= dho_len;
96157419a7fSflorian 			break;
96257419a7fSflorian 		case DHO_DHCP_LEASE_TIME:
96357419a7fSflorian 			if (dho_len != sizeof(lease_time))
96457419a7fSflorian 				goto wrong_length;
96557419a7fSflorian 			memcpy(&lease_time, p, sizeof(lease_time));
96657419a7fSflorian 			lease_time = ntohl(lease_time);
967e998cdbeSflorian 			if (log_getverbose() > 1) {
968e998cdbeSflorian 				log_debug("DHO_DHCP_LEASE_TIME %us",
969e998cdbeSflorian 				    lease_time);
970e998cdbeSflorian 			}
97157419a7fSflorian 			p += dho_len;
97257419a7fSflorian 			rem -= dho_len;
97357419a7fSflorian 			break;
97457419a7fSflorian 		case DHO_SUBNET_MASK:
97557419a7fSflorian 			if (dho_len != sizeof(subnet_mask))
97657419a7fSflorian 				goto wrong_length;
97757419a7fSflorian 			memcpy(&subnet_mask, p, sizeof(subnet_mask));
978e998cdbeSflorian 			if (log_getverbose() > 1) {
979e998cdbeSflorian 				log_debug("DHO_SUBNET_MASK: %s",
980e998cdbeSflorian 				    inet_ntop(AF_INET, &subnet_mask, hbuf,
981e998cdbeSflorian 				    sizeof(hbuf)));
982e998cdbeSflorian 			}
98357419a7fSflorian 			p += dho_len;
98457419a7fSflorian 			rem -= dho_len;
98557419a7fSflorian 			break;
98657419a7fSflorian 		case DHO_ROUTERS:
987351dd593Sflorian 			if (dho_len < sizeof(routes[routes_len].gw))
98857419a7fSflorian 				goto wrong_length;
989351dd593Sflorian 			if (dho_len % sizeof(routes[routes_len].gw) != 0)
99057419a7fSflorian 				goto wrong_length;
991351dd593Sflorian 
992bef7dfb5Sflorian 			/*
993bef7dfb5Sflorian 			 * Ignore routers option if classless static routes
994bef7dfb5Sflorian 			 * are present (RFC3442).
995bef7dfb5Sflorian 			 */
996bef7dfb5Sflorian 			if (!csr) {
997bef7dfb5Sflorian 				routers = 1;
998bef7dfb5Sflorian 				while (routes_len < MAX_DHCP_ROUTES &&
999bef7dfb5Sflorian 				    dho_len > 0) {
1000351dd593Sflorian 					memcpy(&routes[routes_len].gw, p,
1001351dd593Sflorian 					    sizeof(routes[routes_len].gw));
1002e998cdbeSflorian 					if (log_getverbose() > 1) {
1003351dd593Sflorian 						log_debug("DHO_ROUTER: %s",
1004351dd593Sflorian 						    inet_ntop(AF_INET,
1005bef7dfb5Sflorian 						    &routes[routes_len].gw,
1006bef7dfb5Sflorian 						    hbuf, sizeof(hbuf)));
1007e998cdbeSflorian 					}
1008351dd593Sflorian 					p += sizeof(routes[routes_len].gw);
1009351dd593Sflorian 					rem -= sizeof(routes[routes_len].gw);
1010bef7dfb5Sflorian 					dho_len -=
1011bef7dfb5Sflorian 					    sizeof(routes[routes_len].gw);
1012351dd593Sflorian 					routes_len++;
1013351dd593Sflorian 				}
1014bef7dfb5Sflorian 			}
1015351dd593Sflorian 			if (dho_len != 0) {
1016351dd593Sflorian 				/* ignore > MAX_DHCP_ROUTES routes */
101757419a7fSflorian 				p += dho_len;
101857419a7fSflorian 				rem -= dho_len;
1019351dd593Sflorian 			}
102057419a7fSflorian 			break;
102157419a7fSflorian 		case DHO_DOMAIN_NAME_SERVERS:
102257419a7fSflorian 			if (dho_len < sizeof(nameservers[0]))
102357419a7fSflorian 				goto wrong_length;
102457419a7fSflorian 			if (dho_len % sizeof(nameservers[0]) != 0)
102557419a7fSflorian 				goto wrong_length;
102657419a7fSflorian 			/* we limit ourself to 8 nameservers for proposals */
102757419a7fSflorian 			memcpy(&nameservers, p, MINIMUM(sizeof(nameservers),
102857419a7fSflorian 			    dho_len));
1029e998cdbeSflorian 			if (log_getverbose() > 1) {
1030e998cdbeSflorian 				for (i = 0; i < MINIMUM(sizeof(nameservers),
1031e998cdbeSflorian 				    dho_len / sizeof(nameservers[0])); i++) {
103257419a7fSflorian 					log_debug("DHO_DOMAIN_NAME_SERVERS: %s "
103357419a7fSflorian 					    "(%lu/%lu)", inet_ntop(AF_INET,
1034e998cdbeSflorian 					    &nameservers[i], hbuf,
1035e998cdbeSflorian 					    sizeof(hbuf)), i + 1,
103657419a7fSflorian 					    dho_len / sizeof(nameservers[0]));
103757419a7fSflorian 				}
1038e998cdbeSflorian 			}
103957419a7fSflorian 			p += dho_len;
104057419a7fSflorian 			rem -= dho_len;
104157419a7fSflorian 			break;
1042ebace80cSflorian 		case DHO_HOST_NAME:
1043ef603d85Sflorian 			if (dho_len < 1) {
1044ef603d85Sflorian 				/*
1045ef603d85Sflorian 				 * Protocol violation: minimum length is 1;
1046ef603d85Sflorian 				 * pretend the option is not there
1047ef603d85Sflorian 				 */
1048ef603d85Sflorian 				break;
1049ef603d85Sflorian 			}
1050945a7accSflorian 			/* MUST delete trailing NUL, per RFC 2132 */
1051945a7accSflorian 			slen = dho_len;
1052945a7accSflorian 			while (slen > 0 && p[slen - 1] == '\0')
1053945a7accSflorian 				slen--;
1054ef603d85Sflorian 			/* slen might be 0 here, pretend option is not there. */
1055945a7accSflorian 			strvisx(hostname, p, slen, VIS_SAFE);
1056ebace80cSflorian 			if (log_getverbose() > 1)
1057ebace80cSflorian 				log_debug("DHO_HOST_NAME: %s", hostname);
1058ebace80cSflorian 			p += dho_len;
1059ebace80cSflorian 			rem -= dho_len;
1060ebace80cSflorian 			break;
106157419a7fSflorian 		case DHO_DOMAIN_NAME:
1062ef603d85Sflorian 			if (dho_len < 1) {
1063ef603d85Sflorian 				/*
1064ef603d85Sflorian 				 * Protocol violation: minimum length is 1;
1065ef603d85Sflorian 				 * pretend the option is not there
1066ef603d85Sflorian 				 */
1067ef603d85Sflorian 				break;
1068ef603d85Sflorian 			}
1069945a7accSflorian 			/* MUST delete trailing NUL, per RFC 2132 */
1070945a7accSflorian 			slen = dho_len;
1071945a7accSflorian 			while (slen > 0 && p[slen - 1] == '\0')
1072945a7accSflorian 				slen--;
1073ef603d85Sflorian 			/* slen might be 0 here, pretend option is not there. */
1074945a7accSflorian 			strvisx(domainname, p, slen, VIS_SAFE);
1075ebace80cSflorian 			if (log_getverbose() > 1)
1076ebace80cSflorian 				log_debug("DHO_DOMAIN_NAME: %s", domainname);
107757419a7fSflorian 			p += dho_len;
107857419a7fSflorian 			rem -= dho_len;
107957419a7fSflorian 			break;
108057419a7fSflorian 		case DHO_DHCP_RENEWAL_TIME:
108157419a7fSflorian 			if (dho_len != sizeof(renewal_time))
108257419a7fSflorian 				goto wrong_length;
108357419a7fSflorian 			memcpy(&renewal_time, p, sizeof(renewal_time));
108457419a7fSflorian 			renewal_time = ntohl(renewal_time);
1085e998cdbeSflorian 			if (log_getverbose() > 1) {
1086e998cdbeSflorian 				log_debug("DHO_DHCP_RENEWAL_TIME %us",
1087e998cdbeSflorian 				    renewal_time);
1088e998cdbeSflorian 			}
108957419a7fSflorian 			p += dho_len;
109057419a7fSflorian 			rem -= dho_len;
109157419a7fSflorian 			break;
109257419a7fSflorian 		case DHO_DHCP_REBINDING_TIME:
109357419a7fSflorian 			if (dho_len != sizeof(rebinding_time))
109457419a7fSflorian 				goto wrong_length;
109557419a7fSflorian 			memcpy(&rebinding_time, p, sizeof(rebinding_time));
109657419a7fSflorian 			rebinding_time = ntohl(rebinding_time);
1097e998cdbeSflorian 			if (log_getverbose() > 1) {
109857419a7fSflorian 				log_debug("DHO_DHCP_REBINDING_TIME %us",
109957419a7fSflorian 				    rebinding_time);
1100e998cdbeSflorian 			}
110157419a7fSflorian 			p += dho_len;
110257419a7fSflorian 			rem -= dho_len;
110357419a7fSflorian 			break;
110457419a7fSflorian 		case DHO_DHCP_CLIENT_IDENTIFIER:
110557419a7fSflorian 			/* the server is supposed to echo this back to us */
1106a41cc082Sflorian #ifndef SMALL
1107a41cc082Sflorian 			if (iface_conf != NULL && iface_conf->c_id_len > 0) {
1108a41cc082Sflorian 				if (dho_len != iface_conf->c_id[1]) {
1109a41cc082Sflorian 					log_warnx("wrong "
1110a41cc082Sflorian 					    "DHO_DHCP_CLIENT_IDENTIFIER");
1111a41cc082Sflorian 					return;
1112a41cc082Sflorian 				}
1113a41cc082Sflorian 				if (memcmp(p, &iface_conf->c_id[2], dho_len) !=
1114a41cc082Sflorian 				    0) {
1115a41cc082Sflorian 					log_warnx("wrong "
1116a41cc082Sflorian 					    "DHO_DHCP_CLIENT_IDENTIFIER");
1117a41cc082Sflorian 					return;
1118a41cc082Sflorian 				}
1119a41cc082Sflorian 			} else
1120a41cc082Sflorian #endif /* SMALL */
1121a41cc082Sflorian 			{
112257419a7fSflorian 				if (dho_len != 1 + sizeof(iface->hw_address))
112357419a7fSflorian 					goto wrong_length;
112457419a7fSflorian 				if (*p != HTYPE_ETHER) {
1125a41cc082Sflorian 					log_warnx("DHO_DHCP_CLIENT_IDENTIFIER: "
1126a41cc082Sflorian 					    "wrong type");
112757419a7fSflorian 					return;
112857419a7fSflorian 				}
112957419a7fSflorian 				if (memcmp(p + 1, &iface->hw_address,
113057419a7fSflorian 				    sizeof(iface->hw_address)) != 0) {
1131a41cc082Sflorian 					log_warnx("wrong "
1132a41cc082Sflorian 					    "DHO_DHCP_CLIENT_IDENTIFIER");
113357419a7fSflorian 					return;
113457419a7fSflorian 				}
1135a41cc082Sflorian 			}
113657419a7fSflorian 			p += dho_len;
113757419a7fSflorian 			rem -= dho_len;
113857419a7fSflorian 			break;
1139351dd593Sflorian 		case DHO_CLASSLESS_STATIC_ROUTES: {
1140351dd593Sflorian 			int	prefixlen, compressed_prefixlen;
1141351dd593Sflorian 
1142bef7dfb5Sflorian 			csr = 1;
1143bef7dfb5Sflorian 			if (routers) {
1144bef7dfb5Sflorian 				/*
1145bef7dfb5Sflorian 				 * Ignore routers option if classless static
1146bef7dfb5Sflorian 				 * routes are present (RFC3442).
1147bef7dfb5Sflorian 				 */
1148bef7dfb5Sflorian 				routers = 0;
1149bef7dfb5Sflorian 				routes_len = 0;
1150bef7dfb5Sflorian 			}
1151351dd593Sflorian 			while (routes_len < MAX_DHCP_ROUTES && dho_len > 0) {
1152351dd593Sflorian 				prefixlen = *p;
1153351dd593Sflorian 				p += 1;
1154351dd593Sflorian 				rem -= 1;
1155351dd593Sflorian 				dho_len -= 1;
1156351dd593Sflorian 
1157351dd593Sflorian 				if (prefixlen < 0 || prefixlen > 32) {
1158351dd593Sflorian 					log_warnx("%s: invalid prefixlen: %d",
1159351dd593Sflorian 					    __func__, prefixlen);
1160351dd593Sflorian 					return;
1161351dd593Sflorian 				}
1162351dd593Sflorian 
1163351dd593Sflorian 				if (prefixlen > 0)
1164351dd593Sflorian 					routes[routes_len].mask.s_addr =
1165351dd593Sflorian 					    htonl(0xffffffff << (32 -
1166351dd593Sflorian 						prefixlen));
1167351dd593Sflorian 				else
1168351dd593Sflorian 					routes[routes_len].mask.s_addr =
1169351dd593Sflorian 					    INADDR_ANY;
1170351dd593Sflorian 
1171351dd593Sflorian 				compressed_prefixlen = (prefixlen + 7) / 8;
1172351dd593Sflorian 				if (dho_len < compressed_prefixlen)
1173351dd593Sflorian 					goto wrong_length;
1174351dd593Sflorian 
1175351dd593Sflorian 				memcpy(&routes[routes_len].dst, p,
1176351dd593Sflorian 				    compressed_prefixlen);
1177351dd593Sflorian 				p += compressed_prefixlen;
1178351dd593Sflorian 				rem -= compressed_prefixlen;
1179351dd593Sflorian 				dho_len -= compressed_prefixlen;
1180351dd593Sflorian 
1181351dd593Sflorian 				if (dho_len < sizeof(routes[routes_len].gw))
1182351dd593Sflorian 					goto wrong_length;
1183351dd593Sflorian 
1184351dd593Sflorian 				memcpy(&routes[routes_len].gw, p,
1185351dd593Sflorian 				    sizeof(routes[routes_len].gw));
1186351dd593Sflorian 				p += sizeof(routes[routes_len].gw);
1187351dd593Sflorian 				rem -= sizeof(routes[routes_len].gw);
1188351dd593Sflorian 				dho_len -= sizeof(routes[routes_len].gw);
1189351dd593Sflorian 
1190351dd593Sflorian 				routes_len++;
1191351dd593Sflorian 			}
1192351dd593Sflorian 
1193351dd593Sflorian 			if (dho_len != 0) {
1194351dd593Sflorian 				/* ignore > MAX_DHCP_ROUTES routes */
1195351dd593Sflorian 				p += dho_len;
1196351dd593Sflorian 				rem -= dho_len;
1197351dd593Sflorian 			}
1198351dd593Sflorian 			break;
1199351dd593Sflorian 		}
12006d7478f0Sflorian 		case DHO_IPV6_ONLY_PREFERRED:
12016d7478f0Sflorian 			if (dho_len != sizeof(ipv6_only_time))
12026d7478f0Sflorian 				goto wrong_length;
12036d7478f0Sflorian 			memcpy(&ipv6_only_time, p, sizeof(ipv6_only_time));
12046d7478f0Sflorian 			ipv6_only_time = ntohl(ipv6_only_time);
12056d7478f0Sflorian 			if (log_getverbose() > 1) {
12066d7478f0Sflorian 				log_debug("DHO_IPV6_ONLY_PREFERRED %us",
12076d7478f0Sflorian 				    ipv6_only_time);
12086d7478f0Sflorian 			}
12096d7478f0Sflorian 			p += dho_len;
12106d7478f0Sflorian 			rem -= dho_len;
12116d7478f0Sflorian 			break;
121257419a7fSflorian 		default:
1213e998cdbeSflorian 			if (log_getverbose() > 1)
121457419a7fSflorian 				log_debug("DHO_%u, len: %u", dho, dho_len);
121557419a7fSflorian 			p += dho_len;
121657419a7fSflorian 			rem -= dho_len;
121757419a7fSflorian 		}
121857419a7fSflorian 
121957419a7fSflorian 	}
122057419a7fSflorian 	while (rem != 0) {
122157419a7fSflorian 		if (*p != DHO_PAD)
122257419a7fSflorian 			break;
122357419a7fSflorian 		p++;
122457419a7fSflorian 		rem--;
122557419a7fSflorian 	}
122657419a7fSflorian 	if (rem != 0)
12273efa2e2dStb 		log_debug("%s: %lu bytes garbage data from %s", __func__, rem,
122857419a7fSflorian 		    from);
122957419a7fSflorian 
1230e998cdbeSflorian 	log_debug("%s on %s from %s/%s to %s/%s",
1231e998cdbeSflorian 	    dhcp_message_type2str(dhcp_message_type), if_name == NULL ? "?" :
1232e998cdbeSflorian 	    if_name, from, hbuf_src, to, hbuf_dst);
1233e998cdbeSflorian 
123457419a7fSflorian 	switch (dhcp_message_type) {
123557419a7fSflorian 	case DHCPOFFER:
123657419a7fSflorian 		if (iface->state != IF_INIT) {
123757419a7fSflorian 			log_debug("ignoring unexpected DHCPOFFER");
123857419a7fSflorian 			return;
123957419a7fSflorian 		}
124057419a7fSflorian 		if (server_identifier.s_addr == INADDR_ANY &&
124157419a7fSflorian 		    dhcp_hdr->yiaddr.s_addr == INADDR_ANY) {
124257419a7fSflorian 			log_warnx("%s: did not receive server identifier or "
124357419a7fSflorian 			    "offered IP address", __func__);
124457419a7fSflorian 			return;
124557419a7fSflorian 		}
12466d7478f0Sflorian #ifndef SMALL
12476d7478f0Sflorian 		if (iface_conf != NULL && iface_conf->prefer_ipv6 &&
12486d7478f0Sflorian 		    ipv6_only_time > 0) {
12496d7478f0Sflorian 			iface->ipv6_only_time = ipv6_only_time;
12506d7478f0Sflorian 			state_transition(iface, IF_IPV6_ONLY);
12516d7478f0Sflorian 			break;
12526d7478f0Sflorian 		}
12536d7478f0Sflorian #endif
1254b2b40540Sflorian 		iface->server_identifier = server_identifier;
1255b2b40540Sflorian 		iface->dhcp_server = server_identifier;
1256b2b40540Sflorian 		iface->requested_ip = dhcp_hdr->yiaddr;
125757419a7fSflorian 		state_transition(iface, IF_REQUESTING);
125857419a7fSflorian 		break;
125957419a7fSflorian 	case DHCPACK:
126057419a7fSflorian 		switch (iface->state) {
126157419a7fSflorian 		case IF_REQUESTING:
126257419a7fSflorian 		case IF_RENEWING:
126357419a7fSflorian 		case IF_REBINDING:
126457419a7fSflorian 		case IF_REBOOTING:
126557419a7fSflorian 			break;
126657419a7fSflorian 		default:
126757419a7fSflorian 			log_debug("ignoring unexpected DHCPACK");
126857419a7fSflorian 			return;
126957419a7fSflorian 		}
127057419a7fSflorian 		if (server_identifier.s_addr == INADDR_ANY &&
127157419a7fSflorian 		    dhcp_hdr->yiaddr.s_addr == INADDR_ANY) {
127257419a7fSflorian 			log_warnx("%s: did not receive server identifier or "
127357419a7fSflorian 			    "offered IP address", __func__);
127457419a7fSflorian 			return;
127557419a7fSflorian 		}
127657419a7fSflorian 		if (lease_time == 0) {
127757419a7fSflorian 			log_warnx("%s no lease time from %s", __func__, from);
127857419a7fSflorian 			return;
127957419a7fSflorian 		}
128057419a7fSflorian 		if (subnet_mask.s_addr == INADDR_ANY) {
128157419a7fSflorian 			log_warnx("%s: no subnetmask received from %s",
128257419a7fSflorian 			    __func__, from);
128357419a7fSflorian 			return;
128457419a7fSflorian 		}
128557419a7fSflorian 
12865fbfa9a9Sflorian 		/* Defaults if we didn't receive renewal or rebinding time. */
12875fbfa9a9Sflorian 		if (renewal_time == 0)
12885fbfa9a9Sflorian 			renewal_time = lease_time / 2;
12895fbfa9a9Sflorian 		if (rebinding_time == 0)
12905fbfa9a9Sflorian 			rebinding_time = lease_time - (lease_time / 8);
12915fbfa9a9Sflorian 
129257419a7fSflorian 		/* RFC 2131 4.4.5 */
12932edea3cbSmartijn 		/* Ignore invalid T1/T2 options */
12942edea3cbSmartijn 		if (renewal_time >= rebinding_time) {
12952edea3cbSmartijn 			log_warnx("%s: renewal_time(%u) >= rebinding_time(%u) "
12962edea3cbSmartijn 			    "from %s: using defaults",
12972edea3cbSmartijn 			    __func__, renewal_time, rebinding_time, from);
12982edea3cbSmartijn 			renewal_time = rebinding_time = 0;
12992edea3cbSmartijn 		} else if (rebinding_time >= lease_time) {
13002edea3cbSmartijn 			log_warnx("%s: rebinding_time(%u) >= lease_time(%u) "
13012edea3cbSmartijn 			    "from %s: using defaults",
13022edea3cbSmartijn 			    __func__, rebinding_time, lease_time, from);
13032edea3cbSmartijn 			renewal_time = rebinding_time = 0;
13042edea3cbSmartijn 		}
13052edea3cbSmartijn 
13065fbfa9a9Sflorian 		/* Defaults if we received wrong renewal or rebinding time. */
130757419a7fSflorian 		if (renewal_time == 0)
130857419a7fSflorian 			renewal_time = lease_time / 2;
130957419a7fSflorian 		if (rebinding_time == 0)
131007355dbdSotto 			rebinding_time = lease_time - (lease_time / 8);
131157419a7fSflorian 
131257419a7fSflorian 		clock_gettime(CLOCK_MONOTONIC, &iface->request_time);
1313b2b40540Sflorian 		iface->server_identifier = server_identifier;
1314b2b40540Sflorian 		iface->dhcp_server = server_identifier;
1315b2b40540Sflorian 		iface->requested_ip = dhcp_hdr->yiaddr;
1316b2b40540Sflorian 		iface->mask = subnet_mask;
1317c2bc6c6dSflorian #ifndef SMALL
1318c2bc6c6dSflorian 		if (iface_conf != NULL && iface_conf->ignore & IGN_ROUTES) {
1319c2bc6c6dSflorian 			iface->routes_len = 0;
1320c2bc6c6dSflorian 			memset(iface->routes, 0, sizeof(iface->routes));
1321c2bc6c6dSflorian 		} else
1322c2bc6c6dSflorian #endif /* SMALL */
1323c2bc6c6dSflorian 		{
13246aeacae8Sflorian 			iface->prev_routes_len = iface->routes_len;
13256aeacae8Sflorian 			memcpy(iface->prev_routes, iface->routes,
13266aeacae8Sflorian 			    sizeof(iface->prev_routes));
1327351dd593Sflorian 			iface->routes_len = routes_len;
1328351dd593Sflorian 			memcpy(iface->routes, routes, sizeof(iface->routes));
1329c2bc6c6dSflorian 		}
133057419a7fSflorian 		iface->lease_time = lease_time;
133157419a7fSflorian 		iface->renewal_time = renewal_time;
133257419a7fSflorian 		iface->rebinding_time = rebinding_time;
1333c2bc6c6dSflorian 
1334c2bc6c6dSflorian #ifndef SMALL
1335c2bc6c6dSflorian 		if (iface_conf != NULL && iface_conf->ignore & IGN_DNS) {
1336c2bc6c6dSflorian 			memset(iface->nameservers, 0,
1337c2bc6c6dSflorian 			    sizeof(iface->nameservers));
1338c2bc6c6dSflorian 		} else
1339c2bc6c6dSflorian #endif /* SMALL */
1340c2bc6c6dSflorian 		{
134157419a7fSflorian 			memcpy(iface->nameservers, nameservers,
134257419a7fSflorian 			    sizeof(iface->nameservers));
1343c2bc6c6dSflorian 		}
1344ebace80cSflorian 
1345b2b40540Sflorian 		iface->siaddr = dhcp_hdr->siaddr;
1346ebace80cSflorian 
1347ebace80cSflorian 		/* we made sure this is a string futher up */
1348ebace80cSflorian 		strnvis(iface->file, dhcp_hdr->file, sizeof(iface->file),
1349ebace80cSflorian 		    VIS_SAFE);
1350ebace80cSflorian 
1351ebace80cSflorian 		strlcpy(iface->domainname, domainname,
1352ebace80cSflorian 		    sizeof(iface->domainname));
1353ebace80cSflorian 		strlcpy(iface->hostname, hostname, sizeof(iface->hostname));
13546d7478f0Sflorian #ifndef SMALL
13556d7478f0Sflorian 		if (iface_conf != NULL && iface_conf->prefer_ipv6 &&
13566d7478f0Sflorian 		    ipv6_only_time > 0) {
13576d7478f0Sflorian 			iface->ipv6_only_time = ipv6_only_time;
13586d7478f0Sflorian 			state_transition(iface, IF_IPV6_ONLY);
13596d7478f0Sflorian 			break;
13606d7478f0Sflorian 		}
13616d7478f0Sflorian #endif
136257419a7fSflorian 		state_transition(iface, IF_BOUND);
136357419a7fSflorian 		break;
136457419a7fSflorian 	case DHCPNAK:
136557419a7fSflorian 		switch (iface->state) {
136657419a7fSflorian 		case IF_REQUESTING:
136757419a7fSflorian 		case IF_RENEWING:
136857419a7fSflorian 		case IF_REBINDING:
136957419a7fSflorian 		case IF_REBOOTING:
137057419a7fSflorian 			break;
137157419a7fSflorian 		default:
137257419a7fSflorian 			log_debug("ignoring unexpected DHCPNAK");
137357419a7fSflorian 			return;
137457419a7fSflorian 		}
137557419a7fSflorian 
137657419a7fSflorian 		state_transition(iface, IF_INIT);
137757419a7fSflorian 		break;
137857419a7fSflorian 	default:
137957419a7fSflorian 		log_warnx("%s: unimplemented message type %d", __func__,
138057419a7fSflorian 		    dhcp_message_type);
138157419a7fSflorian 		break;
138257419a7fSflorian 	}
138357419a7fSflorian 	return;
138457419a7fSflorian  too_short:
138557419a7fSflorian 	log_warnx("%s: message from %s too short", __func__, from);
138657419a7fSflorian 	return;
138757419a7fSflorian  wrong_length:
138857419a7fSflorian 	log_warnx("%s: received option %d with wrong length: %d", __func__,
138957419a7fSflorian 	    dho, dho_len);
139057419a7fSflorian 	return;
139157419a7fSflorian }
139257419a7fSflorian 
139357419a7fSflorian /* XXX check valid transitions */
139457419a7fSflorian void
139557419a7fSflorian state_transition(struct dhcpleased_iface *iface, enum if_state new_state)
139657419a7fSflorian {
139757419a7fSflorian 	enum if_state	 old_state = iface->state;
139857419a7fSflorian 	struct timespec	 now, res;
1399e998cdbeSflorian 	char		 ifnamebuf[IF_NAMESIZE], *if_name;
140057419a7fSflorian 
140157419a7fSflorian 	iface->state = new_state;
14029e257558Sflorian 
140357419a7fSflorian 	switch (new_state) {
140457419a7fSflorian 	case IF_DOWN:
140557419a7fSflorian 		if (iface->requested_ip.s_addr == INADDR_ANY) {
140657419a7fSflorian 			/* nothing to do until iface comes up */
140757419a7fSflorian 			iface->timo.tv_sec = -1;
140857419a7fSflorian 			break;
140957419a7fSflorian 		}
141057419a7fSflorian 		if (old_state == IF_DOWN) {
1411b7f83d9aSflorian 			/* nameservers already withdrawn when if went down */
141257419a7fSflorian 			send_deconfigure_interface(iface);
141357419a7fSflorian 			/* nothing more to do until iface comes back */
141457419a7fSflorian 			iface->timo.tv_sec = -1;
141557419a7fSflorian 		} else {
141657419a7fSflorian 			send_rdns_withdraw(iface);
141757419a7fSflorian 			clock_gettime(CLOCK_MONOTONIC, &now);
141857419a7fSflorian 			timespecsub(&now, &iface->request_time, &res);
141957419a7fSflorian 			iface->timo.tv_sec = iface->lease_time - res.tv_sec;
142057419a7fSflorian 			if (iface->timo.tv_sec < 0)
142157419a7fSflorian 				iface->timo.tv_sec = 0; /* deconfigure now */
142257419a7fSflorian 		}
142357419a7fSflorian 		break;
142457419a7fSflorian 	case IF_INIT:
14258baf56acSflorian 		switch (old_state) {
14268baf56acSflorian 		case IF_INIT:
14278baf56acSflorian 			if (iface->timo.tv_sec < MAX_EXP_BACKOFF_SLOW)
14288baf56acSflorian 				iface->timo.tv_sec *= 2;
14298baf56acSflorian 			break;
14308baf56acSflorian 		case IF_REQUESTING:
14318baf56acSflorian 		case IF_RENEWING:
14328baf56acSflorian 		case IF_REBINDING:
14338baf56acSflorian 		case IF_REBOOTING:
14348baf56acSflorian 			/* lease expired, got DHCPNAK or timeout: delete IP */
143557419a7fSflorian 			send_rdns_withdraw(iface);
1436b7f83d9aSflorian 			send_deconfigure_interface(iface);
14378baf56acSflorian 			/* fall through */
14388baf56acSflorian 		case IF_DOWN:
14396d7478f0Sflorian 		case IF_IPV6_ONLY:
1440acf27097Sflorian 			iface->timo.tv_sec = START_EXP_BACKOFF;
1441ee9c6200Sflorian 			iface->xid = arc4random();
14428baf56acSflorian 			break;
14438baf56acSflorian 		case IF_BOUND:
1444c7074a52Sflorian 			fatalx("invalid transition Bound -> Init");
14458baf56acSflorian 			break;
14468baf56acSflorian 		}
144757419a7fSflorian 		request_dhcp_discover(iface);
144857419a7fSflorian 		break;
144957419a7fSflorian 	case IF_REBOOTING:
1450af5ab751Sflorian 		if (old_state == IF_REBOOTING)
145157419a7fSflorian 			iface->timo.tv_sec *= 2;
1452ee9c6200Sflorian 		else {
1453acf27097Sflorian 			iface->timo.tv_sec = START_EXP_BACKOFF;
1454ee9c6200Sflorian 			iface->xid = arc4random();
1455ee9c6200Sflorian 		}
145657419a7fSflorian 		request_dhcp_request(iface);
145757419a7fSflorian 		break;
145857419a7fSflorian 	case IF_REQUESTING:
1459af5ab751Sflorian 		if (old_state == IF_REQUESTING)
146057419a7fSflorian 			iface->timo.tv_sec *= 2;
1461af5ab751Sflorian 		else
1462acf27097Sflorian 			iface->timo.tv_sec = START_EXP_BACKOFF;
146357419a7fSflorian 		request_dhcp_request(iface);
146457419a7fSflorian 		break;
146557419a7fSflorian 	case IF_BOUND:
146657419a7fSflorian 		iface->timo.tv_sec = iface->renewal_time;
146757419a7fSflorian 		if (old_state == IF_REQUESTING || old_state == IF_REBOOTING) {
146857419a7fSflorian 			send_configure_interface(iface);
146957419a7fSflorian 			send_rdns_proposal(iface);
147057419a7fSflorian 		}
147157419a7fSflorian 		break;
147257419a7fSflorian 	case IF_RENEWING:
147357419a7fSflorian 		if (old_state == IF_BOUND) {
147457419a7fSflorian 			iface->timo.tv_sec = (iface->rebinding_time -
147557419a7fSflorian 			    iface->renewal_time) / 2; /* RFC 2131 4.4.5 */
1476ee9c6200Sflorian 			iface->xid = arc4random();
147757419a7fSflorian 		} else
147857419a7fSflorian 			iface->timo.tv_sec /= 2;
147957419a7fSflorian 
148057419a7fSflorian 		if (iface->timo.tv_sec < 60)
148157419a7fSflorian 			iface->timo.tv_sec = 60;
148257419a7fSflorian 		request_dhcp_request(iface);
148357419a7fSflorian 		break;
148457419a7fSflorian 	case IF_REBINDING:
148557419a7fSflorian 		if (old_state == IF_RENEWING) {
148657419a7fSflorian 			iface->timo.tv_sec = (iface->lease_time -
1487aefb9fc2Sflorian 			    iface->rebinding_time) / 2; /* RFC 2131 4.4.5 */
148857419a7fSflorian 		} else
148957419a7fSflorian 			iface->timo.tv_sec /= 2;
149057419a7fSflorian 		request_dhcp_request(iface);
149157419a7fSflorian 		break;
14926d7478f0Sflorian 	case IF_IPV6_ONLY:
14936d7478f0Sflorian 		switch (old_state) {
14946d7478f0Sflorian 		case IF_REQUESTING:
14956d7478f0Sflorian 		case IF_RENEWING:
14966d7478f0Sflorian 		case IF_REBINDING:
14976d7478f0Sflorian 		case IF_REBOOTING:
14986d7478f0Sflorian 			/* going IPv6 only: delete legacy IP */
14996d7478f0Sflorian 			send_rdns_withdraw(iface);
15006d7478f0Sflorian 			send_deconfigure_interface(iface);
15016d7478f0Sflorian 			/* fall through */
15026d7478f0Sflorian 		case IF_INIT:
15036d7478f0Sflorian 		case IF_DOWN:
15046d7478f0Sflorian 		case IF_IPV6_ONLY:
15056d7478f0Sflorian 			iface->timo.tv_sec = iface->ipv6_only_time;
15066d7478f0Sflorian 			break;
15076d7478f0Sflorian 		case IF_BOUND:
1508c7074a52Sflorian 			fatalx("invalid transition Bound -> IPv6 only");
15096d7478f0Sflorian 			break;
15106d7478f0Sflorian 		}
151157419a7fSflorian 	}
1512e998cdbeSflorian 
1513e998cdbeSflorian 	if_name = if_indextoname(iface->if_index, ifnamebuf);
1514e998cdbeSflorian 	log_debug("%s[%s] %s -> %s, timo: %lld", __func__, if_name == NULL ?
1515e998cdbeSflorian 	    "?" : if_name, if_state_name[old_state], if_state_name[new_state],
1516e998cdbeSflorian 	    iface->timo.tv_sec);
1517e998cdbeSflorian 
151857419a7fSflorian 	if (iface->timo.tv_sec == -1) {
151957419a7fSflorian 		if (evtimer_pending(&iface->timer, NULL))
152057419a7fSflorian 			evtimer_del(&iface->timer);
152157419a7fSflorian 	} else
152257419a7fSflorian 		evtimer_add(&iface->timer, &iface->timo);
152357419a7fSflorian }
152457419a7fSflorian 
152557419a7fSflorian void
152657419a7fSflorian iface_timeout(int fd, short events, void *arg)
152757419a7fSflorian {
152857419a7fSflorian 	struct dhcpleased_iface	*iface = (struct dhcpleased_iface *)arg;
152957419a7fSflorian 	struct timespec		 now, res;
153057419a7fSflorian 
153157419a7fSflorian 	log_debug("%s[%d]: %s", __func__, iface->if_index,
153257419a7fSflorian 	    if_state_name[iface->state]);
153357419a7fSflorian 
153457419a7fSflorian 	switch (iface->state) {
153557419a7fSflorian 	case IF_DOWN:
153657419a7fSflorian 		state_transition(iface, IF_DOWN);
153757419a7fSflorian 		break;
153857419a7fSflorian 	case IF_INIT:
153957419a7fSflorian 		state_transition(iface, IF_INIT);
154057419a7fSflorian 		break;
154157419a7fSflorian 	case IF_REBOOTING:
15428baf56acSflorian 		if (iface->timo.tv_sec >= MAX_EXP_BACKOFF_FAST)
154357419a7fSflorian 			state_transition(iface, IF_INIT);
154457419a7fSflorian 		else
154557419a7fSflorian 			state_transition(iface, IF_REBOOTING);
154657419a7fSflorian 		break;
154757419a7fSflorian 	case IF_REQUESTING:
1548c6b4c3c3Sflorian 		if (iface->timo.tv_sec >= MAX_EXP_BACKOFF_SLOW)
154957419a7fSflorian 			state_transition(iface, IF_INIT);
155057419a7fSflorian 		else
155157419a7fSflorian 			state_transition(iface, IF_REQUESTING);
155257419a7fSflorian 		break;
155357419a7fSflorian 	case IF_BOUND:
155457419a7fSflorian 		state_transition(iface, IF_RENEWING);
155557419a7fSflorian 		break;
155657419a7fSflorian 	case IF_RENEWING:
155757419a7fSflorian 		clock_gettime(CLOCK_MONOTONIC, &now);
155857419a7fSflorian 		timespecsub(&now, &iface->request_time, &res);
155957419a7fSflorian 		log_debug("%s: res.tv_sec: %lld, rebinding_time: %u", __func__,
156057419a7fSflorian 		    res.tv_sec, iface->rebinding_time);
1561c5d4eb4eSflorian 		if (res.tv_sec >= iface->rebinding_time)
156257419a7fSflorian 			state_transition(iface, IF_REBINDING);
156357419a7fSflorian 		else
156457419a7fSflorian 			state_transition(iface, IF_RENEWING);
156557419a7fSflorian 		break;
156657419a7fSflorian 	case IF_REBINDING:
156757419a7fSflorian 		clock_gettime(CLOCK_MONOTONIC, &now);
156857419a7fSflorian 		timespecsub(&now, &iface->request_time, &res);
156957419a7fSflorian 		log_debug("%s: res.tv_sec: %lld, lease_time: %u", __func__,
157057419a7fSflorian 		    res.tv_sec, iface->lease_time);
157157419a7fSflorian 		if (res.tv_sec > iface->lease_time)
157257419a7fSflorian 			state_transition(iface, IF_INIT);
157357419a7fSflorian 		else
157457419a7fSflorian 			state_transition(iface, IF_REBINDING);
157557419a7fSflorian 		break;
15766d7478f0Sflorian 	case IF_IPV6_ONLY:
15776d7478f0Sflorian 		state_transition(iface, IF_REQUESTING);
15786d7478f0Sflorian 		break;
157957419a7fSflorian 	}
158057419a7fSflorian }
158157419a7fSflorian 
158257419a7fSflorian void
158357419a7fSflorian request_dhcp_discover(struct dhcpleased_iface *iface)
158457419a7fSflorian {
158582a5f3d7Sflorian 	struct imsg_req_dhcp	 imsg;
158657419a7fSflorian 
158782a5f3d7Sflorian 	memset(&imsg, 0, sizeof(imsg));
158882a5f3d7Sflorian 
158982a5f3d7Sflorian 	imsg.if_index = iface->if_index;
159082a5f3d7Sflorian 	imsg.xid = iface->xid;
159182a5f3d7Sflorian 
159282a5f3d7Sflorian 	/*
159382a5f3d7Sflorian 	 * similar to RFC 2131 4.3.6, Table 4 for DHCPDISCOVER
159482a5f3d7Sflorian 	 * ------------------------------
159582a5f3d7Sflorian 	 * |              | INIT         |
159682a5f3d7Sflorian 	 * ------------------------------
159782a5f3d7Sflorian 	 * |broad/unicast | broadcast    |
159882a5f3d7Sflorian 	 * |server-ip     | MUST NOT     |
159982a5f3d7Sflorian 	 * |requested-ip  | MAY          |
160082a5f3d7Sflorian 	 * |ciaddr        | zero         |
160182a5f3d7Sflorian 	 * ------------------------------
160282a5f3d7Sflorian 	 *
160382a5f3d7Sflorian 	 * Leaving everything at 0 from the memset results in this table with
160482a5f3d7Sflorian 	 * requested-ip not set.
160582a5f3d7Sflorian 	*/
160682a5f3d7Sflorian 
160782a5f3d7Sflorian 	engine_imsg_compose_frontend(IMSG_SEND_DISCOVER, 0, &imsg, sizeof(imsg));
160857419a7fSflorian }
160957419a7fSflorian 
161057419a7fSflorian void
161157419a7fSflorian request_dhcp_request(struct dhcpleased_iface *iface)
161257419a7fSflorian {
161382a5f3d7Sflorian 	struct imsg_req_dhcp	 imsg;
161457419a7fSflorian 
161582a5f3d7Sflorian 	imsg.if_index = iface->if_index;
161682a5f3d7Sflorian 	imsg.xid = iface->xid;
161782a5f3d7Sflorian 
161882a5f3d7Sflorian 	/*
161982a5f3d7Sflorian 	 * RFC 2131 4.3.6, Table 4
162082a5f3d7Sflorian 	 * ---------------------------------------------------------------------
162182a5f3d7Sflorian 	 * |              |REBOOTING    |REQUESTING   |RENEWING     |REBINDING |
162282a5f3d7Sflorian 	 * ---------------------------------------------------------------------
162382a5f3d7Sflorian 	 * |broad/unicast |broadcast    |broadcast    |unicast      |broadcast |
162482a5f3d7Sflorian 	 * |server-ip     |MUST NOT     |MUST         |MUST NOT     |MUST NOT  |
162582a5f3d7Sflorian 	 * |requested-ip  |MUST         |MUST         |MUST NOT     |MUST NOT  |
162682a5f3d7Sflorian 	 * |ciaddr        |zero         |zero         |IP address   |IP address|
162782a5f3d7Sflorian 	 * ---------------------------------------------------------------------
162882a5f3d7Sflorian 	*/
162982a5f3d7Sflorian 	switch (iface->state) {
163082a5f3d7Sflorian 	case IF_DOWN:
163182a5f3d7Sflorian 		fatalx("invalid state IF_DOWN in %s", __func__);
163282a5f3d7Sflorian 		break;
163382a5f3d7Sflorian 	case IF_INIT:
163482a5f3d7Sflorian 		fatalx("invalid state IF_INIT in %s", __func__);
163582a5f3d7Sflorian 		break;
163682a5f3d7Sflorian 	case IF_BOUND:
163782a5f3d7Sflorian 		fatalx("invalid state IF_BOUND in %s", __func__);
163882a5f3d7Sflorian 		break;
163982a5f3d7Sflorian 	case IF_REBOOTING:
164082a5f3d7Sflorian 		imsg.dhcp_server.s_addr = INADDR_ANY;		/* broadcast */
164182a5f3d7Sflorian 		imsg.server_identifier.s_addr = INADDR_ANY;	/* MUST NOT */
164282a5f3d7Sflorian 		imsg.requested_ip = iface->requested_ip;	/* MUST */
164382a5f3d7Sflorian 		imsg.ciaddr.s_addr = INADDR_ANY;		/* zero */
164482a5f3d7Sflorian 		break;
164582a5f3d7Sflorian 	case IF_REQUESTING:
164682a5f3d7Sflorian 		imsg.dhcp_server.s_addr = INADDR_ANY;		/* broadcast */
164782a5f3d7Sflorian 		imsg.server_identifier =
164882a5f3d7Sflorian 		    iface->server_identifier;			/* MUST */
164982a5f3d7Sflorian 		imsg.requested_ip = iface->requested_ip;	/* MUST */
165082a5f3d7Sflorian 		imsg.ciaddr.s_addr = INADDR_ANY;		/* zero */
165182a5f3d7Sflorian 		break;
165282a5f3d7Sflorian 	case IF_RENEWING:
165382a5f3d7Sflorian 		imsg.dhcp_server = iface->dhcp_server;		/* unicast */
165482a5f3d7Sflorian 		imsg.server_identifier.s_addr = INADDR_ANY;	/* MUST NOT */
165582a5f3d7Sflorian 		imsg.requested_ip.s_addr = INADDR_ANY;		/* MUST NOT */
165682a5f3d7Sflorian 		imsg.ciaddr = iface->requested_ip;		/* IP address */
165782a5f3d7Sflorian 		break;
165882a5f3d7Sflorian 	case IF_REBINDING:
165982a5f3d7Sflorian 		imsg.dhcp_server.s_addr = INADDR_ANY;		/* broadcast */
166082a5f3d7Sflorian 		imsg.server_identifier.s_addr = INADDR_ANY;	/* MUST NOT */
166182a5f3d7Sflorian 		imsg.requested_ip.s_addr = INADDR_ANY;		/* MUST NOT */
166282a5f3d7Sflorian 		imsg.ciaddr = iface->requested_ip;		/* IP address */
166382a5f3d7Sflorian 		break;
16646d7478f0Sflorian 	case IF_IPV6_ONLY:
16656d7478f0Sflorian 		fatalx("invalid state IF_IPV6_ONLY in %s", __func__);
16666d7478f0Sflorian 		break;
166782a5f3d7Sflorian 	}
166882a5f3d7Sflorian 
166982a5f3d7Sflorian 	engine_imsg_compose_frontend(IMSG_SEND_REQUEST, 0, &imsg, sizeof(imsg));
167057419a7fSflorian }
167157419a7fSflorian 
167257419a7fSflorian void
16730bc729e2Sflorian log_lease(struct dhcpleased_iface *iface, int deconfigure)
16740bc729e2Sflorian {
16750bc729e2Sflorian 	char	 hbuf_lease[INET_ADDRSTRLEN], hbuf_server[INET_ADDRSTRLEN];
1676e998cdbeSflorian 	char	 ifnamebuf[IF_NAMESIZE], *if_name;
16770bc729e2Sflorian 
1678e998cdbeSflorian 	if_name = if_indextoname(iface->if_index, ifnamebuf);
16790bc729e2Sflorian 	inet_ntop(AF_INET, &iface->requested_ip, hbuf_lease,
16800bc729e2Sflorian 	    sizeof(hbuf_lease));
16810bc729e2Sflorian 	inet_ntop(AF_INET, &iface->server_identifier, hbuf_server,
16820bc729e2Sflorian 	    sizeof(hbuf_server));
16830bc729e2Sflorian 
16840bc729e2Sflorian 
16850bc729e2Sflorian 	if (deconfigure)
16860bc729e2Sflorian 		log_info("deleting %s from %s (lease from %s)", hbuf_lease,
1687e998cdbeSflorian 		    if_name == NULL ? "?" : if_name, hbuf_server);
16880bc729e2Sflorian 	else
16890bc729e2Sflorian 		log_info("adding %s to %s (lease from %s)", hbuf_lease,
1690e998cdbeSflorian 		    if_name == NULL ? "?" : if_name, hbuf_server);
16910bc729e2Sflorian }
16920bc729e2Sflorian 
16930bc729e2Sflorian void
169457419a7fSflorian send_configure_interface(struct dhcpleased_iface *iface)
169557419a7fSflorian {
169657419a7fSflorian 	struct imsg_configure_interface	 imsg;
16976aeacae8Sflorian 	int				 i, j, found;
169857419a7fSflorian 
16990bc729e2Sflorian 	log_lease(iface, 0);
17000bc729e2Sflorian 
17016aeacae8Sflorian 	memset(&imsg, 0, sizeof(imsg));
170257419a7fSflorian 	imsg.if_index = iface->if_index;
170357419a7fSflorian 	imsg.rdomain = iface->rdomain;
1704b2b40540Sflorian 	imsg.addr = iface->requested_ip;
1705b2b40540Sflorian 	imsg.mask = iface->mask;
1706b2b40540Sflorian 	imsg.siaddr = iface->siaddr;
1707ebace80cSflorian 	strlcpy(imsg.file, iface->file, sizeof(imsg.file));
1708ebace80cSflorian 	strlcpy(imsg.domainname, iface->domainname, sizeof(imsg.domainname));
1709ebace80cSflorian 	strlcpy(imsg.hostname, iface->hostname, sizeof(imsg.hostname));
17106aeacae8Sflorian 	for (i = 0; i < iface->prev_routes_len; i++) {
17116aeacae8Sflorian 		found = 0;
17126aeacae8Sflorian 		for (j = 0; j < iface->routes_len; j++) {
17136aeacae8Sflorian 			if (memcmp(&iface->prev_routes[i], &iface->routes[j],
17146aeacae8Sflorian 			    sizeof(struct dhcp_route)) == 0) {
17156aeacae8Sflorian 				found = 1;
17166aeacae8Sflorian 				break;
17176aeacae8Sflorian 			}
17186aeacae8Sflorian 		}
17196aeacae8Sflorian 		if (!found)
17206aeacae8Sflorian 			imsg.routes[imsg.routes_len++] = iface->prev_routes[i];
17216aeacae8Sflorian 	}
17226aeacae8Sflorian 	if (imsg.routes_len > 0)
17236aeacae8Sflorian 		engine_imsg_compose_main(IMSG_WITHDRAW_ROUTES, 0, &imsg,
17246aeacae8Sflorian 		    sizeof(imsg));
1725351dd593Sflorian 	imsg.routes_len = iface->routes_len;
1726351dd593Sflorian 	memcpy(imsg.routes, iface->routes, sizeof(imsg.routes));
172757419a7fSflorian 	engine_imsg_compose_main(IMSG_CONFIGURE_INTERFACE, 0, &imsg,
172857419a7fSflorian 	    sizeof(imsg));
172957419a7fSflorian }
173057419a7fSflorian 
173157419a7fSflorian void
173257419a7fSflorian send_deconfigure_interface(struct dhcpleased_iface *iface)
173357419a7fSflorian {
173457419a7fSflorian 	struct imsg_configure_interface	 imsg;
173557419a7fSflorian 
17364083f3dcSflorian 	if (iface->requested_ip.s_addr == INADDR_ANY)
17374083f3dcSflorian 		return;
17384083f3dcSflorian 
17390bc729e2Sflorian 	log_lease(iface, 1);
17400bc729e2Sflorian 
174157419a7fSflorian 	imsg.if_index = iface->if_index;
174257419a7fSflorian 	imsg.rdomain = iface->rdomain;
1743b2b40540Sflorian 	imsg.addr = iface->requested_ip;
1744b2b40540Sflorian 	imsg.mask = iface->mask;
1745b2b40540Sflorian 	imsg.siaddr = iface->siaddr;
1746ebace80cSflorian 	strlcpy(imsg.file, iface->file, sizeof(imsg.file));
1747ebace80cSflorian 	strlcpy(imsg.domainname, iface->domainname, sizeof(imsg.domainname));
1748ebace80cSflorian 	strlcpy(imsg.hostname, iface->hostname, sizeof(imsg.hostname));
1749351dd593Sflorian 	imsg.routes_len = iface->routes_len;
1750351dd593Sflorian 	memcpy(imsg.routes, iface->routes, sizeof(imsg.routes));
175157419a7fSflorian 	engine_imsg_compose_main(IMSG_DECONFIGURE_INTERFACE, 0, &imsg,
175257419a7fSflorian 	    sizeof(imsg));
1753b7f83d9aSflorian 
1754b7f83d9aSflorian 	iface->server_identifier.s_addr = INADDR_ANY;
1755b7f83d9aSflorian 	iface->dhcp_server.s_addr = INADDR_ANY;
1756b7f83d9aSflorian 	iface->requested_ip.s_addr = INADDR_ANY;
1757b7f83d9aSflorian 	iface->mask.s_addr = INADDR_ANY;
1758351dd593Sflorian 	iface->routes_len = 0;
1759351dd593Sflorian 	memset(iface->routes, 0, sizeof(iface->routes));
176057419a7fSflorian }
176157419a7fSflorian 
176257419a7fSflorian void
1763c2bc6c6dSflorian send_routes_withdraw(struct dhcpleased_iface *iface)
1764c2bc6c6dSflorian {
1765c2bc6c6dSflorian 	struct imsg_configure_interface	 imsg;
1766c2bc6c6dSflorian 
1767c2bc6c6dSflorian 	if (iface->requested_ip.s_addr == INADDR_ANY || iface->routes_len == 0)
1768c2bc6c6dSflorian 		return;
1769c2bc6c6dSflorian 
1770c2bc6c6dSflorian 	imsg.if_index = iface->if_index;
1771c2bc6c6dSflorian 	imsg.rdomain = iface->rdomain;
1772b2b40540Sflorian 	imsg.addr = iface->requested_ip;
1773b2b40540Sflorian 	imsg.mask = iface->mask;
1774b2b40540Sflorian 	imsg.siaddr = iface->siaddr;
1775c2bc6c6dSflorian 	strlcpy(imsg.file, iface->file, sizeof(imsg.file));
1776c2bc6c6dSflorian 	strlcpy(imsg.domainname, iface->domainname, sizeof(imsg.domainname));
1777c2bc6c6dSflorian 	strlcpy(imsg.hostname, iface->hostname, sizeof(imsg.hostname));
1778c2bc6c6dSflorian 	imsg.routes_len = iface->routes_len;
1779c2bc6c6dSflorian 	memcpy(imsg.routes, iface->routes, sizeof(imsg.routes));
1780c2bc6c6dSflorian 	engine_imsg_compose_main(IMSG_WITHDRAW_ROUTES, 0, &imsg,
1781c2bc6c6dSflorian 	    sizeof(imsg));
1782c2bc6c6dSflorian }
1783c2bc6c6dSflorian 
1784c2bc6c6dSflorian void
17850bc729e2Sflorian log_rdns(struct dhcpleased_iface *iface, int withdraw)
17860bc729e2Sflorian {
17870bc729e2Sflorian 	int	 i;
17880bc729e2Sflorian 	char	 hbuf_rdns[INET_ADDRSTRLEN], hbuf_server[INET_ADDRSTRLEN];
1789e998cdbeSflorian 	char	 ifnamebuf[IF_NAMESIZE], *if_name, *rdns_buf = NULL, *tmp_buf;
17900bc729e2Sflorian 
1791e998cdbeSflorian 	if_name = if_indextoname(iface->if_index, ifnamebuf);
17920bc729e2Sflorian 
17930bc729e2Sflorian 	inet_ntop(AF_INET, &iface->server_identifier, hbuf_server,
17940bc729e2Sflorian 	    sizeof(hbuf_server));
17950bc729e2Sflorian 
17960bc729e2Sflorian 	for (i = 0; i < MAX_RDNS_COUNT && iface->nameservers[i].s_addr !=
17970bc729e2Sflorian 		 INADDR_ANY; i++) {
17980bc729e2Sflorian 		inet_ntop(AF_INET, &iface->nameservers[i], hbuf_rdns,
17990bc729e2Sflorian 		    sizeof(hbuf_rdns));
18000bc729e2Sflorian 		tmp_buf = rdns_buf;
18010bc729e2Sflorian 		if (asprintf(&rdns_buf, "%s %s", tmp_buf ? tmp_buf : "",
18020bc729e2Sflorian 		    hbuf_rdns) < 0) {
18030bc729e2Sflorian 			rdns_buf = NULL;
18040bc729e2Sflorian 			break;
18050bc729e2Sflorian 		}
18060bc729e2Sflorian 		free(tmp_buf);
18070bc729e2Sflorian 	}
18080bc729e2Sflorian 
18090bc729e2Sflorian 	if (rdns_buf != NULL) {
1810e998cdbeSflorian 		if (withdraw) {
18110bc729e2Sflorian 			log_info("deleting nameservers%s (lease from %s on %s)",
1812e998cdbeSflorian 			    rdns_buf, hbuf_server, if_name == NULL ? "?" :
1813e998cdbeSflorian 			    if_name);
1814e998cdbeSflorian 		} else {
18150bc729e2Sflorian 			log_info("adding nameservers%s (lease from %s on %s)",
1816e998cdbeSflorian 			    rdns_buf, hbuf_server, if_name == NULL ? "?" :
1817e998cdbeSflorian 			    if_name);
1818e998cdbeSflorian 		}
18190bc729e2Sflorian 		free(rdns_buf);
18200bc729e2Sflorian 	}
18210bc729e2Sflorian }
18220bc729e2Sflorian 
18230bc729e2Sflorian void
182457419a7fSflorian send_rdns_proposal(struct dhcpleased_iface *iface)
182557419a7fSflorian {
182657419a7fSflorian 	struct imsg_propose_rdns	 imsg;
182757419a7fSflorian 
18280bc729e2Sflorian 	log_rdns(iface, 0);
18290bc729e2Sflorian 
183057419a7fSflorian 	memset(&imsg, 0, sizeof(imsg));
183157419a7fSflorian 
183257419a7fSflorian 	imsg.if_index = iface->if_index;
183357419a7fSflorian 	imsg.rdomain = iface->rdomain;
183457419a7fSflorian 	for (imsg.rdns_count = 0; imsg.rdns_count < MAX_RDNS_COUNT &&
183557419a7fSflorian 		 iface->nameservers[imsg.rdns_count].s_addr != INADDR_ANY;
183657419a7fSflorian 	    imsg.rdns_count++)
183757419a7fSflorian 		;
183857419a7fSflorian 	memcpy(imsg.rdns, iface->nameservers, sizeof(imsg.rdns));
183957419a7fSflorian 	engine_imsg_compose_main(IMSG_PROPOSE_RDNS, 0, &imsg, sizeof(imsg));
184057419a7fSflorian }
184157419a7fSflorian 
184257419a7fSflorian void
184357419a7fSflorian send_rdns_withdraw(struct dhcpleased_iface *iface)
184457419a7fSflorian {
184557419a7fSflorian 	struct imsg_propose_rdns	 imsg;
184657419a7fSflorian 
18470bc729e2Sflorian 	log_rdns(iface, 1);
18480bc729e2Sflorian 
184957419a7fSflorian 	memset(&imsg, 0, sizeof(imsg));
185057419a7fSflorian 
185157419a7fSflorian 	imsg.if_index = iface->if_index;
185257419a7fSflorian 	imsg.rdomain = iface->rdomain;
185357419a7fSflorian 	engine_imsg_compose_main(IMSG_WITHDRAW_RDNS, 0, &imsg, sizeof(imsg));
1854b7f83d9aSflorian 	memset(iface->nameservers, 0, sizeof(iface->nameservers));
185557419a7fSflorian }
185657419a7fSflorian 
185757419a7fSflorian void
18586e93e3e9Sflorian parse_lease(struct dhcpleased_iface *iface, struct imsg_ifinfo *imsg_ifinfo)
185957419a7fSflorian {
18606e93e3e9Sflorian 	char	*p, *p1;
186157419a7fSflorian 
18626e93e3e9Sflorian 	iface->requested_ip.s_addr = INADDR_ANY;
186357419a7fSflorian 
1864ebace80cSflorian 	if ((p = strstr(imsg_ifinfo->lease, LEASE_IP_PREFIX)) == NULL)
18656e93e3e9Sflorian 		return;
186657419a7fSflorian 
1867ebace80cSflorian 	p += sizeof(LEASE_IP_PREFIX) - 1;
186857419a7fSflorian 	if ((p1 = strchr(p, '\n')) == NULL)
18696e93e3e9Sflorian 		return;
187057419a7fSflorian 	*p1 = '\0';
187157419a7fSflorian 
187257419a7fSflorian 	if (inet_pton(AF_INET, p, &iface->requested_ip) != 1)
187357419a7fSflorian 		iface->requested_ip.s_addr = INADDR_ANY;
187457419a7fSflorian }
1875e998cdbeSflorian 
1876e998cdbeSflorian void
1877e998cdbeSflorian log_dhcp_hdr(struct dhcp_hdr *dhcp_hdr)
1878e998cdbeSflorian {
18794730fc81Sflorian #ifndef	SMALL
1880e998cdbeSflorian 	char	 hbuf[INET_ADDRSTRLEN];
1881e998cdbeSflorian 
1882e998cdbeSflorian 	log_debug("dhcp_hdr op: %s (%d)", dhcp_hdr->op == DHCP_BOOTREQUEST ?
1883e998cdbeSflorian 	    "Boot Request" : dhcp_hdr->op == DHCP_BOOTREPLY ? "Boot Reply" :
1884e998cdbeSflorian 	    "Unknown", dhcp_hdr->op);
1885e998cdbeSflorian 	log_debug("dhcp_hdr htype: %s (%d)", dhcp_hdr->htype == 1 ? "Ethernet":
1886e998cdbeSflorian 	    "Unknown", dhcp_hdr->htype);
1887e998cdbeSflorian 	log_debug("dhcp_hdr hlen: %d", dhcp_hdr->hlen);
1888e998cdbeSflorian 	log_debug("dhcp_hdr hops: %d", dhcp_hdr->hops);
1889c2da98a6Sflorian 	log_debug("dhcp_hdr xid: 0x%x", ntohl(dhcp_hdr->xid));
1890e998cdbeSflorian 	log_debug("dhcp_hdr secs: %u", dhcp_hdr->secs);
1891e998cdbeSflorian 	log_debug("dhcp_hdr flags: 0x%x", dhcp_hdr->flags);
1892e998cdbeSflorian 	log_debug("dhcp_hdr ciaddr: %s", inet_ntop(AF_INET, &dhcp_hdr->ciaddr,
1893e998cdbeSflorian 	    hbuf, sizeof(hbuf)));
1894e998cdbeSflorian 	log_debug("dhcp_hdr yiaddr: %s", inet_ntop(AF_INET, &dhcp_hdr->yiaddr,
1895e998cdbeSflorian 	    hbuf, sizeof(hbuf)));
1896e998cdbeSflorian 	log_debug("dhcp_hdr siaddr: %s", inet_ntop(AF_INET, &dhcp_hdr->siaddr,
1897e998cdbeSflorian 	    hbuf, sizeof(hbuf)));
1898e998cdbeSflorian 	log_debug("dhcp_hdr giaddr: %s", inet_ntop(AF_INET, &dhcp_hdr->giaddr,
1899e998cdbeSflorian 	    hbuf, sizeof(hbuf)));
1900e998cdbeSflorian 	log_debug("dhcp_hdr chaddr: %02x:%02x:%02x:%02x:%02x:%02x "
1901e998cdbeSflorian 	    "(%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x)",
1902e998cdbeSflorian 	    dhcp_hdr->chaddr[0], dhcp_hdr->chaddr[1], dhcp_hdr->chaddr[2],
1903e998cdbeSflorian 	    dhcp_hdr->chaddr[3], dhcp_hdr->chaddr[4], dhcp_hdr->chaddr[5],
1904e998cdbeSflorian 	    dhcp_hdr->chaddr[6], dhcp_hdr->chaddr[7], dhcp_hdr->chaddr[8],
1905e998cdbeSflorian 	    dhcp_hdr->chaddr[9], dhcp_hdr->chaddr[10], dhcp_hdr->chaddr[11],
1906e998cdbeSflorian 	    dhcp_hdr->chaddr[12], dhcp_hdr->chaddr[13], dhcp_hdr->chaddr[14],
1907e998cdbeSflorian 	    dhcp_hdr->chaddr[15]);
1908e998cdbeSflorian 	/* ignore sname and file, if we ever print it use strvis(3) */
19094730fc81Sflorian #endif
1910e998cdbeSflorian }
1911e998cdbeSflorian 
1912e998cdbeSflorian const char *
1913e998cdbeSflorian dhcp_message_type2str(uint8_t dhcp_message_type)
1914e998cdbeSflorian {
1915e998cdbeSflorian 	switch (dhcp_message_type) {
1916e998cdbeSflorian 	case DHCPDISCOVER:
1917e998cdbeSflorian 		return "DHCPDISCOVER";
1918e998cdbeSflorian 	case DHCPOFFER:
1919e998cdbeSflorian 		return "DHCPOFFER";
1920e998cdbeSflorian 	case DHCPREQUEST:
1921e998cdbeSflorian 		return "DHCPREQUEST";
1922e998cdbeSflorian 	case DHCPDECLINE:
1923e998cdbeSflorian 		return "DHCPDECLINE";
1924e998cdbeSflorian 	case DHCPACK:
1925e998cdbeSflorian 		return "DHCPACK";
1926e998cdbeSflorian 	case DHCPNAK:
1927e998cdbeSflorian 		return "DHCPNAK";
1928e998cdbeSflorian 	case DHCPRELEASE:
1929e998cdbeSflorian 		return "DHCPRELEASE";
1930e998cdbeSflorian 	case DHCPINFORM:
1931e998cdbeSflorian 		return "DHCPINFORM";
1932e998cdbeSflorian 	default:
1933e998cdbeSflorian 		return "Unknown";
1934e998cdbeSflorian 	}
1935e998cdbeSflorian }
1936