xref: /openbsd-src/usr.sbin/slaacctl/slaacctl.c (revision f1b790a5738b7375271fee81f99119b1f82f2cfd)
1 /*	$OpenBSD: slaacctl.c,v 1.28 2024/11/21 13:38:15 claudio Exp $	*/
2 
3 /*
4  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
5  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
6  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/types.h>
22 #include <sys/queue.h>
23 #include <sys/socket.h>
24 #include <sys/un.h>
25 #include <arpa/inet.h>
26 
27 #include <net/if.h>
28 #include <net/if_media.h>
29 #include <net/if_types.h>
30 
31 #include <netinet/in.h>
32 #include <netinet/if_ether.h>
33 #include <netinet6/nd6.h>
34 
35 #include <err.h>
36 #include <errno.h>
37 #include <event.h>
38 #include <imsg.h>
39 #include <netdb.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 
45 #include "slaacd.h"
46 #include "frontend.h"
47 #include "parser.h"
48 
49 __dead void	 usage(void);
50 int		 show_interface_msg(struct imsg *);
51 
52 struct imsgbuf	*ibuf;
53 
54 __dead void
55 usage(void)
56 {
57 	extern char *__progname;
58 
59 	fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n",
60 	    __progname);
61 	exit(1);
62 }
63 
64 int
65 main(int argc, char *argv[])
66 {
67 	struct sockaddr_un	 sun;
68 	struct parse_result	*res;
69 	struct imsg		 imsg;
70 	int			 ctl_sock;
71 	int			 done = 0;
72 	int			 n, verbose = 0;
73 	int			 ch;
74 	char			*sockname;
75 
76 	sockname = _PATH_SLAACD_SOCKET;
77 	while ((ch = getopt(argc, argv, "s:")) != -1) {
78 		switch (ch) {
79 		case 's':
80 			sockname = optarg;
81 			break;
82 		default:
83 			usage();
84 		}
85 	}
86 	argc -= optind;
87 	argv += optind;
88 
89 	if (pledge("stdio unix", NULL) == -1)
90 		err(1, "pledge");
91 
92 	/* Parse command line. */
93 	if ((res = parse(argc, argv)) == NULL)
94 		exit(1);
95 
96 	/* Connect to control socket. */
97 	if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
98 		err(1, "socket");
99 
100 	memset(&sun, 0, sizeof(sun));
101 	sun.sun_family = AF_UNIX;
102 	strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path));
103 
104 	if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
105 		err(1, "connect: %s", sockname);
106 
107 	if (pledge("stdio", NULL) == -1)
108 		err(1, "pledge");
109 
110 	if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
111 		err(1, NULL);
112 	if (imsgbuf_init(ibuf, ctl_sock) == -1)
113 		err(1, NULL);
114 	done = 0;
115 
116 	/* Process user request. */
117 	switch (res->action) {
118 	case LOG_VERBOSE:
119 		verbose = 1;
120 		/* FALLTHROUGH */
121 	case LOG_BRIEF:
122 		imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
123 		    &verbose, sizeof(verbose));
124 		printf("logging request sent.\n");
125 		done = 1;
126 		break;
127 	case SHOW_INTERFACE:
128 		imsg_compose(ibuf, IMSG_CTL_SHOW_INTERFACE_INFO, 0, 0, -1,
129 		    &res->if_index, sizeof(res->if_index));
130 		break;
131 	case SEND_SOLICITATION:
132 		imsg_compose(ibuf, IMSG_CTL_SEND_SOLICITATION, 0, 0, -1,
133 		    &res->if_index, sizeof(res->if_index));
134 		done = 1;
135 		break;
136 	default:
137 		usage();
138 	}
139 
140 	if (imsgbuf_flush(ibuf) == -1)
141 		err(1, "write error");
142 
143 	while (!done) {
144 		if ((n = imsgbuf_read(ibuf)) == -1)
145 			err(1, "read error");
146 		if (n == 0)
147 			errx(1, "pipe closed");
148 
149 		while (!done) {
150 			if ((n = imsg_get(ibuf, &imsg)) == -1)
151 				errx(1, "imsg_get error");
152 			if (n == 0)
153 				break;
154 
155 			switch (res->action) {
156 			case SHOW_INTERFACE:
157 				done = show_interface_msg(&imsg);
158 				break;
159 			default:
160 				break;
161 			}
162 
163 			imsg_free(&imsg);
164 		}
165 	}
166 	close(ctl_sock);
167 	free(ibuf);
168 
169 	return (0);
170 }
171 
172 int
173 show_interface_msg(struct imsg *imsg)
174 {
175 	static int				 if_count = 0;
176 	struct ctl_engine_info			*cei;
177 	struct ctl_engine_info_ra		*cei_ra;
178 	struct ctl_engine_info_ra_prefix	*cei_ra_prefix;
179 	struct ctl_engine_info_ra_rdns		*cei_ra_rdns;
180 	struct ctl_engine_info_address_proposal	*cei_addr_proposal;
181 	struct ctl_engine_info_dfr_proposal	*cei_dfr_proposal;
182 	struct ctl_engine_info_rdns_proposal	*cei_rdns_proposal;
183 	struct tm				*t;
184 	struct timespec				 now, diff;
185 	int					 i;
186 	char					 buf[IF_NAMESIZE], *bufp;
187 	char					 hbuf[NI_MAXHOST], whenbuf[255];
188 	char					 ntopbuf[INET6_ADDRSTRLEN];
189 
190 	switch (imsg->hdr.type) {
191 	case IMSG_CTL_SHOW_INTERFACE_INFO:
192 		cei = imsg->data;
193 
194 		if (if_count++ > 0)
195 			printf("\n");
196 
197 		bufp = if_indextoname(cei->if_index, buf);
198 		printf("%s:\n", bufp != NULL ? bufp : "unknown");
199 		printf("\t index: %3u ", cei->if_index);
200 		printf("running: %3s ", cei->running ? "yes" : "no");
201 		printf("temporary: %3s\n", cei->temporary ? "yes" :
202 		    "no");
203 		printf("\tlladdr: %s\n", ether_ntoa(&cei->hw_address));
204 		if (getnameinfo((struct sockaddr *)&cei->ll_address,
205 		    cei->ll_address.sin6_len, hbuf, sizeof(hbuf), NULL, 0,
206 		    NI_NUMERICHOST | NI_NUMERICSERV))
207 			err(1, "cannot get link local address");
208 		printf("\t inet6: %s\n", hbuf);
209 		break;
210 	case IMSG_CTL_SHOW_INTERFACE_INFO_RA:
211 		cei_ra = imsg->data;
212 
213 		if (getnameinfo((struct sockaddr *)&cei_ra->from,
214 		    cei_ra->from.sin6_len, hbuf, sizeof(hbuf), NULL, 0,
215 		    NI_NUMERICHOST | NI_NUMERICSERV))
216 			err(1, "cannot get router IP");
217 
218 		if (clock_gettime(CLOCK_MONOTONIC, &now))
219 			err(1, "clock_gettime");
220 
221 		timespecsub(&now, &cei_ra->uptime, &diff);
222 
223 		t = localtime(&cei_ra->when.tv_sec);
224 		strftime(whenbuf, sizeof(whenbuf), "%F %T", t);
225 		printf("\tRouter Advertisement from %s\n", hbuf);
226 		printf("\t\treceived: %s; %llds ago\n", whenbuf, diff.tv_sec);
227 		printf("\t\tCur Hop Limit: %3u, M: %d, O: %d, Router Lifetime:"
228 		    " %5us\n", cei_ra->curhoplimit, cei_ra->managed ? 1: 0,
229 		    cei_ra->other ? 1 : 0, cei_ra->router_lifetime);
230 		printf("\t\tDefault Router Preference: %s\n", cei_ra->rpref);
231 		printf("\t\tReachable Time: %9ums, Retrans Timer: %9ums\n",
232 		    cei_ra->reachable_time, cei_ra->retrans_time);
233 		if (cei_ra->mtu)
234 			printf("\t\tMTU: %u bytes\n", cei_ra->mtu);
235 		break;
236 	case IMSG_CTL_SHOW_INTERFACE_INFO_RA_PREFIX:
237 		cei_ra_prefix = imsg->data;
238 		printf("\t\tprefix: %s/%u\n", inet_ntop(AF_INET6,
239 		    &cei_ra_prefix->prefix, ntopbuf, INET6_ADDRSTRLEN),
240 			    cei_ra_prefix->prefix_len);
241 		printf("\t\t\tOn-link: %d, Autonomous address-configuration: %d"
242 		    "\n", cei_ra_prefix->onlink ? 1 : 0,
243 		    cei_ra_prefix->autonomous ? 1 : 0);
244 		if (cei_ra_prefix->vltime == ND6_INFINITE_LIFETIME)
245 			printf("\t\t\tvltime: %10s, ", "infinity");
246 		else
247 			printf("\t\t\tvltime: %10u, ", cei_ra_prefix->vltime);
248 		if (cei_ra_prefix->pltime == ND6_INFINITE_LIFETIME)
249 			printf("pltime: %10s\n", "infinity");
250 		else
251 			printf("pltime: %10u\n", cei_ra_prefix->pltime);
252 		break;
253 	case IMSG_CTL_SHOW_INTERFACE_INFO_RA_RDNS:
254 		cei_ra_rdns = imsg->data;
255 		printf("\t\trdns: %s, lifetime: %u\n", inet_ntop(AF_INET6,
256 		    &cei_ra_rdns->rdns, ntopbuf, INET6_ADDRSTRLEN),
257 		    cei_ra_rdns->lifetime);
258 		break;
259 	case IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSALS:
260 		printf("\tAddress proposals\n");
261 		break;
262 	case IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSAL:
263 		cei_addr_proposal = imsg->data;
264 
265 		if (getnameinfo((struct sockaddr *)&cei_addr_proposal->addr,
266 		    cei_addr_proposal->addr.sin6_len, hbuf, sizeof(hbuf),
267 		    NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV))
268 			err(1, "cannot get proposal IP");
269 
270 		printf("\t\tid: %4lld, state: %15s, temporary: %s\n",
271 		    cei_addr_proposal->id, cei_addr_proposal->state,
272 		    cei_addr_proposal->temporary ? "y" : "n");
273 
274 		if (clock_gettime(CLOCK_MONOTONIC, &now))
275 			err(1, "clock_gettime");
276 
277 		timespecsub(&now, &cei_addr_proposal->uptime, &diff);
278 
279 		if (cei_addr_proposal->vltime == ND6_INFINITE_LIFETIME)
280 			printf("\t\tvltime: %10s, ", "infinity");
281 		else
282 			printf("\t\tvltime: %10u, ", cei_addr_proposal->vltime);
283 		if (cei_addr_proposal->pltime == ND6_INFINITE_LIFETIME)
284 			printf("pltime: %10s", "infinity");
285 		else
286 			printf("pltime: %10u", cei_addr_proposal->pltime);
287 		if (cei_addr_proposal->next_timeout != 0)
288 			printf(", timeout: %10llds\n",
289 			    cei_addr_proposal->next_timeout - diff.tv_sec);
290 		else
291 			printf("\n");
292 
293 		t = localtime(&cei_addr_proposal->when.tv_sec);
294 		strftime(whenbuf, sizeof(whenbuf), "%F %T", t);
295 		printf("\t\tupdated: %s; %llds ago\n", whenbuf, diff.tv_sec);
296 		printf("\t\t%s, %s/%u\n", hbuf, inet_ntop(AF_INET6,
297 		    &cei_addr_proposal->prefix, ntopbuf, INET6_ADDRSTRLEN),
298 		    cei_addr_proposal->prefix_len);
299 		break;
300 	case IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSALS:
301 		printf("\tDefault router proposals\n");
302 		break;
303 	case IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSAL:
304 		cei_dfr_proposal = imsg->data;
305 
306 		if (getnameinfo((struct sockaddr *)&cei_dfr_proposal->addr,
307 		    cei_dfr_proposal->addr.sin6_len, hbuf, sizeof(hbuf),
308 		    NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV))
309 			err(1, "cannot get router IP");
310 
311 		printf("\t\tid: %4lld, state: %15s\n",
312 		    cei_dfr_proposal->id, cei_dfr_proposal->state);
313 		printf("\t\trouter: %s\n", hbuf);
314 		printf("\t\trouter lifetime: %10u\n",
315 		    cei_dfr_proposal->router_lifetime);
316 		printf("\t\tPreference: %s\n", cei_dfr_proposal->rpref);
317 		if (clock_gettime(CLOCK_MONOTONIC, &now))
318 			err(1, "clock_gettime");
319 
320 		timespecsub(&now, &cei_dfr_proposal->uptime, &diff);
321 
322 		t = localtime(&cei_dfr_proposal->when.tv_sec);
323 		strftime(whenbuf, sizeof(whenbuf), "%F %T", t);
324 		printf("\t\tupdated: %s; %llds ago", whenbuf, diff.tv_sec);
325 		if (cei_dfr_proposal->next_timeout != 0)
326 			printf(", timeout: %10llds\n",
327 			    cei_dfr_proposal->next_timeout - diff.tv_sec);
328 		else
329 			printf("\n");
330 
331 		break;
332 	case IMSG_CTL_SHOW_INTERFACE_INFO_RDNS_PROPOSALS:
333 		printf("\trDNS proposals\n");
334 		break;
335 	case IMSG_CTL_SHOW_INTERFACE_INFO_RDNS_PROPOSAL:
336 		cei_rdns_proposal = imsg->data;
337 
338 		if (getnameinfo((struct sockaddr *)&cei_rdns_proposal->from,
339 		    cei_rdns_proposal->from.sin6_len, hbuf, sizeof(hbuf),
340 		    NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV))
341 			err(1, "cannot get router IP");
342 
343 		printf("\t\tid: %4lld, state: %15s\n",
344 		    cei_rdns_proposal->id, cei_rdns_proposal->state);
345 		printf("\t\trouter: %s\n", hbuf);
346 		printf("\t\trdns lifetime: %10u\n",
347 		    cei_rdns_proposal->rdns_lifetime);
348 		printf("\t\trdns:\n");
349 		for (i = 0; i < cei_rdns_proposal->rdns_count; i++) {
350 			printf("\t\t\t%s\n", inet_ntop(AF_INET6,
351 			    &cei_rdns_proposal->rdns[i], ntopbuf,
352 			    INET6_ADDRSTRLEN));
353 		}
354 
355 		if (clock_gettime(CLOCK_MONOTONIC, &now))
356 			err(1, "clock_gettime");
357 
358 		timespecsub(&now, &cei_rdns_proposal->uptime, &diff);
359 
360 		t = localtime(&cei_rdns_proposal->when.tv_sec);
361 		strftime(whenbuf, sizeof(whenbuf), "%F %T", t);
362 		printf("\t\tupdated: %s; %llds ago", whenbuf, diff.tv_sec);
363 		if (cei_rdns_proposal->next_timeout != 0)
364 			printf(", timeout: %10llds\n",
365 			    cei_rdns_proposal->next_timeout - diff.tv_sec);
366 		else
367 			printf("\n");
368 
369 		break;
370 	case IMSG_CTL_END:
371 		return (1);
372 	default:
373 		break;
374 	}
375 
376 	return (0);
377 }
378