1/*	$OpenBSD: ripctl.c,v 1.17 2016/08/02 16:05:32 jca Exp $
2 *
3 * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
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/socket.h>
23#include <sys/un.h>
24#include <netinet/in.h>
25#include <arpa/inet.h>
26#include <net/if_media.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 "rip.h"
37#include "ripd.h"
38#include "ripe.h"
39#include "parser.h"
40
41__dead void	 usage(void);
42const char	*fmt_timeframe_core(time_t);
43const char	*get_linkstate(uint8_t, int);
44int		 show_interface_msg(struct imsg *);
45uint64_t	 get_ifms_type(uint8_t);
46int		 show_rib_msg(struct imsg *);
47int		 show_nbr_msg(struct imsg *);
48void		 show_fib_head(void);
49int		 show_fib_msg(struct imsg *);
50void		 show_interface_head(void);
51int		 show_fib_interface_msg(struct imsg *);
52const char	*get_media_descr(uint64_t);
53void		 print_baudrate(uint64_t);
54
55struct imsgbuf	*ibuf;
56
57__dead void
58usage(void)
59{
60	extern char *__progname;
61
62	fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n",
63	    __progname);
64	exit(1);
65}
66
67int
68main(int argc, char *argv[])
69{
70	struct sockaddr_un	 sun;
71	struct parse_result	*res;
72	struct imsg		 imsg;
73	unsigned int		 ifidx = 0;
74	int			 ctl_sock;
75	int			 done = 0, verbose = 0;
76	int			 n;
77	int			 ch;
78	char			*sockname = RIPD_SOCKET;
79
80	while ((ch = getopt(argc, argv, "s:")) != -1) {
81		switch (ch) {
82		case 's':
83			sockname = optarg;
84			break;
85		default:
86			usage();
87			/* NOTREACHED */
88		}
89	}
90
91	argc -= optind;
92	argv += optind;
93
94	/* parse options */
95	if ((res = parse(argc, argv)) == NULL)
96		exit(1);
97
98	/* connect to ripd control socket */
99	if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
100		err(1, "socket");
101
102	bzero(&sun, sizeof(sun));
103	sun.sun_family = AF_UNIX;
104	strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path));
105	if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
106		err(1, "connect: %s", sockname);
107
108	if (pledge("stdio", NULL) == -1)
109		err(1, "pledge");
110
111	if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
112		err(1, NULL);
113	imsg_init(ibuf, ctl_sock);
114	done = 0;
115
116	/* process user request */
117	switch (res->action) {
118	case NONE:
119		usage();
120		/* not reached */
121	case SHOW:
122	case SHOW_IFACE:
123		printf("%-11s %-18s %-10s %-10s %-8s\n",
124		    "Interface", "Address", "State", "Linkstate",
125		    "Uptime");
126		if (*res->ifname) {
127			ifidx = if_nametoindex(res->ifname);
128			if (ifidx == 0)
129				errx(1, "no such interface %s", res->ifname);
130		}
131		imsg_compose(ibuf, IMSG_CTL_SHOW_INTERFACE, 0, 0, -1,
132		    &ifidx, sizeof(ifidx));
133		break;
134	case SHOW_NBR:
135		printf("%-15s %-15s %-15s %-9s %-10s\n", "ID",
136		    "State", "Address", "Iface", "Uptime");
137		imsg_compose(ibuf, IMSG_CTL_SHOW_NBR, 0, 0, -1, NULL, 0);
138		break;
139	case SHOW_RIB:
140		printf("%-20s %-17s %-7s\n", "Destination",
141		    "Nexthop", "Cost");
142		imsg_compose(ibuf, IMSG_CTL_SHOW_RIB, 0, 0, -1, NULL, 0);
143		break;
144	case SHOW_FIB:
145		if (!res->addr.s_addr)
146			imsg_compose(ibuf, IMSG_CTL_KROUTE, 0, 0, -1,
147			    &res->flags, sizeof(res->flags));
148		else
149			imsg_compose(ibuf, IMSG_CTL_KROUTE_ADDR, 0, 0, -1,
150			    &res->addr, sizeof(res->addr));
151		show_fib_head();
152		break;
153	case SHOW_FIB_IFACE:
154		if (*res->ifname)
155			imsg_compose(ibuf, IMSG_CTL_IFINFO, 0, 0, -1,
156			    res->ifname, sizeof(res->ifname));
157		else
158			imsg_compose(ibuf, IMSG_CTL_IFINFO, 0, 0, -1, NULL, 0);
159		show_interface_head();
160		break;
161	case FIB:
162		errx(1, "fib couple|decouple");
163		break;
164	case FIB_COUPLE:
165		imsg_compose(ibuf, IMSG_CTL_FIB_COUPLE, 0, 0, -1, NULL, 0);
166		printf("couple request sent.\n");
167		done = 1;
168		break;
169	case FIB_DECOUPLE:
170		imsg_compose(ibuf, IMSG_CTL_FIB_DECOUPLE, 0, 0, -1, NULL, 0);
171		printf("decouple request sent.\n");
172		done = 1;
173		break;
174	case LOG_VERBOSE:
175		verbose = 1;
176		/* FALLTHROUGH */
177	case LOG_BRIEF:
178		imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
179		    &verbose, sizeof(verbose));
180		printf("logging request sent.\n");
181		done = 1;
182		break;
183	case RELOAD:
184		imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
185		printf("reload request sent.\n");
186		done = 1;
187		break;
188	}
189
190	while (ibuf->w.queued)
191		if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
192			err(1, "write error");
193
194	while (!done) {
195		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
196			errx(1, "imsg_read error");
197		if (n == 0)
198			errx(1, "pipe closed");
199
200		while (!done) {
201			if ((n = imsg_get(ibuf, &imsg)) == -1)
202				errx(1, "imsg_get error");
203			if (n == 0)
204				break;
205			switch (res->action) {
206			case SHOW:
207			case SHOW_IFACE:
208				done = show_interface_msg(&imsg);
209				break;
210			case SHOW_NBR:
211				done = show_nbr_msg(&imsg);
212				break;
213			case SHOW_RIB:
214				done = show_rib_msg(&imsg);
215				break;
216			case SHOW_FIB:
217				done = show_fib_msg(&imsg);
218				break;
219			case SHOW_FIB_IFACE:
220				done = show_fib_interface_msg(&imsg);
221				break;
222			case NONE:
223			case FIB:
224			case FIB_COUPLE:
225			case FIB_DECOUPLE:
226			case LOG_VERBOSE:
227			case LOG_BRIEF:
228			case RELOAD:
229				break;
230			}
231			imsg_free(&imsg);
232		}
233	}
234	close(ctl_sock);
235	free(ibuf);
236
237	return (0);
238}
239
240uint64_t
241get_ifms_type(uint8_t if_type)
242{
243	switch (if_type) {
244	case IFT_ETHER:
245		return (IFM_ETHER);
246		break;
247	case IFT_FDDI:
248		return (IFM_FDDI);
249		break;
250	case IFT_CARP:
251		return (IFM_CARP);
252		break;
253	default:
254		return (0);
255		break;
256	}
257}
258
259#define	TF_BUFS	8
260#define	TF_LEN	9
261
262const char *
263fmt_timeframe_core(time_t t)
264{
265	char		*buf;
266	static char	 tfbuf[TF_BUFS][TF_LEN];	/* ring buffer */
267	static int	 idx = 0;
268	unsigned int	 sec, min, hrs, day;
269	unsigned long long	week;
270
271	if (t == 0)
272		return ("Stopped");
273
274	buf = tfbuf[idx++];
275	if (idx == TF_BUFS)
276		idx = 0;
277
278	week = t;
279
280	sec = week % 60;
281	week /= 60;
282	min = week % 60;
283	week /= 60;
284	hrs = week % 24;
285	week /= 24;
286	day = week % 7;
287	week /= 7;
288
289	if (week > 0)
290		snprintf(buf, TF_LEN, "%02lluw%01ud%02uh", week, day, hrs);
291	else if (day > 0)
292		snprintf(buf, TF_LEN, "%01ud%02uh%02um", day, hrs, min);
293	else
294		snprintf(buf, TF_LEN, "%02u:%02u:%02u", hrs, min, sec);
295
296	return (buf);
297}
298
299/* prototype defined in ripd.h and shared with the kroute.c version */
300u_int8_t
301mask2prefixlen(in_addr_t ina)
302{
303	if (ina == 0)
304		return (0);
305	else
306		return (33 - ffs(ntohl(ina)));
307}
308
309int
310show_interface_msg(struct imsg *imsg)
311{
312	struct ctl_iface	*iface;
313	char			*netid;
314
315	switch (imsg->hdr.type) {
316	case IMSG_CTL_SHOW_IFACE:
317		iface = imsg->data;
318
319		if (asprintf(&netid, "%s/%d", inet_ntoa(iface->addr),
320		    mask2prefixlen(iface->mask.s_addr)) == -1)
321			err(1, NULL);
322		printf("%-11s %-18s %-10s %-10s %-8s\n",
323		    iface->name, netid, if_state_name(iface->state),
324		    get_linkstate(iface->if_type, iface->linkstate),
325		    iface->uptime == 0 ? "00:00:00" :
326		    fmt_timeframe_core(iface->uptime));
327		free(netid);
328		break;
329	case IMSG_CTL_END:
330		printf("\n");
331		return (1);
332	default:
333		break;
334	}
335
336	return (0);
337}
338
339int
340show_rib_msg(struct imsg *imsg)
341{
342	struct ctl_rt	*rt;
343	char		*dstnet;
344
345	switch (imsg->hdr.type) {
346	case IMSG_CTL_SHOW_RIB:
347		rt = imsg->data;
348		if (asprintf(&dstnet, "%s/%d", inet_ntoa(rt->prefix),
349		    mask2prefixlen(rt->netmask.s_addr)) == -1)
350			err(1, NULL);
351
352		printf("%-20s %-17s %-7d\n", dstnet,
353		    inet_ntoa(rt->nexthop),
354		    rt->metric);
355		free(dstnet);
356
357		break;
358	case IMSG_CTL_END:
359		printf("\n");
360		return (1);
361	default:
362		break;
363	}
364
365	return (0);
366}
367
368int
369show_nbr_msg(struct imsg *imsg)
370{
371	struct ctl_nbr	*nbr;
372	char		*state;
373
374	switch (imsg->hdr.type) {
375	case IMSG_CTL_SHOW_NBR:
376		nbr = imsg->data;
377		if (asprintf(&state, "%s/%s", nbr_state_name(nbr->nbr_state),
378		    if_state_name(nbr->iface_state)) == -1)
379			err(1, NULL);
380		printf("%-15s %-16s", inet_ntoa(nbr->id),
381		    state);
382		printf("%-15s %-10s", inet_ntoa(nbr->addr), nbr->name);
383		printf("%-15s\n", nbr->uptime == 0 ? "-" :
384		    fmt_timeframe_core(nbr->uptime));
385		free(state);
386		break;
387	case IMSG_CTL_END:
388		printf("\n");
389		return (1);
390	default:
391		break;
392	}
393
394	return (0);
395}
396
397void
398show_fib_head(void)
399{
400	printf("flags: * = valid, R = RIP, C = Connected, S = Static\n");
401	printf("%-6s %-20s %-17s\n", "Flags", "Destination", "Nexthop");
402}
403
404int
405show_fib_msg(struct imsg *imsg)
406{
407	struct kroute		*k;
408	char			*p;
409
410	switch (imsg->hdr.type) {
411	case IMSG_CTL_KROUTE:
412		if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(struct kroute))
413			errx(1, "wrong imsg len");
414		k = imsg->data;
415
416		if (k->flags & F_DOWN)
417			printf(" ");
418		else
419			printf("*");
420
421		if (k->flags & F_RIPD_INSERTED)
422			printf("R");
423		else if (k->flags & F_CONNECTED)
424			printf("C");
425		else if (k->flags & F_STATIC)
426			printf("S");
427		else
428			printf(" ");
429
430		printf("     ");
431		if (asprintf(&p, "%s/%u", inet_ntoa(k->prefix),
432		    mask2prefixlen(k->netmask.s_addr)) == -1)
433			err(1, NULL);
434		printf("%-20s ", p);
435		free(p);
436
437		if (k->nexthop.s_addr)
438			printf("%s", inet_ntoa(k->nexthop));
439		else if (k->flags & F_CONNECTED)
440			printf("link#%u", k->ifindex);
441		printf("\n");
442
443		break;
444	case IMSG_CTL_END:
445		printf("\n");
446		return (1);
447	default:
448		break;
449	}
450
451	return (0);
452}
453
454void
455show_interface_head(void)
456{
457	printf("%-15s%-15s%s\n", "Interface", "Flags",
458	    "Link state");
459}
460
461int
462show_fib_interface_msg(struct imsg *imsg)
463{
464	struct kif	*k;
465	uint64_t	 ifms_type;
466
467	switch (imsg->hdr.type) {
468	case IMSG_CTL_IFINFO:
469		k = imsg->data;
470		printf("%-15s", k->ifname);
471		printf("%-15s", k->flags & IFF_UP ? "UP" : "");
472		ifms_type = get_ifms_type(k->if_type);
473		if (ifms_type)
474			printf("%s, ", get_media_descr(ifms_type));
475
476		printf("%s", get_linkstate(k->if_type, k->link_state));
477
478		if (k->link_state != LINK_STATE_DOWN && k->baudrate > 0) {
479			printf(", ");
480			print_baudrate(k->baudrate);
481		}
482		printf("\n");
483		break;
484	case IMSG_CTL_END:
485		printf("\n");
486		return (1);
487	default:
488		break;
489	}
490
491	return (0);
492}
493
494const struct if_status_description
495		if_status_descriptions[] = LINK_STATE_DESCRIPTIONS;
496const struct ifmedia_description
497		ifm_type_descriptions[] = IFM_TYPE_DESCRIPTIONS;
498
499const char *
500get_media_descr(uint64_t media_type)
501{
502	const struct ifmedia_description	*p;
503
504	for (p = ifm_type_descriptions; p->ifmt_string != NULL; p++)
505		if (media_type == p->ifmt_word)
506			return (p->ifmt_string);
507
508	return ("unknown");
509}
510
511const char *
512get_linkstate(uint8_t if_type, int link_state)
513{
514	const struct if_status_description *p;
515	static char buf[8];
516
517	for (p = if_status_descriptions; p->ifs_string != NULL; p++) {
518		if (LINK_STATE_DESC_MATCH(p, if_type, link_state))
519			return (p->ifs_string);
520	}
521	snprintf(buf, sizeof(buf), "[#%d]", link_state);
522	return (buf);
523}
524
525void
526print_baudrate(uint64_t baudrate)
527{
528	if (baudrate > IF_Gbps(1))
529		printf("%llu GBit/s", baudrate / IF_Gbps(1));
530	else if (baudrate > IF_Mbps(1))
531		printf("%llu MBit/s", baudrate / IF_Mbps(1));
532	else if (baudrate > IF_Kbps(1))
533		printf("%llu KBit/s", baudrate / IF_Kbps(1));
534	else
535		printf("%llu Bit/s", baudrate);
536}
537