mrinfo.c revision 1.1 1 /*
2 * This tool requests configuration info from a multicast router
3 * and prints the reply (if any). Invoke it as:
4 *
5 * mrinfo router-name-or-address
6 *
7 * Written Wed Mar 24 1993 by Van Jacobson (adapted from the
8 * multicast mapper written by Pavel Curtis).
9 *
10 * The lawyers insist we include the following UC copyright notice.
11 * The mapper from which this is derived contained a Xerox copyright
12 * notice which follows the UC one. Try not to get depressed noting
13 * that the legal gibberish is larger than the program.
14 *
15 * Copyright (c) 1993 Regents of the University of California.
16 * All rights reserved.
17 *
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions
20 * are met:
21 * 1. Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
23 * 2. Redistributions in binary form must reproduce the above copyright
24 * notice, this list of conditions and the following disclaimer in the
25 * documentation and/or other materials provided with the distribution.
26 * 3. All advertising materials mentioning features or use of this software
27 * must display the following acknowledgement:
28 * This product includes software developed by the Computer Systems
29 * Engineering Group at Lawrence Berkeley Laboratory.
30 * 4. Neither the name of the University nor of the Laboratory may be used
31 * to endorse or promote products derived from this software without
32 * specific prior written permission.
33 *
34 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
35 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44 * SUCH DAMAGE.
45 * ---------------------------------
46 * Copyright (c) Xerox Corporation 1992. All rights reserved.
47 *
48 * License is granted to copy, to use, and to make and to use derivative works
49 * for research and evaluation purposes, provided that Xerox is acknowledged
50 * in all documentation pertaining to any such copy or derivative work. Xerox
51 * grants no other licenses expressed or implied. The Xerox trade name should
52 * not be used in any advertising without its written permission.
53 *
54 * XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
55 * MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE FOR
56 * ANY PARTICULAR PURPOSE. The software is provided "as is" without express
57 * or implied warranty of any kind.
58 *
59 * These notices must be retained in any copies of any part of this software.
60 */
61
62 #ifndef lint
63 static char rcsid[] =
64 "@(#) $Id: mrinfo.c,v 1.1 1995/06/01 05:19:13 mycroft Exp $";
65 /* original rcsid:
66 "@(#) Header: mrinfo.c,v 1.6 93/04/08 15:14:16 van Exp (LBL)";
67 */
68 #endif
69
70 #include <netdb.h>
71 #include <sys/time.h>
72 #include "defs.h"
73 #include <arpa/inet.h>
74
75 #define DEFAULT_TIMEOUT 4 /* How long to wait before retrying requests */
76 #define DEFAULT_RETRIES 3 /* How many times to ask each router */
77
78 u_int32_t our_addr, target_addr = 0; /* in NET order */
79 int debug = 0;
80 int nflag = 0;
81 int retries = DEFAULT_RETRIES;
82 int timeout = DEFAULT_TIMEOUT;
83 int target_level;
84 vifi_t numvifs; /* to keep loader happy */
85 /* (see COPY_TABLES macro called in kern.c) */
86
87 char *
88 inet_name(addr)
89 u_int32_t addr;
90 {
91 struct hostent *e;
92 struct in_addr in;
93
94 if (addr == 0)
95 return "local";
96
97 if (nflag ||
98 (e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET)) == NULL) {
99 in.s_addr = addr;
100 return (inet_ntoa(in));
101 }
102 return (e->h_name);
103 }
104
105 /*
106 * Log errors and other messages to stderr, according to the severity of the
107 * message and the current debug level. For errors of severity LOG_ERR or
108 * worse, terminate the program.
109 */
110 void
111 log(severity, syserr, format, a, b, c, d, e)
112 int severity, syserr;
113 char *format;
114 int a, b, c, d, e;
115 {
116 char fmt[100];
117
118 switch (debug) {
119 case 0:
120 if (severity > LOG_WARNING)
121 return;
122 case 1:
123 if (severity > LOG_NOTICE)
124 return;
125 case 2:
126 if (severity > LOG_INFO)
127 return;
128 default:
129 fmt[0] = '\0';
130 if (severity == LOG_WARNING)
131 strcat(fmt, "warning - ");
132 strncat(fmt, format, 80);
133 fprintf(stderr, fmt, a, b, c, d, e);
134 if (syserr == 0)
135 fprintf(stderr, "\n");
136 else if (syserr < sys_nerr)
137 fprintf(stderr, ": %s\n", sys_errlist[syserr]);
138 else
139 fprintf(stderr, ": errno %d\n", syserr);
140 }
141
142 if (severity <= LOG_ERR)
143 exit(-1);
144 }
145
146 /*
147 * Send a neighbors-list request.
148 */
149 void
150 ask(dst)
151 u_int32_t dst;
152 {
153 send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
154 htonl(MROUTED_LEVEL), 0);
155 }
156
157 void
158 ask2(dst)
159 u_int32_t dst;
160 {
161 send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
162 htonl(MROUTED_LEVEL), 0);
163 }
164
165 /*
166 * Process an incoming neighbor-list message.
167 */
168 void
169 accept_neighbors(src, dst, p, datalen)
170 u_int32_t src, dst;
171 u_char *p;
172 int datalen;
173 {
174 u_char *ep = p + datalen;
175 #define GET_ADDR(a) (a = ((u_int32_t)*p++ << 24), a += ((u_int32_t)*p++ << 16),\
176 a += ((u_int32_t)*p++ << 8), a += *p++)
177
178 printf("%s (%s):\n", inet_fmt(src, s1), inet_name(src));
179 while (p < ep) {
180 register u_int32_t laddr;
181 register u_char metric;
182 register u_char thresh;
183 register int ncount;
184
185 GET_ADDR(laddr);
186 laddr = htonl(laddr);
187 metric = *p++;
188 thresh = *p++;
189 ncount = *p++;
190 while (--ncount >= 0) {
191 register u_int32_t neighbor;
192 GET_ADDR(neighbor);
193 neighbor = htonl(neighbor);
194 printf(" %s -> ", inet_fmt(laddr, s1));
195 printf("%s (%s) [%d/%d]\n", inet_fmt(neighbor, s1),
196 inet_name(neighbor), metric, thresh);
197 }
198 }
199 }
200
201 void
202 accept_neighbors2(src, dst, p, datalen)
203 u_int32_t src, dst;
204 u_char *p;
205 int datalen;
206 {
207 u_char *ep = p + datalen;
208 u_int broken_cisco = ((target_level & 0xffff) == 0x020a); /* 10.2 */
209 /* well, only possibly_broken_cisco, but that's too long to type. */
210
211 printf("%s (%s) [version %d.%d]:\n", inet_fmt(src, s1), inet_name(src),
212 target_level & 0xff, (target_level >> 8) & 0xff);
213
214 while (p < ep) {
215 register u_char metric;
216 register u_char thresh;
217 register u_char flags;
218 register int ncount;
219 register u_int32_t laddr = *(u_int32_t*)p;
220
221 p += 4;
222 metric = *p++;
223 thresh = *p++;
224 flags = *p++;
225 ncount = *p++;
226 if (broken_cisco && ncount == 0) /* dumb Ciscos */
227 ncount = 1;
228 if (broken_cisco && ncount > 15) /* dumb Ciscos */
229 ncount = ncount & 0xf;
230 while (--ncount >= 0 && p < ep) {
231 register u_int32_t neighbor = *(u_int32_t*)p;
232 p += 4;
233 printf(" %s -> ", inet_fmt(laddr, s1));
234 printf("%s (%s) [%d/%d", inet_fmt(neighbor, s1),
235 inet_name(neighbor), metric, thresh);
236 if (flags & DVMRP_NF_TUNNEL)
237 printf("/tunnel");
238 if (flags & DVMRP_NF_SRCRT)
239 printf("/srcrt");
240 if (flags & DVMRP_NF_PIM)
241 printf("/pim");
242 if (flags & DVMRP_NF_QUERIER)
243 printf("/querier");
244 if (flags & DVMRP_NF_DISABLED)
245 printf("/disabled");
246 if (flags & DVMRP_NF_DOWN)
247 printf("/down");
248 if (flags & DVMRP_NF_LEAF)
249 printf("/leaf");
250 printf("]\n");
251 }
252 }
253 }
254
255 int
256 get_number(var, deflt, pargv, pargc)
257 int *var, *pargc, deflt;
258 char ***pargv;
259 {
260 if ((*pargv)[0][2] == '\0') { /* Get the value from the next
261 * argument */
262 if (*pargc > 1 && isdigit((*pargv)[1][0])) {
263 (*pargv)++, (*pargc)--;
264 *var = atoi((*pargv)[0]);
265 return 1;
266 } else if (deflt >= 0) {
267 *var = deflt;
268 return 1;
269 } else
270 return 0;
271 } else { /* Get value from the rest of this argument */
272 if (isdigit((*pargv)[0][2])) {
273 *var = atoi((*pargv)[0] + 2);
274 return 1;
275 } else {
276 return 0;
277 }
278 }
279 }
280
281 u_int32_t
282 host_addr(name)
283 char *name;
284 {
285 struct hostent *e;
286 u_int32_t addr;
287
288 addr = inet_addr(name);
289 if ((int)addr == -1) {
290 e = gethostbyname(name);
291 if (e == NULL || e->h_length != sizeof(addr))
292 return (0);
293 memcpy(&addr, e->h_addr_list[0], e->h_length);
294 }
295 return(addr);
296 }
297
298 void
299 usage()
300 {
301 fprintf(stderr,
302 "Usage: mrinfo [-n] [-t timeout] [-r retries] [router]\n");
303 exit(1);
304 }
305
306 int
307 main(argc, argv)
308 int argc;
309 char *argv[];
310 {
311 setlinebuf(stderr);
312
313 if (geteuid() != 0) {
314 fprintf(stderr, "mrinfo: must be root\n");
315 exit(1);
316 }
317 argv++, argc--;
318 while (argc > 0 && argv[0][0] == '-') {
319 switch (argv[0][1]) {
320 case 'd':
321 if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc))
322 usage();
323 break;
324 case 'n':
325 ++nflag;
326 break;
327 case 'r':
328 if (!get_number(&retries, -1, &argv, &argc))
329 usage();
330 break;
331 case 't':
332 if (!get_number(&timeout, -1, &argv, &argc))
333 usage();
334 break;
335 default:
336 usage();
337 }
338 argv++, argc--;
339 }
340 if (argc > 1)
341 usage();
342 if (argc == 1)
343 target_addr = host_addr(argv[0]);
344 else
345 target_addr = host_addr("127.0.0.1");
346
347 if (target_addr == 0) {
348 fprintf(stderr, "mrinfo: %s: no such host\n", argv[0]);
349 exit(1);
350 }
351 if (debug)
352 fprintf(stderr, "Debug level %u\n", debug);
353
354 init_igmp();
355
356 { /* Find a good local address for us. */
357 int udp;
358 struct sockaddr_in addr;
359 int addrlen = sizeof(addr);
360
361 addr.sin_family = AF_INET;
362 #if (defined(BSD) && (BSD >= 199103))
363 addr.sin_len = sizeof addr;
364 #endif
365 addr.sin_addr.s_addr = target_addr;
366 addr.sin_port = htons(2000); /* any port over 1024 will
367 * do... */
368 if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
369 || connect(udp, (struct sockaddr *) & addr, sizeof(addr)) < 0
370 || getsockname(udp, (struct sockaddr *) & addr, &addrlen) < 0) {
371 perror("Determining local address");
372 exit(-1);
373 }
374 close(udp);
375 our_addr = addr.sin_addr.s_addr;
376 }
377
378 ask(target_addr);
379
380 /* Main receive loop */
381 for (;;) {
382 fd_set fds;
383 struct timeval tv;
384 int count, recvlen, dummy = 0;
385 register u_int32_t src, dst, group;
386 struct ip *ip;
387 struct igmp *igmp;
388 int ipdatalen, iphdrlen, igmpdatalen;
389
390 FD_ZERO(&fds);
391 FD_SET(igmp_socket, &fds);
392
393 tv.tv_sec = timeout;
394 tv.tv_usec = 0;
395
396 count = select(igmp_socket + 1, &fds, 0, 0, &tv);
397
398 if (count < 0) {
399 if (errno != EINTR)
400 perror("select");
401 continue;
402 } else if (count == 0) {
403 log(LOG_DEBUG, 0, "Timed out receiving neighbor lists");
404 if (--retries < 0)
405 exit(1);
406 if (target_level == 0)
407 ask(target_addr);
408 else
409 ask2(target_addr);
410 continue;
411 }
412 recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
413 0, NULL, &dummy);
414 if (recvlen <= 0) {
415 if (recvlen && errno != EINTR)
416 perror("recvfrom");
417 continue;
418 }
419
420 if (recvlen < sizeof(struct ip)) {
421 log(LOG_WARNING, 0,
422 "packet too short (%u bytes) for IP header",
423 recvlen);
424 continue;
425 }
426 ip = (struct ip *) recv_buf;
427 if (ip->ip_p == 0)
428 continue; /* Request to install cache entry */
429 src = ip->ip_src.s_addr;
430 dst = ip->ip_dst.s_addr;
431 iphdrlen = ip->ip_hl << 2;
432 ipdatalen = ip->ip_len;
433 if (iphdrlen + ipdatalen != recvlen) {
434 log(LOG_WARNING, 0,
435 "packet shorter (%u bytes) than hdr+data length (%u+%u)",
436 recvlen, iphdrlen, ipdatalen);
437 continue;
438 }
439 igmp = (struct igmp *) (recv_buf + iphdrlen);
440 group = igmp->igmp_group.s_addr;
441 igmpdatalen = ipdatalen - IGMP_MINLEN;
442 if (igmpdatalen < 0) {
443 log(LOG_WARNING, 0,
444 "IP data field too short (%u bytes) for IGMP, from %s",
445 ipdatalen, inet_fmt(src, s1));
446 continue;
447 }
448 if (igmp->igmp_type != IGMP_DVMRP)
449 continue;
450
451 switch (igmp->igmp_code) {
452 case DVMRP_NEIGHBORS:
453 case DVMRP_NEIGHBORS2:
454 if (src != target_addr) {
455 fprintf(stderr, "mrinfo: got reply from %s",
456 inet_fmt(src, s1));
457 fprintf(stderr, " instead of %s\n",
458 inet_fmt(target_addr, s1));
459 /*continue;*/
460 }
461 break;
462 default:
463 continue; /* ignore all other DVMRP messages */
464 }
465
466 switch (igmp->igmp_code) {
467
468 case DVMRP_NEIGHBORS:
469 if (group) {
470 /* knows about DVMRP_NEIGHBORS2 msg */
471 if (target_level == 0) {
472 target_level = ntohl(group);
473 ask2(target_addr);
474 }
475 } else {
476 accept_neighbors(src, dst, (char *)(igmp + 1),
477 igmpdatalen);
478 exit(0);
479 }
480 break;
481
482 case DVMRP_NEIGHBORS2:
483 accept_neighbors2(src, dst, (char *)(igmp + 1),
484 igmpdatalen);
485 exit(0);
486 }
487 }
488 }
489
490 /* dummies */
491 void accept_probe()
492 {
493 }
494 void accept_group_report()
495 {
496 }
497 void accept_neighbor_request2()
498 {
499 }
500 void accept_report()
501 {
502 }
503 void accept_neighbor_request()
504 {
505 }
506 void accept_prune()
507 {
508 }
509 void accept_graft()
510 {
511 }
512 void accept_g_ack()
513 {
514 }
515 void add_table_entry()
516 {
517 }
518 void check_vif_state()
519 {
520 }
521 void accept_leave_message()
522 {
523 }
524 void accept_mtrace()
525 {
526 }
527 void accept_membership_query()
528 {
529 }
530