xref: /openbsd-src/usr.sbin/dvmrpctl/dvmrpctl.c (revision f1b790a5738b7375271fee81f99119b1f82f2cfd)
1 /*	$OpenBSD: dvmrpctl.c,v 1.21 2024/11/21 13:38:14 claudio Exp $ */
2 
3 /*
4  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
5  * Copyright (c) 2004, 2005, 2006 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/socket.h>
23 #include <sys/un.h>
24 #include <netinet/in.h>
25 #include <netinet/ip_mroute.h>
26 #include <arpa/inet.h>
27 #include <net/if_types.h>
28 
29 #include <err.h>
30 #include <errno.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 
36 #include "igmp.h"
37 #include "dvmrp.h"
38 #include "dvmrpd.h"
39 #include "dvmrpe.h"
40 #include "parser.h"
41 #include "log.h"
42 
43 __dead void	 usage(void);
44 int		 show_summary_msg(struct imsg *);
45 int		 show_interface_msg(struct imsg *);
46 int		 show_interface_detail_msg(struct imsg *);
47 int		 show_igmp_msg(struct imsg *);
48 const char	*print_if_type(enum iface_type type);
49 const char	*print_nbr_state(int);
50 const char	*print_link(int);
51 const char	*fmt_timeframe(time_t t);
52 const char	*fmt_timeframe_core(time_t t);
53 int		 show_nbr_msg(struct imsg *);
54 const char	*print_dvmrp_options(u_int8_t);
55 int		 show_nbr_detail_msg(struct imsg *);
56 int		 show_rib_msg(struct imsg *);
57 int		 show_rib_detail_msg(struct imsg *);
58 int		 show_mfc_msg(struct imsg *);
59 int		 show_mfc_detail_msg(struct imsg *);
60 const char *	 get_linkstate(uint8_t, int);
61 
62 struct imsgbuf	*ibuf;
63 
64 __dead void
65 usage(void)
66 {
67 	extern char *__progname;
68 
69 	fprintf(stderr, "usage: %s command [argument ...]\n", __progname);
70 	exit(1);
71 }
72 
73 int
74 main(int argc, char *argv[])
75 {
76 	struct sockaddr_un	 sun;
77 	struct parse_result	*res;
78 	struct imsg		 imsg;
79 	unsigned int		 ifidx = 0;
80 	int			 ctl_sock;
81 	int			 done = 0, verbose = 0;
82 	int			 n;
83 
84 	/* parse options */
85 	if ((res = parse(argc - 1, argv + 1)) == NULL)
86 		exit(1);
87 
88 	/* connect to dvmrpd control socket */
89 	if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
90 		err(1, "socket");
91 
92 	bzero(&sun, sizeof(sun));
93 	sun.sun_family = AF_UNIX;
94 	strlcpy(sun.sun_path, DVMRPD_SOCKET, sizeof(sun.sun_path));
95 	if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
96 		err(1, "connect: %s", DVMRPD_SOCKET);
97 
98 	if (pledge("stdio", NULL) == -1)
99 		err(1, "pledge");
100 
101 	if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
102 		fatal(NULL);
103 	if (imsgbuf_init(ibuf, ctl_sock) == -1)
104 		fatal(NULL);
105 	done = 0;
106 
107 	/* process user request */
108 	switch (res->action) {
109 	case NONE:
110 		usage();
111 		/* NOTREACHED */
112 	case SHOW:
113 	case SHOW_SUM:
114 		imsg_compose(ibuf, IMSG_CTL_SHOW_SUM, 0, 0, -1, NULL, 0);
115 		break;
116 	case SHOW_IFACE:
117 		printf("%-11s %-18s %-10s %-10s %-10s %-8s %s\n",
118 		    "Interface", "Address", "State", "ProbeTimer", "Linkstate",
119 		    "Uptime", "Groups");
120 		/* FALLTHROUGH */
121 	case SHOW_IFACE_DTAIL:
122 		if (*res->ifname) {
123 			ifidx = if_nametoindex(res->ifname);
124 			if (ifidx == 0)
125 				errx(1, "no such interface %s", res->ifname);
126 		}
127 		imsg_compose(ibuf, IMSG_CTL_SHOW_IFACE, 0, 0, -1, &ifidx,
128 		    sizeof(ifidx));
129 		break;
130 	case SHOW_IGMP:
131 		if (*res->ifname) {
132 			ifidx = if_nametoindex(res->ifname);
133 			if (ifidx == 0)
134 				errx(1, "no such interface %s", res->ifname);
135 		}
136 		imsg_compose(ibuf, IMSG_CTL_SHOW_IGMP, 0, 0, -1, &ifidx,
137 		    sizeof(ifidx));
138 		break;
139 	case SHOW_NBR:
140 		printf("%-15s %-10s %-9s %-15s %-11s %-8s\n", "ID", "State",
141 		    "DeadTime", "Address", "Interface", "Uptime");
142 		/* FALLTHROUGH */
143 	case SHOW_NBR_DTAIL:
144 		imsg_compose(ibuf, IMSG_CTL_SHOW_NBR, 0, 0, -1, NULL, 0);
145 		break;
146 	case SHOW_RIB:
147 		printf("%-20s %-17s %-7s %-10s %-s\n", "Destination", "Nexthop",
148 		    "Cost", "Uptime", "Expire");
149 		/* FALLTHROUGH */
150 	case SHOW_RIB_DTAIL:
151 		imsg_compose(ibuf, IMSG_CTL_SHOW_RIB, 0, 0, -1, NULL, 0);
152 		break;
153 	case SHOW_MFC:
154 		printf("%-16s %-16s %-9s %-9s %-4s %-10s %-10s\n", "Group",
155 		    "Origin", "Incoming", "Outgoing", "TTL", "Uptime",
156 		    "Expire");
157 		/* FALLTHROUGH */
158 	case SHOW_MFC_DTAIL:
159 		imsg_compose(ibuf, IMSG_CTL_SHOW_MFC, 0, 0, -1, NULL, 0);
160 		break;
161 	case LOG_VERBOSE:
162 		verbose = 1;
163 		/* FALLTHROUGH */
164 	case LOG_BRIEF:
165 		imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
166 		    &verbose, sizeof(verbose));
167 		printf("logging request sent.\n");
168 		done = 1;
169 		break;
170 	case RELOAD:
171 		imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
172 		printf("reload request sent.\n");
173 		done = 1;
174 		break;
175 	}
176 
177 	if (imsgbuf_flush(ibuf) == -1)
178 		err(1, "write error");
179 
180 	while (!done) {
181 		if ((n = imsgbuf_read(ibuf)) == -1)
182 			err(1, "read error");
183 		if (n == 0)
184 			errx(1, "pipe closed");
185 
186 		while (!done) {
187 			if ((n = imsg_get(ibuf, &imsg)) == -1)
188 				errx(1, "imsg_get error");
189 			if (n == 0)
190 				break;
191 			switch (res->action) {
192 			case SHOW:
193 			case SHOW_SUM:
194 				done = show_summary_msg(&imsg);
195 				break;
196 			case SHOW_IFACE:
197 				done = show_interface_msg(&imsg);
198 				break;
199 			case SHOW_IFACE_DTAIL:
200 				done = show_interface_detail_msg(&imsg);
201 				break;
202 			case SHOW_IGMP:
203 				done = show_igmp_msg(&imsg);
204 				break;
205 			case SHOW_NBR:
206 				done = show_nbr_msg(&imsg);
207 				break;
208 			case SHOW_NBR_DTAIL:
209 				done = show_nbr_detail_msg(&imsg);
210 				break;
211 			case SHOW_RIB:
212 				done = show_rib_msg(&imsg);
213 				break;
214 			case SHOW_RIB_DTAIL:
215 				done = show_rib_detail_msg(&imsg);
216 				break;
217 			case SHOW_MFC:
218 				done = show_mfc_msg(&imsg);
219 				break;
220 			case SHOW_MFC_DTAIL:
221 				done = show_mfc_detail_msg(&imsg);
222 				break;
223 			case NONE:
224 			case LOG_VERBOSE:
225 			case LOG_BRIEF:
226 			case RELOAD:
227 				break;
228 			}
229 			imsg_free(&imsg);
230 		}
231 	}
232 	close(ctl_sock);
233 	free(ibuf);
234 
235 	return (0);
236 }
237 
238 int
239 show_summary_msg(struct imsg *imsg)
240 {
241 	struct ctl_sum		*sum;
242 
243 	switch (imsg->hdr.type) {
244 	case IMSG_CTL_SHOW_SUM:
245 		sum = imsg->data;
246 		printf("Router ID: %s\n", inet_ntoa(sum->rtr_id));
247 		printf("Hold time is %d sec(s)\n", sum->hold_time);
248 		break;
249 	case IMSG_CTL_END:
250 		printf("\n");
251 		return (1);
252 	default:
253 		break;
254 	}
255 
256 	return (0);
257 }
258 
259 int
260 show_interface_msg(struct imsg *imsg)
261 {
262 	struct ctl_iface	*iface;
263 	char			*netid;
264 
265 	switch (imsg->hdr.type) {
266 	case IMSG_CTL_SHOW_IFACE:
267 		iface = imsg->data;
268 
269 		if (asprintf(&netid, "%s/%d", inet_ntoa(iface->addr),
270 		    mask2prefixlen(iface->mask.s_addr)) == -1)
271 			err(1, NULL);
272 		printf("%-11s %-18s %-10s %-10s %-10s %-8s %5d\n",
273 		    iface->name, netid, if_state_name(iface->state),
274 		    iface->probe_timer == 0 ? "00:00:00" :
275 		    fmt_timeframe_core(iface->probe_timer),
276 		    get_linkstate(iface->if_type, iface->linkstate),
277 		    iface->uptime == 0 ? "00:00:00" :
278 		    fmt_timeframe_core(iface->uptime), iface->group_cnt);
279 		free(netid);
280 		break;
281 	case IMSG_CTL_END:
282 		printf("\n");
283 		return (1);
284 	default:
285 		break;
286 	}
287 
288 	return (0);
289 }
290 
291 int
292 show_interface_detail_msg(struct imsg *imsg)
293 {
294 	struct ctl_iface	*iface;
295 
296 	switch (imsg->hdr.type) {
297 	case IMSG_CTL_SHOW_IFACE:
298 		iface = imsg->data;
299 
300 		printf("\n");
301 		printf("Interface %s, line protocol is %s\n",
302 		    iface->name, print_link(iface->flags));
303 		printf("  Internet address %s/%d\n",
304 		    inet_ntoa(iface->addr),
305 		    mask2prefixlen(iface->mask.s_addr));
306 		printf("  Linkstate %s\n",
307 		    get_linkstate(iface->if_type, iface->linkstate));
308 		printf("  Network type %s, cost: %d\n",
309 		    if_type_name(iface->type), iface->metric);
310 		printf("  State %s, querier ", if_state_name(iface->state));
311 		if (iface->state == IF_STA_QUERIER)
312 			printf("%s\n", inet_ntoa(iface->addr));
313 		else
314 			printf("%s\n", inet_ntoa(iface->querier));
315 		printf("  Generation ID %d\n", iface->gen_id);
316 		printf("  Timer intervals configured, "
317 		    "probe %d, dead %d\n", iface->probe_interval,
318 		    iface->dead_interval);
319 		if (iface->passive)
320 			printf("    Passive interface (No Hellos)\n");
321 		else if (iface->probe_timer < 0)
322 			printf("    Hello timer not running\n");
323 		else
324 			printf("    Hello timer due in %s\n",
325 			    fmt_timeframe_core(iface->probe_timer));
326 		printf("    Uptime %s\n", iface->uptime == 0 ?
327 		    "00:00:00" : fmt_timeframe_core(iface->uptime));
328 		printf("  Adjacent neighbor count is "
329 		    "%d\n", iface->adj_cnt);
330 		break;
331 	case IMSG_CTL_END:
332 		printf("\n");
333 		return (1);
334 	default:
335 		break;
336 	}
337 
338 	return (0);
339 }
340 
341 int
342 show_igmp_msg(struct imsg *imsg)
343 {
344 	struct ctl_iface	*iface;
345 	struct ctl_group	*group;
346 	char			*netid;
347 
348 	switch (imsg->hdr.type) {
349 	case IMSG_CTL_SHOW_IFACE:
350 		iface = imsg->data;
351 		if (asprintf(&netid, "%s/%d", inet_ntoa(iface->addr),
352 		    mask2prefixlen(iface->mask.s_addr)) == -1)
353 			err(1, NULL);
354 		printf("\nInterface %s, address %s, state %s, groups %d\n",
355 		    iface->name, netid, if_state_name(iface->state),
356 		    iface->group_cnt);
357 		free(netid);
358 		printf("  %-16s %-10s %-10s %-10s\n", "Group", "State",
359 		    "DeadTimer", "Uptime");
360 		break;
361 	case IMSG_CTL_SHOW_IGMP:
362 		group = imsg->data;
363 		printf("  %-16s %-10s %-10s %-10s\n", inet_ntoa(group->addr),
364 		    group_state_name(group->state),
365 		    group->dead_timer == 0 ? "00:00:00" :
366 		    fmt_timeframe_core(group->dead_timer),
367 		    group->uptime == 0 ? "00:00:00" :
368 		    fmt_timeframe_core(group->uptime));
369 		break;
370 	case IMSG_CTL_END:
371 		printf("\n");
372 		return (1);
373 	default:
374 		break;
375 	}
376 
377 	return (0);
378 }
379 
380 const char *
381 print_if_type(enum iface_type type)
382 {
383 	switch (type) {
384 	case IF_TYPE_POINTOPOINT:
385 		return ("POINTOPOINT");
386 	case IF_TYPE_BROADCAST:
387 		return ("BROADCAST");
388 	default:
389 		return ("UNKNOWN");
390 	}
391 }
392 
393 const char *
394 print_nbr_state(int state)
395 {
396 	switch (state) {
397 	case NBR_STA_DOWN:
398 		return ("DOWN");
399 	case NBR_STA_1_WAY:
400 		return ("1-WAY");
401 	case NBR_STA_2_WAY:
402 		return ("2-WAY");
403 	default:
404 		return ("UNKNOWN");
405 	}
406 }
407 
408 const char *
409 print_link(int state)
410 {
411 	if (state & IFF_UP)
412 		return ("UP");
413 	else
414 		return ("DOWN");
415 }
416 
417 #define TF_BUFS	8
418 #define TF_LEN	9
419 
420 const char *
421 fmt_timeframe(time_t t)
422 {
423 	if (t == 0)
424 		return ("Never");
425 	else
426 		return (fmt_timeframe_core(time(NULL) - t));
427 }
428 
429 const char *
430 fmt_timeframe_core(time_t t)
431 {
432 	char		*buf;
433 	static char	 tfbuf[TF_BUFS][TF_LEN];	/* ring buffer */
434 	static int	 idx = 0;
435 	unsigned int	 sec, min, hrs, day;
436 	unsigned long long	week;
437 
438 	if (t == 0)
439 		return ("Stopped");
440 
441 	buf = tfbuf[idx++];
442 	if (idx == TF_BUFS)
443 		idx = 0;
444 
445 	week = t;
446 
447 	sec = week % 60;
448 	week /= 60;
449 	min = week % 60;
450 	week /= 60;
451 	hrs = week % 24;
452 	week /= 24;
453 	day = week % 7;
454 	week /= 7;
455 
456 	if (week > 0)
457 		snprintf(buf, TF_LEN, "%02lluw%01ud%02uh", week, day, hrs);
458 	else if (day > 0)
459 		snprintf(buf, TF_LEN, "%01ud%02uh%02um", day, hrs, min);
460 	else
461 		snprintf(buf, TF_LEN, "%02u:%02u:%02u", hrs, min, sec);
462 
463 	return (buf);
464 }
465 
466 /* prototype defined in dvmrpd.h and shared with the kroute.c version */
467 u_int8_t
468 mask2prefixlen(in_addr_t ina)
469 {
470 	if (ina == 0)
471 		return (0);
472 	else
473 		return (33 - ffs(ntohl(ina)));
474 }
475 
476 int
477 show_nbr_msg(struct imsg *imsg)
478 {
479 	struct ctl_nbr	*nbr;
480 
481 	switch (imsg->hdr.type) {
482 	case IMSG_CTL_SHOW_NBR:
483 		nbr = imsg->data;
484 		printf("%-15s %-10s %-10s", inet_ntoa(nbr->id),
485 		    print_nbr_state(nbr->state),
486 		    fmt_timeframe_core(nbr->dead_timer));
487 		printf("%-15s %-11s %s\n", inet_ntoa(nbr->addr),
488 		    nbr->name, fmt_timeframe_core(nbr->uptime));
489 		break;
490 	case IMSG_CTL_END:
491 		printf("\n");
492 		return (1);
493 	default:
494 		break;
495 	}
496 
497 	return (0);
498 }
499 
500 const char *
501 print_dvmrp_options(u_int8_t opts)
502 {
503 	static char	optbuf[32];
504 
505 	snprintf(optbuf, sizeof(optbuf), "*|*|%s|%s|%s|%s|%s|%s",
506 	    opts & DVMRP_CAP_NETMASK ? "N" : "-",
507 	    opts & DVMRP_CAP_SNMP ? "S" : "-",
508 	    opts & DVMRP_CAP_MTRACE ? "M" : "-",
509 	    opts & DVMRP_CAP_GENID ? "G" : "-",
510 	    opts & DVMRP_CAP_PRUNE ? "P" : "-",
511 	    opts & DVMRP_CAP_LEAF ? "L" : "-");
512 	return (optbuf);
513 }
514 
515 int
516 show_nbr_detail_msg(struct imsg *imsg)
517 {
518 	switch (imsg->hdr.type) {
519 	case IMSG_CTL_SHOW_NBR:
520 		break;
521 	case IMSG_CTL_END:
522 		printf("\n");
523 		return (1);
524 	default:
525 		break;
526 	}
527 
528 	return (0);
529 }
530 
531 int
532 show_rib_msg(struct imsg *imsg)
533 {
534 	struct ctl_rt	*rt;
535 	char		*dstnet;
536 
537 	switch (imsg->hdr.type) {
538 	case IMSG_CTL_SHOW_RIB:
539 		rt = imsg->data;
540 		if (asprintf(&dstnet, "%s/%d", inet_ntoa(rt->prefix),
541 		    rt->prefixlen) == -1)
542 			err(1, NULL);
543 
544 		printf("%-20s %-17s %-7d %-9s %9s\n", dstnet,
545 		    inet_ntoa(rt->nexthop),
546 		    rt->cost, rt->uptime == 0 ? "-" :
547 		    fmt_timeframe_core(rt->uptime),
548 		    rt->expire == 0 ? "00:00:00" :
549 		    fmt_timeframe_core(rt->expire));
550 		free(dstnet);
551 
552 		break;
553 	case IMSG_CTL_END:
554 		printf("\n");
555 		return (1);
556 	default:
557 		break;
558 	}
559 
560 	return (0);
561 }
562 
563 int
564 show_rib_detail_msg(struct imsg *imsg)
565 {
566 
567 	switch (imsg->hdr.type) {
568 	case IMSG_CTL_SHOW_RIB:
569 		break;
570 	case IMSG_CTL_END:
571 		printf("\n");
572 		return (1);
573 	default:
574 		break;
575 	}
576 
577 	return (0);
578 }
579 
580 int
581 show_mfc_msg(struct imsg *imsg)
582 {
583 	char		 iname[IF_NAMESIZE];
584 	char		 oname[IF_NAMESIZE] = "-";
585 	struct ctl_mfc	*mfc;
586 	int		 i;
587 
588 
589 	switch (imsg->hdr.type) {
590 	case IMSG_CTL_SHOW_MFC:
591 		mfc = imsg->data;
592 		if_indextoname(mfc->ifindex, iname);
593 
594 		/* search for first entry with ttl > 0 */
595 		for (i = 0; i < MAXVIFS; i++) {
596 			if (mfc->ttls[i] > 0) {
597 				if_indextoname(i, oname);
598 				i++;
599 				break;
600 			}
601 		}
602 
603 		/* display first entry with uptime */
604 		printf("%-16s ", inet_ntoa(mfc->group));
605 		printf("%-16s %-9s %-9s %-4d %-10s %-10s\n",
606 		    inet_ntoa(mfc->origin), iname, oname, mfc->ttls[i - 1],
607 		    mfc->uptime == 0 ? "-" : fmt_timeframe_core(mfc->uptime),
608 		    mfc->expire == 0 ? "-" : fmt_timeframe_core(mfc->expire));
609 
610 		/* display remaining entries with ttl > 0 */
611 		for (; i < MAXVIFS; i++) {
612 			if (mfc->ttls[i] > 0) {
613 				if_indextoname(i, oname);
614 				printf("%43s %-9s %-4d\n", " ", oname,
615 				    mfc->ttls[i]);
616 			}
617 		}
618 		break;
619 	case IMSG_CTL_END:
620 		printf("\n");
621 		return (1);
622 	default:
623 		break;
624 	}
625 
626 	return (0);
627 }
628 
629 int
630 show_mfc_detail_msg(struct imsg *imsg)
631 {
632 
633 	switch (imsg->hdr.type) {
634 	case IMSG_CTL_SHOW_MFC:
635 		break;
636 	case IMSG_CTL_END:
637 		printf("\n");
638 		return (1);
639 	default:
640 		break;
641 	}
642 
643 	return (0);
644 }
645 
646 const struct if_status_description
647 		if_status_descriptions[] = LINK_STATE_DESCRIPTIONS;
648 
649 const char *
650 get_linkstate(uint8_t if_type, int link_state)
651 {
652 	const struct if_status_description *p;
653 	static char buf[8];
654 
655 	for (p = if_status_descriptions; p->ifs_string != NULL; p++) {
656 		if (LINK_STATE_DESC_MATCH(p, if_type, link_state))
657 			return (p->ifs_string);
658 	}
659 	snprintf(buf, sizeof(buf), "[#%d]", link_state);
660 	return (buf);
661 }
662