xref: /openbsd-src/usr.sbin/dhcp6leasectl/dhcp6leasectl.c (revision f1b790a5738b7375271fee81f99119b1f82f2cfd)
1 /*	$OpenBSD: dhcp6leasectl.c,v 1.6 2024/11/21 13:38:14 claudio Exp $	*/
2 
3 /*
4  * Copyright (c) 2021, 2024 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 "dhcp6leased.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_CTRL_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 && !maxwait_set)
107 		maxwait = 0;
108 
109 	/* Connect to control socket. */
110 	if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
111 		err(1, "socket");
112 
113 	memset(&sun, 0, sizeof(sun));
114 	sun.sun_family = AF_UNIX;
115 	strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path));
116 
117 	if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
118 		err(1, "connect: %s", sockname);
119 
120 	if (pledge("stdio", NULL) == -1)
121 		err(1, "pledge");
122 
123 	if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
124 		err(1, NULL);
125 	if (imsgbuf_init(ibuf, ctl_sock) == -1)
126 		err(1, NULL);
127 
128 	if (!lFlag) {
129 		imsg_compose(ibuf, IMSG_CTL_SEND_REQUEST, 0, 0, -1,
130 		    &if_index, sizeof(if_index));
131 		if (imsgbuf_flush(ibuf) == -1)
132 			err(1, "write error");
133 
134 	}
135 
136 	for(;;) {
137 		imsg_compose(ibuf, IMSG_CTL_SHOW_INTERFACE_INFO, 0, 0, -1,
138 		    &if_index, sizeof(if_index));
139 
140 		if (imsgbuf_flush(ibuf) == -1)
141 			err(1, "write error");
142 
143 		if ((n = imsgbuf_read(ibuf)) == -1)
144 			err(1, "read error");
145 		if (n == 0)
146 			errx(1, "pipe closed");
147 
148 		if ((n = imsg_get(ibuf, &imsg)) == -1)
149 			errx(1, "imsg_get error");
150 		if (n == 0)
151 			break;
152 
153 		if (imsg.hdr.type == IMSG_CTL_END) {
154 			if (lFlag)
155 				errx(1, "non-autoconf interface %s", argv[0]);
156 			else if (--maxwait < 0)
157 				break;
158 			else
159 				continue;
160 		}
161 
162 		cei = imsg.data;
163 		if (strcmp(cei->state, "Bound") == 0)
164 			bound = 1;
165 
166 		if (bound || --maxwait < 0) {
167 			if (didot)
168 				putchar('\n');
169 			show_interface_msg(cei);
170 			break;
171 		} else {
172 			didot = 1;
173 			putchar('.');
174 			fflush(stdout);
175 		}
176 		imsg_free(&imsg);
177 		sleep(1);
178 	}
179 	close(ctl_sock);
180 	free(ibuf);
181 
182 	return (0);
183 }
184 
185 void
186 show_interface_msg(struct ctl_engine_info *cei)
187 {
188 	struct timespec		 now, diff;
189 	time_t			 d, h, m, s;
190 	int			 i, has_pd = 0;
191 	char			 buf[IF_NAMESIZE], *bufp;
192 	char			 ntopbuf[INET6_ADDRSTRLEN];
193 
194 	bufp = if_indextoname(cei->if_index, buf);
195 	printf("%s [%s]\n", bufp != NULL ? bufp : "unknown", cei->state);
196 
197 	for (i = 0; i < MAX_IA; i++) {
198 		if (cei->pds[i].prefix_len == 0)
199 			continue;
200 		has_pd = 1;
201 		printf ("\tIA_PD %d: %s/%d\n", i, inet_ntop(AF_INET6,
202 		    &cei->pds[i], ntopbuf, INET6_ADDRSTRLEN),
203 		    cei->pds[i].prefix_len);
204 	}
205 
206 	if (has_pd) {
207 		clock_gettime(CLOCK_MONOTONIC, &now);
208 		timespecsub(&now, &cei->request_time, &diff);
209 		s = cei->lease_time - diff.tv_sec;
210 		if (s < 0)
211 			s = 0;
212 
213 		if ( s > 86400 ) {
214 			d = s / 86400;
215 
216 			/* round up */
217 			if (s - d * 86400 > 43200)
218 				d++;
219 			printf("\tlease %lld day%s\n", d, d  > 1 ? "s" : "");
220 		} else if (s > 3600) {
221 			h = s / 3600;
222 
223 			/* round up */
224 			if (s - h * 3600 > 1800)
225 				h++;
226 			printf("\tlease %lld hour%s\n", h, h > 1 ? "s" : "");
227 		} else if (s > 60) {
228 			m = s / 60;
229 
230 			/* round up */
231 			if (s - m * 60 > 30)
232 				m++;
233 			printf("\tlease %lld minute%s\n", m, m > 1 ? "s" : "");
234 		} else
235 			printf("\tlease %lld second%s\n", s, s > 1 ? "s" : "");
236 
237 	}
238 }
239