1 /* $NetBSD: rtquery.c,v 1.21 2008/07/20 01:20:23 lukem Exp $ */
4 * Copyright (c) 1982, 1986, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgment:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
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>
56 #define UNUSED __unused
58 #define __RCSID(_s) static const char rcsid[] UNUSED = _s
61 #define __COPYRIGHT(_s) static const char copyright[] UNUSED = _s
63 __COPYRIGHT("@(#) Copyright (c) 1983, 1988, 1993\
64 The Regents of the University of California. All rights reserved.");
66 __RCSID("$NetBSD: rtquery.c,v 1.21 2008/07/20 01:20:23 lukem Exp $");
67 #elif defined(__FreeBSD__)
70 __RCSID("Revision: 2.26 ");
71 #ident "Revision: 2.26 "
81 #define MD5_DIGEST_LEN 16
83 u_int32_t state
[4]; /* state (ABCD) */
84 u_int32_t count
[2]; /* # of bits, modulo 2^64 (LSB 1st) */
85 unsigned char buffer
[64]; /* input buffer */
87 extern void MD5Init(MD5_CTX
*);
88 extern void MD5Update(MD5_CTX
*, u_char
*, u_int
);
89 extern void MD5Final(u_char
[MD5_DIGEST_LEN
], MD5_CTX
*);
93 #define WTIME 15 /* Time to wait for all responses */
94 #define STIME (250*1000) /* usec to wait for another response */
102 char packet
[MAXPACKETSIZE
+MAXPATHLEN
];
104 #define OMSG omsg_buf.rip
105 int omsg_len
= sizeof(struct rip
);
109 char packet
[MAXPACKETSIZE
+1024];
111 #define IMSG imsg_buf.rip
113 int nflag
; /* numbers, no names */
114 int pflag
; /* play the `gated` game */
115 int ripv2
= 1; /* use RIP version 2 */
117 int rflag
; /* 1=ask about a particular route */
118 int trace
, not_trace
; /* send trace command or not */
119 int auth_type
= RIP_AUTH_NONE
;
120 char passwd
[RIP_AUTH_PW_LEN
];
123 struct timeval sent
; /* when query sent */
125 static char localhost_str
[] = "localhost";
126 static char *default_argv
[] = {localhost_str
, 0};
128 static void rip_input(struct sockaddr_in
*, int);
129 static int out(const char *);
130 static void trace_loop(char *argv
[]) __dead
;
131 static void query_loop(char *argv
[], int) __dead
;
132 static int getnet(char *, struct netinfo
*);
133 static u_int
std_mask(u_int
);
134 static int parse_quote(char **, const char *, char *, char *, int);
135 static void usage(void);
143 char *p
, *options
, *value
, delim
;
146 delim
= 0; /* XXX gcc */
148 OMSG
.rip_nets
[0].n_dst
= RIP_DEFAULT
;
149 OMSG
.rip_nets
[0].n_family
= RIP_AF_UNSPEC
;
150 OMSG
.rip_nets
[0].n_metric
= htonl(HOPCNT_INFINITY
);
153 while ((ch
= getopt(argc
, argv
, "np1w:r:t:a:")) != -1)
171 wtime
= (int)strtoul(optarg
, &p
, 0);
181 rflag
= getnet(optarg
, &OMSG
.rip_nets
[0]);
183 struct hostent
*hp
= gethostbyname(optarg
);
185 fprintf(stderr
, "%s: %s:",
190 memcpy(&OMSG
.rip_nets
[0].n_dst
, hp
->h_addr
,
191 sizeof(OMSG
.rip_nets
[0].n_dst
));
192 OMSG
.rip_nets
[0].n_family
= RIP_AF_INET
;
193 OMSG
.rip_nets
[0].n_mask
= -1;
201 while (*options
!= '\0') {
202 /* messy complications to make -W -Wall happy */
203 static char on_str
[] = "on";
204 static char more_str
[] = "more";
205 static char off_str
[] = "off";
206 static char dump_str
[] = "dump";
207 static char *traceopts
[] = {
210 # define TRACE_MORE 1
214 # define TRACE_DUMP 3
219 switch (getsubopt(&options
,traceopts
,&value
)) {
221 OMSG
.rip_cmd
= RIPCMD_TRACEON
;
223 || strlen(value
) > MAXPATHLEN
)
230 OMSG
.rip_cmd
= RIPCMD_TRACEON
;
235 OMSG
.rip_cmd
= RIPCMD_TRACEOFF
;
240 OMSG
.rip_cmd
= RIPCMD_TRACEON
;
241 result
= "dump/../table";
246 strcpy((char*)OMSG
.rip_tracefile
, result
);
247 omsg_len
+= strlen(result
) - sizeof(OMSG
.ripun
);
253 p
= strchr(optarg
,'=');
257 if (!strcasecmp("passwd",optarg
))
258 auth_type
= RIP_AUTH_PW
;
259 else if (!strcasecmp("md5_passwd",optarg
))
260 auth_type
= RIP_AUTH_MD5
;
263 if (0 > parse_quote(&p
,"|",&delim
,
264 passwd
, sizeof(passwd
)))
266 if (auth_type
== RIP_AUTH_MD5
268 keyid
= strtoul(p
+1,&p
,0);
269 if (keyid
> 255 || *p
!= '\0')
271 } else if (delim
!= '\0') {
281 if (not_trace
&& trace
)
288 soc
= socket(AF_INET
, SOCK_DGRAM
, 0);
294 /* be prepared to receive a lot of routes */
295 for (bsize
= 127*1024; ; bsize
-= 1024) {
296 if (setsockopt(soc
, SOL_SOCKET
, SO_RCVBUF
,
297 &bsize
, sizeof(bsize
)) == 0)
299 if (bsize
<= 4*1024) {
300 perror("setsockopt SO_RCVBUF");
308 query_loop(argv
, argc
);
318 "usage: rtquery [-np1] [-r tgt_rt] [-w wtime]"
319 " [-a type=passwd] host1 [host2 ...]\n"
320 "\trtquery -t {on=filename|more|off|dump}"
321 " host1 [host2 ...]\n");
326 /* tell the target hosts about tracing
329 trace_loop(char *argv
[])
331 struct sockaddr_in myaddr
;
334 if (geteuid() != 0) {
335 (void)fprintf(stderr
, "-t requires UID 0\n");
340 OMSG
.rip_vers
= RIPv2
;
342 OMSG
.rip_vers
= RIPv1
;
345 memset(&myaddr
, 0, sizeof(myaddr
));
346 myaddr
.sin_family
= AF_INET
;
348 myaddr
.sin_len
= sizeof(myaddr
);
350 myaddr
.sin_port
= htons(IPPORT_RESERVED
-1);
351 while (bind(soc
, (struct sockaddr
*)&myaddr
, sizeof(myaddr
)) < 0) {
352 if (errno
!= EADDRINUSE
353 || myaddr
.sin_port
== 0) {
357 myaddr
.sin_port
= ntohs(myaddr
.sin_port
)-1;
358 myaddr
.sin_port
= htons(myaddr
.sin_port
);
363 if (out(*argv
++) <= 0)
370 /* query all of the listed hosts
373 query_loop(char *argv
[], int argc
)
375 # define NA0 (OMSG.rip_auths[0])
376 # define NA2 (OMSG.rip_auths[2])
384 struct timeval now
, delay
;
385 struct sockaddr_in from
;
390 OMSG
.rip_cmd
= (pflag
) ? RIPCMD_POLL
: RIPCMD_REQUEST
;
392 OMSG
.rip_vers
= RIPv2
;
393 if (auth_type
== RIP_AUTH_PW
) {
394 OMSG
.rip_nets
[1] = OMSG
.rip_nets
[0];
395 NA0
.a_family
= RIP_AF_AUTH
;
396 NA0
.a_type
= RIP_AUTH_PW
;
397 memcpy(NA0
.au
.au_pw
, passwd
, RIP_AUTH_PW_LEN
);
398 omsg_len
+= sizeof(OMSG
.rip_nets
[0]);
400 } else if (auth_type
== RIP_AUTH_MD5
) {
401 OMSG
.rip_nets
[1] = OMSG
.rip_nets
[0];
402 NA0
.a_family
= RIP_AF_AUTH
;
403 NA0
.a_type
= RIP_AUTH_MD5
;
404 NA0
.au
.a_md5
.md5_keyid
= (int8_t)keyid
;
405 NA0
.au
.a_md5
.md5_auth_len
= RIP_AUTH_MD5_KEY_LEN
;
406 NA0
.au
.a_md5
.md5_seqno
= 0;
407 cc
= (char *)&NA2
-(char *)&OMSG
;
408 NA0
.au
.a_md5
.md5_pkt_len
= htons(cc
);
409 NA2
.a_family
= RIP_AF_AUTH
;
410 NA2
.a_type
= htons(1);
413 (u_char
*)&OMSG
, cc
);
415 (u_char
*)passwd
, RIP_AUTH_MD5_HASH_LEN
);
416 MD5Final(NA2
.au
.au_pw
, &md5_ctx
);
417 omsg_len
+= 2*sizeof(OMSG
.rip_nets
[0]);
421 OMSG
.rip_vers
= RIPv1
;
422 OMSG
.rip_nets
[0].n_mask
= 0;
425 /* ask the first (valid) host */
427 while (0 > out(*argv
++)) {
437 delay
.tv_usec
= STIME
;
438 cc
= select(soc
+1, &bits
, 0,0, &delay
);
440 fromlen
= sizeof(from
);
441 cc
= recvfrom(soc
, imsg_buf
.packet
,
442 sizeof(imsg_buf
.packet
), 0,
443 (struct sockaddr
*)&from
, &fromlen
);
448 /* count the distinct responding hosts.
449 * You cannot match responding hosts with
450 * addresses to which queries were transmitted,
451 * because a router might respond with a
452 * different source address.
454 for (sp
= seen
; sp
!= 0; sp
= sp
->next
) {
455 if (sp
->addr
.s_addr
== from
.sin_addr
.s_addr
)
459 sp
= malloc(sizeof(*sp
));
462 "rtquery: malloc failed\n");
465 sp
->addr
= from
.sin_addr
;
471 rip_input(&from
, cc
);
482 /* After a pause in responses, probe another host.
483 * This reduces the intermingling of answers.
485 while (*argv
!= 0 && 0 > out(*argv
++))
488 /* continue until no more packets arrive
489 * or we have heard from all hosts
491 if (answered
>= argc
)
494 /* or until we have waited a long time
496 if (gettimeofday(&now
, 0) < 0) {
497 perror("gettimeofday(now)");
500 if (sent
.tv_sec
+ wtime
<= now
.tv_sec
)
504 /* fail if there was no answer */
505 exit (answered
>= argc
? 0 : 1);
512 out(const char *host
)
514 struct sockaddr_in router
;
517 if (gettimeofday(&sent
, 0) < 0) {
518 perror("gettimeofday(sent)");
522 memset(&router
, 0, sizeof(router
));
523 router
.sin_family
= AF_INET
;
525 router
.sin_len
= sizeof(router
);
527 if (!inet_aton(host
, &router
.sin_addr
)) {
528 hp
= gethostbyname(host
);
533 memcpy(&router
.sin_addr
, hp
->h_addr
, sizeof(router
.sin_addr
));
535 router
.sin_port
= htons(RIP_PORT
);
537 if (sendto(soc
, &omsg_buf
, omsg_len
, 0,
538 (struct sockaddr
*)&router
, sizeof(router
)) < 0) {
548 * Convert string to printable characters
551 qstring(u_char
*s
, int len
)
553 static char buf
[8*20+1];
558 for (p
= buf
; len
!= 0 && p
< &buf
[sizeof(buf
)-1]; len
--) {
561 for (s2
= s
+1; s2
< &s
[len
]; s2
++) {
569 if (c
>= ' ' && c
< 0x7f && c
!= '\\') {
591 p
+= sprintf(p
,"%o",c
);
602 * Handle an incoming RIP packet.
605 rip_input(struct sockaddr_in
*from
,
608 struct netinfo
*n
, *lim
;
612 u_char hash
[RIP_AUTH_MD5_KEY_LEN
];
614 u_char md5_authed
= 0;
624 printf("%s:", inet_ntoa(from
->sin_addr
));
626 hp
= gethostbyaddr((char*)&from
->sin_addr
,
627 sizeof(struct in_addr
), AF_INET
);
630 inet_ntoa(from
->sin_addr
));
632 printf("%s (%s):", hp
->h_name
,
633 inet_ntoa(from
->sin_addr
));
636 if (IMSG
.rip_cmd
!= RIPCMD_RESPONSE
) {
637 printf("\n unexpected response type %d\n", IMSG
.rip_cmd
);
640 printf(" RIPv%d%s %d bytes\n", IMSG
.rip_vers
,
641 (IMSG
.rip_vers
!= RIPv1
&& IMSG
.rip_vers
!= RIPv2
) ? " ?" : "",
643 if (size
> MAXPACKETSIZE
) {
644 if (size
> (int)sizeof(imsg_buf
) - (int)sizeof(*n
)) {
645 printf(" at least %d bytes too long\n",
647 size
= (int)sizeof(imsg_buf
) - (int)sizeof(*n
);
649 printf(" %d bytes too long\n",
652 } else if (size
%sizeof(*n
) != sizeof(struct rip
)%sizeof(*n
)) {
653 printf(" response of bad length=%d\n", size
);
657 lim
= (struct netinfo
*)((char*)n
+ size
) - 1;
658 for (; n
<= lim
; n
++) {
660 if (n
->n_family
== RIP_AF_INET
) {
661 in
.s_addr
= n
->n_dst
;
662 (void)strcpy(net_buf
, inet_ntoa(in
));
664 mask
= ntohl(n
->n_mask
);
665 dmask
= mask
& -mask
;
667 sp
= &net_buf
[strlen(net_buf
)];
668 if (IMSG
.rip_vers
== RIPv1
) {
669 (void)sprintf(sp
," mask=%#x ? ",mask
);
671 } else if (mask
+ dmask
== 0) {
674 && ((1<<i
)&mask
) == 0);
677 (void)sprintf(sp
, "/%d",32-i
);
679 (void)sprintf(sp
," (mask %#x)", mask
);
685 mask
= std_mask(in
.s_addr
);
686 if ((ntohl(in
.s_addr
) & ~mask
) != 0)
689 /* Without a netmask, do not worry about
690 * whether the destination is a host or a
691 * network. Try both and use the first name
694 * If we have a netmask we can make a
697 if ((in
.s_addr
& ~mask
) == 0) {
698 np
= getnetbyaddr((long)in
.s_addr
,
702 else if (in
.s_addr
== 0)
706 && ((in
.s_addr
& ~mask
) != 0
707 || mask
== 0xffffffff)) {
708 hp
= gethostbyaddr((char*)&in
,
716 } else if (n
->n_family
== RIP_AF_AUTH
) {
717 na
= (struct netauth
*)n
;
718 if (na
->a_type
== RIP_AUTH_PW
719 && n
== IMSG
.rip_nets
) {
720 (void)printf(" Password Authentication:"
722 qstring(na
->au
.au_pw
,
727 if (na
->a_type
== RIP_AUTH_MD5
728 && n
== IMSG
.rip_nets
) {
729 (void)printf(" MD5 Auth"
734 ntohs(na
->au
.a_md5
.md5_pkt_len
),
735 na
->au
.a_md5
.md5_keyid
,
736 na
->au
.a_md5
.md5_auth_len
,
737 (int)ntohl(na
->au
.a_md5
.md5_seqno
),
738 na
->au
.a_md5
.rsvd
[0],
739 na
->au
.a_md5
.rsvd
[1]);
743 (void)printf(" Authentication type %d: ",
745 for (i
= 0; i
< (int)sizeof(na
->au
.au_pw
); i
++)
746 (void)printf("%02x ", na
->au
.au_pw
[i
]);
748 if (md5_authed
&& n
+1 > lim
749 && na
->a_type
== ntohs(1)) {
751 MD5Update(&md5_ctx
, (u_char
*)&IMSG
,
752 (char *)na
-(char *)&IMSG
753 +RIP_AUTH_MD5_HASH_XTRA
);
754 MD5Update(&md5_ctx
, (u_char
*)passwd
,
755 RIP_AUTH_MD5_KEY_LEN
);
756 MD5Final(hash
, &md5_ctx
);
757 (void)printf(" %s hash\n",
758 memcmp(hash
, na
->au
.au_pw
,
760 ? "WRONG" : "correct");
765 (void)sprintf(net_buf
, "(af %#x) %d.%d.%d.%d",
767 (u_char
)(n
->n_dst
>> 24),
768 (u_char
)(n
->n_dst
>> 16),
769 (u_char
)(n
->n_dst
>> 8),
773 (void)printf(" %-18s metric %2d %-10s",
774 net_buf
, (int)ntohl(n
->n_metric
), name
);
776 if (n
->n_nhop
!= 0) {
777 in
.s_addr
= n
->n_nhop
;
781 hp
= gethostbyaddr((char*)&in
, sizeof(in
),
783 (void)printf(" nhop=%-15s%s",
784 (hp
!= 0) ? hp
->h_name
: inet_ntoa(in
),
785 (IMSG
.rip_vers
== RIPv1
) ? " ?" : "");
788 (void)printf(" tag=%#x%s", n
->n_tag
,
789 (IMSG
.rip_vers
== RIPv1
) ? " ?" : "");
795 /* Return the classical netmask for an IP address.
798 std_mask(u_int addr
) /* in network order */
800 addr
= ntohl(addr
); /* was a host, not a network */
802 if (addr
== 0) /* default route has mask 0 */
805 return IN_CLASSA_NET
;
807 return IN_CLASSB_NET
;
808 return IN_CLASSC_NET
;
812 /* get a network number as a name or a number, with an optional "/xx"
815 static int /* 0=bad */
820 struct netent
*nentp
;
823 char hname
[MAXHOSTNAMELEN
+1];
827 /* Detect and separate "1.2.3.4/24"
829 if (0 != (mname
= strrchr(name
,'/'))) {
830 i
= (int)(mname
- name
);
831 if (i
> (int)sizeof(hname
)-1) /* name too long */
833 memmove(hname
, name
, i
);
839 nentp
= getnetbyname(name
);
841 in
.s_addr
= nentp
->n_net
;
842 } else if (inet_aton(name
, &in
) == 1) {
843 in
.s_addr
= ntohl(in
.s_addr
);
849 mask
= std_mask(in
.s_addr
);
850 if ((~mask
& in
.s_addr
) != 0)
853 mask
= (u_int
)strtoul(mname
, &p
, 0);
854 if (*p
!= '\0' || mask
> 32)
856 mask
= 0xffffffff << (32-mask
);
859 rt
->n_dst
= htonl(in
.s_addr
);
860 rt
->n_family
= RIP_AF_INET
;
861 rt
->n_mask
= htonl(mask
);
866 /* strtok(), but honoring backslash
868 static int /* -1=bad */
869 parse_quote(char **linep
,
890 if (c
== '\\' && *pc
!= '\0') {
891 if ((c
= *pc
++) == 'n') {
893 } else if (c
== 'r') {
895 } else if (c
== 't') {
897 } else if (c
== 'b') {
899 } else if (c
>= '0' && c
<= '7') {
901 if (*pc
>= '0' && *pc
<= '7') {
902 c
= (c
<<3)+(*pc
++ - '0');
903 if (*pc
>= '0' && *pc
<= '7')
904 c
= (c
<<3)+(*pc
++ - '0');
909 for (p
= delims
; *p
!= '\0'; ++p
) {