xref: /openbsd-src/usr.sbin/mrinfo/mrinfo.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
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 u_int32_t	host_addr(char *name);
103 void		usage(void);
104 
105 char *
106 inet_name(u_int32_t addr)
107 {
108 	struct hostent *e;
109 	struct in_addr in;
110 
111 	if (addr == 0)
112 		return "local";
113 
114 	if (nflag ||
115 	    (e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET)) == NULL) {
116 		in.s_addr = addr;
117 		return (inet_ntoa(in));
118 	}
119 	return (e->h_name);
120 }
121 
122 /*
123  * Log errors and other messages to stderr, according to the severity of the
124  * message and the current debug level.  For errors of severity LOG_ERR or
125  * worse, terminate the program.
126  */
127 void
128 logit(int severity, int syserr, char *format, ...)
129 {
130 	va_list ap;
131 
132 	switch (debug) {
133 	case 0:
134 		if (severity > LOG_WARNING)
135 			return;
136 	case 1:
137 		if (severity > LOG_NOTICE)
138 			return;
139 	case 2:
140 		if (severity > LOG_INFO)
141 			return;
142 	default:
143 		if (severity == LOG_WARNING)
144 			fprintf(stderr, "warning - ");
145 		va_start(ap, format);
146 		vfprintf(stderr, format, ap);
147 		va_end(ap);
148 		if (syserr == 0)
149 			fputc('\n', stderr);
150 		else if (syserr < sys_nerr)
151 			fprintf(stderr, ": %s\n", sys_errlist[syserr]);
152 		else
153 			fprintf(stderr, ": errno %d\n", syserr);
154 	}
155 
156 	if (severity <= LOG_ERR)
157 		exit(1);
158 }
159 
160 /*
161  * Send a neighbors-list request.
162  */
163 void
164 ask(u_int32_t dst)
165 {
166 	send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
167 	    htonl(MROUTED_LEVEL), 0);
168 }
169 
170 void
171 ask2(u_int32_t dst)
172 {
173 	send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
174 	    htonl(MROUTED_LEVEL), 0);
175 }
176 
177 /*
178  * Process an incoming neighbor-list message.
179  */
180 void
181 accept_neighbors(u_int32_t src, u_int32_t dst, u_char *p, int datalen,
182     u_int32_t level)
183 {
184 	u_char *ep = p + datalen;
185 
186 #define GET_ADDR(a) (a = ((u_int32_t)*p++ << 24), a += ((u_int32_t)*p++ << 16),\
187 		     a += ((u_int32_t)*p++ << 8), a += *p++)
188 
189 	printf("%s (%s):\n", inet_fmt(src, s1), inet_name(src));
190 	while (p < ep) {
191 		u_char metric, thresh;
192 		u_int32_t laddr;
193 		int ncount;
194 
195 		GET_ADDR(laddr);
196 		laddr = htonl(laddr);
197 		metric = *p++;
198 		thresh = *p++;
199 		ncount = *p++;
200 		while (--ncount >= 0) {
201 			u_int32_t neighbor;
202 
203 			GET_ADDR(neighbor);
204 			neighbor = htonl(neighbor);
205 			printf("  %s -> ", inet_fmt(laddr, s1));
206 			printf("%s (%s) [%d/%d]\n", inet_fmt(neighbor, s1),
207 			    inet_name(neighbor), metric, thresh);
208 		}
209 	}
210 }
211 
212 void
213 accept_neighbors2(u_int32_t src, u_int32_t dst, u_char *p, int datalen,
214     u_int32_t level)
215 {
216 	u_char *ep = p + datalen;
217 	u_int broken_cisco = ((level & 0xffff) == 0x020a); /* 10.2 */
218 	/* well, only possibly_broken_cisco, but that's too long to type. */
219 
220 	printf("%s (%s) [version %d.%d", inet_fmt(src, s1), inet_name(src),
221 	    level & 0xff, (level >> 8) & 0xff);
222 	if ((level >> 16) & NF_LEAF)
223 		printf (",leaf");
224 	if ((level >> 16) & NF_PRUNE)
225 		printf (",prune");
226 	if ((level >> 16) & NF_GENID)
227 		printf (",genid");
228 	if ((level >> 16) & NF_MTRACE)
229 		printf (",mtrace");
230 	printf ("]:\n");
231 
232 	while (p < ep) {
233 		u_char metric, thresh, flags;
234 		u_int32_t laddr = *(u_int32_t*)p;
235 		int ncount;
236 
237 		p += 4;
238 		metric = *p++;
239 		thresh = *p++;
240 		flags = *p++;
241 		ncount = *p++;
242 		if (broken_cisco && ncount == 0)	/* dumb Ciscos */
243 			ncount = 1;
244 		if (broken_cisco && ncount > 15)	/* dumb Ciscos */
245 			ncount = ncount & 0xf;
246 		while (--ncount >= 0 && p < ep) {
247 			u_int32_t neighbor = *(u_int32_t*)p;
248 			p += 4;
249 			printf("  %s -> ", inet_fmt(laddr, s1));
250 			printf("%s (%s) [%d/%d", inet_fmt(neighbor, s1),
251 			    inet_name(neighbor), metric, thresh);
252 			if (flags & DVMRP_NF_TUNNEL)
253 				printf("/tunnel");
254 			if (flags & DVMRP_NF_SRCRT)
255 				printf("/srcrt");
256 			if (flags & DVMRP_NF_PIM)
257 				printf("/pim");
258 			if (flags & DVMRP_NF_QUERIER)
259 				printf("/querier");
260 			if (flags & DVMRP_NF_DISABLED)
261 				printf("/disabled");
262 			if (flags & DVMRP_NF_DOWN)
263 				printf("/down");
264 			if (flags & DVMRP_NF_LEAF)
265 				printf("/leaf");
266 			printf("]\n");
267 		}
268 	}
269 }
270 
271 void
272 usage()
273 {
274 	fprintf(stderr,
275 	    "Usage: mrinfo [-d [debug_level]] [-n] [-t timeout] [-r retries] [router]\n");
276 	exit(1);
277 }
278 
279 int
280 main(int argc, char *argv[])
281 {
282 	int tries, trynew, curaddr, udp, ch;
283 	struct hostent *hp, bogus;
284 	struct sockaddr_in addr;
285 	socklen_t addrlen;
286 	struct timeval et;
287 	char *host;
288 	uid_t uid;
289 	const char *errstr;
290 
291 	if (geteuid() != 0) {
292 		fprintf(stderr, "mrinfo: must be root\n");
293 		exit(1);
294 	}
295 
296 	init_igmp();
297 
298 	uid = getuid();
299 	if (setresuid(uid, uid, uid) == -1)
300 		err(1, "setresuid");
301 
302 	setvbuf(stderr, NULL, _IOLBF, 0);
303 
304 	while ((ch = getopt(argc, argv, "d::nr:t:")) != -1) {
305 		switch (ch) {
306 		case 'd':
307 			if (!optarg)
308 				debug = DEFAULT_DEBUG;
309 			else {
310 				debug = strtonum(optarg, 0, 3, &errstr);
311 				if (errstr) {
312 					warnx("debug level %s", errstr);
313 					debug = DEFAULT_DEBUG;
314 				}
315 			}
316 			break;
317 		case 'n':
318 			++nflag;
319 			break;
320 		case 'r':
321 			retries = strtonum(optarg, 0, INT_MAX, &errstr);
322 			if (errstr) {
323 				warnx("retries %s", errstr);
324 				usage();
325 			}
326 			break;
327 		case 't':
328 			timeout = strtonum(optarg, 0, INT_MAX, &errstr);
329 			if (errstr) {
330 				warnx("timeout %s", errstr);
331 				usage();
332 			}
333 			break;
334 		default:
335 			usage();
336 		}
337 	}
338 	argc -= optind;
339 	argv += optind;
340 
341 	if (argc > 1)
342 		usage();
343 	if (argc == 1)
344 		host = argv[0];
345 	else
346 		host = "127.0.0.1";
347 
348 	if ((target_addr = inet_addr(host)) != -1) {
349 		hp = &bogus;
350 		hp->h_length = sizeof(target_addr);
351 		if (!(hp->h_addr_list = calloc(2, sizeof(char *))))
352 			err(1, "can't allocate memory");
353 		if (!(hp->h_addr_list[0] = malloc(hp->h_length)))
354 			err(1, "can't allocate memory");
355 		memcpy(hp->h_addr_list[0], &target_addr, hp->h_length);
356 		hp->h_addr_list[1] = 0;
357 	} else
358 		hp = gethostbyname(host);
359 
360 	if (hp == NULL || hp->h_length != sizeof(target_addr)) {
361 		fprintf(stderr, "mrinfo: %s: no such host\n", argv[0]);
362 		exit(1);
363 	}
364 	if (debug)
365 		fprintf(stderr, "Debug level %u\n", debug);
366 
367 	/* Check all addresses; mrouters often have unreachable interfaces */
368 	for (curaddr = 0; hp->h_addr_list[curaddr] != NULL; curaddr++) {
369 		memcpy(&target_addr, hp->h_addr_list[curaddr], hp->h_length);
370 		/* Find a good local address for us. */
371 		addrlen = sizeof(addr);
372 		memset(&addr, 0, sizeof addr);
373 		addr.sin_family = AF_INET;
374 		addr.sin_len = sizeof addr;
375 		addr.sin_addr.s_addr = target_addr;
376 		addr.sin_port = htons(2000);	/* any port over 1024 will
377 						 * do... */
378 		if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ||
379 		    connect(udp, (struct sockaddr *) & addr, sizeof(addr)) < 0 ||
380 		    getsockname(udp, (struct sockaddr *) & addr, &addrlen) < 0) {
381 			perror("Determining local address");
382 			exit(1);
383 		}
384 		close(udp);
385 		our_addr = addr.sin_addr.s_addr;
386 
387 		tries = 0;
388 		trynew = 1;
389 		/*
390 		 * New strategy: send 'ask2' for two timeouts, then fall back
391 		 * to 'ask', since it's not very likely that we are going to
392 		 * find someone who only responds to 'ask' these days
393 		 */
394 		ask2(target_addr);
395 
396 		gettimeofday(&et, 0);
397 		et.tv_sec += timeout;
398 
399 		/* Main receive loop */
400 		for (;;) {
401 			int count, recvlen, ipdatalen, iphdrlen, igmpdatalen;
402 			u_int32_t src, dst, group;
403 			struct timeval tv, now;
404 			socklen_t dummy = 0;
405 			struct igmp *igmp;
406 			struct ip *ip;
407 			struct pollfd pfd[1];
408 
409 			pfd[0].fd = igmp_socket;
410 			pfd[0].events = POLLIN;
411 
412 			gettimeofday(&now, 0);
413 			tv.tv_sec = et.tv_sec - now.tv_sec;
414 			tv.tv_usec = et.tv_usec - now.tv_usec;
415 
416 			if (tv.tv_usec < 0) {
417 				tv.tv_usec += 1000000L;
418 				--tv.tv_sec;
419 			}
420 			if (tv.tv_sec < 0)
421 				timerclear(&tv);
422 
423 			count = poll(pfd, 1, tv.tv_sec * 1000);
424 
425 			if (count < 0) {
426 				if (errno != EINTR)
427 					perror("select");
428 				continue;
429 			} else if (count == 0) {
430 				logit(LOG_DEBUG, 0,
431 				    "Timed out receiving neighbor lists");
432 				if (++tries > retries)
433 					break;
434 				/* If we've tried ASK_NEIGHBORS2 twice with
435 				 * no response, fall back to ASK_NEIGHBORS
436 				 */
437 				if (tries == 2 && target_level == 0)
438 					trynew = 0;
439 				if (target_level == 0 && trynew == 0)
440 					ask(target_addr);
441 				else
442 					ask2(target_addr);
443 				gettimeofday(&et, 0);
444 				et.tv_sec += timeout;
445 				continue;
446 			}
447 			recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
448 			    0, NULL, &dummy);
449 			if (recvlen <= 0) {
450 				if (recvlen && errno != EINTR)
451 					perror("recvfrom");
452 				continue;
453 			}
454 
455 			if (recvlen < sizeof(struct ip)) {
456 				logit(LOG_WARNING, 0,
457 				    "packet too short (%u bytes) for IP header",
458 				    recvlen);
459 				continue;
460 			}
461 			ip = (struct ip *) recv_buf;
462 			if (ip->ip_p == 0)
463 				continue;	/* Request to install cache entry */
464 			src = ip->ip_src.s_addr;
465 			dst = ip->ip_dst.s_addr;
466 			iphdrlen = ip->ip_hl << 2;
467 			ipdatalen = ntohs(ip->ip_len) - iphdrlen;
468 			if (iphdrlen + ipdatalen != recvlen) {
469 				logit(LOG_WARNING, 0,
470 				    "packet shorter (%u bytes) than "
471 				    "hdr+data length (%u+%u)",
472 				    recvlen, iphdrlen, ipdatalen);
473 				continue;
474 			}
475 			igmp = (struct igmp *) (recv_buf + iphdrlen);
476 			group = igmp->igmp_group.s_addr;
477 			igmpdatalen = ipdatalen - IGMP_MINLEN;
478 			if (igmpdatalen < 0) {
479 				logit(LOG_WARNING, 0,
480 				    "IP data field too short (%u bytes) "
481 				    "for IGMP, from %s",
482 				    ipdatalen, inet_fmt(src, s1));
483 				continue;
484 			}
485 			if (igmp->igmp_type != IGMP_DVMRP)
486 				continue;
487 
488 			switch (igmp->igmp_code) {
489 			case DVMRP_NEIGHBORS:
490 			case DVMRP_NEIGHBORS2:
491 				if (src != target_addr) {
492 					fprintf(stderr, "mrinfo: got reply from %s",
493 					    inet_fmt(src, s1));
494 					fprintf(stderr, " instead of %s\n",
495 					    inet_fmt(target_addr, s1));
496 					/*continue;*/
497 				}
498 				break;
499 			default:
500 				continue;	/* ignore all other DVMRP messages */
501 			}
502 
503 			switch (igmp->igmp_code) {
504 			case DVMRP_NEIGHBORS:
505 				if (group) {
506 					/* knows about DVMRP_NEIGHBORS2 msg */
507 					if (target_level == 0) {
508 						target_level = ntohl(group);
509 						ask2(target_addr);
510 					}
511 				} else {
512 					accept_neighbors(src, dst, (u_char *)(igmp + 1),
513 					    igmpdatalen, ntohl(group));
514 					exit(0);
515 				}
516 				break;
517 			case DVMRP_NEIGHBORS2:
518 				accept_neighbors2(src, dst, (u_char *)(igmp + 1),
519 				    igmpdatalen, ntohl(group));
520 				exit(0);
521 			}
522 		}
523 	}
524 	exit(1);
525 }
526 
527 /* dummies */
528 void
529 accept_probe(u_int32_t src, u_int32_t dst, char *p, int datalen,
530     u_int32_t level)
531 {
532 }
533 
534 void
535 accept_group_report(u_int32_t src, u_int32_t dst, u_int32_t group, int r_type)
536 {
537 }
538 
539 void
540 accept_neighbor_request2(u_int32_t src, u_int32_t dst)
541 {
542 }
543 
544 void
545 accept_report(u_int32_t src, u_int32_t dst, char *p, int datalen,
546     u_int32_t level)
547 {
548 }
549 
550 void
551 accept_neighbor_request(u_int32_t src, u_int32_t dst)
552 {
553 }
554 
555 void
556 accept_prune(u_int32_t src, u_int32_t dst, char *p, int datalen)
557 {
558 }
559 
560 void
561 accept_graft(u_int32_t src, u_int32_t dst, char *p, int datalen)
562 {
563 }
564 
565 void
566 accept_g_ack(u_int32_t src, u_int32_t dst, char *p, int datalen)
567 {
568 }
569 
570 void
571 add_table_entry(u_int32_t origin, u_int32_t mcastgrp)
572 {
573 }
574 
575 void
576 check_vif_state(void)
577 {
578 }
579 
580 void
581 accept_leave_message(u_int32_t src, u_int32_t dst, u_int32_t group)
582 {
583 }
584 
585 void
586 accept_mtrace(u_int32_t src, u_int32_t dst, u_int32_t group, char *data,
587     u_int no, int datalen)
588 {
589 }
590 
591 void
592 accept_membership_query(u_int32_t src, u_int32_t dst, u_int32_t group, int tmo)
593 {
594 }
595 
596 void
597 accept_info_request(u_int32_t src, u_int32_t dst, u_char *p, int datalen)
598 {
599 }
600 
601 void
602 accept_info_reply(u_int32_t src, u_int32_t dst, u_char *p, int datalen)
603 {
604 }
605