xref: /openbsd-src/usr.sbin/ospfctl/ospfctl.c (revision f1b790a5738b7375271fee81f99119b1f82f2cfd)
1 /*	$OpenBSD: ospfctl.c,v 1.73 2024/11/21 13:38:14 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/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 "ospf.h"
37 #include "ospfd.h"
38 #include "ospfctl.h"
39 #include "ospfe.h"
40 #include "parser.h"
41 
42 __dead void	 usage(void);
43 
44 int show(struct imsg *, struct parse_result *);
45 
46 struct imsgbuf		*ibuf;
47 const struct output	*output = &show_output;
48 
49 __dead void
50 usage(void)
51 {
52 	extern char *__progname;
53 
54 	fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n",
55 	    __progname);
56 	exit(1);
57 }
58 
59 int
60 main(int argc, char *argv[])
61 {
62 	struct sockaddr_un	 sun;
63 	struct parse_result	*res;
64 	struct imsg		 imsg;
65 	unsigned int		 ifidx = 0;
66 	int			 ctl_sock, r;
67 	int			 done = 0;
68 	int			 n, verbose = 0;
69 	int			 ch;
70 	char			*sockname;
71 
72 	r = getrtable();
73 	if (asprintf(&sockname, "%s.%d", OSPFD_SOCKET, r) == -1)
74 		err(1, "asprintf");
75 
76 	while ((ch = getopt(argc, argv, "s:")) != -1) {
77 		switch (ch) {
78 		case 's':
79 			sockname = optarg;
80 			break;
81 		default:
82 			usage();
83 			/* NOTREACHED */
84 		}
85 	}
86 	argc -= optind;
87 	argv += optind;
88 
89 	/* parse options */
90 	if ((res = parse(argc, argv)) == NULL)
91 		exit(1);
92 
93 	/* connect to ospfd control socket */
94 	if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
95 		err(1, "socket");
96 
97 	bzero(&sun, sizeof(sun));
98 	sun.sun_family = AF_UNIX;
99 	strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path));
100 
101 	if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
102 		err(1, "connect: %s", sockname);
103 
104 	if (pledge("stdio", NULL) == -1)
105 		err(1, "pledge");
106 
107 	if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
108 		err(1, NULL);
109 	if (imsgbuf_init(ibuf, ctl_sock) == -1)
110 		err(1, NULL);
111 	done = 0;
112 
113 	/* process user request */
114 	switch (res->action) {
115 	case NONE:
116 		usage();
117 		/* not reached */
118 	case SHOW:
119 	case SHOW_SUM:
120 		imsg_compose(ibuf, IMSG_CTL_SHOW_SUM, 0, 0, -1, NULL, 0);
121 		break;
122 	case SHOW_IFACE:
123 	case SHOW_IFACE_DTAIL:
124 		if (*res->ifname) {
125 			ifidx = if_nametoindex(res->ifname);
126 			if (ifidx == 0)
127 				errx(1, "no such interface %s", res->ifname);
128 		}
129 		imsg_compose(ibuf, IMSG_CTL_SHOW_INTERFACE, 0, 0, -1,
130 		    &ifidx, sizeof(ifidx));
131 		break;
132 	case SHOW_NBR:
133 	case SHOW_NBR_DTAIL:
134 		imsg_compose(ibuf, IMSG_CTL_SHOW_NBR, 0, 0, -1, NULL, 0);
135 		break;
136 	case SHOW_DB:
137 		imsg_compose(ibuf, IMSG_CTL_SHOW_DATABASE, 0, 0, -1, NULL, 0);
138 		break;
139 	case SHOW_DBBYAREA:
140 		imsg_compose(ibuf, IMSG_CTL_SHOW_DATABASE, 0, 0, -1,
141 		    &res->addr, sizeof(res->addr));
142 		break;
143 	case SHOW_DBEXT:
144 		imsg_compose(ibuf, IMSG_CTL_SHOW_DB_EXT, 0, 0, -1, NULL, 0);
145 		break;
146 	case SHOW_DBNET:
147 		imsg_compose(ibuf, IMSG_CTL_SHOW_DB_NET, 0, 0, -1, NULL, 0);
148 		break;
149 	case SHOW_DBRTR:
150 		imsg_compose(ibuf, IMSG_CTL_SHOW_DB_RTR, 0, 0, -1, NULL, 0);
151 		break;
152 	case SHOW_DBSELF:
153 		imsg_compose(ibuf, IMSG_CTL_SHOW_DB_SELF, 0, 0, -1, NULL, 0);
154 		break;
155 	case SHOW_DBSUM:
156 		imsg_compose(ibuf, IMSG_CTL_SHOW_DB_SUM, 0, 0, -1, NULL, 0);
157 		break;
158 	case SHOW_DBASBR:
159 		imsg_compose(ibuf, IMSG_CTL_SHOW_DB_ASBR, 0, 0, -1, NULL, 0);
160 		break;
161 	case SHOW_DBOPAQ:
162 		imsg_compose(ibuf, IMSG_CTL_SHOW_DB_OPAQ, 0, 0, -1, NULL, 0);
163 		break;
164 	case SHOW_RIB:
165 	case SHOW_RIB_DTAIL:
166 		imsg_compose(ibuf, IMSG_CTL_SHOW_RIB, 0, 0, -1, NULL, 0);
167 		break;
168 	case SHOW_FIB:
169 		if (!res->addr.s_addr)
170 			imsg_compose(ibuf, IMSG_CTL_KROUTE, 0, 0, -1,
171 			    &res->flags, sizeof(res->flags));
172 		else
173 			imsg_compose(ibuf, IMSG_CTL_KROUTE_ADDR, 0, 0, -1,
174 			    &res->addr, sizeof(res->addr));
175 		break;
176 	case SHOW_FIB_IFACE:
177 		if (*res->ifname)
178 			imsg_compose(ibuf, IMSG_CTL_IFINFO, 0, 0, -1,
179 			    res->ifname, sizeof(res->ifname));
180 		else
181 			imsg_compose(ibuf, IMSG_CTL_IFINFO, 0, 0, -1, NULL, 0);
182 		break;
183 	case FIB:
184 		errx(1, "fib couple|decouple");
185 		break;
186 	case FIB_COUPLE:
187 		imsg_compose(ibuf, IMSG_CTL_FIB_COUPLE, 0, 0, -1, NULL, 0);
188 		printf("couple request sent.\n");
189 		done = 1;
190 		break;
191 	case FIB_DECOUPLE:
192 		imsg_compose(ibuf, IMSG_CTL_FIB_DECOUPLE, 0, 0, -1, NULL, 0);
193 		printf("decouple request sent.\n");
194 		done = 1;
195 		break;
196 	case FIB_RELOAD:
197 		imsg_compose(ibuf, IMSG_CTL_FIB_RELOAD, 0, 0, -1, NULL, 0);
198 		printf("reload request sent.\n");
199 		done = 1;
200 		break;
201 	case LOG_VERBOSE:
202 		verbose = 1;
203 		/* FALLTHROUGH */
204 	case LOG_BRIEF:
205 		imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
206 		    &verbose, sizeof(verbose));
207 		printf("logging request sent.\n");
208 		done = 1;
209 		break;
210 	case RELOAD:
211 		imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
212 		printf("reload request sent.\n");
213 		done = 1;
214 		break;
215 	}
216 
217 	if (imsgbuf_flush(ibuf) == -1)
218 		err(1, "write error");
219 
220 	/* no output for certain commands such as log verbose */
221 	if (!done) {
222 		output->head(res);
223 
224 		while (!done) {
225 			if ((n = imsgbuf_read(ibuf)) == -1)
226 				err(1, "read error");
227 			if (n == 0)
228 				errx(1, "pipe closed");
229 
230 			while (!done) {
231 				if ((n = imsg_get(ibuf, &imsg)) == -1)
232 					errx(1, "imsg_get error");
233 				if (n == 0)
234 					break;
235 
236 				done = show(&imsg, res);
237 				imsg_free(&imsg);
238 			}
239 		}
240 
241 		output->tail();
242 	}
243 
244 	close(ctl_sock);
245 	free(ibuf);
246 
247 	return (0);
248 }
249 
250 int
251 show(struct imsg *imsg, struct parse_result *res)
252 {
253 	struct ctl_sum		*sum;
254 	struct ctl_sum_area	*sumarea;
255 	struct ctl_iface	*ctliface;
256 	struct ctl_nbr		*nbr;
257 	struct ctl_rt		*rt;
258 	struct kroute		*k;
259 	struct kif		*kif;
260 	static struct in_addr	 area_id;
261 	struct area		*area;
262 	static u_int8_t		 lasttype;
263 	static char		 ifname[IF_NAMESIZE];
264 	struct iface		*iface;
265 	struct lsa		*lsa;
266 	struct lsa_hdr		*lsa_hdr;
267 
268 	switch (imsg->hdr.type) {
269 	case IMSG_CTL_SHOW_SUM:
270 		sum = imsg->data;
271 		output->summary(sum);
272 		break;
273 	case IMSG_CTL_SHOW_SUM_AREA:
274 		sumarea = imsg->data;
275 		output->summary_area(sumarea);
276 		break;
277 	case IMSG_CTL_SHOW_INTERFACE:
278 		ctliface = imsg->data;
279 		if(res->action == SHOW_IFACE_DTAIL)
280 			output->interface(ctliface, 1);
281 		else
282 			output->interface(ctliface, 0);
283 		break;
284 	case IMSG_CTL_SHOW_NBR:
285 		nbr = imsg->data;
286 		if(res->action == SHOW_NBR_DTAIL)
287 			output->neighbor(nbr, 1);
288 		else
289 			output->neighbor(nbr, 0);
290 		break;
291 	case IMSG_CTL_SHOW_RIB:
292 		rt = imsg->data;
293 		if(res->action == SHOW_RIB_DTAIL)
294 			output->rib(rt, 1);
295 		else
296 			output->rib(rt, 0);
297 		break;
298 	case IMSG_CTL_KROUTE:
299 		if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(struct kroute))
300 			errx(1, "wrong imsg len");
301 		k = imsg->data;
302 		output->fib(k);
303 		break;
304 	case IMSG_CTL_IFINFO:
305 		kif = imsg->data;
306 		output->fib_interface(kif);
307 		break;
308 	case IMSG_CTL_SHOW_DB_EXT:
309 	case IMSG_CTL_SHOW_DB_NET:
310 	case IMSG_CTL_SHOW_DB_RTR:
311 	case IMSG_CTL_SHOW_DB_SUM:
312 	case IMSG_CTL_SHOW_DB_ASBR:
313 	case IMSG_CTL_SHOW_DB_OPAQ:
314 		lsa = imsg->data;
315 		output->db(lsa, area_id, lasttype, ifname);
316 		lasttype = lsa->hdr.type;
317 		break;
318 	case IMSG_CTL_SHOW_DATABASE:
319 	case IMSG_CTL_SHOW_DB_SELF:
320 		lsa_hdr = imsg->data;
321 		output->db_simple(lsa_hdr, area_id, lasttype, ifname);
322 		lasttype = lsa_hdr->type;
323 		break;
324 	case IMSG_CTL_AREA:
325 		area = imsg->data;
326 		area_id = area->id;
327 		lasttype = 0;
328 		break;
329 	case IMSG_CTL_IFACE:
330 		iface = imsg->data;
331 		strlcpy(ifname, iface->name, sizeof(ifname));
332 		lasttype = 0;
333 		break;
334 	case IMSG_CTL_END:
335 		return (1);
336 	default:
337 		warnx("unknown imsg %d received", imsg->hdr.type);
338 		break;
339 	}
340 
341 	return (0);
342 }
343 
344 uint64_t
345 get_ifms_type(uint8_t if_type)
346 {
347 	switch (if_type) {
348 	case IFT_ETHER:
349 		return (IFM_ETHER);
350 	case IFT_FDDI:
351 		return (IFM_FDDI);
352 	case IFT_CARP:
353 		return (IFM_CARP);
354 	case IFT_PPP:
355 		return (IFM_TDM);
356 	default:
357 		return (0);
358 	}
359 }
360 
361 const char *
362 print_link(int state)
363 {
364 	if (state & IFF_UP)
365 		return ("UP");
366 	else
367 		return ("DOWN");
368 }
369 
370 #define TF_BUFS	8
371 #define TF_LEN	9
372 
373 const char *
374 fmt_timeframe_core(time_t t)
375 {
376 	char			*buf;
377 	static char		 tfbuf[TF_BUFS][TF_LEN];/* ring buffer */
378 	static int		 idx = 0;
379 	unsigned int		 sec, min, hrs, day;
380 	unsigned long long	 week;
381 
382 	if (t == 0)
383 		return ("00:00:00");
384 
385 	buf = tfbuf[idx++];
386 	if (idx == TF_BUFS)
387 		idx = 0;
388 
389 	week = t;
390 
391 	sec = week % 60;
392 	week /= 60;
393 	min = week % 60;
394 	week /= 60;
395 	hrs = week % 24;
396 	week /= 24;
397 	day = week % 7;
398 	week /= 7;
399 
400 	if (week > 0)
401 		snprintf(buf, TF_LEN, "%02lluw%01ud%02uh", week, day, hrs);
402 	else if (day > 0)
403 		snprintf(buf, TF_LEN, "%01ud%02uh%02um", day, hrs, min);
404 	else
405 		snprintf(buf, TF_LEN, "%02u:%02u:%02u", hrs, min, sec);
406 
407 	return (buf);
408 }
409 
410 const char *
411 log_id(u_int32_t id)
412 {
413 	static char	buf[48];
414 	struct in_addr	addr;
415 
416 	addr.s_addr = id;
417 
418 	if (inet_ntop(AF_INET, &addr, buf, sizeof(buf)) == NULL)
419 		return ("?");
420 	else
421 		return (buf);
422 }
423 
424 const char *
425 log_adv_rtr(u_int32_t adv_rtr)
426 {
427 	static char	buf[48];
428 	struct in_addr	addr;
429 
430 	addr.s_addr = adv_rtr;
431 
432 	if (inet_ntop(AF_INET, &addr, buf, sizeof(buf)) == NULL)
433 		return ("?");
434 	else
435 		return (buf);
436 }
437 
438 /* prototype defined in ospfd.h and shared with the kroute.c version */
439 u_int8_t
440 mask2prefixlen(in_addr_t ina)
441 {
442 	if (ina == 0)
443 		return (0);
444 	else
445 		return (33 - ffs(ntohl(ina)));
446 }
447 
448 char *
449 print_ls_type(u_int8_t type)
450 {
451 	switch (type) {
452 	case LSA_TYPE_ROUTER:
453 		return ("Router");
454 	case LSA_TYPE_NETWORK:
455 		return ("Network");
456 	case LSA_TYPE_SUM_NETWORK:
457 		return ("Summary (Network)");
458 	case LSA_TYPE_SUM_ROUTER:
459 		return ("Summary (Router)");
460 	case LSA_TYPE_EXTERNAL:
461 		return ("AS External");
462 	case LSA_TYPE_LINK_OPAQ:
463 		return ("Type-9 Opaque");
464 	case LSA_TYPE_AREA_OPAQ:
465 		return ("Type-10 Opaque");
466 	case LSA_TYPE_AS_OPAQ:
467 		return ("Type-11 Opaque");
468 	default:
469 		return ("Unknown");
470 	}
471 }
472 
473 char *
474 print_rtr_link_type(u_int8_t type)
475 {
476 	switch (type) {
477 	case LINK_TYPE_POINTTOPOINT:
478 		return ("Point-to-Point");
479 	case LINK_TYPE_TRANSIT_NET:
480 		return ("Transit Network");
481 	case LINK_TYPE_STUB_NET:
482 		return ("Stub Network");
483 	case LINK_TYPE_VIRTUAL:
484 		return ("Virtual Link");
485 	default:
486 		return ("Unknown");
487 	}
488 }
489 
490 const char *
491 print_ospf_flags(u_int8_t opts)
492 {
493 	static char	optbuf[32];
494 
495 	snprintf(optbuf, sizeof(optbuf), "*|*|*|*|*|%s|%s|%s",
496 	    opts & OSPF_RTR_V ? "V" : "-",
497 	    opts & OSPF_RTR_E ? "E" : "-",
498 	    opts & OSPF_RTR_B ? "B" : "-");
499 	return (optbuf);
500 }
501 
502 const char *
503 print_ospf_options(u_int8_t opts)
504 {
505 	static char	optbuf[32];
506 
507 	snprintf(optbuf, sizeof(optbuf), "%s|%s|%s|%s|%s|%s|%s|%s",
508 	    opts & OSPF_OPTION_DN ? "DN" : "-",
509 	    opts & OSPF_OPTION_O ? "O" : "-",
510 	    opts & OSPF_OPTION_DC ? "DC" : "-",
511 	    opts & OSPF_OPTION_EA ? "EA" : "-",
512 	    opts & OSPF_OPTION_NP ? "N/P" : "-",
513 	    opts & OSPF_OPTION_MC ? "MC" : "-",
514 	    opts & OSPF_OPTION_E ? "E" : "-",
515 	    opts & OSPF_OPTION_MT ? "MT" : "-");
516 	return (optbuf);
517 }
518 
519 const char *
520 print_ospf_rtr_flags(u_int8_t opts)
521 {
522 	static char	optbuf[32];
523 
524 	snprintf(optbuf, sizeof(optbuf), "%s%s%s",
525 	    opts & OSPF_RTR_E ? "AS" : "",
526 	    opts & OSPF_RTR_E && opts & OSPF_RTR_B ? "+" : "",
527 	    opts & OSPF_RTR_B ? "ABR" : "");
528 	return (optbuf);
529 }
530 
531 const struct if_status_description
532 		if_status_descriptions[] = LINK_STATE_DESCRIPTIONS;
533 const struct ifmedia_description
534 		ifm_type_descriptions[] = IFM_TYPE_DESCRIPTIONS;
535 
536 const char *
537 get_media_descr(uint64_t media_type)
538 {
539 	const struct ifmedia_description	*p;
540 
541 	for (p = ifm_type_descriptions; p->ifmt_string != NULL; p++)
542 		if (media_type == p->ifmt_word)
543 			return (p->ifmt_string);
544 
545 	return ("unknown");
546 }
547 
548 const char *
549 get_linkstate(uint8_t if_type, int link_state)
550 {
551 	const struct if_status_description *p;
552 	static char buf[8];
553 
554 	for (p = if_status_descriptions; p->ifs_string != NULL; p++) {
555 		if (LINK_STATE_DESC_MATCH(p, if_type, link_state))
556 			return (p->ifs_string);
557 	}
558 	snprintf(buf, sizeof(buf), "[#%d]", link_state);
559 	return (buf);
560 }
561 
562 const char *
563 print_baudrate(u_int64_t baudrate)
564 {
565 	static char	buf[32];
566 
567 	if (baudrate > IF_Gbps(1))
568 		snprintf(buf, sizeof(buf), "%llu GBit/s", baudrate / IF_Gbps(1));
569 	else if (baudrate > IF_Mbps(1))
570 		snprintf(buf, sizeof(buf), "%llu MBit/s", baudrate / IF_Mbps(1));
571 	else if (baudrate > IF_Kbps(1))
572 		snprintf(buf, sizeof(buf), "%llu KBit/s", baudrate / IF_Kbps(1));
573 	else
574 		snprintf(buf, sizeof(buf), "%llu Bit/s", baudrate);
575 	return (buf);
576 }
577