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. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * $FreeBSD: src/sbin/routed/rtquery/rtquery.c,v 1.13 1999/08/28 00:14:21 peter Exp $
33 "@(#) Copyright (c) 1982, 1986, 1993\n\
34 The Regents of the University of California. All rights reserved.\n";
36 #include <sys/cdefs.h>
37 #include <sys/param.h>
38 #include <sys/protosw.h>
39 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #define RIPVERSION RIPv2
43 #include <protocols/routed.h>
44 #include <arpa/inet.h>
52 #if !defined(__NetBSD__)
53 static char sccsid
[] __attribute__((unused
))= "@(#)query.c 8.1 (Berkeley) 6/5/93";
54 #elif defined(__NetBSD__)
55 __RCSID("$NetBSD: rtquery.c,v 1.10 1999/02/23 10:47:41 christos Exp $");
60 #define MD5_DIGEST_LEN 16
62 u_int32_t state
[4]; /* state (ABCD) */
63 u_int32_t count
[2]; /* # of bits, modulo 2^64 (LSB 1st) */
64 unsigned char buffer
[64]; /* input buffer */
66 extern void MD5Init(MD5_CTX
*);
67 extern void MD5Update(MD5_CTX
*, u_char
*, u_int
);
68 extern void MD5Final(u_char
[MD5_DIGEST_LEN
], MD5_CTX
*);
71 #define WTIME 15 /* Time to wait for all responses */
72 #define STIME (250*1000) /* usec to wait for another response */
80 char packet
[MAXPACKETSIZE
+MAXPATHLEN
];
82 #define OMSG omsg_buf.rip
83 int omsg_len
= sizeof(struct rip
);
87 char packet
[MAXPACKETSIZE
+1024];
89 #define IMSG imsg_buf.rip
91 int nflag
; /* numbers, no names */
92 int pflag
; /* play the `gated` game */
93 int ripv2
= 1; /* use RIP version 2 */
95 int rflag
; /* 1=ask about a particular route */
96 int trace
, not_trace
; /* send trace command or not */
97 int auth_type
= RIP_AUTH_NONE
;
98 char passwd
[RIP_AUTH_PW_LEN
];
101 struct timeval sent
; /* when query sent */
103 static char localhost_str
[] = "localhost";
104 static char *default_argv
[] = {localhost_str
, 0};
106 static void rip_input(struct sockaddr_in
*, int);
107 static int out(const char *);
108 static void trace_loop(char *argv
[]) __attribute((__noreturn__
));
109 static void query_loop(char *argv
[], int) __attribute((__noreturn__
));
110 static int getnet(char *, struct netinfo
*);
111 static u_int
std_mask(u_int
);
112 static int parse_quote(char **, const char *, char *, char *, int);
113 static void usage(void);
121 char *p
, *options
, *value
, delim
;
124 OMSG
.rip_nets
[0].n_dst
= RIP_DEFAULT
;
125 OMSG
.rip_nets
[0].n_family
= RIP_AF_UNSPEC
;
126 OMSG
.rip_nets
[0].n_metric
= htonl(HOPCNT_INFINITY
);
130 while ((ch
= getopt(argc
, argv
, "np1w:r:t:a:")) != -1)
148 wtime
= (int)strtoul(optarg
, &p
, 0);
158 rflag
= getnet(optarg
, &OMSG
.rip_nets
[0]);
160 struct hostent
*hp
= gethostbyname(optarg
);
162 fprintf(stderr
, "%s: %s:",
167 memcpy(&OMSG
.rip_nets
[0].n_dst
, hp
->h_addr
,
168 sizeof(OMSG
.rip_nets
[0].n_dst
));
169 OMSG
.rip_nets
[0].n_family
= RIP_AF_INET
;
170 OMSG
.rip_nets
[0].n_mask
= -1;
178 while (*options
!= '\0') {
179 /* messy complications to make -W -Wall happy */
180 static char on_str
[] = "on";
181 static char more_str
[] = "more";
182 static char off_str
[] = "off";
183 static char dump_str
[] = "dump";
184 static char *traceopts
[] = {
187 # define TRACE_MORE 1
191 # define TRACE_DUMP 3
196 switch (getsubopt(&options
,traceopts
,&value
)) {
198 OMSG
.rip_cmd
= RIPCMD_TRACEON
;
200 || strlen(value
) > MAXPATHLEN
)
207 OMSG
.rip_cmd
= RIPCMD_TRACEON
;
212 OMSG
.rip_cmd
= RIPCMD_TRACEOFF
;
217 OMSG
.rip_cmd
= RIPCMD_TRACEON
;
218 result
= "dump/../table";
223 strcpy((char*)OMSG
.rip_tracefile
, result
);
224 omsg_len
+= strlen(result
) - sizeof(OMSG
.ripun
);
230 p
= strchr(optarg
,'=');
234 if (!strcasecmp("passwd",optarg
))
235 auth_type
= RIP_AUTH_PW
;
236 else if (!strcasecmp("md5_passwd",optarg
))
237 auth_type
= RIP_AUTH_MD5
;
240 if (0 > parse_quote(&p
,"|",&delim
,
241 passwd
, sizeof(passwd
)))
243 if (auth_type
== RIP_AUTH_MD5
245 keyid
= strtoul(p
+1,&p
,0);
246 if (keyid
> 255 || *p
!= '\0')
248 } else if (delim
!= '\0') {
258 if (not_trace
&& trace
)
265 soc
= socket(AF_INET
, SOCK_DGRAM
, 0);
271 /* be prepared to receive a lot of routes */
272 for (bsize
= 127*1024; ; bsize
-= 1024) {
273 if (setsockopt(soc
, SOL_SOCKET
, SO_RCVBUF
,
274 &bsize
, sizeof(bsize
)) == 0)
276 if (bsize
<= 4*1024) {
277 perror("setsockopt SO_RCVBUF");
285 query_loop(argv
, argc
);
295 "usage: rtquery [-np1] [-r tgt_rt] [-w wtime]"
296 " [-a type=passwd] host1 [host2 ...]\n"
297 "\trtquery -t {on=filename|more|off|dump}"
298 " host1 [host2 ...]\n");
303 /* tell the target hosts about tracing
306 trace_loop(char *argv
[])
308 struct sockaddr_in myaddr
;
311 if (geteuid() != 0) {
312 fprintf(stderr
, "-t requires UID 0\n");
317 OMSG
.rip_vers
= RIPv2
;
319 OMSG
.rip_vers
= RIPv1
;
322 memset(&myaddr
, 0, sizeof(myaddr
));
323 myaddr
.sin_family
= AF_INET
;
325 myaddr
.sin_len
= sizeof(myaddr
);
327 myaddr
.sin_port
= htons(IPPORT_RESERVED
-1);
328 while (bind(soc
, (struct sockaddr
*)&myaddr
, sizeof(myaddr
)) < 0) {
329 if (errno
!= EADDRINUSE
330 || myaddr
.sin_port
== 0) {
334 myaddr
.sin_port
= htons(ntohs(myaddr
.sin_port
)-1);
338 while (*argv
!= NULL
) {
339 if (out(*argv
++) <= 0)
346 /* query all of the listed hosts
349 query_loop(char *argv
[], int argc
)
351 # define NA0 (OMSG.rip_auths[0])
352 # define NA2 (OMSG.rip_auths[2])
360 struct timeval now
, delay
;
361 struct sockaddr_in from
;
366 OMSG
.rip_cmd
= (pflag
) ? RIPCMD_POLL
: RIPCMD_REQUEST
;
368 OMSG
.rip_vers
= RIPv2
;
369 if (auth_type
== RIP_AUTH_PW
) {
370 OMSG
.rip_nets
[1] = OMSG
.rip_nets
[0];
371 NA0
.a_family
= RIP_AF_AUTH
;
372 NA0
.a_type
= RIP_AUTH_PW
;
373 memcpy(NA0
.au
.au_pw
, passwd
, RIP_AUTH_PW_LEN
);
374 omsg_len
+= sizeof(OMSG
.rip_nets
[0]);
376 } else if (auth_type
== RIP_AUTH_MD5
) {
377 OMSG
.rip_nets
[1] = OMSG
.rip_nets
[0];
378 NA0
.a_family
= RIP_AF_AUTH
;
379 NA0
.a_type
= RIP_AUTH_MD5
;
380 NA0
.au
.a_md5
.md5_keyid
= (int8_t)keyid
;
381 NA0
.au
.a_md5
.md5_auth_len
= RIP_AUTH_MD5_LEN
;
382 NA0
.au
.a_md5
.md5_seqno
= 0;
383 cc
= (char *)&NA2
-(char *)&OMSG
;
384 NA0
.au
.a_md5
.md5_pkt_len
= htons(cc
);
385 NA2
.a_family
= RIP_AF_AUTH
;
386 NA2
.a_type
= htons(1);
389 (u_char
*)&OMSG
, cc
);
391 (u_char
*)passwd
, RIP_AUTH_MD5_LEN
);
392 MD5Final(NA2
.au
.au_pw
, &md5_ctx
);
393 omsg_len
+= 2*sizeof(OMSG
.rip_nets
[0]);
397 OMSG
.rip_vers
= RIPv1
;
398 OMSG
.rip_nets
[0].n_mask
= 0;
401 /* ask the first (valid) host */
403 while (0 > out(*argv
++)) {
413 delay
.tv_usec
= STIME
;
414 cc
= select(soc
+1, &bits
, 0,0, &delay
);
416 fromlen
= sizeof(from
);
417 cc
= recvfrom(soc
, imsg_buf
.packet
,
418 sizeof(imsg_buf
.packet
), 0,
419 (struct sockaddr
*)&from
, &fromlen
);
424 /* count the distinct responding hosts.
425 * You cannot match responding hosts with
426 * addresses to which queries were transmitted,
427 * because a router might respond with a
428 * different source address.
430 for (sp
= seen
; sp
!= NULL
; sp
= sp
->next
) {
431 if (sp
->addr
.s_addr
== from
.sin_addr
.s_addr
)
435 sp
= malloc(sizeof(*sp
));
438 "rtquery: malloc failed\n");
441 sp
->addr
= from
.sin_addr
;
447 rip_input(&from
, cc
);
458 /* After a pause in responses, probe another host.
459 * This reduces the intermingling of answers.
461 while (*argv
!= NULL
&& 0 > out(*argv
++))
464 /* continue until no more packets arrive
465 * or we have heard from all hosts
467 if (answered
>= argc
)
470 /* or until we have waited a long time
472 if (gettimeofday(&now
, 0) < 0) {
473 perror("gettimeofday(now)");
476 if (sent
.tv_sec
+ wtime
<= now
.tv_sec
)
480 /* fail if there was no answer */
481 exit (answered
>= argc
? 0 : 1);
488 out(const char *host
)
490 struct sockaddr_in router
;
493 if (gettimeofday(&sent
, 0) < 0) {
494 perror("gettimeofday(sent)");
498 memset(&router
, 0, sizeof(router
));
499 router
.sin_family
= AF_INET
;
501 router
.sin_len
= sizeof(router
);
503 if (!inet_aton(host
, &router
.sin_addr
)) {
504 hp
= gethostbyname(host
);
509 memcpy(&router
.sin_addr
, hp
->h_addr
, sizeof(router
.sin_addr
));
511 router
.sin_port
= htons(RIP_PORT
);
513 if (sendto(soc
, &omsg_buf
, omsg_len
, 0,
514 (struct sockaddr
*)&router
, sizeof(router
)) < 0) {
524 * Convert string to printable characters
527 qstring(u_char
*s
, int len
)
529 static char buf
[8*20+1];
534 for (p
= buf
; len
!= 0 && p
< &buf
[sizeof(buf
)-1]; len
--) {
537 for (s2
= s
+1; s2
< &s
[len
]; s2
++) {
545 if (c
>= ' ' && c
< 0x7f && c
!= '\\') {
567 p
+= sprintf(p
,"%o",c
);
578 * Handle an incoming RIP packet.
581 rip_input(struct sockaddr_in
*from
,
584 struct netinfo
*n
, *lim
;
588 u_char hash
[RIP_AUTH_MD5_LEN
];
590 u_char md5_authed
= 0;
600 printf("%s:", inet_ntoa(from
->sin_addr
));
602 hp
= gethostbyaddr(&from
->sin_addr
, sizeof(struct in_addr
),
606 inet_ntoa(from
->sin_addr
));
608 printf("%s (%s):", hp
->h_name
,
609 inet_ntoa(from
->sin_addr
));
612 if (IMSG
.rip_cmd
!= RIPCMD_RESPONSE
) {
613 printf("\n unexpected response type %d\n", IMSG
.rip_cmd
);
616 printf(" RIPv%d%s %d bytes\n", IMSG
.rip_vers
,
617 (IMSG
.rip_vers
!= RIPv1
&& IMSG
.rip_vers
!= RIPv2
) ? " ?" : "",
619 if (size
> MAXPACKETSIZE
) {
620 if (size
> (int)sizeof(imsg_buf
) - (int)sizeof(*n
)) {
621 printf(" at least %d bytes too long\n",
623 size
= (int)sizeof(imsg_buf
) - (int)sizeof(*n
);
625 printf(" %d bytes too long\n",
628 } else if (size
%sizeof(*n
) != sizeof(struct rip
)%sizeof(*n
)) {
629 printf(" response of bad length=%d\n", size
);
633 lim
= (struct netinfo
*)((char*)n
+ size
) - 1;
634 for (; n
<= lim
; n
++) {
636 if (n
->n_family
== RIP_AF_INET
) {
637 in
.s_addr
= n
->n_dst
;
638 strcpy(net_buf
, inet_ntoa(in
));
640 mask
= ntohl(n
->n_mask
);
641 dmask
= mask
& -mask
;
643 sp
= &net_buf
[strlen(net_buf
)];
644 if (IMSG
.rip_vers
== RIPv1
) {
645 sprintf(sp
," mask=%#x ? ",mask
);
647 } else if (mask
+ dmask
== 0) {
650 && ((1<<i
)&mask
) == 0);
653 sprintf(sp
, "/%d",32-i
);
655 sprintf(sp
," (mask %#x)", mask
);
661 mask
= std_mask(in
.s_addr
);
662 if ((ntohl(in
.s_addr
) & ~mask
) != 0)
665 /* Without a netmask, do not worry about
666 * whether the destination is a host or a
667 * network. Try both and use the first name
670 * If we have a netmask we can make a
673 if ((in
.s_addr
& ~mask
) == 0) {
674 np
= getnetbyaddr((long)in
.s_addr
,
678 else if (in
.s_addr
== 0)
682 && ((in
.s_addr
& ~mask
) != 0
683 || mask
== 0xffffffff)) {
684 hp
= gethostbyaddr(&in
, sizeof(in
),
691 } else if (n
->n_family
== RIP_AF_AUTH
) {
692 na
= (struct netauth
*)n
;
693 if (na
->a_type
== RIP_AUTH_PW
694 && n
== IMSG
.rip_nets
) {
695 printf(" Password Authentication: \"%s\"\n",
696 qstring(na
->au
.au_pw
, RIP_AUTH_PW_LEN
));
700 if (na
->a_type
== RIP_AUTH_MD5
701 && n
== IMSG
.rip_nets
) {
707 ntohs(na
->au
.a_md5
.md5_pkt_len
),
708 na
->au
.a_md5
.md5_keyid
,
709 na
->au
.a_md5
.md5_auth_len
,
710 (int)ntohl(na
->au
.a_md5
.md5_seqno
),
711 na
->au
.a_md5
.rsvd
[0],
712 na
->au
.a_md5
.rsvd
[1]);
716 printf(" Authentication type %d: ", ntohs(na
->a_type
));
717 for (i
= 0; i
< (int)sizeof(na
->au
.au_pw
); i
++)
718 printf("%02x ", na
->au
.au_pw
[i
]);
720 if (md5_authed
&& n
+1 > lim
721 && na
->a_type
== ntohs(1)) {
723 MD5Update(&md5_ctx
, (u_char
*)&IMSG
,
724 (char *)na
-(char *)&IMSG
);
725 MD5Update(&md5_ctx
, (u_char
*)passwd
,
727 MD5Final(hash
, &md5_ctx
);
729 memcmp(hash
, na
->au
.au_pw
, sizeof(hash
))
730 ? "WRONG" : "correct");
735 sprintf(net_buf
, "(af %#x) %d.%d.%d.%d",
737 (char)(n
->n_dst
>> 24),
738 (char)(n
->n_dst
>> 16),
739 (char)(n
->n_dst
>> 8),
743 printf(" %-18s metric %2d %-10s",
744 net_buf
, (int)ntohl(n
->n_metric
), name
);
746 if (n
->n_nhop
!= 0) {
747 in
.s_addr
= n
->n_nhop
;
751 hp
= gethostbyaddr(&in
, sizeof(in
), AF_INET
);
752 printf(" nhop=%-15s%s",
753 (hp
!= NULL
) ? hp
->h_name
: inet_ntoa(in
),
754 (IMSG
.rip_vers
== RIPv1
) ? " ?" : "");
757 printf(" tag=%#x%s", n
->n_tag
,
758 (IMSG
.rip_vers
== RIPv1
) ? " ?" : "");
764 /* Return the classical netmask for an IP address.
767 std_mask(u_int addr
) /* in network order */
769 addr
= ntohl(addr
); /* was a host, not a network */
771 if (addr
== 0) /* default route has mask 0 */
774 return IN_CLASSA_NET
;
776 return IN_CLASSB_NET
;
777 return IN_CLASSC_NET
;
781 /* get a network number as a name or a number, with an optional "/xx"
784 static int /* 0=bad */
789 struct netent
*nentp
;
792 char hname
[MAXHOSTNAMELEN
+1];
796 /* Detect and separate "1.2.3.4/24"
798 if (NULL
!= (mname
= strrchr(name
,'/'))) {
799 i
= (int)(mname
- name
);
800 if (i
> (int)sizeof(hname
)-1) /* name too long */
802 memmove(hname
, name
, i
);
808 nentp
= getnetbyname(name
);
810 in
.s_addr
= nentp
->n_net
;
811 } else if (inet_aton(name
, &in
) == 1) {
812 in
.s_addr
= ntohl(in
.s_addr
);
818 mask
= std_mask(in
.s_addr
);
819 if ((~mask
& in
.s_addr
) != 0)
822 mask
= (u_int
)strtoul(mname
, &p
, 0);
823 if (*p
!= '\0' || mask
> 32)
825 mask
= 0xffffffff << (32-mask
);
828 rt
->n_dst
= htonl(in
.s_addr
);
829 rt
->n_family
= RIP_AF_INET
;
830 rt
->n_mask
= htonl(mask
);
835 /* strtok(), but honoring backslash
837 static int /* -1=bad */
838 parse_quote(char **linep
,
859 if (c
== '\\' && *pc
!= '\0') {
860 if ((c
= *pc
++) == 'n') {
862 } else if (c
== 'r') {
864 } else if (c
== 't') {
866 } else if (c
== 'b') {
868 } else if (c
>= '0' && c
<= '7') {
870 if (*pc
>= '0' && *pc
<= '7') {
871 c
= (c
<<3)+(*pc
++ - '0');
872 if (*pc
>= '0' && *pc
<= '7')
873 c
= (c
<<3)+(*pc
++ - '0');
878 for (p
= delims
; *p
!= '\0'; ++p
) {