2 * Copyright (c) 1982, 1986, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgment:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * $FreeBSD: src/sbin/routed/rtquery/rtquery.c,v 1.13 1999/08/28 00:14:21 peter Exp $
34 * $DragonFly: src/sbin/routed/rtquery/rtquery.c,v 1.4 2004/12/18 21:43:46 swildner Exp $
38 "@(#) Copyright (c) 1982, 1986, 1993\n\
39 The Regents of the University of California. All rights reserved.\n";
41 #include <sys/cdefs.h>
42 #include <sys/param.h>
43 #include <sys/protosw.h>
44 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #define RIPVERSION RIPv2
48 #include <protocols/routed.h>
49 #include <arpa/inet.h>
61 #if !defined(sgi) && !defined(__NetBSD__)
62 static char sccsid
[] __attribute__((unused
))= "@(#)query.c 8.1 (Berkeley) 6/5/93";
63 #elif defined(__NetBSD__)
64 __RCSID("$NetBSD: rtquery.c,v 1.10 1999/02/23 10:47:41 christos Exp $");
71 #define MD5_DIGEST_LEN 16
73 u_int32_t state
[4]; /* state (ABCD) */
74 u_int32_t count
[2]; /* # of bits, modulo 2^64 (LSB 1st) */
75 unsigned char buffer
[64]; /* input buffer */
77 extern void MD5Init(MD5_CTX
*);
78 extern void MD5Update(MD5_CTX
*, u_char
*, u_int
);
79 extern void MD5Final(u_char
[MD5_DIGEST_LEN
], MD5_CTX
*);
82 #define WTIME 15 /* Time to wait for all responses */
83 #define STIME (250*1000) /* usec to wait for another response */
91 char packet
[MAXPACKETSIZE
+MAXPATHLEN
];
93 #define OMSG omsg_buf.rip
94 int omsg_len
= sizeof(struct rip
);
98 char packet
[MAXPACKETSIZE
+1024];
100 #define IMSG imsg_buf.rip
102 int nflag
; /* numbers, no names */
103 int pflag
; /* play the `gated` game */
104 int ripv2
= 1; /* use RIP version 2 */
106 int rflag
; /* 1=ask about a particular route */
107 int trace
, not_trace
; /* send trace command or not */
108 int auth_type
= RIP_AUTH_NONE
;
109 char passwd
[RIP_AUTH_PW_LEN
];
112 struct timeval sent
; /* when query sent */
114 static char localhost_str
[] = "localhost";
115 static char *default_argv
[] = {localhost_str
, 0};
117 static void rip_input(struct sockaddr_in
*, int);
118 static int out(const char *);
119 static void trace_loop(char *argv
[]) __attribute((__noreturn__
));
120 static void query_loop(char *argv
[], int) __attribute((__noreturn__
));
121 static int getnet(char *, struct netinfo
*);
122 static u_int
std_mask(u_int
);
123 static int parse_quote(char **, const char *, char *, char *, int);
124 static void usage(void);
132 char *p
, *options
, *value
, delim
;
135 OMSG
.rip_nets
[0].n_dst
= RIP_DEFAULT
;
136 OMSG
.rip_nets
[0].n_family
= RIP_AF_UNSPEC
;
137 OMSG
.rip_nets
[0].n_metric
= htonl(HOPCNT_INFINITY
);
140 while ((ch
= getopt(argc
, argv
, "np1w:r:t:a:")) != -1)
158 wtime
= (int)strtoul(optarg
, &p
, 0);
168 rflag
= getnet(optarg
, &OMSG
.rip_nets
[0]);
170 struct hostent
*hp
= gethostbyname(optarg
);
172 fprintf(stderr
, "%s: %s:",
177 memcpy(&OMSG
.rip_nets
[0].n_dst
, hp
->h_addr
,
178 sizeof(OMSG
.rip_nets
[0].n_dst
));
179 OMSG
.rip_nets
[0].n_family
= RIP_AF_INET
;
180 OMSG
.rip_nets
[0].n_mask
= -1;
188 while (*options
!= '\0') {
189 /* messy complications to make -W -Wall happy */
190 static char on_str
[] = "on";
191 static char more_str
[] = "more";
192 static char off_str
[] = "off";
193 static char dump_str
[] = "dump";
194 static char *traceopts
[] = {
197 # define TRACE_MORE 1
201 # define TRACE_DUMP 3
206 switch (getsubopt(&options
,traceopts
,&value
)) {
208 OMSG
.rip_cmd
= RIPCMD_TRACEON
;
210 || strlen(value
) > MAXPATHLEN
)
217 OMSG
.rip_cmd
= RIPCMD_TRACEON
;
222 OMSG
.rip_cmd
= RIPCMD_TRACEOFF
;
227 OMSG
.rip_cmd
= RIPCMD_TRACEON
;
228 result
= "dump/../table";
233 strcpy((char*)OMSG
.rip_tracefile
, result
);
234 omsg_len
+= strlen(result
) - sizeof(OMSG
.ripun
);
240 p
= strchr(optarg
,'=');
244 if (!strcasecmp("passwd",optarg
))
245 auth_type
= RIP_AUTH_PW
;
246 else if (!strcasecmp("md5_passwd",optarg
))
247 auth_type
= RIP_AUTH_MD5
;
250 if (0 > parse_quote(&p
,"|",&delim
,
251 passwd
, sizeof(passwd
)))
253 if (auth_type
== RIP_AUTH_MD5
255 keyid
= strtoul(p
+1,&p
,0);
256 if (keyid
> 255 || *p
!= '\0')
258 } else if (delim
!= '\0') {
268 if (not_trace
&& trace
)
275 soc
= socket(AF_INET
, SOCK_DGRAM
, 0);
281 /* be prepared to receive a lot of routes */
282 for (bsize
= 127*1024; ; bsize
-= 1024) {
283 if (setsockopt(soc
, SOL_SOCKET
, SO_RCVBUF
,
284 &bsize
, sizeof(bsize
)) == 0)
286 if (bsize
<= 4*1024) {
287 perror("setsockopt SO_RCVBUF");
295 query_loop(argv
, argc
);
305 "usage: rtquery [-np1] [-r tgt_rt] [-w wtime]"
306 " [-a type=passwd] host1 [host2 ...]\n"
307 "\trtquery -t {on=filename|more|off|dump}"
308 " host1 [host2 ...]\n");
313 /* tell the target hosts about tracing
316 trace_loop(char *argv
[])
318 struct sockaddr_in myaddr
;
321 if (geteuid() != 0) {
322 fprintf(stderr
, "-t requires UID 0\n");
327 OMSG
.rip_vers
= RIPv2
;
329 OMSG
.rip_vers
= RIPv1
;
332 memset(&myaddr
, 0, sizeof(myaddr
));
333 myaddr
.sin_family
= AF_INET
;
335 myaddr
.sin_len
= sizeof(myaddr
);
337 myaddr
.sin_port
= htons(IPPORT_RESERVED
-1);
338 while (bind(soc
, (struct sockaddr
*)&myaddr
, sizeof(myaddr
)) < 0) {
339 if (errno
!= EADDRINUSE
340 || myaddr
.sin_port
== 0) {
344 myaddr
.sin_port
= htons(ntohs(myaddr
.sin_port
)-1);
349 if (out(*argv
++) <= 0)
356 /* query all of the listed hosts
359 query_loop(char *argv
[], int argc
)
361 # define NA0 (OMSG.rip_auths[0])
362 # define NA2 (OMSG.rip_auths[2])
370 struct timeval now
, delay
;
371 struct sockaddr_in from
;
376 OMSG
.rip_cmd
= (pflag
) ? RIPCMD_POLL
: RIPCMD_REQUEST
;
378 OMSG
.rip_vers
= RIPv2
;
379 if (auth_type
== RIP_AUTH_PW
) {
380 OMSG
.rip_nets
[1] = OMSG
.rip_nets
[0];
381 NA0
.a_family
= RIP_AF_AUTH
;
382 NA0
.a_type
= RIP_AUTH_PW
;
383 memcpy(NA0
.au
.au_pw
, passwd
, RIP_AUTH_PW_LEN
);
384 omsg_len
+= sizeof(OMSG
.rip_nets
[0]);
386 } else if (auth_type
== RIP_AUTH_MD5
) {
387 OMSG
.rip_nets
[1] = OMSG
.rip_nets
[0];
388 NA0
.a_family
= RIP_AF_AUTH
;
389 NA0
.a_type
= RIP_AUTH_MD5
;
390 NA0
.au
.a_md5
.md5_keyid
= (int8_t)keyid
;
391 NA0
.au
.a_md5
.md5_auth_len
= RIP_AUTH_MD5_LEN
;
392 NA0
.au
.a_md5
.md5_seqno
= 0;
393 cc
= (char *)&NA2
-(char *)&OMSG
;
394 NA0
.au
.a_md5
.md5_pkt_len
= htons(cc
);
395 NA2
.a_family
= RIP_AF_AUTH
;
396 NA2
.a_type
= htons(1);
399 (u_char
*)&OMSG
, cc
);
401 (u_char
*)passwd
, RIP_AUTH_MD5_LEN
);
402 MD5Final(NA2
.au
.au_pw
, &md5_ctx
);
403 omsg_len
+= 2*sizeof(OMSG
.rip_nets
[0]);
407 OMSG
.rip_vers
= RIPv1
;
408 OMSG
.rip_nets
[0].n_mask
= 0;
411 /* ask the first (valid) host */
413 while (0 > out(*argv
++)) {
423 delay
.tv_usec
= STIME
;
424 cc
= select(soc
+1, &bits
, 0,0, &delay
);
426 fromlen
= sizeof(from
);
427 cc
= recvfrom(soc
, imsg_buf
.packet
,
428 sizeof(imsg_buf
.packet
), 0,
429 (struct sockaddr
*)&from
, &fromlen
);
434 /* count the distinct responding hosts.
435 * You cannot match responding hosts with
436 * addresses to which queries were transmitted,
437 * because a router might respond with a
438 * different source address.
440 for (sp
= seen
; sp
!= 0; sp
= sp
->next
) {
441 if (sp
->addr
.s_addr
== from
.sin_addr
.s_addr
)
445 sp
= malloc(sizeof(*sp
));
448 "rtquery: malloc failed\n");
451 sp
->addr
= from
.sin_addr
;
457 rip_input(&from
, cc
);
468 /* After a pause in responses, probe another host.
469 * This reduces the intermingling of answers.
471 while (*argv
!= 0 && 0 > out(*argv
++))
474 /* continue until no more packets arrive
475 * or we have heard from all hosts
477 if (answered
>= argc
)
480 /* or until we have waited a long time
482 if (gettimeofday(&now
, 0) < 0) {
483 perror("gettimeofday(now)");
486 if (sent
.tv_sec
+ wtime
<= now
.tv_sec
)
490 /* fail if there was no answer */
491 exit (answered
>= argc
? 0 : 1);
498 out(const char *host
)
500 struct sockaddr_in router
;
503 if (gettimeofday(&sent
, 0) < 0) {
504 perror("gettimeofday(sent)");
508 memset(&router
, 0, sizeof(router
));
509 router
.sin_family
= AF_INET
;
511 router
.sin_len
= sizeof(router
);
513 if (!inet_aton(host
, &router
.sin_addr
)) {
514 hp
= gethostbyname(host
);
519 memcpy(&router
.sin_addr
, hp
->h_addr
, sizeof(router
.sin_addr
));
521 router
.sin_port
= htons(RIP_PORT
);
523 if (sendto(soc
, &omsg_buf
, omsg_len
, 0,
524 (struct sockaddr
*)&router
, sizeof(router
)) < 0) {
534 * Convert string to printable characters
537 qstring(u_char
*s
, int len
)
539 static char buf
[8*20+1];
544 for (p
= buf
; len
!= 0 && p
< &buf
[sizeof(buf
)-1]; len
--) {
547 for (s2
= s
+1; s2
< &s
[len
]; s2
++) {
555 if (c
>= ' ' && c
< 0x7f && c
!= '\\') {
577 p
+= sprintf(p
,"%o",c
);
588 * Handle an incoming RIP packet.
591 rip_input(struct sockaddr_in
*from
,
594 struct netinfo
*n
, *lim
;
598 u_char hash
[RIP_AUTH_MD5_LEN
];
600 u_char md5_authed
= 0;
610 printf("%s:", inet_ntoa(from
->sin_addr
));
612 hp
= gethostbyaddr(&from
->sin_addr
, sizeof(struct in_addr
),
616 inet_ntoa(from
->sin_addr
));
618 printf("%s (%s):", hp
->h_name
,
619 inet_ntoa(from
->sin_addr
));
622 if (IMSG
.rip_cmd
!= RIPCMD_RESPONSE
) {
623 printf("\n unexpected response type %d\n", IMSG
.rip_cmd
);
626 printf(" RIPv%d%s %d bytes\n", IMSG
.rip_vers
,
627 (IMSG
.rip_vers
!= RIPv1
&& IMSG
.rip_vers
!= RIPv2
) ? " ?" : "",
629 if (size
> MAXPACKETSIZE
) {
630 if (size
> (int)sizeof(imsg_buf
) - (int)sizeof(*n
)) {
631 printf(" at least %d bytes too long\n",
633 size
= (int)sizeof(imsg_buf
) - (int)sizeof(*n
);
635 printf(" %d bytes too long\n",
638 } else if (size
%sizeof(*n
) != sizeof(struct rip
)%sizeof(*n
)) {
639 printf(" response of bad length=%d\n", size
);
643 lim
= (struct netinfo
*)((char*)n
+ size
) - 1;
644 for (; n
<= lim
; n
++) {
646 if (n
->n_family
== RIP_AF_INET
) {
647 in
.s_addr
= n
->n_dst
;
648 strcpy(net_buf
, inet_ntoa(in
));
650 mask
= ntohl(n
->n_mask
);
651 dmask
= mask
& -mask
;
653 sp
= &net_buf
[strlen(net_buf
)];
654 if (IMSG
.rip_vers
== RIPv1
) {
655 sprintf(sp
," mask=%#x ? ",mask
);
657 } else if (mask
+ dmask
== 0) {
660 && ((1<<i
)&mask
) == 0);
663 sprintf(sp
, "/%d",32-i
);
665 sprintf(sp
," (mask %#x)", mask
);
671 mask
= std_mask(in
.s_addr
);
672 if ((ntohl(in
.s_addr
) & ~mask
) != 0)
675 /* Without a netmask, do not worry about
676 * whether the destination is a host or a
677 * network. Try both and use the first name
680 * If we have a netmask we can make a
683 if ((in
.s_addr
& ~mask
) == 0) {
684 np
= getnetbyaddr((long)in
.s_addr
,
688 else if (in
.s_addr
== 0)
692 && ((in
.s_addr
& ~mask
) != 0
693 || mask
== 0xffffffff)) {
694 hp
= gethostbyaddr(&in
, sizeof(in
),
701 } else if (n
->n_family
== RIP_AF_AUTH
) {
702 na
= (struct netauth
*)n
;
703 if (na
->a_type
== RIP_AUTH_PW
704 && n
== IMSG
.rip_nets
) {
705 printf(" Password Authentication: \"%s\"\n",
706 qstring(na
->au
.au_pw
, RIP_AUTH_PW_LEN
));
710 if (na
->a_type
== RIP_AUTH_MD5
711 && n
== IMSG
.rip_nets
) {
717 ntohs(na
->au
.a_md5
.md5_pkt_len
),
718 na
->au
.a_md5
.md5_keyid
,
719 na
->au
.a_md5
.md5_auth_len
,
720 (int)ntohl(na
->au
.a_md5
.md5_seqno
),
721 na
->au
.a_md5
.rsvd
[0],
722 na
->au
.a_md5
.rsvd
[1]);
726 printf(" Authentication type %d: ", ntohs(na
->a_type
));
727 for (i
= 0; i
< (int)sizeof(na
->au
.au_pw
); i
++)
728 printf("%02x ", na
->au
.au_pw
[i
]);
730 if (md5_authed
&& n
+1 > lim
731 && na
->a_type
== ntohs(1)) {
733 MD5Update(&md5_ctx
, (u_char
*)&IMSG
,
734 (char *)na
-(char *)&IMSG
);
735 MD5Update(&md5_ctx
, (u_char
*)passwd
,
737 MD5Final(hash
, &md5_ctx
);
739 memcmp(hash
, na
->au
.au_pw
, sizeof(hash
))
740 ? "WRONG" : "correct");
745 sprintf(net_buf
, "(af %#x) %d.%d.%d.%d",
747 (char)(n
->n_dst
>> 24),
748 (char)(n
->n_dst
>> 16),
749 (char)(n
->n_dst
>> 8),
753 printf(" %-18s metric %2d %-10s",
754 net_buf
, (int)ntohl(n
->n_metric
), name
);
756 if (n
->n_nhop
!= 0) {
757 in
.s_addr
= n
->n_nhop
;
761 hp
= gethostbyaddr(&in
, sizeof(in
), AF_INET
);
762 printf(" nhop=%-15s%s",
763 (hp
!= 0) ? hp
->h_name
: inet_ntoa(in
),
764 (IMSG
.rip_vers
== RIPv1
) ? " ?" : "");
767 printf(" tag=%#x%s", n
->n_tag
,
768 (IMSG
.rip_vers
== RIPv1
) ? " ?" : "");
774 /* Return the classical netmask for an IP address.
777 std_mask(u_int addr
) /* in network order */
779 addr
= ntohl(addr
); /* was a host, not a network */
781 if (addr
== 0) /* default route has mask 0 */
784 return IN_CLASSA_NET
;
786 return IN_CLASSB_NET
;
787 return IN_CLASSC_NET
;
791 /* get a network number as a name or a number, with an optional "/xx"
794 static int /* 0=bad */
799 struct netent
*nentp
;
802 char hname
[MAXHOSTNAMELEN
+1];
806 /* Detect and separate "1.2.3.4/24"
808 if (0 != (mname
= strrchr(name
,'/'))) {
809 i
= (int)(mname
- name
);
810 if (i
> (int)sizeof(hname
)-1) /* name too long */
812 memmove(hname
, name
, i
);
818 nentp
= getnetbyname(name
);
820 in
.s_addr
= nentp
->n_net
;
821 } else if (inet_aton(name
, &in
) == 1) {
822 in
.s_addr
= ntohl(in
.s_addr
);
828 mask
= std_mask(in
.s_addr
);
829 if ((~mask
& in
.s_addr
) != 0)
832 mask
= (u_int
)strtoul(mname
, &p
, 0);
833 if (*p
!= '\0' || mask
> 32)
835 mask
= 0xffffffff << (32-mask
);
838 rt
->n_dst
= htonl(in
.s_addr
);
839 rt
->n_family
= RIP_AF_INET
;
840 rt
->n_mask
= htonl(mask
);
845 /* strtok(), but honoring backslash
847 static int /* -1=bad */
848 parse_quote(char **linep
,
869 if (c
== '\\' && *pc
!= '\0') {
870 if ((c
= *pc
++) == 'n') {
872 } else if (c
== 'r') {
874 } else if (c
== 't') {
876 } else if (c
== 'b') {
878 } else if (c
>= '0' && c
<= '7') {
880 if (*pc
>= '0' && *pc
<= '7') {
881 c
= (c
<<3)+(*pc
++ - '0');
882 if (*pc
>= '0' && *pc
<= '7')
883 c
= (c
<<3)+(*pc
++ - '0');
888 for (p
= delims
; *p
!= '\0'; ++p
) {