xref: /netbsd-src/sbin/routed/rtquery/rtquery.c (revision 76dfffe33547c37f8bdd446e3e4ab0f3c16cea4b)
1 /*	$NetBSD: rtquery.c,v 1.3 1996/09/24 16:24:26 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 1982, 1986, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
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 acknowledgement:
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.
22  *
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
33  * SUCH DAMAGE.
34  */
35 
36 char copyright[] =
37 "@(#) Copyright (c) 1982, 1986, 1993\n\
38 	The Regents of the University of California.  All rights reserved.\n";
39 
40 #if !defined(lint) && !defined(sgi) && !defined(__NetBSD__)
41 static char sccsid[] = "@(#)query.c	8.1 (Berkeley) 6/5/93";
42 #elif defined(__NetBSD__)
43 static char rcsid[] = "$NetBSD: rtquery.c,v 1.3 1996/09/24 16:24:26 christos Exp $";
44 #endif
45 
46 #include <sys/param.h>
47 #include <sys/protosw.h>
48 #include <sys/socket.h>
49 #include <sys/time.h>
50 #include <netinet/in.h>
51 #define RIPVERSION RIPv2
52 #include <protocols/routed.h>
53 #include <arpa/inet.h>
54 #include <netdb.h>
55 #include <errno.h>
56 #include <unistd.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #ifdef sgi
61 #include <strings.h>
62 #include <bstring.h>
63 #endif
64 
65 #ifndef sgi
66 #define _HAVE_SIN_LEN
67 #endif
68 
69 #define	WTIME	15		/* Time to wait for all responses */
70 #define	STIME	(250*1000)	/* usec to wait for another response */
71 
72 int	s;
73 
74 char	*pgmname;
75 
76 union {
77 	struct rip rip;
78 	char	packet[MAXPACKETSIZE+MAXPATHLEN];
79 } omsg_buf;
80 #define OMSG omsg_buf.rip
81 int omsg_len = sizeof(struct rip);
82 
83 union {
84 	struct	rip rip;
85 	char	packet[MAXPACKETSIZE+1024];
86 	} imsg_buf;
87 #define IMSG imsg_buf.rip
88 
89 int	nflag;				/* numbers, no names */
90 int	pflag;				/* play the `gated` game */
91 int	ripv2 = 1;			/* use RIP version 2 */
92 int	wtime = WTIME;
93 int	rflag;				/* 1=ask about a particular route */
94 int	trace;
95 int	not_trace;
96 
97 struct timeval sent;			/* when query sent */
98 
99 static void rip_input(struct sockaddr_in*, int);
100 static int out(char *);
101 static void trace_loop(char *argv[]);
102 static void query_loop(char *argv[], int);
103 static int getnet(char *, struct netinfo *);
104 static u_int std_mask(u_int);
105 
106 
107 int
108 main(int argc,
109      char *argv[])
110 {
111 	int ch, bsize;
112 	char *p, *options, *value;
113 
114 	OMSG.rip_nets[0].n_dst = RIP_DEFAULT;
115 	OMSG.rip_nets[0].n_family = RIP_AF_UNSPEC;
116 	OMSG.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY);
117 
118 	pgmname = argv[0];
119 	while ((ch = getopt(argc, argv, "np1w:r:t:")) != EOF)
120 		switch (ch) {
121 		case 'n':
122 			not_trace = 1;
123 			nflag = 1;
124 			break;
125 
126 		case 'p':
127 			not_trace = 1;
128 			pflag = 1;
129 			break;
130 
131 		case '1':
132 			ripv2 = 0;
133 			break;
134 
135 		case 'w':
136 			not_trace = 1;
137 			wtime = (int)strtoul(optarg, &p, 0);
138 			if (*p != '\0'
139 			    || wtime <= 0)
140 				goto usage;
141 			break;
142 
143 		case 'r':
144 			not_trace = 1;
145 			if (rflag)
146 				goto usage;
147 			rflag = getnet(optarg, &OMSG.rip_nets[0]);
148 			if (!rflag) {
149 				struct hostent *hp = gethostbyname(optarg);
150 				if (hp == 0) {
151 					fprintf(stderr, "%s: %s:",
152 						pgmname, optarg);
153 					herror(0);
154 					exit(1);
155 				}
156 				bcopy(hp->h_addr, &OMSG.rip_nets[0].n_dst,
157 				      sizeof(OMSG.rip_nets[0].n_dst));
158 				OMSG.rip_nets[0].n_family = RIP_AF_INET;
159 				OMSG.rip_nets[0].n_mask = -1;
160 				rflag = 1;
161 			}
162 			break;
163 
164 		case 't':
165 			trace = 1;
166 			options = optarg;
167 			while (*options != '\0') {
168 				char *traceopts[] = {
169 #				    define TRACE_ON	0
170 					"on",
171 #				    define TRACE_MORE	1
172 					"more",
173 #				    define TRACE_OFF	2
174 					"off",
175 #				    define TRACE_DUMP	3
176 					"dump",
177 					0
178 				};
179 				switch (getsubopt(&options,traceopts,&value)) {
180 				case TRACE_ON:
181 					OMSG.rip_cmd = RIPCMD_TRACEON;
182 					if (!value
183 					    || strlen(value) > MAXPATHLEN)
184 						goto usage;
185 					break;
186 				case TRACE_MORE:
187 					if (value)
188 						goto usage;
189 					OMSG.rip_cmd = RIPCMD_TRACEON;
190 					value = "";
191 					break;
192 				case TRACE_OFF:
193 					if (value)
194 						goto usage;
195 					OMSG.rip_cmd = RIPCMD_TRACEOFF;
196 					value = "";
197 					break;
198 				case TRACE_DUMP:
199 					if (value)
200 						goto usage;
201 					OMSG.rip_cmd = RIPCMD_TRACEON;
202 					value = "dump/../table";
203 					break;
204 				default:
205 					goto usage;
206 				}
207 				strcpy((char*)OMSG.rip_tracefile, value);
208 				omsg_len += strlen(value) - sizeof(OMSG.ripun);
209 			}
210 			break;
211 
212 		default:
213 			goto usage;
214 	}
215 	argv += optind;
216 	argc -= optind;
217 	if ((not_trace && trace) || argc == 0) {
218 usage:		fprintf(stderr, "%s: [-np1v] [-r tgt_rt] [-w wtime]"
219 			" host1 [host2 ...]\n"
220 			"or\t-t {on=filename|more|off} host1 host2 ...\n",
221 			pgmname);
222 		exit(1);
223 	}
224 
225 	s = socket(AF_INET, SOCK_DGRAM, 0);
226 	if (s < 0) {
227 		perror("socket");
228 		exit(2);
229 	}
230 
231 	/* be prepared to receive a lot of routes */
232 	for (bsize = 127*1024; ; bsize -= 1024) {
233 		if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
234 			       &bsize, sizeof(bsize)) == 0)
235 			break;
236 		if (bsize <= 4*1024) {
237 			perror("setsockopt SO_RCVBUF");
238 			break;
239 		}
240 	}
241 
242 	if (trace)
243 		trace_loop(argv);
244 	else
245 		query_loop(argv, argc);
246 	/* NOTREACHED */
247 }
248 
249 
250 /* tell the target hosts about tracing
251  */
252 static void
253 trace_loop(char *argv[])
254 {
255 	struct sockaddr_in myaddr;
256 	int res;
257 
258 	if (geteuid() != 0) {
259 		(void)fprintf(stderr, "-t requires UID 0\n");
260 		exit(1);
261 	}
262 
263 	if (ripv2) {
264 		OMSG.rip_vers = RIPv2;
265 	} else {
266 		OMSG.rip_vers = RIPv1;
267 	}
268 
269 	bzero(&myaddr, sizeof(myaddr));
270 	myaddr.sin_family = AF_INET;
271 #ifdef _HAVE_SIN_LEN
272 	myaddr.sin_len = sizeof(myaddr);
273 #endif
274 	myaddr.sin_port = htons(IPPORT_RESERVED-1);
275 	while (bind(s, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) {
276 		if (errno != EADDRINUSE
277 		    || myaddr.sin_port == 0) {
278 			perror("bind");
279 			exit(2);
280 		}
281 		myaddr.sin_port = htons(ntohs(myaddr.sin_port)-1);
282 	}
283 
284 	res = 1;
285 	while (*argv != 0) {
286 		if (out(*argv++) <= 0)
287 			res = 0;
288 	}
289 	exit(res);
290 }
291 
292 
293 /* query all of the listed hosts
294  */
295 static void
296 query_loop(char *argv[], int argc)
297 {
298 	struct seen {
299 		struct seen *next;
300 		struct in_addr addr;
301 	} *seen, *sp;
302 	int answered = 0;
303 	int cc;
304 	fd_set bits;
305 	struct timeval now, delay;
306 	struct sockaddr_in from;
307 	int fromlen;
308 
309 
310 	OMSG.rip_cmd = (pflag) ? RIPCMD_POLL : RIPCMD_REQUEST;
311 	if (ripv2) {
312 		OMSG.rip_vers = RIPv2;
313 	} else {
314 		OMSG.rip_vers = RIPv1;
315 		OMSG.rip_nets[0].n_mask = 0;
316 	}
317 
318 	/* ask the first (valid) host */
319 	seen = 0;
320 	while (0 > out(*argv++)) {
321 		if (*argv == 0)
322 			exit(-1);
323 		answered++;
324 	}
325 
326 	FD_ZERO(&bits);
327 	for (;;) {
328 		FD_SET(s, &bits);
329 		delay.tv_sec = 0;
330 		delay.tv_usec = STIME;
331 		cc = select(s+1, &bits, 0,0, &delay);
332 		if (cc > 0) {
333 			fromlen = sizeof(from);
334 			cc = recvfrom(s, imsg_buf.packet,
335 				      sizeof(imsg_buf.packet), 0,
336 				      (struct sockaddr *)&from, &fromlen);
337 			if (cc < 0) {
338 				perror("recvfrom");
339 				exit(1);
340 			}
341 			/* count the distinct responding hosts.
342 			 * You cannot match responding hosts with
343 			 * addresses to which queries were transmitted,
344 			 * because a router might respond with a
345 			 * different source address.
346 			 */
347 			for (sp = seen; sp != 0; sp = sp->next) {
348 				if (sp->addr.s_addr == from.sin_addr.s_addr)
349 					break;
350 			}
351 			if (sp == 0) {
352 				sp = malloc(sizeof(*sp));
353 				sp->addr = from.sin_addr;
354 				sp->next = seen;
355 				seen = sp;
356 				answered++;
357 			}
358 
359 			rip_input(&from, cc);
360 			continue;
361 		}
362 
363 		if (cc < 0) {
364 			if ( errno == EINTR)
365 				continue;
366 			perror("select");
367 			exit(1);
368 		}
369 
370 		/* After a pause in responses, probe another host.
371 		 * This reduces the intermingling of answers.
372 		 */
373 		while (*argv != 0 && 0 > out(*argv++))
374 			answered++;
375 
376 		/* continue until no more packets arrive
377 		 * or we have heard from all hosts
378 		 */
379 		if (answered >= argc)
380 			break;
381 
382 		/* or until we have waited a long time
383 		 */
384 		if (gettimeofday(&now, 0) < 0) {
385 			perror("gettimeofday(now)");
386 			exit(1);
387 		}
388 		if (sent.tv_sec + wtime <= now.tv_sec)
389 			break;
390 	}
391 
392 	/* fail if there was no answer */
393 	exit (answered >= argc ? 0 : 1);
394 }
395 
396 
397 /* sent do one host
398  */
399 static int
400 out(char *host)
401 {
402 	struct sockaddr_in router;
403 	struct hostent *hp;
404 
405 	if (gettimeofday(&sent, 0) < 0) {
406 		perror("gettimeofday(sent)");
407 		return -1;
408 	}
409 
410 	bzero(&router, sizeof(router));
411 	router.sin_family = AF_INET;
412 #ifdef _HAVE_SIN_LEN
413 	router.sin_len = sizeof(router);
414 #endif
415 	if (!inet_aton(host, &router.sin_addr)) {
416 		hp = gethostbyname(host);
417 		if (hp == 0) {
418 			herror(host);
419 			return -1;
420 		}
421 		bcopy(hp->h_addr, &router.sin_addr, sizeof(router.sin_addr));
422 	}
423 	router.sin_port = htons(RIP_PORT);
424 
425 	if (sendto(s, &omsg_buf, omsg_len, 0,
426 		   (struct sockaddr *)&router, sizeof(router)) < 0) {
427 		perror(host);
428 		return -1;
429 	}
430 
431 	return 0;
432 }
433 
434 
435 /*
436  * Handle an incoming RIP packet.
437  */
438 static void
439 rip_input(struct sockaddr_in *from,
440 	  int size)
441 {
442 	struct netinfo *n, *lim;
443 	struct in_addr in;
444 	char *name;
445 	char net_buf[80];
446 	u_int mask, dmask;
447 	char *sp;
448 	int i;
449 	struct hostent *hp;
450 	struct netent *np;
451 	struct netauth *a;
452 
453 
454 	if (nflag) {
455 		printf("%s:", inet_ntoa(from->sin_addr));
456 	} else {
457 		hp = gethostbyaddr((char*)&from->sin_addr,
458 				   sizeof(struct in_addr), AF_INET);
459 		if (hp == 0) {
460 			printf("%s:",
461 			       inet_ntoa(from->sin_addr));
462 		} else {
463 			printf("%s (%s):", hp->h_name,
464 			       inet_ntoa(from->sin_addr));
465 		}
466 	}
467 	if (IMSG.rip_cmd != RIPCMD_RESPONSE) {
468 		printf("\n    unexpected response type %d\n", IMSG.rip_cmd);
469 		return;
470 	}
471 	printf(" RIPv%d%s %d bytes\n", IMSG.rip_vers,
472 	       (IMSG.rip_vers != RIPv1 && IMSG.rip_vers != RIPv2) ? " ?" : "",
473 	       size);
474 	if (size > MAXPACKETSIZE) {
475 		if (size > sizeof(imsg_buf) - sizeof(*n)) {
476 			printf("       at least %d bytes too long\n",
477 			       size-MAXPACKETSIZE);
478 			size = sizeof(imsg_buf) - sizeof(*n);
479 		} else {
480 			printf("       %d bytes too long\n",
481 			       size-MAXPACKETSIZE);
482 		}
483 	} else if (size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) {
484 		printf("    response of bad length=%d\n", size);
485 	}
486 
487 	n = IMSG.rip_nets;
488 	lim = (struct netinfo *)((char*)n + size) - 1;
489 	for (; n <= lim; n++) {
490 		name = "";
491 		if (n->n_family == RIP_AF_INET) {
492 			in.s_addr = n->n_dst;
493 			(void)strcpy(net_buf, inet_ntoa(in));
494 
495 			mask = ntohl(n->n_mask);
496 			dmask = mask & -mask;
497 			if (mask != 0) {
498 				sp = &net_buf[strlen(net_buf)];
499 				if (IMSG.rip_vers == RIPv1) {
500 					(void)sprintf(sp," mask=%#x ? ",mask);
501 					mask = 0;
502 				} else if (mask + dmask == 0) {
503 					for (i = 0;
504 					     (i != 32
505 					      && ((1<<i)&mask) == 0);
506 					     i++)
507 						continue;
508 					(void)sprintf(sp, "/%d",32-i);
509 				} else {
510 					(void)sprintf(sp," (mask %#x)", mask);
511 				}
512 			}
513 
514 			if (!nflag) {
515 				if (mask == 0) {
516 					mask = std_mask(in.s_addr);
517 					if ((ntohl(in.s_addr) & ~mask) != 0)
518 						mask = 0;
519 				}
520 				/* Without a netmask, do not worry about
521 				 * whether the destination is a host or a
522 				 * network. Try both and use the first name
523 				 * we get.
524 				 *
525 				 * If we have a netmask we can make a
526 				 * good guess.
527 				 */
528 				if ((in.s_addr & ~mask) == 0) {
529 					np = getnetbyaddr((long)in.s_addr,
530 							  AF_INET);
531 					if (np != 0)
532 						name = np->n_name;
533 					else if (in.s_addr == 0)
534 						name = "default";
535 				}
536 				if (name[0] == '\0'
537 				    && ((in.s_addr & ~mask) != 0
538 					|| mask == 0xffffffff)) {
539 					hp = gethostbyaddr((char*)&in,
540 							   sizeof(in),
541 							   AF_INET);
542 					if (hp != 0)
543 						name = hp->h_name;
544 				}
545 			}
546 
547 		} else if (n->n_family == RIP_AF_AUTH) {
548 			a = (struct netauth*)n;
549 			(void)printf("    authentication type %d: ",
550 				     a->a_type);
551 			for (i = 0; i < sizeof(a->au.au_pw); i++)
552 				(void)printf("%02x ", a->au.au_pw[i]);
553 			putc('\n', stdout);
554 			continue;
555 
556 		} else {
557 			(void)sprintf(net_buf, "(af %#x) %d.%d.%d.%d",
558 				      ntohs(n->n_family),
559 				      (char)(n->n_dst >> 24),
560 				      (char)(n->n_dst >> 16),
561 				      (char)(n->n_dst >> 8),
562 				      (char)n->n_dst);
563 		}
564 
565 		(void)printf("  %-18s metric %2d %-10s",
566 			     net_buf, ntohl(n->n_metric), name);
567 
568 		if (n->n_nhop != 0) {
569 			in.s_addr = n->n_nhop;
570 			if (nflag)
571 				hp = 0;
572 			else
573 				hp = gethostbyaddr((char*)&in, sizeof(in),
574 						   AF_INET);
575 			(void)printf(" nhop=%-15s%s",
576 				     (hp != 0) ? hp->h_name : inet_ntoa(in),
577 				     (IMSG.rip_vers == RIPv1) ? " ?" : "");
578 		}
579 		if (n->n_tag != 0)
580 			(void)printf(" tag=%#x%s", n->n_tag,
581 				     (IMSG.rip_vers == RIPv1) ? " ?" : "");
582 		putc('\n', stdout);
583 	}
584 }
585 
586 
587 /* Return the classical netmask for an IP address.
588  */
589 static u_int
590 std_mask(u_int addr)			/* in network order */
591 {
592 	NTOHL(addr);			/* was a host, not a network */
593 
594 	if (addr == 0)			/* default route has mask 0 */
595 		return 0;
596 	if (IN_CLASSA(addr))
597 		return IN_CLASSA_NET;
598 	if (IN_CLASSB(addr))
599 		return IN_CLASSB_NET;
600 	return IN_CLASSC_NET;
601 }
602 
603 
604 /* get a network number as a name or a number, with an optional "/xx"
605  * netmask.
606  */
607 static int				/* 0=bad */
608 getnet(char *name,
609        struct netinfo *rt)
610 {
611 	int i;
612 	struct netent *nentp;
613 	u_int mask;
614 	struct in_addr in;
615 	char hname[MAXHOSTNAMELEN+1];
616 	char *mname, *p;
617 
618 
619 	/* Detect and separate "1.2.3.4/24"
620 	 */
621 	if (0 != (mname = rindex(name,'/'))) {
622 		i = (int)(mname - name);
623 		if (i > sizeof(hname)-1)	/* name too long */
624 			return 0;
625 		bcopy(name, hname, i);
626 		hname[i] = '\0';
627 		mname++;
628 		name = hname;
629 	}
630 
631 	nentp = getnetbyname(name);
632 	if (nentp != 0) {
633 		in.s_addr = nentp->n_net;
634 	} else if (inet_aton(name, &in) == 1) {
635 		NTOHL(in.s_addr);
636 	} else {
637 		return 0;
638 	}
639 
640 	if (mname == 0) {
641 		mask = std_mask(in.s_addr);
642 		if ((~mask & in.s_addr) != 0)
643 			mask = 0xffffffff;
644 	} else {
645 		mask = (u_int)strtoul(mname, &p, 0);
646 		if (*p != '\0' || mask > 32)
647 			return 0;
648 		mask = 0xffffffff << (32-mask);
649 	}
650 
651 	rt->n_dst = htonl(in.s_addr);
652 	rt->n_family = RIP_AF_INET;
653 	rt->n_mask = htonl(mask);
654 	return 1;
655 }
656