1/*	$OpenBSD: slaacctl.c,v 1.23 2022/03/21 16:25:47 florian 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);
50int		 show_interface_msg(struct imsg *);
51
52struct imsgbuf	*ibuf;
53
54__dead void
55usage(void)
56{
57	extern char *__progname;
58
59	fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n",
60	    __progname);
61	exit(1);
62}
63
64int
65main(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	imsg_init(ibuf, ctl_sock);
113	done = 0;
114
115	/* Process user request. */
116	switch (res->action) {
117	case LOG_VERBOSE:
118		verbose = 1;
119		/* FALLTHROUGH */
120	case LOG_BRIEF:
121		imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
122		    &verbose, sizeof(verbose));
123		printf("logging request sent.\n");
124		done = 1;
125		break;
126	case SHOW_INTERFACE:
127		imsg_compose(ibuf, IMSG_CTL_SHOW_INTERFACE_INFO, 0, 0, -1,
128		    &res->if_index, sizeof(res->if_index));
129		break;
130	case SEND_SOLICITATION:
131		imsg_compose(ibuf, IMSG_CTL_SEND_SOLICITATION, 0, 0, -1,
132		    &res->if_index, sizeof(res->if_index));
133		done = 1;
134		break;
135	default:
136		usage();
137	}
138
139	while (ibuf->w.queued)
140		if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
141			err(1, "write error");
142
143	while (!done) {
144		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
145			errx(1, "imsg_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
172int
173show_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