2 * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
5 * Copyright (c) 1982, 1986, 1993
6 * The Regents of the University of California. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgment:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * $FreeBSD: src/sbin/routed/rtquery/rtquery.c,v 1.14 2000/08/11 08:24:39
38 * char copyright[] = "@(#) Copyright (c) 1982, 1986, 1993\n"
39 * "The Regents of the University of California. All rights reserved.\n";
42 #pragma ident "%Z%%M% %I% %E% SMI"
45 #include <sys/types.h>
46 #include <sys/param.h>
47 #include <sys/protosw.h>
48 #include <sys/socket.h>
50 #include <netinet/in.h>
51 #define RIPVERSION RIPv2
52 #include <protocols/routed.h>
53 #include <arpa/inet.h>
64 #include <netinet/udp.h>
66 #ident "$Revision: 1.12 $"
68 #define WTIME 15 /* Time to wait for all responses */
69 #define STIME (250*1000) /* usec to wait for another response */
72 * The size of the control buffer passed to recvmsg() used to receive
75 #define CONTROL_BUFSIZE 1024
77 static const char *pgmname
;
81 char packet
[MAXPACKETSIZE
+MAXPATHLEN
];
83 #define OMSG omsg_buf.rip
84 static int omsg_len
= sizeof (struct rip
);
88 char packet
[MAXPACKETSIZE
+1024];
90 #define IMSG imsg_buf.rip
92 static int wtime
= WTIME
;
93 static int auth_type
= RIP_AUTH_NONE
;
94 char passwd
[RIP_AUTH_PW_LEN
+1];
96 static boolean_t ripv2
= _B_TRUE
; /* use RIP version 2 */
97 static boolean_t trace
, not_trace
; /* send trace command or not */
98 static boolean_t nflag
; /* numbers, no names */
99 static boolean_t pflag
; /* play the `gated` game */
100 static boolean_t rflag
; /* 1=ask about a particular route */
102 static struct timeval sent
; /* when query sent */
104 static char *default_argv
[] = {"localhost", 0};
106 static void rip_input(struct sockaddr_in
*, int, uint_t
);
107 static int out(const char *, int);
108 static void trace_loop(char *argv
[], int);
109 static void query_loop(char *argv
[], int, int);
110 static uint_t
incoming_interface(struct msghdr
*);
111 static void usage(void);
115 main(int argc
, char *argv
[])
117 #define MAX_RCVBUF 127*1024
118 #define MIN_RCVBUF 4*1024
121 char *p
, *tmp_ptr
, *options
, *value
, delim
;
123 in_addr_t netaddr
, netmask
;
126 (void) setlocale(LC_ALL
, "");
127 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
128 #define TEXT_DOMAIN "SYS_TEXT"
129 #endif /* ! TEXT_DOMAIN */
131 (void) textdomain(TEXT_DOMAIN
);
133 OMSG
.rip_nets
[0].n_dst
= RIP_DEFAULT
;
134 OMSG
.rip_nets
[0].n_family
= RIP_AF_UNSPEC
;
135 OMSG
.rip_nets
[0].n_metric
= htonl(HOPCNT_INFINITY
);
137 if ((pgmname
= argv
[0]) == NULL
)
139 while ((ch
= getopt(argc
, argv
, "np1w:r:t:a:")) != -1)
157 wtime
= (int)strtoul(optarg
, &p
, 0);
158 if (*p
!= '\0' || wtime
<= 0 || p
== optarg
)
166 rflag
= getnet(optarg
, &netaddr
, &netmask
);
168 OMSG
.rip_nets
[0].n_dst
= htonl(netaddr
);
169 OMSG
.rip_nets
[0].n_family
= RIP_AF_INET
;
170 OMSG
.rip_nets
[0].n_mask
= htonl(netmask
);
172 struct hostent
*hp
= gethostbyname(optarg
);
174 (void) fprintf(stderr
, "%s: %s: %s\n",
179 (void) memcpy(&OMSG
.rip_nets
[0].n_dst
,
181 sizeof (OMSG
.rip_nets
[0].n_dst
));
182 OMSG
.rip_nets
[0].n_family
= RIP_AF_INET
;
183 OMSG
.rip_nets
[0].n_mask
= INADDR_BROADCAST
;
191 while (*options
!= '\0') {
192 /* messy complications to make -W -Wall happy */
193 static char on_str
[] = "on";
194 static char more_str
[] = "more";
195 static char off_str
[] = "off";
196 static char dump_str
[] = "dump";
197 static char *traceopts
[] = {
209 switch (getsubopt(&options
, traceopts
,
212 OMSG
.rip_cmd
= RIPCMD_TRACEON
;
214 strlen(value
) > MAXPATHLEN
)
221 OMSG
.rip_cmd
= RIPCMD_TRACEON
;
226 OMSG
.rip_cmd
= RIPCMD_TRACEOFF
;
231 OMSG
.rip_cmd
= RIPCMD_TRACEON
;
232 result
= "dump/../table";
237 (void) strlcpy((char *)OMSG
.rip_tracefile
,
239 omsg_len
+= strlen(result
) -
246 p
= strchr(optarg
, '=');
250 if (0 == strcasecmp("passwd", optarg
))
251 auth_type
= RIP_AUTH_PW
;
252 else if (0 == strcasecmp("md5_passwd", optarg
))
253 auth_type
= RIP_AUTH_MD5
;
256 if (0 > parse_quote(&p
, "|", &delim
,
257 passwd
, sizeof (passwd
)))
259 if (auth_type
== RIP_AUTH_MD5
&&
262 keyid
= strtoul(p
+1, &p
, 0);
263 if (keyid
> 255 || *p
!= '\0' ||
266 } else if (delim
!= '\0') {
276 if (not_trace
&& trace
)
283 soc
= socket(PF_INET
, SOCK_DGRAM
, 0);
285 perror("rtquery: socket");
290 if (setsockopt(soc
, IPPROTO_IP
, IP_RECVIF
, &on
, sizeof (on
)))
291 perror("rtquery: setsockopt IP_RECVIF");
293 /* be prepared to receive a lot of routes */
294 for (bsize
= MAX_RCVBUF
; ; bsize
-= 1024) {
295 if (setsockopt(soc
, SOL_SOCKET
, SO_RCVBUF
,
296 &bsize
, sizeof (bsize
)) == 0)
298 if (bsize
<= MIN_RCVBUF
) {
299 perror("rtquery: setsockopt SO_RCVBUF");
305 trace_loop(argv
, soc
);
307 query_loop(argv
, argc
, soc
);
316 (void) fprintf(stderr
,
317 gettext("usage: %s [-np1] [-r tgt_rt] [-w wtime]"
318 " [-a type=passwd] [host1 ...]\n"),
320 (void) fprintf(stderr
,
321 gettext("\t%s -t {on=filename|more|off|dump} [host1 ...]\n"),
327 /* Tell the target hosts about tracing */
329 trace_loop(char *argv
[], int soc
)
331 struct sockaddr_in myaddr
;
336 OMSG
.rip_vers
= RIPv2
;
338 OMSG
.rip_vers
= RIPv1
;
341 (void) memset(&myaddr
, 0, sizeof (myaddr
));
342 myaddr
.sin_family
= AF_INET
;
343 if (setsockopt(soc
, IPPROTO_UDP
, UDP_ANONPRIVBIND
,
344 &optval
, sizeof (optval
)) < 0) {
345 perror("rtquery: setsockopt UDP_ANONPRIVBIND");
349 if (bind(soc
, (struct sockaddr
*)&myaddr
, sizeof (myaddr
)) < 0) {
350 perror("rtquery: bind");
355 while (*argv
!= NULL
) {
356 if (out(*argv
++, soc
) == 0)
363 /* Query all of the listed hosts */
365 query_loop(char *argv
[], int argc
, int soc
)
367 #define NA0 (OMSG.rip_auths[0])
368 #define NA2 (OMSG.rip_auths[2])
376 struct timeval now
, delay
;
377 struct sockaddr_in from
;
382 uint8_t ancillary_data
[CONTROL_BUFSIZE
];
385 OMSG
.rip_cmd
= (pflag
) ? RIPCMD_POLL
: RIPCMD_REQUEST
;
387 OMSG
.rip_vers
= RIPv2
;
388 if (auth_type
== RIP_AUTH_PW
) {
389 OMSG
.rip_nets
[1] = OMSG
.rip_nets
[0];
390 NA0
.a_family
= RIP_AF_AUTH
;
391 NA0
.a_type
= RIP_AUTH_PW
;
392 (void) memcpy(NA0
.au
.au_pw
, passwd
, RIP_AUTH_PW_LEN
);
393 omsg_len
+= sizeof (OMSG
.rip_nets
[0]);
395 } else if (auth_type
== RIP_AUTH_MD5
) {
396 OMSG
.rip_nets
[1] = OMSG
.rip_nets
[0];
397 NA0
.a_family
= RIP_AF_AUTH
;
398 NA0
.a_type
= RIP_AUTH_MD5
;
399 NA0
.au
.a_md5
.md5_keyid
= (int8_t)keyid
;
400 NA0
.au
.a_md5
.md5_auth_len
= RIP_AUTH_MD5_LEN
;
401 NA0
.au
.a_md5
.md5_seqno
= 0;
402 cc
= (char *)&NA2
-(char *)&OMSG
;
403 NA0
.au
.a_md5
.md5_pkt_len
= htons(cc
);
404 NA2
.a_family
= RIP_AF_AUTH
;
405 NA2
.a_type
= RIP_AUTH_TRAILER
;
407 MD5Update(&md5_ctx
, (uchar_t
*)&OMSG
, cc
+4);
409 (uchar_t
*)passwd
, RIP_AUTH_MD5_LEN
);
410 MD5Final(NA2
.au
.au_pw
, &md5_ctx
);
411 omsg_len
+= 2*sizeof (OMSG
.rip_nets
[0]);
415 OMSG
.rip_vers
= RIPv1
;
416 OMSG
.rip_nets
[0].n_mask
= 0;
419 /* ask the first (valid) host */
421 while (0 > out(*argv
++, soc
)) {
427 iov
.iov_base
= &imsg_buf
;
428 iov
.iov_len
= sizeof (imsg_buf
);
431 msg
.msg_name
= &from
;
432 msg
.msg_control
= &ancillary_data
;
434 (void) FD_ZERO(&bits
);
438 delay
.tv_usec
= STIME
;
439 cc
= select(soc
+1, &bits
, 0, 0, &delay
);
441 msg
.msg_namelen
= sizeof (from
);
442 msg
.msg_controllen
= sizeof (ancillary_data
);
443 cc
= recvmsg(soc
, &msg
, 0);
445 perror("rtquery: recvmsg");
449 /* avoid looping on high traffic */
450 if (answered
> argc
+ 200)
454 * count the distinct responding hosts.
455 * You cannot match responding hosts with
456 * addresses to which queries were transmitted,
457 * because a router might respond with a
458 * different source address.
460 for (sp
= seen
; sp
!= NULL
; sp
= sp
->next
) {
461 if (sp
->addr
.s_addr
== from
.sin_addr
.s_addr
)
465 sp
= malloc(sizeof (*sp
));
467 sp
->addr
= from
.sin_addr
;
471 perror("rtquery: malloc");
476 ifindex
= incoming_interface(&msg
);
477 rip_input(&from
, cc
, ifindex
);
484 perror("rtquery: select");
489 * After a pause in responses, probe another host.
490 * This reduces the intermingling of answers.
492 while (*argv
!= NULL
&& 0 > out(*argv
++, soc
))
496 * continue until no more packets arrive
497 * or we have heard from all hosts
499 if (answered
>= argc
)
502 /* or until we have waited a long time */
503 if (gettimeofday(&now
, 0) < 0) {
504 perror("rtquery: gettimeofday");
507 if (sent
.tv_sec
+ wtime
<= now
.tv_sec
)
511 /* fail if there was no answer */
512 exit(answered
>= argc
? EXIT_SUCCESS
: EXIT_FAILURE
);
516 /* Send to one host */
518 out(const char *host
, int soc
)
521 struct addrinfo hints
, *res
;
524 if (gettimeofday(&sent
, 0) < 0) {
525 perror("rtquery: gettimeofday");
529 (void) memset(&hints
, 0, sizeof (hints
));
530 hints
.ai_family
= PF_INET
;
531 hints
.ai_socktype
= SOCK_DGRAM
;
532 if ((ret
= getaddrinfo(host
, "route", &hints
, &res
)) != 0) {
533 (void) fprintf(stderr
, "%s: getaddrinfo: %s: %s\n", pgmname
,
534 host
, gai_strerror(ret
));
538 if (sendto(soc
, &omsg_buf
, omsg_len
, 0, res
->ai_addr
,
539 res
->ai_addrlen
) < 0) {
540 perror("rtquery: sendto");
549 * Handle an incoming RIP packet.
552 rip_input(struct sockaddr_in
*from
, int size
, uint_t ifindex
)
554 struct netinfo
*n
, *lim
;
558 uchar_t hash
[RIP_AUTH_MD5_LEN
];
560 uchar_t md5_authed
= 0;
561 in_addr_t mask
, dmask
;
562 struct in_addr tmp_addr
;
564 char ifname
[IF_NAMESIZE
+1];
569 char srcaddr
[MAXHOSTNAMELEN
+ sizeof (" (123.123.123.123)") + 1];
570 char ifstring
[IF_NAMESIZE
+ 3*sizeof (ifindex
) + sizeof (" ()") + 1];
572 if (!nflag
&& (hp
= gethostbyaddr((char *)&from
->sin_addr
,
573 sizeof (struct in_addr
), AF_INET
)) != NULL
) {
574 (void) snprintf(srcaddr
, sizeof (srcaddr
), "%s (%s)",
575 hp
->h_name
, inet_ntoa(from
->sin_addr
));
577 /* safe; cannot overflow destination */
578 (void) strcpy(srcaddr
, inet_ntoa(from
->sin_addr
));
581 (void) printf("%s:", srcaddr
);
583 if (if_indextoname(ifindex
, ifname
) != NULL
)
584 (void) snprintf(ifstring
, sizeof (ifstring
), "%s (%d)",
587 (void) snprintf(ifstring
, sizeof (ifstring
), "%d",
589 (void) printf(gettext("%1$s received on interface %2$s:"),
593 if (IMSG
.rip_cmd
!= RIPCMD_RESPONSE
) {
594 (void) printf(gettext("\n unexpected response type %d\n"),
598 (void) printf(gettext(" RIPv%1$d%2$s %3$d bytes\n"), IMSG
.rip_vers
,
599 (IMSG
.rip_vers
!= RIPv1
&& IMSG
.rip_vers
!= RIPv2
) ? " ?" : "",
601 if (size
> MAXPACKETSIZE
) {
602 if (size
> sizeof (imsg_buf
) - sizeof (*n
)) {
604 gettext(" at least %d bytes too long\n"),
606 size
= sizeof (imsg_buf
) - sizeof (*n
);
608 (void) printf(gettext(" %d bytes too long\n"),
611 } else if (size
%sizeof (*n
) != sizeof (struct rip
)%sizeof (*n
)) {
612 (void) printf(gettext(" response of bad length=%d\n"), size
);
616 lim
= n
+ (size
- 4) / sizeof (struct netinfo
);
617 for (; n
< lim
; n
++) {
619 if (n
->n_family
== RIP_AF_INET
) {
620 in
.s_addr
= n
->n_dst
;
621 (void) strlcpy(net_buf
, inet_ntoa(in
),
624 tmp_addr
.s_addr
= (n
->n_mask
);
625 mask
= ntohl(n
->n_mask
);
626 dmask
= mask
& -mask
;
628 sp
= &net_buf
[strlen(net_buf
)];
629 if (IMSG
.rip_vers
== RIPv1
) {
633 gettext(" mask=%s ? "),
634 inet_ntoa(tmp_addr
));
636 } else if (mask
+ dmask
== 0) {
640 strlen(net_buf
)), "/%d", 32-i
);
645 gettext(" (mask %s)"),
646 inet_ntoa(tmp_addr
));
652 mask
= std_mask(in
.s_addr
);
653 if ((ntohl(in
.s_addr
) & ~mask
) != 0)
657 * Without a netmask, do not worry about
658 * whether the destination is a host or a
659 * network. Try both and use the first name
662 * If we have a netmask we can make a
665 if ((in
.s_addr
& ~mask
) == 0) {
666 np
= getnetbyaddr((long)in
.s_addr
,
670 else if (in
.s_addr
== 0)
673 if (name
[0] == '\0' &&
674 ((in
.s_addr
& ~mask
) != 0 ||
675 mask
== 0xffffffff)) {
676 hp
= gethostbyaddr((char *)&in
,
677 sizeof (in
), AF_INET
);
683 } else if (n
->n_family
== RIP_AF_AUTH
) {
684 na
= (struct netauth
*)n
;
685 if (na
->a_type
== RIP_AUTH_PW
&&
686 n
== IMSG
.rip_nets
) {
688 gettext(" Password Authentication:"
690 qstring(na
->au
.au_pw
,
695 if (na
->a_type
== RIP_AUTH_MD5
&&
696 n
== IMSG
.rip_nets
) {
697 (void) printf(gettext(" MD5 Auth"
698 " len=%1$d KeyID=%2$d"
701 " rsvd=%5$#x,%6$#x\n"),
702 ntohs(na
->au
.a_md5
.md5_pkt_len
),
703 na
->au
.a_md5
.md5_keyid
,
704 na
->au
.a_md5
.md5_auth_len
,
705 (int)ntohl(na
->au
.a_md5
.md5_seqno
),
706 na
->au
.a_md5
.rsvd
[0],
707 na
->au
.a_md5
.rsvd
[1]);
711 (void) printf(gettext(" Authentication type %d: "),
713 for (i
= 0; i
< sizeof (na
->au
.au_pw
); i
++)
714 (void) printf("%02x ",
716 (void) putchar('\n');
717 if (md5_authed
&& n
+1 > lim
&&
718 na
->a_type
== RIP_AUTH_TRAILER
) {
720 MD5Update(&md5_ctx
, (uchar_t
*)&IMSG
,
721 (char *)na
-(char *)&IMSG
);
723 (uchar_t
*)passwd
, RIP_AUTH_MD5_LEN
);
724 MD5Final(hash
, &md5_ctx
);
725 (void) printf(gettext(" %s hash\n"),
726 memcmp(hash
, na
->au
.au_pw
, sizeof (hash
)) ?
727 gettext("WRONG") : gettext("correct"));
728 } else if (md5_authed
&& n
+1 > lim
&&
729 na
->a_type
!= RIP_AUTH_TRAILER
) {
730 (void) printf(gettext("Error -"
731 "authentication entry missing hash\n"));
736 tmp_addr
.s_addr
= n
->n_dst
;
737 (void) snprintf(net_buf
, sizeof (net_buf
),
738 gettext("(address family %1$u) %2$s"),
739 ntohs(n
->n_family
), inet_ntoa(tmp_addr
));
742 (void) printf(gettext(" %1$-18s metric %2$2lu %3$-10s"),
743 net_buf
, ntohl(n
->n_metric
), name
);
745 if (n
->n_nhop
!= 0) {
746 in
.s_addr
= n
->n_nhop
;
750 hp
= gethostbyaddr((char *)&in
, sizeof (in
),
752 (void) printf(gettext(" nhop=%1$-15s%2$s"),
753 (hp
!= NULL
) ? hp
->h_name
: inet_ntoa(in
),
754 (IMSG
.rip_vers
== RIPv1
) ? " ?" : "");
757 (void) printf(gettext(" tag=%1$#x%2$s"), n
->n_tag
,
758 (IMSG
.rip_vers
== RIPv1
) ? " ?" : "");
759 (void) putchar('\n');
764 * Find the interface which received the given message.
767 incoming_interface(struct msghdr
*msg
)
773 * Determine which physical interface this packet was received on by
774 * processing the message's ancillary data to find the
775 * IP_RECVIF option we requested.
777 if ((opt
= find_ancillary(msg
, IP_RECVIF
)) == NULL
)
778 (void) fprintf(stderr
,
779 gettext("%s: unable to retrieve input interface\n"),
782 ifindex
= *(uint_t
*)opt
;