1 /* $NetBSD: mrinfo.c,v 1.26 2007/02/22 01:29:35 hubertf Exp $ */
4 * This tool requests configuration info from a multicast router
5 * and prints the reply (if any). Invoke it as:
7 * mrinfo router-name-or-address
9 * Written Wed Mar 24 1993 by Van Jacobson (adapted from the
10 * multicast mapper written by Pavel Curtis).
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.
17 * Copyright (c) 1993 Regents of the University of California.
18 * All rights reserved.
20 * Redistribution and use in source and binary forms, with or without
21 * modification, are permitted provided that the following conditions
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.
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
47 * ---------------------------------
48 * Copyright (c) 1992, 2001 Xerox Corporation. All rights reserved.
50 * Redistribution and use in source and binary forms, with or without modification,
51 * are permitted provided that the following conditions are met:
53 * Redistributions of source code must retain the above copyright notice,
54 * this list of conditions and the following disclaimer.
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.
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.
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.
77 #include <sys/cdefs.h>
81 "@(#) Header: mrinfo.c,v 1.6 93/04/08 15:14:16 van Exp (LBL)";
83 __RCSID("$NetBSD: mrinfo.c,v 1.26 2007/02/22 01:29:35 hubertf Exp $");
93 #include <arpa/inet.h>
96 #define DEFAULT_TIMEOUT 4 /* How long to wait before retrying requests */
97 #define DEFAULT_RETRIES 3 /* How many times to ask each router */
99 u_int32_t our_addr
, target_addr
= 0; /* in NET order */
102 int retries
= DEFAULT_RETRIES
;
103 int timeout
= DEFAULT_TIMEOUT
;
104 int target_level
= 0;
105 vifi_t numvifs
; /* to keep loader happy */
106 /* (see COPY_TABLES macro called in kern.c) */
108 const char * inet_name(u_int32_t addr
);
109 void ask(u_int32_t dst
);
110 void ask2(u_int32_t dst
);
111 int get_number(int *var
, int deflt
, char ***pargv
,
113 u_int32_t
host_addr(char *name
);
116 /* to shut up -Wstrict-prototypes */
117 int main(int argc
, char *argv
[]);
118 /* logit() prototyped in defs.h */
122 inet_name(u_int32_t addr
)
131 (e
= gethostbyaddr((char *)&addr
, sizeof(addr
), AF_INET
)) == NULL
) {
133 return (inet_ntoa(in
));
139 * Log errors and other messages to stderr, according to the severity of the
140 * message and the current debug level. For errors of severity LOG_ERR or
141 * worse, terminate the program.
144 logit(int severity
, int syserr
, const char *format
, ...)
150 if (severity
> LOG_WARNING
)
153 if (severity
> LOG_NOTICE
)
156 if (severity
> LOG_INFO
)
159 if (severity
== LOG_WARNING
)
160 fprintf(stderr
, "warning - ");
161 va_start(ap
, format
);
162 vfprintf(stderr
, format
, ap
);
165 fprintf(stderr
, "\n");
167 fprintf(stderr
, ": %s\n", strerror(syserr
));
170 if (severity
<= LOG_ERR
)
175 * Send a neighbors-list request.
180 send_igmp(our_addr
, dst
, IGMP_DVMRP
, DVMRP_ASK_NEIGHBORS
,
181 htonl(MROUTED_LEVEL
), 0);
187 send_igmp(our_addr
, dst
, IGMP_DVMRP
, DVMRP_ASK_NEIGHBORS2
,
188 htonl(MROUTED_LEVEL
), 0);
192 * Process an incoming neighbor-list message.
195 accept_neighbors(u_int32_t src
, u_int32_t dst
, u_char
*p
, int datalen
,
198 u_char
*ep
= p
+ datalen
;
199 #define GET_ADDR(a) (a = ((u_int32_t)*p++ << 24), a += ((u_int32_t)*p++ << 16),\
200 a += ((u_int32_t)*p++ << 8), a += *p++)
202 printf("%s (%s):\n", inet_fmt(src
), inet_name(src
));
210 laddr
= htonl(laddr
);
214 while (--ncount
>= 0) {
217 neighbor
= htonl(neighbor
);
218 printf(" %s -> ", inet_fmt(laddr
));
219 printf("%s (%s) [%d/%d]\n",
221 inet_name(neighbor
), metric
, thresh
);
227 accept_neighbors2(u_int32_t src
, u_int32_t dst
, u_char
*p
, int datalen
,
230 u_char
*ep
= p
+ datalen
;
231 u_int broken_cisco
= ((level
& 0xffff) == 0x020a); /* 10.2 */
232 /* well, only possibly_broken_cisco, but that's too long to type. */
234 printf("%s (%s) [version %d.%d", inet_fmt(src
),
235 inet_name(src
), level
& 0xff, (level
>> 8) & 0xff);
236 if ((level
>> 16) & NF_LEAF
) { printf (",leaf"); }
237 if ((level
>> 16) & NF_PRUNE
) { printf (",prune"); }
238 if ((level
>> 16) & NF_GENID
) { printf (",genid"); }
239 if ((level
>> 16) & NF_MTRACE
) { printf (",mtrace"); }
247 u_int32_t laddr
= *(u_int32_t
*)p
;
254 if (broken_cisco
&& ncount
== 0) /* dumb Ciscos */
256 if (broken_cisco
&& ncount
> 15) /* dumb Ciscos */
257 ncount
= ncount
& 0xf;
258 while (--ncount
>= 0 && p
< ep
) {
259 u_int32_t neighbor
= *(u_int32_t
*)p
;
261 printf(" %s -> ", inet_fmt(laddr
));
262 printf("%s (%s) [%d/%d",
264 inet_name(neighbor
), metric
, thresh
);
265 if (flags
& DVMRP_NF_TUNNEL
)
267 if (flags
& DVMRP_NF_SRCRT
)
269 if (flags
& DVMRP_NF_PIM
)
271 if (flags
& DVMRP_NF_QUERIER
)
273 if (flags
& DVMRP_NF_DISABLED
)
275 if (flags
& DVMRP_NF_DOWN
)
277 if (flags
& DVMRP_NF_LEAF
)
285 get_number(int *var
, int deflt
, char ***pargv
, int *pargc
)
287 if ((*pargv
)[0][2] == '\0') { /* Get the value from the next
289 if (*pargc
> 1 && isdigit((unsigned char)(*pargv
)[1][0])) {
290 (*pargv
)++, (*pargc
)--;
291 *var
= atoi((*pargv
)[0]);
293 } else if (deflt
>= 0) {
298 } else { /* Get value from the rest of this argument */
299 if (isdigit((unsigned char)(*pargv
)[0][2])) {
300 *var
= atoi((*pargv
)[0] + 2);
312 "usage: mrinfo [-n] [-t timeout] [-r retries] [router]\n");
317 main(int argc
, char *argv
[])
323 struct hostent bogus
;
327 if (geteuid() != 0) {
328 fprintf(stderr
, "mrinfo: must be root\n");
332 if (setuid(getuid()) == -1)
333 logit(LOG_ERR
, errno
, "setuid");
338 while (argc
> 0 && argv
[0][0] == '-') {
339 switch (argv
[0][1]) {
341 if (!get_number(&debug
, DEFAULT_DEBUG
, &argv
, &argc
))
348 if (!get_number(&retries
, -1, &argv
, &argc
))
352 if (!get_number(&timeout
, -1, &argv
, &argc
))
367 if ((target_addr
= inet_addr(host
)) != (in_addr_t
)-1) {
369 hp
->h_length
= sizeof(target_addr
);
370 hp
->h_addr_list
= (char **)malloc(2 * sizeof(char *));
371 if (hp
->h_addr_list
== NULL
)
372 logit(LOG_ERR
, errno
, "malloc");
373 hp
->h_addr_list
[0] = malloc(hp
->h_length
);
374 if (hp
->h_addr_list
[0] == NULL
)
375 logit(LOG_ERR
, errno
, "malloc");
376 memcpy(hp
->h_addr_list
[0], &target_addr
, sizeof(hp
->h_addr_list
[0]));
377 hp
->h_addr_list
[1] = NULL
;
379 hp
= gethostbyname(host
);
381 if (hp
== NULL
|| hp
->h_length
!= sizeof(target_addr
)) {
382 fprintf(stderr
, "mrinfo: %s: no such host\n", argv
[0]);
386 fprintf(stderr
, "Debug level %u\n", debug
);
388 /* Check all addresses; mrouters often have unreachable interfaces */
389 for (curaddr
= 0; hp
->h_addr_list
[curaddr
] != NULL
; curaddr
++) {
390 memcpy(&target_addr
, hp
->h_addr_list
[curaddr
], sizeof(target_addr
));
391 { /* Find a good local address for us. */
393 struct sockaddr_in addr
;
394 socklen_t addrlen
= sizeof(addr
);
396 memset(&addr
, 0, sizeof(addr
));
397 addr
.sin_family
= AF_INET
;
398 #if (defined(BSD) && (BSD >= 199103))
399 addr
.sin_len
= sizeof addr
;
401 addr
.sin_addr
.s_addr
= target_addr
;
402 addr
.sin_port
= htons(2000); /* any port over 1024 will
404 if ((udp
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0
405 || connect(udp
, (struct sockaddr
*) & addr
, sizeof(addr
)) < 0
406 || getsockname(udp
, (struct sockaddr
*) & addr
, &addrlen
) < 0) {
407 perror("Determining local address");
411 our_addr
= addr
.sin_addr
.s_addr
;
417 * New strategy: send 'ask2' for two timeouts, then fall back
418 * to 'ask', since it's not very likely that we are going to
419 * find someone who only responds to 'ask' these days
423 gettimeofday(&et
, 0);
424 et
.tv_sec
+= timeout
;
426 /* Main receive loop */
428 struct pollfd set
[1];
429 struct timeval tv
, now
;
432 u_int32_t src
, dst
, group
;
435 int ipdatalen
, iphdrlen
, igmpdatalen
;
437 set
[0].fd
= igmp_socket
;
438 set
[0].events
= POLLIN
;
440 gettimeofday(&now
, 0);
441 tv
.tv_sec
= et
.tv_sec
- now
.tv_sec
;
442 tv
.tv_usec
= et
.tv_usec
- now
.tv_usec
;
444 if (tv
.tv_usec
< 0) {
445 tv
.tv_usec
+= 1000000L;
449 tv
.tv_sec
= tv
.tv_usec
= 0;
451 count
= poll(set
, 1, tv
.tv_sec
* 1000 + tv
.tv_usec
/ 1000);
457 } else if (count
== 0) {
458 logit(LOG_DEBUG
, 0, "Timed out receiving neighbor lists");
459 if (++tries
> retries
)
461 /* If we've tried ASK_NEIGHBORS2 twice with
462 * no response, fall back to ASK_NEIGHBORS
464 if (tries
== 2 && target_level
== 0)
466 if (target_level
== 0 && trynew
== 0)
470 gettimeofday(&et
, 0);
471 et
.tv_sec
+= timeout
;
474 recvlen
= recvfrom(igmp_socket
, recv_buf
, RECV_BUF_SIZE
,
477 if (recvlen
&& errno
!= EINTR
)
482 if (recvlen
< (int)sizeof(struct ip
)) {
483 logit(LOG_WARNING
, 0,
484 "packet too short (%u bytes) for IP header",
488 ip
= (struct ip
*) recv_buf
;
490 continue; /* Request to install cache entry */
491 src
= ip
->ip_src
.s_addr
;
492 dst
= ip
->ip_dst
.s_addr
;
493 iphdrlen
= ip
->ip_hl
<< 2;
494 ipdatalen
= ip
->ip_len
;
495 if (iphdrlen
+ ipdatalen
!= recvlen
) {
496 logit(LOG_WARNING
, 0,
497 "packet shorter (%u bytes) than hdr+data length (%u+%u)",
498 recvlen
, iphdrlen
, ipdatalen
);
501 igmp
= (struct igmp
*) (recv_buf
+ iphdrlen
);
502 group
= igmp
->igmp_group
.s_addr
;
503 igmpdatalen
= ipdatalen
- IGMP_MINLEN
;
504 if (igmpdatalen
< 0) {
505 logit(LOG_WARNING
, 0,
506 "IP data field too short (%u bytes) for IGMP, from %s",
507 ipdatalen
, inet_fmt(src
));
510 if (igmp
->igmp_type
!= IGMP_DVMRP
)
513 switch (igmp
->igmp_code
) {
514 case DVMRP_NEIGHBORS
:
515 case DVMRP_NEIGHBORS2
:
516 if (src
!= target_addr
) {
517 fprintf(stderr
, "mrinfo: got reply from %s",
519 fprintf(stderr
, " instead of %s\n",
520 inet_fmt(target_addr
));
525 continue; /* ignore all other DVMRP messages */
528 switch (igmp
->igmp_code
) {
530 case DVMRP_NEIGHBORS
:
532 /* knows about DVMRP_NEIGHBORS2 msg */
533 if (target_level
== 0) {
534 target_level
= ntohl(group
);
538 accept_neighbors(src
, dst
, (u_char
*)(igmp
+ 1),
539 igmpdatalen
, ntohl(group
));
544 case DVMRP_NEIGHBORS2
:
545 accept_neighbors2(src
, dst
, (u_char
*)(igmp
+ 1),
546 igmpdatalen
, ntohl(group
));
555 void accept_probe(u_int32_t src
, u_int32_t dst
, char *p
, int datalen
,
559 void accept_group_report(u_int32_t src
, u_int32_t dst
, u_int32_t group
,
563 void accept_neighbor_request2(u_int32_t src
, u_int32_t dst
)
566 void accept_report(u_int32_t src
, u_int32_t dst
, char *p
, int datalen
,
570 void accept_neighbor_request(u_int32_t src
, u_int32_t dst
)
573 void accept_prune(u_int32_t src
, u_int32_t dst
, char *p
, int datalen
)
576 void accept_graft(u_int32_t src
, u_int32_t dst
, char *p
, int datalen
)
579 void accept_g_ack(u_int32_t src
, u_int32_t dst
, char *p
, int datalen
)
582 void add_table_entry(u_int32_t origin
, u_int32_t mcastgrp
)
585 void check_vif_state(void)
588 void accept_leave_message(u_int32_t src
, u_int32_t dst
, u_int32_t group
)
591 void accept_mtrace(u_int32_t src
, u_int32_t dst
, u_int32_t group
, char *data
,
592 u_int no
, int datalen
)
595 void accept_membership_query(u_int32_t src
, u_int32_t dst
, u_int32_t group
,
599 void accept_info_request(u_int32_t src
, u_int32_t dst
, u_char
*p
, int datalen
)
602 void accept_info_reply(u_int32_t src
, u_int32_t dst
, u_char
*p
, int datalen
)