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