dvmrpctl.c revision 1.13
1/*	$OpenBSD: dvmrpctl.c,v 1.13 2015/10/10 22:11:37 deraadt 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);
44int		 show_summary_msg(struct imsg *);
45int		 show_interface_msg(struct imsg *);
46int		 show_interface_detail_msg(struct imsg *);
47int		 show_igmp_msg(struct imsg *);
48const char	*print_if_type(enum iface_type type);
49const char	*print_nbr_state(int);
50const char	*print_link(int);
51const char	*fmt_timeframe(time_t t);
52const char	*fmt_timeframe_core(time_t t);
53int		 show_nbr_msg(struct imsg *);
54const char	*print_dvmrp_options(u_int8_t);
55int		 show_nbr_detail_msg(struct imsg *);
56int		 show_rib_msg(struct imsg *);
57int		 show_rib_detail_msg(struct imsg *);
58int		 show_mfc_msg(struct imsg *);
59int		 show_mfc_detail_msg(struct imsg *);
60const char *	 get_linkstate(uint8_t, int);
61
62struct imsgbuf	*ibuf;
63
64__dead void
65usage(void)
66{
67	extern char *__progname;
68
69	fprintf(stderr, "usage: %s command [argument ...]\n", __progname);
70	exit(1);
71}
72
73int
74main(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 route", NULL) == -1)
99		err(1, "pledge");
100
101	if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
102		fatal(NULL);
103	imsg_init(ibuf, ctl_sock);
104	done = 0;
105
106	/* process user request */
107	switch (res->action) {
108	case NONE:
109		usage();
110		/* NOTREACHED */
111	case SHOW:
112	case SHOW_SUM:
113		imsg_compose(ibuf, IMSG_CTL_SHOW_SUM, 0, 0, -1, NULL, 0);
114		break;
115	case SHOW_IFACE:
116		printf("%-11s %-18s %-10s %-10s %-10s %-8s %s\n",
117		    "Interface", "Address", "State", "ProbeTimer", "Linkstate",
118		    "Uptime", "Groups");
119		/* FALLTHROUGH */
120	case SHOW_IFACE_DTAIL:
121		if (*res->ifname) {
122			ifidx = if_nametoindex(res->ifname);
123			if (ifidx == 0)
124				errx(1, "no such interface %s", res->ifname);
125		}
126		imsg_compose(ibuf, IMSG_CTL_SHOW_IFACE, 0, 0, -1, &ifidx,
127		    sizeof(ifidx));
128		break;
129	case SHOW_IGMP:
130		if (*res->ifname) {
131			ifidx = if_nametoindex(res->ifname);
132			if (ifidx == 0)
133				errx(1, "no such interface %s", res->ifname);
134		}
135		imsg_compose(ibuf, IMSG_CTL_SHOW_IGMP, 0, 0, -1, &ifidx,
136		    sizeof(ifidx));
137		break;
138	case SHOW_NBR:
139		printf("%-15s %-10s %-9s %-15s %-11s %-8s\n", "ID", "State",
140		    "DeadTime", "Address", "Interface", "Uptime");
141		/* FALLTHROUGH */
142	case SHOW_NBR_DTAIL:
143		imsg_compose(ibuf, IMSG_CTL_SHOW_NBR, 0, 0, -1, NULL, 0);
144		break;
145	case SHOW_RIB:
146		printf("%-20s %-17s %-7s %-10s %-s\n", "Destination", "Nexthop",
147		    "Cost", "Uptime", "Expire");
148		/* FALLTHROUGH */
149	case SHOW_RIB_DTAIL:
150		imsg_compose(ibuf, IMSG_CTL_SHOW_RIB, 0, 0, -1, NULL, 0);
151		break;
152	case SHOW_MFC:
153		printf("%-16s %-16s %-9s %-9s %-4s %-10s %-10s\n", "Group",
154		    "Origin", "Incoming", "Outgoing", "TTL", "Uptime",
155		    "Expire");
156		/* FALLTHROUGH */
157	case SHOW_MFC_DTAIL:
158		imsg_compose(ibuf, IMSG_CTL_SHOW_MFC, 0, 0, -1, NULL, 0);
159		break;
160	case LOG_VERBOSE:
161		verbose = 1;
162		/* FALLTHROUGH */
163	case LOG_BRIEF:
164		imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
165		    &verbose, sizeof(verbose));
166		printf("logging request sent.\n");
167		done = 1;
168		break;
169	case RELOAD:
170		imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
171		printf("reload request sent.\n");
172		done = 1;
173		break;
174	}
175
176	while (ibuf->w.queued)
177		if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
178			err(1, "write error");
179
180	while (!done) {
181		if ((n = imsg_read(ibuf)) == -1)
182			errx(1, "imsg_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
238int
239show_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
259int
260show_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
291int
292show_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
341int
342show_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
380const char *
381print_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
393const char *
394print_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
408const char *
409print_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
420const char *
421fmt_timeframe(time_t t)
422{
423	if (t == 0)
424		return ("Never");
425	else
426		return (fmt_timeframe_core(time(NULL) - t));
427}
428
429const char *
430fmt_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 */
467u_int8_t
468mask2prefixlen(in_addr_t ina)
469{
470	if (ina == 0)
471		return (0);
472	else
473		return (33 - ffs(ntohl(ina)));
474}
475
476int
477show_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
500const char *
501print_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
515int
516show_nbr_detail_msg(struct imsg *imsg)
517{
518	struct ctl_nbr	*nbr;
519
520	switch (imsg->hdr.type) {
521	case IMSG_CTL_SHOW_NBR:
522		nbr = imsg->data;
523		break;
524	case IMSG_CTL_END:
525		printf("\n");
526		return (1);
527	default:
528		break;
529	}
530
531	return (0);
532}
533
534int
535show_rib_msg(struct imsg *imsg)
536{
537	struct ctl_rt	*rt;
538	char		*dstnet;
539
540	switch (imsg->hdr.type) {
541	case IMSG_CTL_SHOW_RIB:
542		rt = imsg->data;
543		if (asprintf(&dstnet, "%s/%d", inet_ntoa(rt->prefix),
544		    rt->prefixlen) == -1)
545			err(1, NULL);
546
547		printf("%-20s %-17s %-7d %-9s %9s\n", dstnet,
548		    inet_ntoa(rt->nexthop),
549		    rt->cost, rt->uptime == 0 ? "-" :
550		    fmt_timeframe_core(rt->uptime),
551		    rt->expire == 0 ? "00:00:00" :
552		    fmt_timeframe_core(rt->expire));
553		free(dstnet);
554
555		break;
556	case IMSG_CTL_END:
557		printf("\n");
558		return (1);
559	default:
560		break;
561	}
562
563	return (0);
564}
565
566int
567show_rib_detail_msg(struct imsg *imsg)
568{
569
570	switch (imsg->hdr.type) {
571	case IMSG_CTL_SHOW_RIB:
572		break;
573	case IMSG_CTL_END:
574		printf("\n");
575		return (1);
576	default:
577		break;
578	}
579
580	return (0);
581}
582
583int
584show_mfc_msg(struct imsg *imsg)
585{
586	char		 iname[IF_NAMESIZE];
587	char		 oname[IF_NAMESIZE] = "-";
588	struct ctl_mfc	*mfc;
589	int		 i;
590
591
592	switch (imsg->hdr.type) {
593	case IMSG_CTL_SHOW_MFC:
594		mfc = imsg->data;
595		if_indextoname(mfc->ifindex, iname);
596
597		/* search for first entry with ttl > 0 */
598		for (i = 0; i < MAXVIFS; i++) {
599			if (mfc->ttls[i] > 0) {
600				if_indextoname(i, oname);
601				i++;
602				break;
603			}
604		}
605
606		/* display first entry with uptime */
607		printf("%-16s ", inet_ntoa(mfc->group));
608		printf("%-16s %-9s %-9s %-4d %-10s %-10s\n",
609		    inet_ntoa(mfc->origin), iname, oname, mfc->ttls[i - 1],
610		    mfc->uptime == 0 ? "-" : fmt_timeframe_core(mfc->uptime),
611		    mfc->expire == 0 ? "-" : fmt_timeframe_core(mfc->expire));
612
613		/* display remaining entries with ttl > 0 */
614		for (; i < MAXVIFS; i++) {
615			if (mfc->ttls[i] > 0) {
616				if_indextoname(i, oname);
617				printf("%43s %-9s %-4d\n", " ", oname,
618				    mfc->ttls[i]);
619			}
620		}
621		break;
622	case IMSG_CTL_END:
623		printf("\n");
624		return (1);
625	default:
626		break;
627	}
628
629	return (0);
630}
631
632int
633show_mfc_detail_msg(struct imsg *imsg)
634{
635
636	switch (imsg->hdr.type) {
637	case IMSG_CTL_SHOW_MFC:
638		break;
639	case IMSG_CTL_END:
640		printf("\n");
641		return (1);
642	default:
643		break;
644	}
645
646	return (0);
647}
648
649const struct if_status_description
650		if_status_descriptions[] = LINK_STATE_DESCRIPTIONS;
651
652const char *
653get_linkstate(uint8_t if_type, int link_state)
654{
655	const struct if_status_description *p;
656	static char buf[8];
657
658	for (p = if_status_descriptions; p->ifs_string != NULL; p++) {
659		if (LINK_STATE_DESC_MATCH(p, if_type, link_state))
660			return (p->ifs_string);
661	}
662	snprintf(buf, sizeof(buf), "[#%d]", link_state);
663	return (buf);
664}
665