1/*	$OpenBSD: kroute.c,v 1.197 2021/03/28 17:25:21 krw Exp $	*/
2
3/*
4 * Copyright 2012 Kenneth R Westerback <krw@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/ioctl.h>
20#include <sys/queue.h>
21#include <sys/socket.h>
22#include <sys/stat.h>
23#include <sys/sysctl.h>
24
25#include <arpa/inet.h>
26
27#include <net/if.h>
28#include <net/if_types.h>
29#include <net/route.h>
30
31#include <netinet/in.h>
32#include <netinet/if_ether.h>
33
34#include <errno.h>
35#include <fcntl.h>
36#include <ifaddrs.h>
37#include <imsg.h>
38#include <limits.h>
39#include <poll.h>
40#include <resolv.h>
41#include <signal.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46
47#include "dhcp.h"
48#include "dhcpd.h"
49#include "log.h"
50#include "privsep.h"
51
52#define ROUNDUP(a) \
53	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
54
55#define	CIDR_MAX_BITS	32
56
57int		 delete_addresses(char *, int, struct in_addr, struct in_addr);
58void		 set_address(char *, int, struct in_addr, struct in_addr);
59void		 delete_address(char *, int, struct in_addr);
60
61char		*get_routes(int, size_t *);
62void		 get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
63unsigned int	 route_pos(struct rt_msghdr *, uint8_t *, unsigned int,
64    struct in_addr);
65void		 flush_routes(int, int, int, uint8_t *, unsigned int,
66    struct in_addr);
67void		 discard_route(uint8_t *, unsigned int);
68void		 add_route(char *, int, int, struct in_addr, struct in_addr,
69		    struct in_addr, struct in_addr, int);
70void		 set_routes(char *, int, int, int, struct in_addr,
71    struct in_addr, uint8_t *, unsigned int);
72
73int		 default_route_index(int, int);
74char		*resolv_conf_tail(void);
75char		*set_resolv_conf(char *, char *, struct unwind_info *);
76
77void		 set_mtu(char *, int, uint16_t);
78
79/*
80 * delete_addresses() removes all inet addresses on the named interface, except
81 * for newaddr/newnetmask.
82 *
83 * If newaddr/newmask is already present, return 1, else 0.
84 */
85int
86delete_addresses(char *name, int ioctlfd, struct in_addr newaddr,
87    struct in_addr newnetmask)
88{
89	struct in_addr			 addr, netmask;
90	struct ifaddrs			*ifap, *ifa;
91	int				 found;
92
93	if (getifaddrs(&ifap) == -1)
94		fatal("getifaddrs");
95
96	found = 0;
97	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
98		if ((ifa->ifa_flags & IFF_LOOPBACK) != 0 ||
99		    (ifa->ifa_flags & IFF_POINTOPOINT) != 0 ||
100		    ((ifa->ifa_flags & IFF_UP) == 0) ||
101		    (ifa->ifa_addr->sa_family != AF_INET) ||
102		    (strcmp(name, ifa->ifa_name) != 0))
103			continue;
104
105		memcpy(&addr,
106		    &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr,
107		    sizeof(addr));
108		memcpy(&netmask,
109		    &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr,
110		    sizeof(netmask));
111
112		if (addr.s_addr == newaddr.s_addr &&
113		    netmask.s_addr == newnetmask.s_addr) {
114			found = 1;
115		} else {
116			delete_address(name, ioctlfd, addr);
117		}
118	}
119
120	freeifaddrs(ifap);
121	return found;
122}
123
124/*
125 * set_address() is the equivalent of
126 *
127 *	ifconfig <if> inet <addr> netmask <mask> broadcast <addr>
128 */
129void
130set_address(char *name, int ioctlfd, struct in_addr addr,
131    struct in_addr netmask)
132{
133	struct ifaliasreq	 ifaliasreq;
134	struct sockaddr_in	*in;
135
136	if (delete_addresses(name, ioctlfd, addr, netmask) == 1)
137		return;
138
139	memset(&ifaliasreq, 0, sizeof(ifaliasreq));
140	strncpy(ifaliasreq.ifra_name, name, sizeof(ifaliasreq.ifra_name));
141
142	/* The actual address in ifra_addr. */
143	in = (struct sockaddr_in *)&ifaliasreq.ifra_addr;
144	in->sin_family = AF_INET;
145	in->sin_len = sizeof(ifaliasreq.ifra_addr);
146	in->sin_addr.s_addr = addr.s_addr;
147
148	/* And the netmask in ifra_mask. */
149	in = (struct sockaddr_in *)&ifaliasreq.ifra_mask;
150	in->sin_family = AF_INET;
151	in->sin_len = sizeof(ifaliasreq.ifra_mask);
152	in->sin_addr.s_addr = netmask.s_addr;
153
154	/* No need to set broadcast address. Kernel can figure it out. */
155
156	if (ioctl(ioctlfd, SIOCAIFADDR, &ifaliasreq) == -1)
157		log_warn("%s: SIOCAIFADDR %s", log_procname,
158		    inet_ntoa(addr));
159}
160
161void
162delete_address(char *name, int ioctlfd, struct in_addr addr)
163{
164	struct ifaliasreq	 ifaliasreq;
165	struct sockaddr_in	*in;
166
167	/*
168	 * Delete specified address on specified interface.
169	 *
170	 * Deleting the address also clears out arp entries.
171	 */
172
173	memset(&ifaliasreq, 0, sizeof(ifaliasreq));
174	strncpy(ifaliasreq.ifra_name, name, sizeof(ifaliasreq.ifra_name));
175
176	in = (struct sockaddr_in *)&ifaliasreq.ifra_addr;
177	in->sin_family = AF_INET;
178	in->sin_len = sizeof(ifaliasreq.ifra_addr);
179	in->sin_addr.s_addr = addr.s_addr;
180
181	/* SIOCDIFADDR will result in a RTM_DELADDR message we must catch! */
182	if (ioctl(ioctlfd, SIOCDIFADDR, &ifaliasreq) == -1) {
183		if (errno != EADDRNOTAVAIL)
184			log_warn("%s: SIOCDIFADDR %s", log_procname,
185			    inet_ntoa(addr));
186	}
187}
188
189/*
190 * get_routes() returns all relevant routes currently configured, and the
191 * length of the buffer being returned.
192 */
193char *
194get_routes(int rdomain, size_t *len)
195{
196	int		 mib[7];
197	char		*buf, *bufp, *errmsg = NULL;
198	size_t		 needed;
199
200	mib[0] = CTL_NET;
201	mib[1] = PF_ROUTE;	/* PF_ROUTE (not AF_ROUTE) for sysctl(2)! */
202	mib[2] = 0;
203	mib[3] = AF_INET;
204	mib[4] = NET_RT_FLAGS;
205	mib[5] = RTF_STATIC;
206	mib[6] = rdomain;
207
208	buf = NULL;
209	errmsg = NULL;
210	for (;;) {
211		if (sysctl(mib, 7, NULL, &needed, NULL, 0) == -1) {
212			errmsg = "sysctl size of routes:";
213			break;
214		}
215		if (needed == 0) {
216			free(buf);
217			return NULL;
218		}
219		if ((bufp = realloc(buf, needed)) == NULL) {
220			errmsg = "routes buf realloc:";
221			break;
222		}
223		buf = bufp;
224		if (sysctl(mib, 7, buf, &needed, NULL, 0) == -1) {
225			if (errno == ENOMEM)
226				continue;
227			errmsg = "sysctl retrieval of routes:";
228			break;
229		}
230		break;
231	}
232
233	if (errmsg != NULL) {
234		log_warn("%s: get_routes - %s (msize=%zu)", log_procname,
235		    errmsg, needed);
236		free(buf);
237		buf = NULL;
238	}
239
240	*len = needed;
241	return buf;
242}
243
244/*
245 * get_rtaddrs() populates rti_info with pointers to the
246 * sockaddr's contained in a rtm message.
247 */
248void
249get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
250{
251	int	i;
252
253	for (i = 0; i < RTAX_MAX; i++) {
254		if (addrs & (1 << i)) {
255			rti_info[i] = sa;
256			sa = (struct sockaddr *)((char *)(sa) +
257			    ROUNDUP(sa->sa_len));
258		} else
259			rti_info[i] = NULL;
260	}
261}
262/*
263 * route_pos() finds the position of the *rtm route within
264 * routes.
265 *
266 * If the *rtm route is not in routes, return routes_len.
267 */
268unsigned int
269route_pos(struct rt_msghdr *rtm, uint8_t *routes, unsigned int routes_len,
270    struct in_addr address)
271{
272	struct sockaddr		*rti_info[RTAX_MAX];
273	struct sockaddr		*dst, *netmask, *gateway;
274	in_addr_t		 dstaddr, netmaskaddr, gatewayaddr;
275	in_addr_t		 routesdstaddr, routesnetmaskaddr;
276	in_addr_t		 routesgatewayaddr;
277	unsigned int		 i, len;
278
279	get_rtaddrs(rtm->rtm_addrs,
280	    (struct sockaddr *)((char *)(rtm) + rtm->rtm_hdrlen),
281	    rti_info);
282
283	dst = rti_info[RTAX_DST];
284	netmask = rti_info[RTAX_NETMASK];
285	gateway = rti_info[RTAX_GATEWAY];
286
287	if (dst == NULL || netmask == NULL || gateway == NULL)
288		return routes_len;
289
290	if (dst->sa_family != AF_INET || netmask->sa_family != AF_INET ||
291	    gateway->sa_family != AF_INET)
292		return routes_len;
293
294	dstaddr = ((struct sockaddr_in *)dst)->sin_addr.s_addr;
295	netmaskaddr = ((struct sockaddr_in *)netmask)->sin_addr.s_addr;
296	gatewayaddr = ((struct sockaddr_in *)gateway)->sin_addr.s_addr;
297
298	dstaddr &= netmaskaddr;
299	i = 0;
300	while (i < routes_len)  {
301		len = extract_route(&routes[i], routes_len - i, &routesdstaddr,
302		    &routesnetmaskaddr, &routesgatewayaddr);
303		if (len == 0)
304			break;
305
306		/* Direct route in routes:
307		 *
308		 * dst=1.2.3.4 netmask=255.255.255.255 gateway=0.0.0.0
309		 *
310		 * direct route in rtm:
311		 *
312		 * dst=1.2.3.4 netmask=255.255.255.255 gateway = address
313		 *
314		 * So replace 0.0.0.0 with address for comparison.
315		 */
316		if (routesgatewayaddr == INADDR_ANY)
317			routesgatewayaddr = address.s_addr;
318		routesdstaddr &= routesnetmaskaddr;
319
320		if (dstaddr == routesdstaddr &&
321		    netmaskaddr == routesnetmaskaddr &&
322		    gatewayaddr == routesgatewayaddr)
323			return i;
324
325		i += len;
326	}
327
328	return routes_len;
329}
330
331void
332flush_routes(int index, int routefd, int rdomain, uint8_t *routes,
333    unsigned int routes_len, struct in_addr address)
334{
335	static int			 seqno;
336	char				*lim, *buf, *next;
337	struct rt_msghdr		*rtm;
338	size_t				 len;
339	ssize_t				 rlen;
340	unsigned int			 pos;
341
342	buf = get_routes(rdomain, &len);
343	if (buf == NULL)
344		return;
345
346	lim = buf + len;
347	for (next = buf; next < lim; next += rtm->rtm_msglen) {
348		rtm = (struct rt_msghdr *)next;
349		if (rtm->rtm_version != RTM_VERSION)
350			continue;
351		if (rtm->rtm_index != index)
352			continue;
353		if (rtm->rtm_tableid != rdomain)
354			continue;
355		if ((rtm->rtm_flags & RTF_STATIC) == 0)
356			continue;
357		if ((rtm->rtm_flags & (RTF_LOCAL|RTF_BROADCAST)) != 0)
358			continue;
359
360		pos = route_pos(rtm, routes, routes_len, address);
361		if (pos < routes_len) {
362			discard_route(routes + pos, routes_len - pos);
363			continue;
364		}
365
366		rtm->rtm_type = RTM_DELETE;
367		rtm->rtm_seq = seqno++;
368
369		rlen = write(routefd, (char *)rtm, rtm->rtm_msglen);
370		if (rlen == -1) {
371			if (errno != ESRCH)
372				log_warn("%s: write(RTM_DELETE)", log_procname);
373		} else if (rlen < (int)rtm->rtm_msglen)
374			log_warnx("%s: write(RTM_DELETE): %zd of %u bytes",
375			    log_procname, rlen, rtm->rtm_msglen);
376	}
377
378	free(buf);
379}
380
381void
382discard_route(uint8_t *routes, unsigned int routes_len)
383{
384	unsigned int		len;
385
386	len = 1 + sizeof(struct in_addr) + (routes[0] + 7) / 8;
387	memmove(routes, routes + len, routes_len - len);
388	routes[routes_len - len] = CIDR_MAX_BITS + 1;
389}
390
391/*
392 * add_route() adds a single route to the routing table.
393 */
394void
395add_route(char *name, int rdomain, int routefd, struct in_addr dest,
396    struct in_addr netmask, struct in_addr gateway, struct in_addr address,
397    int flags)
398{
399	char			 destbuf[INET_ADDRSTRLEN];
400	char			 maskbuf[INET_ADDRSTRLEN];
401	struct iovec		 iov[5];
402	struct sockaddr_in	 sockaddr_in[4];
403	struct rt_msghdr	 rtm;
404	int			 i, iovcnt = 0;
405
406	memset(&rtm, 0, sizeof(rtm));
407	rtm.rtm_index = if_nametoindex(name);
408	if (rtm.rtm_index == 0)
409		return;
410
411	rtm.rtm_version = RTM_VERSION;
412	rtm.rtm_type = RTM_ADD;
413	rtm.rtm_tableid = rdomain;
414	rtm.rtm_priority = RTP_NONE;
415	rtm.rtm_flags = flags;
416
417	iov[0].iov_base = &rtm;
418	iov[0].iov_len = sizeof(rtm);
419
420	memset(sockaddr_in, 0, sizeof(sockaddr_in));
421	for (i = 0; i < 4; i++) {
422		sockaddr_in[i].sin_len = sizeof(sockaddr_in[i]);
423		sockaddr_in[i].sin_family = AF_INET;
424		iov[i+1].iov_base = &sockaddr_in[i];
425		iov[i+1].iov_len = sizeof(sockaddr_in[i]);
426	}
427
428	/* Order of sockaddr_in's is mandatory! */
429	sockaddr_in[0].sin_addr = dest;
430	sockaddr_in[1].sin_addr = gateway;
431	sockaddr_in[2].sin_addr = netmask;
432	sockaddr_in[3].sin_addr = address;
433	if (address.s_addr == INADDR_ANY) {
434		rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
435		iovcnt = 4;
436	} else {
437		rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_IFA;
438		iovcnt = 5;
439	}
440
441	for (i = 0; i < iovcnt; i++)
442		rtm.rtm_msglen += iov[i].iov_len;
443
444	if (writev(routefd, iov, iovcnt) == -1) {
445		if (errno != EEXIST || log_getverbose() != 0) {
446			strlcpy(destbuf, inet_ntoa(dest), sizeof(destbuf));
447			strlcpy(maskbuf, inet_ntoa(netmask),sizeof(maskbuf));
448			log_warn("%s: add route %s/%s via %s", log_procname,
449			    destbuf, maskbuf, inet_ntoa(gateway));
450		}
451	}
452}
453
454/*
455 * set_routes() adds the routes contained in 'routes' to the routing table.
456 */
457void
458set_routes(char *name, int index, int rdomain, int routefd, struct in_addr addr,
459    struct in_addr addrmask, uint8_t *routes, unsigned int routes_len)
460{
461	const struct in_addr	 any = { INADDR_ANY };
462	const struct in_addr	 broadcast = { INADDR_BROADCAST };
463	struct in_addr		 dest, gateway, netmask;
464	in_addr_t		 addrnet, gatewaynet;
465	unsigned int		 i, len;
466
467	flush_routes(index, routefd, rdomain, routes, routes_len, addr);
468
469	addrnet = addr.s_addr & addrmask.s_addr;
470
471	/* Add classless static routes. */
472	i = 0;
473	while (i < routes_len) {
474		len = extract_route(&routes[i], routes_len - i,
475		    &dest.s_addr, &netmask.s_addr, &gateway.s_addr);
476		if (len == 0)
477			return;
478		i += len;
479
480		if (gateway.s_addr == INADDR_ANY) {
481			/*
482			 * DIRECT ROUTE
483			 *
484			 * route add -net $dest -netmask $netmask -cloning
485			 *     -iface $addr
486			 */
487			add_route(name, rdomain, routefd, dest, netmask,
488			    addr, any, RTF_STATIC | RTF_CLONING);
489		} else if (netmask.s_addr == INADDR_ANY) {
490			/*
491			 * DEFAULT ROUTE
492			 */
493			gatewaynet = gateway.s_addr & addrmask.s_addr;
494			if (gatewaynet != addrnet) {
495				/*
496				 * DIRECT ROUTE TO DEFAULT GATEWAY
497				 *
498				 * route add -net $gateway
499				 *	-netmask 255.255.255.255
500				 *	-cloning -iface $addr
501				 *
502				 * If the default route gateway is not reachable
503				 * via the IP assignment then add a cloning
504				 * direct route for the gateway. Deals with
505				 * weird configs seen in the wild.
506				 *
507				 * e.g. add the route if we were given a /32 IP
508				 * assignment. a.k.a. "make Google Cloud DHCP
509				 * work".
510				 *
511				 */
512				add_route(name, rdomain, routefd, gateway,
513				    broadcast, addr, any,
514				    RTF_STATIC | RTF_CLONING);
515			}
516
517			if (memcmp(&gateway, &addr, sizeof(addr)) == 0) {
518				/*
519				 * DEFAULT ROUTE IS A DIRECT ROUTE
520				 *
521				 * route add default -iface $addr
522				 */
523				add_route(name, rdomain, routefd, any, any,
524				    gateway, any, RTF_STATIC);
525			} else {
526				/*
527				 * DEFAULT ROUTE IS VIA GATEWAY
528				 *
529				 * route add default $gateway -ifa $addr
530				 *
531				 */
532				add_route(name, rdomain, routefd, any, any,
533				    gateway, addr, RTF_STATIC | RTF_GATEWAY);
534			}
535		} else {
536			/*
537			 * NON-DIRECT, NON-DEFAULT ROUTE
538			 *
539			 * route add -net $dest -netmask $netmask $gateway
540			 */
541			add_route(name, rdomain, routefd, dest, netmask,
542			    gateway, any, RTF_STATIC | RTF_GATEWAY);
543		}
544	}
545}
546
547/*
548 * default_route_index() returns the interface index of the current
549 * default route (a.k.a. 0.0.0.0/0).
550 */
551int
552default_route_index(int rdomain, int routefd)
553{
554	struct pollfd		 fds[1];
555	struct timespec		 now, stop, timeout;
556	int			 nfds;
557	struct iovec		 iov[3];
558	struct sockaddr_in	 sin;
559	struct {
560		struct rt_msghdr	m_rtm;
561		char			m_space[512];
562	} m_rtmsg;
563	pid_t			 pid;
564	ssize_t			 len;
565	int			 seq;
566
567	memset(&m_rtmsg, 0, sizeof(m_rtmsg));
568	m_rtmsg.m_rtm.rtm_version = RTM_VERSION;
569	m_rtmsg.m_rtm.rtm_type = RTM_GET;
570	m_rtmsg.m_rtm.rtm_tableid = rdomain;
571	m_rtmsg.m_rtm.rtm_seq = seq = arc4random();
572	m_rtmsg.m_rtm.rtm_addrs = RTA_DST | RTA_NETMASK;
573	m_rtmsg.m_rtm.rtm_msglen = sizeof(struct rt_msghdr) +
574	    2 * sizeof(struct sockaddr_in);
575
576	memset(&sin, 0, sizeof(sin));
577	sin.sin_len = sizeof(sin);
578	sin.sin_family = AF_INET;
579
580	iov[0].iov_base = &m_rtmsg.m_rtm;
581	iov[0].iov_len = sizeof(m_rtmsg.m_rtm);
582	iov[1].iov_base = &sin;
583	iov[1].iov_len = sizeof(sin);
584	iov[2].iov_base = &sin;
585	iov[2].iov_len = sizeof(sin);
586
587	pid = getpid();
588	clock_gettime(CLOCK_MONOTONIC, &now);
589	timespecclear(&timeout);
590	timeout.tv_sec = 3;
591	timespecadd(&now, &timeout, &stop);
592
593	if (writev(routefd, iov, 3) == -1) {
594		if (errno == ESRCH)
595			log_debug("%s: writev(RTM_GET) - no default route",
596			    log_procname);
597		else
598			log_warn("%s: writev(RTM_GET)", log_procname);
599		return 0;
600	}
601
602	for (;;) {
603		clock_gettime(CLOCK_MONOTONIC, &now);
604		if (timespeccmp(&stop, &now, <=))
605			break;
606		timespecsub(&stop, &now, &timeout);
607
608		fds[0].fd = routefd;
609		fds[0].events = POLLIN;
610		nfds = ppoll(fds, 1, &timeout, NULL);
611		if (nfds == -1) {
612			if (errno == EINTR)
613				continue;
614			log_warn("%s: ppoll(routefd)", log_procname);
615			break;
616		}
617		if ((fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) != 0) {
618			log_warnx("%s: routefd: ERR|HUP|NVAL", log_procname);
619			break;
620		}
621		if (nfds == 0 || (fds[0].revents & POLLIN) == 0)
622			continue;
623
624		len = read(routefd, &m_rtmsg, sizeof(m_rtmsg));
625		if (len == -1) {
626			log_warn("%s: read(RTM_GET)", log_procname);
627			break;
628		} else if (len == 0) {
629			log_warnx("%s: read(RTM_GET): 0 bytes", log_procname);
630			break;
631		}
632
633		if (m_rtmsg.m_rtm.rtm_version == RTM_VERSION &&
634		    m_rtmsg.m_rtm.rtm_type == RTM_GET &&
635		    m_rtmsg.m_rtm.rtm_pid == pid &&
636		    m_rtmsg.m_rtm.rtm_seq == seq &&
637		    (m_rtmsg.m_rtm.rtm_flags & RTF_UP) == RTF_UP) {
638			if (m_rtmsg.m_rtm.rtm_errno != 0) {
639				log_warnx("%s: read(RTM_GET): %s", log_procname,
640				    strerror(m_rtmsg.m_rtm.rtm_errno));
641				break;
642			}
643			return m_rtmsg.m_rtm.rtm_index;
644		}
645	}
646
647	return 0;
648}
649
650/*
651 * resolv_conf_tail() returns the contents of /etc/resolv.conf.tail, if
652 * any. NULL is returned if there is no such file, the file is emtpy
653 * or any errors are encounted in reading the file.
654 */
655char *
656resolv_conf_tail(void)
657{
658	struct stat		 sb;
659	const char		*tail_path = "/etc/resolv.conf.tail";
660	char			*tailcontents = NULL;
661	ssize_t			 tailn;
662	int			 tailfd;
663
664	tailfd = open(tail_path, O_RDONLY);
665	if (tailfd == -1) {
666		if (errno != ENOENT)
667			log_warn("%s: open(%s)", log_procname, tail_path);
668	} else if (fstat(tailfd, &sb) == -1) {
669		log_warn("%s: fstat(%s)", log_procname, tail_path);
670	} else if (sb.st_size > 0 && sb.st_size < LLONG_MAX) {
671		tailcontents = calloc(1, sb.st_size + 1);
672		if (tailcontents == NULL)
673			fatal("%s contents", tail_path);
674		tailn = read(tailfd, tailcontents, sb.st_size);
675		if (tailn == -1)
676			log_warn("%s: read(%s)", log_procname,
677			    tail_path);
678		else if (tailn == 0)
679			log_warnx("%s: got no data from %s",
680			    log_procname,tail_path);
681		else if (tailn != sb.st_size)
682			log_warnx("%s: short read of %s",
683			    log_procname, tail_path);
684		else {
685			close(tailfd);
686			return tailcontents;
687		}
688
689		close(tailfd);
690		free(tailcontents);
691	}
692
693	return NULL;
694}
695
696/*
697 * set_resolv_conf() creates a string that are the resolv.conf contents
698 * that should be used when IMSG_WRITE_RESOLV_CONF messages are received.
699 */
700char *
701set_resolv_conf(char *name, char *search, struct unwind_info *ns_info)
702{
703	char		*ns, *p, *tail;
704	struct in_addr	 addr;
705	unsigned int	 i;
706	int		 rslt;
707
708	ns = NULL;
709	for (i = 0; i < ns_info->count; i++) {
710		addr.s_addr = ns_info->ns[i];
711		rslt = asprintf(&p, "%snameserver %s\n",
712		    (ns == NULL) ? "" : ns, inet_ntoa(addr));
713		if (rslt == -1)
714			fatal("nameserver");
715		free(ns);
716		ns = p;
717	}
718
719	if (search == NULL && ns == NULL)
720		return NULL;
721
722	tail = resolv_conf_tail();
723
724	rslt = asprintf(&p, "# Generated by %s dhclient\n%s%s%s", name,
725	    (search == NULL) ? "" : search,
726	    (ns == NULL) ? "" : ns,
727	    (tail == NULL) ? "" : tail);
728	if (rslt == -1)
729		fatal("resolv.conf");
730
731	free(tail);
732	free(ns);
733
734	return p;
735}
736
737/*
738 * set_mtu() is the equivalent of
739 *
740 *      ifconfig <if> mtu <mtu>
741 */
742void
743set_mtu(char *name, int ioctlfd, uint16_t mtu)
744{
745	struct ifreq	 ifr;
746
747	memset(&ifr, 0, sizeof(ifr));
748	strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
749
750	if (ioctl(ioctlfd, SIOCGIFMTU, &ifr) == -1) {
751		log_warn("%s: SIOCGIFMTU", log_procname);
752		return;
753	}
754	if (ifr.ifr_mtu == mtu)
755		return;	/* Avoid unnecessary RTM_IFINFO! */
756
757	ifr.ifr_mtu = mtu;
758	if (ioctl(ioctlfd, SIOCSIFMTU, &ifr) == -1)
759		log_warn("%s: SIOCSIFMTU %u", log_procname, mtu);
760}
761
762/*
763 * extract_route() decodes the route pointed to by routes into its
764 * {destination, netmask, gateway} and returns the number of bytes consumed
765 * from routes.
766 */
767unsigned int
768extract_route(uint8_t *routes, unsigned int routes_len, in_addr_t *dest,
769    in_addr_t *netmask, in_addr_t *gateway)
770{
771	unsigned int	 bits, bytes, len;
772
773	if (routes[0] > CIDR_MAX_BITS)
774		return 0;
775
776	bits = routes[0];
777	bytes = (bits + 7) / 8;
778	len = 1 + bytes + sizeof(*gateway);
779	if (len > routes_len)
780		return 0;
781
782	if (dest != NULL)
783		memcpy(dest, &routes[1], bytes);
784
785	if (netmask != NULL) {
786		if (bits == 0)
787			*netmask = INADDR_ANY;
788		else
789			*netmask = htonl(0xffffffff << (CIDR_MAX_BITS - bits));
790		if (dest != NULL)
791			*dest &= *netmask;
792	}
793
794	if (gateway != NULL)
795		memcpy(gateway, &routes[1 +  bytes], sizeof(*gateway));
796
797	return len;
798}
799
800/*
801 * [priv_]write_resolv_conf write out a new resolv.conf.
802 */
803void
804write_resolv_conf(void)
805{
806	int	 rslt;
807
808	rslt = imsg_compose(unpriv_ibuf, IMSG_WRITE_RESOLV_CONF,
809	    0, 0, -1, NULL, 0);
810	if (rslt == -1)
811		log_warn("%s: imsg_compose(IMSG_WRITE_RESOLV_CONF)",
812		    log_procname);
813}
814
815void
816priv_write_resolv_conf(int index, int routefd, int rdomain, char *contents,
817    int *lastidx)
818{
819	char		 ifname[IF_NAMESIZE];
820	const char	*path = "/etc/resolv.conf";
821	ssize_t		 n;
822	size_t		 sz;
823	int		 fd, retries, newidx;
824
825	if (contents == NULL)
826		return;
827
828	retries = 0;
829	do {
830		newidx = default_route_index(rdomain, routefd);
831		retries++;
832	} while (newidx == 0 && retries < 3);
833
834	if (newidx == 0) {
835		log_debug("%s: %s not updated, no default route is UP",
836		    log_procname, path);
837		return;
838	} else if (newidx != index) {
839		*lastidx = newidx;
840		if (if_indextoname(newidx, ifname) == NULL) {
841			memset(ifname, 0, sizeof(ifname));
842			strlcat(ifname, "<unknown>", sizeof(ifname));
843		}
844		log_debug("%s: %s not updated, default route on %s",
845		    log_procname, path, ifname);
846		return;
847	} else if (newidx == *lastidx) {
848		log_debug("%s: %s not updated, same as last write",
849		    log_procname, path);
850		return;
851	}
852
853	*lastidx = newidx;
854	log_debug("%s: %s updated", log_procname, path);
855
856	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC,
857	    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
858
859	if (fd == -1) {
860		log_warn("%s: open(%s)", log_procname, path);
861		return;
862	}
863
864	sz = strlen(contents);
865	n = write(fd, contents, sz);
866	if (n == -1)
867		log_warn("%s: write(%s)", log_procname, path);
868	else if ((size_t)n < sz)
869		log_warnx("%s: write(%s): %zd of %zu bytes", log_procname,
870		    path, n, sz);
871
872	close(fd);
873}
874
875/*
876 * [priv_]propose implements a proposal.
877 */
878void
879propose(struct proposal *proposal)
880{
881	struct option_data	 opt;
882	int			 rslt;
883
884	log_debug("%s: proposing address %s netmask 0x%08x", log_procname,
885	    inet_ntoa(proposal->address), ntohl(proposal->netmask.s_addr));
886
887	opt.data = (u_int8_t *)proposal + sizeof(struct proposal);
888	opt.len = proposal->routes_len;
889	if (opt.len > 0)
890		log_debug("%s: proposing static route(s) %s", log_procname,
891		    pretty_print_option(DHO_CLASSLESS_STATIC_ROUTES, &opt, 0));
892
893	opt.data += opt.len;
894	opt.len = proposal->domains_len;
895	if (opt.len > 0)
896		log_debug("%s: proposing search domain(s) %s", log_procname,
897		    pretty_print_option(DHO_DOMAIN_SEARCH, &opt, 0));
898
899	opt.data += opt.len;
900	opt.len = proposal->ns_len;
901	if (opt.len > 0)
902		log_debug("%s: proposing DNS server(s) %s", log_procname,
903		    pretty_print_option(DHO_DOMAIN_NAME_SERVERS, &opt, 0));
904
905	if (proposal->mtu != 0)
906		log_debug("%s: proposing mtu %u", log_procname, proposal->mtu);
907
908	rslt = imsg_compose(unpriv_ibuf, IMSG_PROPOSE, 0, 0, -1, proposal,
909	    sizeof(*proposal) + proposal->routes_len +
910	    proposal->domains_len + proposal->ns_len);
911	if (rslt == -1)
912		log_warn("%s: imsg_compose(IMSG_PROPOSE)", log_procname);
913}
914
915void
916priv_propose(char *name, int ioctlfd, struct proposal *proposal,
917    size_t sz, char **resolv_conf, int routefd, int rdomain, int index,
918    int *lastidx)
919{
920	struct unwind_info	 unwind_info;
921	uint8_t			*dns, *domains, *routes;
922	char			*search = NULL;
923	int			 rslt;
924
925	if (sz != proposal->routes_len + proposal->domains_len +
926	    proposal->ns_len) {
927		log_warnx("%s: bad IMSG_PROPOSE data", log_procname);
928		return;
929	}
930
931	routes = (uint8_t *)proposal + sizeof(struct proposal);
932	domains = routes + proposal->routes_len;
933	dns = domains + proposal->domains_len;
934
935	memset(&unwind_info, 0, sizeof(unwind_info));
936	if (proposal->ns_len >= sizeof(in_addr_t)) {
937		if (proposal->ns_len > sizeof(unwind_info.ns)) {
938			memcpy(unwind_info.ns, dns, sizeof(unwind_info.ns));
939			unwind_info.count = sizeof(unwind_info.ns) /
940			    sizeof(in_addr_t);
941		} else {
942			memcpy(unwind_info.ns, dns, proposal->ns_len);
943			unwind_info.count = proposal->ns_len /
944			    sizeof(in_addr_t);
945		}
946	}
947
948	if (proposal->domains_len > 0) {
949		rslt = asprintf(&search, "search %.*s\n",
950		    proposal->domains_len, domains);
951		if (rslt == -1)
952			search = NULL;
953	}
954
955	free(*resolv_conf);
956	*resolv_conf = set_resolv_conf(name, search, &unwind_info);
957	free(search);
958
959	if (proposal->mtu != 0) {
960		if (proposal->mtu < 68)
961			log_warnx("%s: mtu size %d < 68: ignored", log_procname,
962			    proposal->mtu);
963		else
964			set_mtu(name, ioctlfd, proposal->mtu);
965	}
966
967	set_address(name, ioctlfd, proposal->address, proposal->netmask);
968
969	set_routes(name, index, rdomain, routefd, proposal->address,
970	    proposal->netmask, routes, proposal->routes_len);
971
972	*lastidx = 0;
973	priv_write_resolv_conf(index, routefd, rdomain, *resolv_conf, lastidx);
974}
975
976/*
977 * [priv_]revoke_proposal de-configures a proposal.
978 */
979void
980revoke_proposal(struct proposal *proposal)
981{
982	int			 rslt;
983
984	if (proposal == NULL)
985		return;
986
987	rslt = imsg_compose(unpriv_ibuf, IMSG_REVOKE, 0, 0, -1, proposal,
988	    sizeof(*proposal));
989	if (rslt == -1)
990		log_warn("%s: imsg_compose(IMSG_REVOKE)", log_procname);
991}
992
993void
994priv_revoke_proposal(char *name, int ioctlfd, struct proposal *proposal,
995    char **resolv_conf)
996{
997	free(*resolv_conf);
998	*resolv_conf = NULL;
999
1000	delete_address(name, ioctlfd, proposal->address);
1001}
1002
1003/*
1004 * [priv_]tell_unwind sends out inforation unwind may be intereted in.
1005 */
1006void
1007tell_unwind(struct unwind_info *unwind_info, int ifi_flags)
1008{
1009	struct	unwind_info	 	 noinfo;
1010	int				 rslt;
1011
1012	if ((ifi_flags & IFI_IN_CHARGE) == 0)
1013		return;
1014
1015	if (unwind_info != NULL)
1016		rslt = imsg_compose(unpriv_ibuf, IMSG_TELL_UNWIND, 0, 0, -1,
1017		    unwind_info, sizeof(*unwind_info));
1018	else {
1019		memset(&noinfo, 0, sizeof(noinfo));
1020		rslt = imsg_compose(unpriv_ibuf, IMSG_TELL_UNWIND, 0, 0, -1,
1021		    &noinfo, sizeof(noinfo));
1022	}
1023
1024	if (rslt == -1)
1025		log_warn("%s: imsg_compose(IMSG_TELL_UNWIND)", log_procname);
1026}
1027
1028void
1029priv_tell_unwind(int index, int routefd, int rdomain,
1030    struct unwind_info *unwind_info)
1031{
1032	struct rt_msghdr		 rtm;
1033	struct sockaddr_rtdns		 rtdns;
1034	struct iovec			 iov[3];
1035	long				 pad = 0;
1036	int				 iovcnt = 0, padlen;
1037
1038	memset(&rtm, 0, sizeof(rtm));
1039
1040	rtm.rtm_version = RTM_VERSION;
1041	rtm.rtm_type = RTM_PROPOSAL;
1042	rtm.rtm_msglen = sizeof(rtm);
1043	rtm.rtm_tableid = rdomain;
1044	rtm.rtm_index = index;
1045	rtm.rtm_seq = arc4random();
1046	rtm.rtm_priority = RTP_PROPOSAL_DHCLIENT;
1047	rtm.rtm_addrs = RTA_DNS;
1048	rtm.rtm_flags = RTF_UP;
1049
1050	iov[iovcnt].iov_base = &rtm;
1051	iov[iovcnt++].iov_len = sizeof(rtm);
1052
1053	memset(&rtdns, 0, sizeof(rtdns));
1054	rtdns.sr_family = AF_INET;
1055
1056	rtdns.sr_len = 2 + unwind_info->count * sizeof(in_addr_t);
1057	memcpy(rtdns.sr_dns, unwind_info->ns,
1058	    unwind_info->count * sizeof(in_addr_t));
1059
1060	iov[iovcnt].iov_base = &rtdns;
1061	iov[iovcnt++].iov_len = sizeof(rtdns);
1062	rtm.rtm_msglen += sizeof(rtdns);
1063	padlen = ROUNDUP(sizeof(rtdns)) - sizeof(rtdns);
1064	if (padlen > 0) {
1065		iov[iovcnt].iov_base = &pad;
1066		iov[iovcnt++].iov_len = padlen;
1067		rtm.rtm_msglen += padlen;
1068	}
1069
1070	if (writev(routefd, iov, iovcnt) == -1)
1071		log_warn("failed to tell unwind");
1072}
1073