xref: /openbsd-src/usr.sbin/mrinfo/mrinfo.c (revision 347b73737b960ba02354886b66fadea4b3ddc405)
1 /*	$NetBSD: mrinfo.c,v 1.4 1995/12/10 11:00:51 mycroft Exp $	*/
2 
3 /*
4  * This tool requests configuration info from a multicast router
5  * and prints the reply (if any).  Invoke it as:
6  *
7  *	mrinfo router-name-or-address
8  *
9  * Written Wed Mar 24 1993 by Van Jacobson (adapted from the
10  * multicast mapper written by Pavel Curtis).
11  *
12  * The lawyers insist we include the following UC copyright notice.
13  * The mapper from which this is derived contained a Xerox copyright
14  * notice which follows the UC one.  Try not to get depressed noting
15  * that the legal gibberish is larger than the program.
16  *
17  * Copyright (c) 1993 Regents of the University of California.
18  * All rights reserved.
19  *
20  * Redistribution and use in source and binary forms, with or without
21  * modification, are permitted provided that the following conditions
22  * are met:
23  * 1. Redistributions of source code must retain the above copyright
24  *    notice, this list of conditions and the following disclaimer.
25  * 2. Redistributions in binary form must reproduce the above copyright
26  *    notice, this list of conditions and the following disclaimer in the
27  *    documentation and/or other materials provided with the distribution.
28  * 3. All advertising materials mentioning features or use of this software
29  *    must display the following acknowledgement:
30  *	This product includes software developed by the Computer Systems
31  *	Engineering Group at Lawrence Berkeley Laboratory.
32  * 4. Neither the name of the University nor of the Laboratory may be used
33  *    to endorse or promote products derived from this software without
34  *    specific prior written permission.
35  *
36  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
37  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
39  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
40  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
41  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
42  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
44  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
45  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ---------------------------------
48  * Copyright (c) 1992, 2001 Xerox Corporation.  All rights reserved.
49  *
50  * Redistribution and use in source and binary forms, with or without modification,
51  * are permitted provided that the following conditions are met:
52  *
53  * Redistributions of source code must retain the above copyright notice,
54  * this list of conditions and the following disclaimer.
55  *
56  * Redistributions in binary form must reproduce the above copyright notice,
57  * this list of conditions and the following disclaimer in the documentation
58  * and/or other materials provided with the distribution.
59 
60  * Neither name of the Xerox, PARC, nor the names of its contributors may be used
61  * to endorse or promote products derived from this software
62  * without specific prior written permission.
63  *
64  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
65  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
66  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
67  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE XEROX CORPORATION OR CONTRIBUTORS
68  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
69  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
70  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
71  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
72  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
73  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
74  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
75  */
76 
77 #include <string.h>
78 #include <netdb.h>
79 #include <sys/time.h>
80 #include "defs.h"
81 #include <arpa/inet.h>
82 #include <stdarg.h>
83 #include <poll.h>
84 #include <limits.h>
85 #include <err.h>
86 
87 #define DEFAULT_TIMEOUT	4	/* How long to wait before retrying requests */
88 #define DEFAULT_RETRIES 3	/* How many times to ask each router */
89 
90 u_int32_t	our_addr, target_addr = 0;	/* in NET order */
91 int     debug = 0;
92 int	nflag = 0;
93 int     retries = DEFAULT_RETRIES;
94 int     timeout = DEFAULT_TIMEOUT;
95 int	target_level = 0;
96 vifi_t  numvifs;		/* to keep loader happy */
97 				/* (see COPY_TABLES macro called in kern.c) */
98 
99 char		*inet_name(u_int32_t addr);
100 void		ask(u_int32_t dst);
101 void		ask2(u_int32_t dst);
102 void		usage(void);
103 
104 char *
105 inet_name(u_int32_t addr)
106 {
107 	struct hostent *e;
108 	struct in_addr in;
109 
110 	if (addr == 0)
111 		return "local";
112 
113 	if (nflag ||
114 	    (e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET)) == NULL) {
115 		in.s_addr = addr;
116 		return (inet_ntoa(in));
117 	}
118 	return (e->h_name);
119 }
120 
121 /*
122  * Log errors and other messages to stderr, according to the severity of the
123  * message and the current debug level.  For errors of severity LOG_ERR or
124  * worse, terminate the program.
125  */
126 void
127 logit(int severity, int syserr, char *format, ...)
128 {
129 	va_list ap;
130 
131 	switch (debug) {
132 	case 0:
133 		if (severity > LOG_WARNING)
134 			return;
135 	case 1:
136 		if (severity > LOG_NOTICE)
137 			return;
138 	case 2:
139 		if (severity > LOG_INFO)
140 			return;
141 	default:
142 		if (severity == LOG_WARNING)
143 			fprintf(stderr, "warning - ");
144 		va_start(ap, format);
145 		vfprintf(stderr, format, ap);
146 		va_end(ap);
147 		if (syserr == 0)
148 			fputc('\n', stderr);
149 		else if (syserr < sys_nerr)
150 			fprintf(stderr, ": %s\n", sys_errlist[syserr]);
151 		else
152 			fprintf(stderr, ": errno %d\n", syserr);
153 	}
154 
155 	if (severity <= LOG_ERR)
156 		exit(1);
157 }
158 
159 /*
160  * Send a neighbors-list request.
161  */
162 void
163 ask(u_int32_t dst)
164 {
165 	send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
166 	    htonl(MROUTED_LEVEL), 0);
167 }
168 
169 void
170 ask2(u_int32_t dst)
171 {
172 	send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
173 	    htonl(MROUTED_LEVEL), 0);
174 }
175 
176 /*
177  * Process an incoming neighbor-list message.
178  */
179 void
180 accept_neighbors(u_int32_t src, u_int32_t dst, u_char *p, int datalen,
181     u_int32_t level)
182 {
183 	u_char *ep = p + datalen;
184 
185 #define GET_ADDR(a) (a = ((u_int32_t)*p++ << 24), a += ((u_int32_t)*p++ << 16),\
186 		     a += ((u_int32_t)*p++ << 8), a += *p++)
187 
188 	printf("%s (%s):\n", inet_fmt(src, s1), inet_name(src));
189 	while (p < ep) {
190 		u_char metric, thresh;
191 		u_int32_t laddr;
192 		int ncount;
193 
194 		GET_ADDR(laddr);
195 		laddr = htonl(laddr);
196 		metric = *p++;
197 		thresh = *p++;
198 		ncount = *p++;
199 		while (--ncount >= 0) {
200 			u_int32_t neighbor;
201 
202 			GET_ADDR(neighbor);
203 			neighbor = htonl(neighbor);
204 			printf("  %s -> ", inet_fmt(laddr, s1));
205 			printf("%s (%s) [%d/%d]\n", inet_fmt(neighbor, s1),
206 			    inet_name(neighbor), metric, thresh);
207 		}
208 	}
209 }
210 
211 void
212 accept_neighbors2(u_int32_t src, u_int32_t dst, u_char *p, int datalen,
213     u_int32_t level)
214 {
215 	u_char *ep = p + datalen;
216 	u_int broken_cisco = ((level & 0xffff) == 0x020a); /* 10.2 */
217 	/* well, only possibly_broken_cisco, but that's too long to type. */
218 
219 	printf("%s (%s) [version %d.%d", inet_fmt(src, s1), inet_name(src),
220 	    level & 0xff, (level >> 8) & 0xff);
221 	if ((level >> 16) & NF_LEAF)
222 		printf (",leaf");
223 	if ((level >> 16) & NF_PRUNE)
224 		printf (",prune");
225 	if ((level >> 16) & NF_GENID)
226 		printf (",genid");
227 	if ((level >> 16) & NF_MTRACE)
228 		printf (",mtrace");
229 	printf ("]:\n");
230 
231 	while (p < ep) {
232 		u_char metric, thresh, flags;
233 		u_int32_t laddr = *(u_int32_t*)p;
234 		int ncount;
235 
236 		p += 4;
237 		metric = *p++;
238 		thresh = *p++;
239 		flags = *p++;
240 		ncount = *p++;
241 		if (broken_cisco && ncount == 0)	/* dumb Ciscos */
242 			ncount = 1;
243 		if (broken_cisco && ncount > 15)	/* dumb Ciscos */
244 			ncount = ncount & 0xf;
245 		while (--ncount >= 0 && p < ep) {
246 			u_int32_t neighbor = *(u_int32_t*)p;
247 			p += 4;
248 			printf("  %s -> ", inet_fmt(laddr, s1));
249 			printf("%s (%s) [%d/%d", inet_fmt(neighbor, s1),
250 			    inet_name(neighbor), metric, thresh);
251 			if (flags & DVMRP_NF_TUNNEL)
252 				printf("/tunnel");
253 			if (flags & DVMRP_NF_SRCRT)
254 				printf("/srcrt");
255 			if (flags & DVMRP_NF_PIM)
256 				printf("/pim");
257 			if (flags & DVMRP_NF_QUERIER)
258 				printf("/querier");
259 			if (flags & DVMRP_NF_DISABLED)
260 				printf("/disabled");
261 			if (flags & DVMRP_NF_DOWN)
262 				printf("/down");
263 			if (flags & DVMRP_NF_LEAF)
264 				printf("/leaf");
265 			printf("]\n");
266 		}
267 	}
268 }
269 
270 void
271 usage()
272 {
273 	fprintf(stderr,
274 	    "Usage: mrinfo [-d[debug_level]] [-n] [-t timeout] [-r retries] [router]\n");
275 	exit(1);
276 }
277 
278 int
279 main(int argc, char *argv[])
280 {
281 	int tries, trynew, curaddr, udp, ch;
282 	struct hostent *hp, bogus;
283 	struct sockaddr_in addr;
284 	socklen_t addrlen;
285 	struct timeval et;
286 	char *host;
287 	uid_t uid;
288 	const char *errstr;
289 
290 	if (geteuid() != 0) {
291 		fprintf(stderr, "mrinfo: must be root\n");
292 		exit(1);
293 	}
294 
295 	init_igmp();
296 
297 	uid = getuid();
298 	if (setresuid(uid, uid, uid) == -1)
299 		err(1, "setresuid");
300 
301 	setvbuf(stderr, NULL, _IOLBF, 0);
302 
303 	while ((ch = getopt(argc, argv, "d::nr:t:")) != -1) {
304 		switch (ch) {
305 		case 'd':
306 			if (!optarg)
307 				debug = DEFAULT_DEBUG;
308 			else {
309 				debug = strtonum(optarg, 0, 3, &errstr);
310 				if (errstr) {
311 					warnx("debug level %s", errstr);
312 					debug = DEFAULT_DEBUG;
313 				}
314 			}
315 			break;
316 		case 'n':
317 			++nflag;
318 			break;
319 		case 'r':
320 			retries = strtonum(optarg, 0, INT_MAX, &errstr);
321 			if (errstr) {
322 				warnx("retries %s", errstr);
323 				usage();
324 			}
325 			break;
326 		case 't':
327 			timeout = strtonum(optarg, 0, INT_MAX, &errstr);
328 			if (errstr) {
329 				warnx("timeout %s", errstr);
330 				usage();
331 			}
332 			break;
333 		default:
334 			usage();
335 		}
336 	}
337 	argc -= optind;
338 	argv += optind;
339 
340 	if (argc > 1)
341 		usage();
342 	if (argc == 1)
343 		host = argv[0];
344 	else
345 		host = "127.0.0.1";
346 
347 	if ((target_addr = inet_addr(host)) != -1) {
348 		hp = &bogus;
349 		hp->h_length = sizeof(target_addr);
350 		if (!(hp->h_addr_list = calloc(2, sizeof(char *))))
351 			err(1, "can't allocate memory");
352 		if (!(hp->h_addr_list[0] = malloc(hp->h_length)))
353 			err(1, "can't allocate memory");
354 		memcpy(hp->h_addr_list[0], &target_addr, hp->h_length);
355 		hp->h_addr_list[1] = 0;
356 	} else
357 		hp = gethostbyname(host);
358 
359 	if (hp == NULL || hp->h_length != sizeof(target_addr)) {
360 		fprintf(stderr, "mrinfo: %s: no such host\n", argv[0]);
361 		exit(1);
362 	}
363 	if (debug)
364 		fprintf(stderr, "Debug level %u\n", debug);
365 
366 	/* Check all addresses; mrouters often have unreachable interfaces */
367 	for (curaddr = 0; hp->h_addr_list[curaddr] != NULL; curaddr++) {
368 		memcpy(&target_addr, hp->h_addr_list[curaddr], hp->h_length);
369 		/* Find a good local address for us. */
370 		addrlen = sizeof(addr);
371 		memset(&addr, 0, sizeof addr);
372 		addr.sin_family = AF_INET;
373 		addr.sin_len = sizeof addr;
374 		addr.sin_addr.s_addr = target_addr;
375 		addr.sin_port = htons(2000);	/* any port over 1024 will
376 						 * do... */
377 		if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) == -1 ||
378 		    connect(udp, (struct sockaddr *) & addr, sizeof(addr)) == -1 ||
379 		    getsockname(udp, (struct sockaddr *) & addr, &addrlen) == -1) {
380 			perror("Determining local address");
381 			exit(1);
382 		}
383 		close(udp);
384 		our_addr = addr.sin_addr.s_addr;
385 
386 		tries = 0;
387 		trynew = 1;
388 		/*
389 		 * New strategy: send 'ask2' for two timeouts, then fall back
390 		 * to 'ask', since it's not very likely that we are going to
391 		 * find someone who only responds to 'ask' these days
392 		 */
393 		ask2(target_addr);
394 
395 		gettimeofday(&et, 0);
396 		et.tv_sec += timeout;
397 
398 		/* Main receive loop */
399 		for (;;) {
400 			int count, recvlen, ipdatalen, iphdrlen, igmpdatalen;
401 			u_int32_t src, dst, group;
402 			struct timeval tv, now;
403 			socklen_t dummy = 0;
404 			struct igmp *igmp;
405 			struct ip *ip;
406 			struct pollfd pfd[1];
407 
408 			pfd[0].fd = igmp_socket;
409 			pfd[0].events = POLLIN;
410 
411 			gettimeofday(&now, 0);
412 			tv.tv_sec = et.tv_sec - now.tv_sec;
413 			tv.tv_usec = et.tv_usec - now.tv_usec;
414 
415 			if (tv.tv_usec < 0) {
416 				tv.tv_usec += 1000000L;
417 				--tv.tv_sec;
418 			}
419 			if (tv.tv_sec < 0)
420 				timerclear(&tv);
421 
422 			count = poll(pfd, 1, tv.tv_sec * 1000);
423 
424 			if (count == -1) {
425 				if (errno != EINTR)
426 					perror("select");
427 				continue;
428 			} else if (count == 0) {
429 				logit(LOG_DEBUG, 0,
430 				    "Timed out receiving neighbor lists");
431 				if (++tries > retries)
432 					break;
433 				/* If we've tried ASK_NEIGHBORS2 twice with
434 				 * no response, fall back to ASK_NEIGHBORS
435 				 */
436 				if (tries == 2 && target_level == 0)
437 					trynew = 0;
438 				if (target_level == 0 && trynew == 0)
439 					ask(target_addr);
440 				else
441 					ask2(target_addr);
442 				gettimeofday(&et, 0);
443 				et.tv_sec += timeout;
444 				continue;
445 			}
446 			recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
447 			    0, NULL, &dummy);
448 			if (recvlen <= 0) {
449 				if (recvlen && errno != EINTR)
450 					perror("recvfrom");
451 				continue;
452 			}
453 
454 			if (recvlen < sizeof(struct ip)) {
455 				logit(LOG_WARNING, 0,
456 				    "packet too short (%u bytes) for IP header",
457 				    recvlen);
458 				continue;
459 			}
460 			ip = (struct ip *) recv_buf;
461 			if (ip->ip_p == 0)
462 				continue;	/* Request to install cache entry */
463 			src = ip->ip_src.s_addr;
464 			dst = ip->ip_dst.s_addr;
465 			iphdrlen = ip->ip_hl << 2;
466 			ipdatalen = ntohs(ip->ip_len) - iphdrlen;
467 			if (iphdrlen + ipdatalen != recvlen) {
468 				logit(LOG_WARNING, 0,
469 				    "packet shorter (%u bytes) than "
470 				    "hdr+data length (%u+%u)",
471 				    recvlen, iphdrlen, ipdatalen);
472 				continue;
473 			}
474 			igmp = (struct igmp *) (recv_buf + iphdrlen);
475 			group = igmp->igmp_group.s_addr;
476 			igmpdatalen = ipdatalen - IGMP_MINLEN;
477 			if (igmpdatalen < 0) {
478 				logit(LOG_WARNING, 0,
479 				    "IP data field too short (%u bytes) "
480 				    "for IGMP, from %s",
481 				    ipdatalen, inet_fmt(src, s1));
482 				continue;
483 			}
484 			if (igmp->igmp_type != IGMP_DVMRP)
485 				continue;
486 
487 			switch (igmp->igmp_code) {
488 			case DVMRP_NEIGHBORS:
489 			case DVMRP_NEIGHBORS2:
490 				if (src != target_addr) {
491 					fprintf(stderr, "mrinfo: got reply from %s",
492 					    inet_fmt(src, s1));
493 					fprintf(stderr, " instead of %s\n",
494 					    inet_fmt(target_addr, s1));
495 					/*continue;*/
496 				}
497 				break;
498 			default:
499 				continue;	/* ignore all other DVMRP messages */
500 			}
501 
502 			switch (igmp->igmp_code) {
503 			case DVMRP_NEIGHBORS:
504 				if (group) {
505 					/* knows about DVMRP_NEIGHBORS2 msg */
506 					if (target_level == 0) {
507 						target_level = ntohl(group);
508 						ask2(target_addr);
509 					}
510 				} else {
511 					accept_neighbors(src, dst, (u_char *)(igmp + 1),
512 					    igmpdatalen, ntohl(group));
513 					exit(0);
514 				}
515 				break;
516 			case DVMRP_NEIGHBORS2:
517 				accept_neighbors2(src, dst, (u_char *)(igmp + 1),
518 				    igmpdatalen, ntohl(group));
519 				exit(0);
520 			}
521 		}
522 	}
523 	exit(1);
524 }
525 
526 /* dummies */
527 void
528 accept_probe(u_int32_t src, u_int32_t dst, char *p, int datalen,
529     u_int32_t level)
530 {
531 }
532 
533 void
534 accept_group_report(u_int32_t src, u_int32_t dst, u_int32_t group, int r_type)
535 {
536 }
537 
538 void
539 accept_neighbor_request2(u_int32_t src, u_int32_t dst)
540 {
541 }
542 
543 void
544 accept_report(u_int32_t src, u_int32_t dst, char *p, int datalen,
545     u_int32_t level)
546 {
547 }
548 
549 void
550 accept_neighbor_request(u_int32_t src, u_int32_t dst)
551 {
552 }
553 
554 void
555 accept_prune(u_int32_t src, u_int32_t dst, char *p, int datalen)
556 {
557 }
558 
559 void
560 accept_graft(u_int32_t src, u_int32_t dst, char *p, int datalen)
561 {
562 }
563 
564 void
565 accept_g_ack(u_int32_t src, u_int32_t dst, char *p, int datalen)
566 {
567 }
568 
569 void
570 add_table_entry(u_int32_t origin, u_int32_t mcastgrp)
571 {
572 }
573 
574 void
575 check_vif_state(void)
576 {
577 }
578 
579 void
580 accept_leave_message(u_int32_t src, u_int32_t dst, u_int32_t group)
581 {
582 }
583 
584 void
585 accept_mtrace(u_int32_t src, u_int32_t dst, u_int32_t group, char *data,
586     u_int no, int datalen)
587 {
588 }
589 
590 void
591 accept_membership_query(u_int32_t src, u_int32_t dst, u_int32_t group, int tmo)
592 {
593 }
594 
595 void
596 accept_info_request(u_int32_t src, u_int32_t dst, u_char *p, int datalen)
597 {
598 }
599 
600 void
601 accept_info_reply(u_int32_t src, u_int32_t dst, u_char *p, int datalen)
602 {
603 }
604