xref: /openbsd-src/usr.sbin/dhcpleasectl/dhcpleasectl.c (revision 5a38ef86d0b61900239c7913d24a05e7b88a58f0)
1 /*	$OpenBSD: dhcpleasectl.c,v 1.7 2021/09/16 06:23:01 jmc Exp $	*/
2 
3 /*
4  * Copyright (c) 2021 Florian Obser <florian@openbsd.org>
5  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
6  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
7  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 
22 #include <sys/types.h>
23 #include <sys/ioctl.h>
24 #include <sys/queue.h>
25 #include <sys/socket.h>
26 #include <sys/time.h>
27 #include <sys/un.h>
28 
29 #include <arpa/inet.h>
30 
31 #include <net/if.h>
32 
33 #include <netinet/in.h>
34 #include <netinet/if_ether.h>
35 
36 #include <err.h>
37 #include <errno.h>
38 #include <event.h>
39 #include <imsg.h>
40 #include <limits.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <time.h>
45 #include <unistd.h>
46 
47 #include "dhcpleased.h"
48 
49 __dead void	 usage(void);
50 void		 show_interface_msg(struct ctl_engine_info *);
51 
52 struct imsgbuf	*ibuf;
53 
54 __dead void
55 usage(void)
56 {
57 	extern char *__progname;
58 
59 	fprintf(stderr, "usage: %s [-l] [-s socket] [-w maxwait] interface\n",
60 	    __progname);
61 	exit(1);
62 }
63 
64 int
65 main(int argc, char *argv[])
66 {
67 	struct sockaddr_un	 sun;
68 	struct imsg		 imsg;
69 	struct ctl_engine_info	*cei;
70 	int			 ctl_sock;
71 	int			 n, lFlag = 0, maxwait_set = 0, didot = 0;
72 	int			 ch, if_index = 0, maxwait = 10, bound = 0;
73 	char			*sockname;
74 	const char		*errstr;
75 
76 	sockname = _PATH_DHCPLEASED_SOCKET;
77 	while ((ch = getopt(argc, argv, "ls:w:")) != -1) {
78 		switch (ch) {
79 		case 'l':
80 			lFlag = 1;
81 			break;
82 		case 's':
83 			sockname = optarg;
84 			break;
85 		case 'w':
86 			maxwait_set = 1;
87 			maxwait = strtonum(optarg, 1, INT_MAX, &errstr);
88 			if (errstr)
89 				errx(1, "maxwait value is %s: %s",
90 				    errstr, optarg);
91 			break;
92 
93 		default:
94 			usage();
95 		}
96 	}
97 	argc -= optind;
98 	argv += optind;
99 
100 	if (argc != 1)
101 		usage();
102 
103 	if ((if_index = if_nametoindex(argv[0])) == 0)
104 		errx(1, "unknown interface");
105 
106 	if (!lFlag) {
107 		struct ifreq	 ifr, ifr_x;
108 		int		 ioctl_sock;
109 
110 		if ((ioctl_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
111 			err(1, NULL);
112 
113 		strlcpy(ifr.ifr_name, argv[0], sizeof(ifr.ifr_name));
114 		strlcpy(ifr_x.ifr_name, argv[0], sizeof(ifr.ifr_name));
115 
116 		if (ioctl(ioctl_sock, SIOCGIFFLAGS, &ifr) == -1)
117 			err(1, "SIOCGIFFLAGS");
118 
119 		if (ioctl(ioctl_sock, SIOCGIFXFLAGS, &ifr_x) == -1)
120 			err(1, "SIOCGIFFLAGS");
121 
122 		if (!(ifr.ifr_flags & IFF_UP) ||
123 		    !(ifr_x.ifr_flags & IFXF_AUTOCONF4)) {
124 			if (geteuid())
125 				errx(1, "need root privileges");
126 		}
127 
128 		if (!(ifr.ifr_flags & IFF_UP)) {
129 			ifr.ifr_flags |= IFF_UP;
130 			if (ioctl(ioctl_sock, SIOCSIFFLAGS, &ifr) == -1)
131 				err(1, "SIOCSIFFLAGS");
132 		}
133 		if (!(ifr_x.ifr_flags & IFXF_AUTOCONF4)) {
134 			ifr_x.ifr_flags |= IFXF_AUTOCONF4;
135 			if (ioctl(ioctl_sock, SIOCSIFXFLAGS, &ifr_x) == -1)
136 				err(1, "SIOCSIFFLAGS");
137 		}
138 	}
139 
140 	if (lFlag && !maxwait_set)
141 		maxwait = 0;
142 
143 	/* Connect to control socket. */
144 	if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
145 		err(1, "socket");
146 
147 	memset(&sun, 0, sizeof(sun));
148 	sun.sun_family = AF_UNIX;
149 	strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path));
150 
151 	if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
152 		err(1, "connect: %s", sockname);
153 
154 	if (pledge("stdio", NULL) == -1)
155 		err(1, "pledge");
156 
157 	if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
158 		err(1, NULL);
159 	imsg_init(ibuf, ctl_sock);
160 
161 	if (!lFlag) {
162 		imsg_compose(ibuf, IMSG_CTL_SEND_REQUEST, 0, 0, -1,
163 		    &if_index, sizeof(if_index));
164 		while (ibuf->w.queued)
165 			if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
166 				err(1, "write error");
167 
168 	}
169 
170 	for(;;) {
171 		imsg_compose(ibuf, IMSG_CTL_SHOW_INTERFACE_INFO, 0, 0, -1,
172 		    &if_index, sizeof(if_index));
173 
174 		while (ibuf->w.queued)
175 			if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
176 				err(1, "write error");
177 
178 
179 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
180 			errx(1, "imsg_read error");
181 		if (n == 0)
182 			errx(1, "pipe closed");
183 
184 		if ((n = imsg_get(ibuf, &imsg)) == -1)
185 			errx(1, "imsg_get error");
186 		if (n == 0)
187 			break;
188 
189 		if (imsg.hdr.type == IMSG_CTL_END) {
190 			if (lFlag)
191 				errx(1, "non-autoconf interface %s", argv[0]);
192 			else if (--maxwait < 0)
193 				break;
194 			else
195 				continue;
196 		}
197 
198 		cei = imsg.data;
199 		if (strcmp(cei->state, "Bound") == 0)
200 			bound = 1;
201 
202 		if (bound || --maxwait < 0) {
203 			if (didot)
204 				putchar('\n');
205 			show_interface_msg(cei);
206 			break;
207 		} else {
208 			didot = 1;
209 			putchar('.');
210 			fflush(stdout);
211 		}
212 		imsg_free(&imsg);
213 		sleep(1);
214 	}
215 	close(ctl_sock);
216 	free(ibuf);
217 
218 	return (0);
219 }
220 
221 void
222 show_interface_msg(struct ctl_engine_info *cei)
223 {
224 	struct timespec		 now, diff;
225 	time_t			 d, h, m, s;
226 	int			 i;
227 	char			 buf[IF_NAMESIZE], *bufp;
228 	char			 ipbuf[INET_ADDRSTRLEN];
229 	char			 maskbuf[INET_ADDRSTRLEN];
230 	char			 gwbuf[INET_ADDRSTRLEN];
231 
232 	bufp = if_indextoname(cei->if_index, buf);
233 	printf("%s [%s]\n", bufp != NULL ? bufp : "unknown", cei->state);
234 	memset(ipbuf, 0, sizeof(ipbuf));
235 	if (cei->requested_ip.s_addr != INADDR_ANY) {
236 		clock_gettime(CLOCK_MONOTONIC, &now);
237 		timespecsub(&now, &cei->request_time, &diff);
238 		memset(ipbuf, 0, sizeof(ipbuf));
239 		memset(maskbuf, 0, sizeof(maskbuf));
240 		memset(gwbuf, 0, sizeof(gwbuf));
241 		if (inet_ntop(AF_INET, &cei->requested_ip, ipbuf,
242 		    sizeof(ipbuf)) == NULL)
243 			ipbuf[0] = '\0';
244 		if (inet_ntop(AF_INET, &cei->mask, maskbuf, sizeof(maskbuf))
245 		    == NULL)
246 			maskbuf[0] = '\0';
247 		printf("\tinet %s netmask %s\n", ipbuf, maskbuf);
248 		for (i = 0; i < cei->routes_len; i++) {
249 			if (inet_ntop(AF_INET, &cei->routes[i].dst, ipbuf,
250 			    sizeof(ipbuf)) == NULL)
251 				ipbuf[0] = '\0';
252 			if (inet_ntop(AF_INET, &cei->routes[i].mask, maskbuf,
253 			    sizeof(maskbuf)) == NULL)
254 				maskbuf[0] = '\0';
255 			if (inet_ntop(AF_INET, &cei->routes[i].gw,
256 			    gwbuf, sizeof(gwbuf)) == NULL)
257 				gwbuf[0] = '\0';
258 
259 			if (cei->routes[i].dst.s_addr == INADDR_ANY
260 			    && cei->routes[i].mask.s_addr == INADDR_ANY)
261 				printf("\tdefault gateway %s\n", gwbuf);
262 			else
263 				printf("\troute %s/%d gateway %s\n",
264 				    ipbuf, 33 -
265 				    ffs(ntohl(cei->routes[i].mask.s_addr)),
266 				    gwbuf);
267 		}
268 		if (cei->nameservers[0].s_addr != INADDR_ANY) {
269 			printf("\tnameservers");
270 			for (i = 0; i < MAX_RDNS_COUNT &&
271 				 cei->nameservers[i].s_addr != INADDR_ANY;
272 			     i++) {
273 				if (inet_ntop(AF_INET, &cei->nameservers[i],
274 				    ipbuf, sizeof(ipbuf)) == NULL)
275 					continue;
276 				printf(" %s", ipbuf);
277 			}
278 			printf("\n");
279 		}
280 		s = cei->lease_time - diff.tv_sec;
281 		if (s < 0)
282 			s = 0;
283 
284 		if ( s > 86400 ) {
285 			d = s / 86400;
286 
287 			/* round up */
288 			if (s - d * 86400 > 43200)
289 				d++;
290 			printf("\tlease %lld days\n", d);
291 		} else if (s > 3600) {
292 			h = s / 3600;
293 
294 			/* round up */
295 			if (s - h * 3600 > 1800)
296 				h++;
297 			printf("\tlease %lld hours\n", h);
298 		} else if (s > 60) {
299 			m = s / 60;
300 
301 			/* round up */
302 			if (s - m * 60 > 30)
303 				m++;
304 			printf("\tlease %lld minutes\n", m);
305 		} else
306 			printf("\tlease %lld seconds\n", s);
307 	}
308 	if (cei->server_identifier.s_addr != INADDR_ANY) {
309 		if (inet_ntop(AF_INET, &cei->server_identifier, ipbuf,
310 		    sizeof(ipbuf)) == NULL)
311 			ipbuf[0] = '\0';
312 	} else if (cei->dhcp_server.s_addr != INADDR_ANY) {
313 		if (inet_ntop(AF_INET, &cei->dhcp_server, ipbuf, sizeof(ipbuf))
314 		    == NULL)
315 			ipbuf[0] = '\0';
316 	}
317 	if (ipbuf[0] != '\0')
318 		printf("\tdhcp server %s\n", ipbuf);
319 }
320