blob: c142773faf57119a14479fa99af8da78c7710f9a [file] [log] [blame]
Lorenzo Colitti313379e2013-07-11 01:07:11 +09001/*
2 * Rdisc (this program) was developed by Sun Microsystems, Inc. and is
3 * provided for unrestricted use provided that this legend is included on
4 * all tape media and as a part of the software program in whole or part.
5 * Users may copy or modify Rdisc without charge, and they may freely
6 * distribute it.
7 *
8 * RDISC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
9 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
10 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
11 *
12 * Rdisc is provided with no support and without any obligation on the
13 * part of Sun Microsystems, Inc. to assist in its use, correction,
14 * modification or enhancement.
15 *
16 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
17 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY RDISC
18 * OR ANY PART THEREOF.
19 *
20 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
21 * or profits or other special, indirect and consequential damages, even if
22 * Sun has been advised of the possibility of such damages.
23 *
24 * Sun Microsystems, Inc.
25 * 2550 Garcia Avenue
26 * Mountain View, California 94043
27 */
28#include <stdio.h>
29#include <errno.h>
30#include <signal.h>
31#include <unistd.h>
32#include <stdlib.h>
33#include <sys/types.h>
34#include <sys/time.h>
35/* Do not use "improved" glibc version! */
36#include <linux/limits.h>
37
38#include <sys/param.h>
39#include <sys/socket.h>
40#include <sys/file.h>
41#include <malloc.h>
42
43#include <sys/ioctl.h>
44#include <linux/if.h>
45#include <linux/route.h>
46
47#include <netinet/in.h>
48#include <netinet/ip.h>
49#include <netinet/ip_icmp.h>
50
51/*
52 * The next include contains all defs and structures for multicast
53 * that are not in SunOS 4.1.x. On a SunOS 4.1.x system none of this code
54 * is ever used because it does not support multicast
55 * Fraser Gardiner - Sun Microsystems Australia
56 */
57
58#include <netdb.h>
59#include <arpa/inet.h>
60
61#include <string.h>
62#include <syslog.h>
63
64#include "SNAPSHOT.h"
65
66struct interface
67{
68 struct in_addr address; /* Used to identify the interface */
69 struct in_addr localaddr; /* Actual address if the interface */
70 int preference;
71 int flags;
72 struct in_addr bcastaddr;
73 struct in_addr remoteaddr;
74 struct in_addr netmask;
75 int ifindex;
76 char name[IFNAMSIZ];
77};
78
79/*
80 * TBD
81 * Use 255.255.255.255 for broadcasts - not the interface broadcast
82 * address.
83 */
84
85#define ALLIGN(ptr) (ptr)
86
87static int join(int sock, struct sockaddr_in *sin);
88static void solicitor(struct sockaddr_in *);
89#ifdef RDISC_SERVER
90static void advertise(struct sockaddr_in *, int lft);
91#endif
92static char *pr_name(struct in_addr addr);
93static void pr_pack(char *buf, int cc, struct sockaddr_in *from);
94static void age_table(int time);
95static void record_router(struct in_addr router, int preference, int ttl);
96static void add_route(struct in_addr addr);
97static void del_route(struct in_addr addr);
98static void rtioctl(struct in_addr addr, int op);
99static int support_multicast(void);
100static int sendbcast(int s, char *packet, int packetlen);
101static int sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *);
102static int sendbcastif(int s, char *packet, int packetlen, struct interface *ifp);
103static int sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin, struct interface *ifp);
104static int is_directly_connected(struct in_addr in);
105static void initlog(void);
106static void discard_table(void);
107static void init(void);
108
109#define ICMP_ROUTER_ADVERTISEMENT 9
110#define ICMP_ROUTER_SOLICITATION 10
111
112#define ALL_HOSTS_ADDRESS "224.0.0.1"
113#define ALL_ROUTERS_ADDRESS "224.0.0.2"
114
115#define MAXIFS 32
116
117#if !defined(__GLIBC__) || __GLIBC__ < 2
118/* For router advertisement */
119struct icmp_ra
120{
121 u_char icmp_type; /* type of message, see below */
122 u_char icmp_code; /* type sub code */
123 u_short icmp_cksum; /* ones complement cksum of struct */
124 u_char icmp_num_addrs;
125 u_char icmp_wpa; /* Words per address */
126 short icmp_lifetime;
127};
128
129struct icmp_ra_addr
130{
131 __u32 ira_addr;
132 __u32 ira_preference;
133};
134#else
135#define icmp_ra icmp
136#endif
137
138/* Router constants */
139#define MAX_INITIAL_ADVERT_INTERVAL 16
140#define MAX_INITIAL_ADVERTISEMENTS 3
141#define MAX_RESPONSE_DELAY 2 /* Not used */
142
143/* Host constants */
144#define MAX_SOLICITATIONS 3
145#define SOLICITATION_INTERVAL 3
146#define MAX_SOLICITATION_DELAY 1 /* Not used */
147
148#define INELIGIBLE_PREF 0x80000000 /* Maximum negative */
149
150#define MAX_ADV_INT 600
151
152/* Statics */
153static int num_interfaces;
154
155static struct interface *interfaces;
156static int interfaces_size; /* Number of elements in interfaces */
157
158
159#define MAXPACKET 4096 /* max packet size */
160
161/* fraser */
162int debugfile;
163
164const char usage[] =
165"Usage: rdisc [-b] [-d] [-s] [-v] [-f] [-a] [-V] [send_address] [receive_address]\n"
166#ifdef RDISC_SERVER
167" rdisc -r [-b] [-d] [-s] [-v] [-f] [-a] [-V] [-p <preference>] [-T <secs>]\n"
168" [send_address] [receive_address]\n"
169#endif
170;
171
172
173int s; /* Socket file descriptor */
174struct sockaddr_in whereto;/* Address to send to */
175
176/* Common variables */
177int verbose = 0;
178int debug = 0;
179int trace = 0;
180int solicit = 0;
181int ntransmitted = 0;
182int nreceived = 0;
183int forever = 0; /* Never give up on host. If 0 defer fork until
184 * first response.
185 */
186
187#ifdef RDISC_SERVER
188/* Router variables */
189int responder;
190int max_adv_int = MAX_ADV_INT;
191int min_adv_int;
192int lifetime;
193int initial_advert_interval = MAX_INITIAL_ADVERT_INTERVAL;
194int initial_advertisements = MAX_INITIAL_ADVERTISEMENTS;
195int preference = 0; /* Setable with -p option */
196#endif
197
198/* Host variables */
199int max_solicitations = MAX_SOLICITATIONS;
200unsigned int solicitation_interval = SOLICITATION_INTERVAL;
201int best_preference = 1; /* Set to record only the router(s) with the
202 best preference in the kernel. Not set
203 puts all routes in the kernel. */
204
205
206static void graceful_finish(void);
207static void finish(void);
208static void timer(void);
209static void initifs(void);
210static u_short in_cksum(u_short *addr, int len);
211
212static int logging = 0;
213
214#define logerr(fmt...) ({ if (logging) syslog(LOG_ERR, fmt); \
215 else fprintf(stderr, fmt); })
216#define logtrace(fmt...) ({ if (logging) syslog(LOG_INFO, fmt); \
217 else fprintf(stderr, fmt); })
218#define logdebug(fmt...) ({ if (logging) syslog(LOG_DEBUG, fmt); \
219 else fprintf(stderr, fmt); })
220static void logperror(char *str);
221
222static __inline__ int isbroadcast(struct sockaddr_in *sin)
223{
224 return (sin->sin_addr.s_addr == INADDR_BROADCAST);
225}
226
227static __inline__ int ismulticast(struct sockaddr_in *sin)
228{
229 return IN_CLASSD(ntohl(sin->sin_addr.s_addr));
230}
231
232static void prusage(void)
233{
234 fputs(usage, stderr);
235 exit(1);
236}
237
238void do_fork(void)
239{
240 int t;
241 pid_t pid;
242 long open_max;
243
244 if (trace)
245 return;
246 if ((open_max = sysconf(_SC_OPEN_MAX)) == -1) {
247 if (errno == 0) {
248 (void) fprintf(stderr, "OPEN_MAX is not supported\n");
249 }
250 else {
251 (void) fprintf(stderr, "sysconf() error\n");
252 }
253 exit(1);
254 }
255
256
257 if ((pid=fork()) != 0)
258 exit(0);
259
260 for (t = 0; t < open_max; t++)
261 if (t != s)
262 close(t);
263
264 setsid();
265 initlog();
266}
267
268void signal_setup(int signo, void (*handler)(void))
269{
270 struct sigaction sa;
271
272 memset(&sa, 0, sizeof(sa));
273
274 sa.sa_handler = (void (*)(int))handler;
275#ifdef SA_INTERRUPT
276 sa.sa_flags = SA_INTERRUPT;
277#endif
278 sigaction(signo, &sa, NULL);
279}
280
281/*
282 * M A I N
283 */
284char *sendaddress, *recvaddress;
285
286int main(int argc, char **argv)
287{
288 struct sockaddr_in from;
289 char **av = argv;
290 struct sockaddr_in *to = &whereto;
291 struct sockaddr_in joinaddr;
292 sigset_t sset, sset_empty;
293#ifdef RDISC_SERVER
294 int val;
295
296 min_adv_int =( max_adv_int * 3 / 4);
297 lifetime = (3*max_adv_int);
298#endif
299
300 argc--, av++;
301 while (argc > 0 && *av[0] == '-') {
302 while (*++av[0]) {
303 switch (*av[0]) {
304 case 'd':
305 debug = 1;
306 break;
307 case 't':
308 trace = 1;
309 break;
310 case 'v':
311 verbose++;
312 break;
313 case 's':
314 solicit = 1;
315 break;
316#ifdef RDISC_SERVER
317 case 'r':
318 responder = 1;
319 break;
320#endif
321 case 'a':
322 best_preference = 0;
323 break;
324 case 'b':
325 best_preference = 1;
326 break;
327 case 'f':
328 forever = 1;
329 break;
330 case 'V':
331 printf("rdisc utility, iputils-%s\n", SNAPSHOT);
332 exit(0);
333#ifdef RDISC_SERVER
334 case 'T':
335 argc--, av++;
336 if (argc != 0) {
337 val = strtol(av[0], (char **)NULL, 0);
338 if (val < 4 || val > 1800) {
339 (void) fprintf(stderr,
340 "Bad Max Advertizement Interval\n");
341 exit(1);
342 }
343 max_adv_int = val;
344 min_adv_int =( max_adv_int * 3 / 4);
345 lifetime = (3*max_adv_int);
346 } else {
347 prusage();
348 /* NOTREACHED*/
349 }
350 goto next;
351 case 'p':
352 argc--, av++;
353 if (argc != 0) {
354 val = strtol(av[0], (char **)NULL, 0);
355 preference = val;
356 } else {
357 prusage();
358 /* NOTREACHED*/
359 }
360 goto next;
361#endif
362 default:
363 prusage();
364 /* NOTREACHED*/
365 }
366 }
367#ifdef RDISC_SERVER
368next:
369#endif
370 argc--, av++;
371 }
372 if( argc < 1) {
373 if (support_multicast()) {
374 sendaddress = ALL_ROUTERS_ADDRESS;
375#ifdef RDISC_SERVER
376 if (responder)
377 sendaddress = ALL_HOSTS_ADDRESS;
378#endif
379 } else
380 sendaddress = "255.255.255.255";
381 } else {
382 sendaddress = av[0];
383 argc--;
384 }
385
386 if (argc < 1) {
387 if (support_multicast()) {
388 recvaddress = ALL_HOSTS_ADDRESS;
389#ifdef RDISC_SERVER
390 if (responder)
391 recvaddress = ALL_ROUTERS_ADDRESS;
392#endif
393 } else
394 recvaddress = "255.255.255.255";
395 } else {
396 recvaddress = av[0];
397 argc--;
398 }
399 if (argc != 0) {
400 (void) fprintf(stderr, "Extra parameters\n");
401 prusage();
402 /* NOTREACHED */
403 }
404
405#ifdef RDISC_SERVER
406 if (solicit && responder) {
407 prusage();
408 /* NOTREACHED */
409 }
410#endif
411
412 if (!(solicit && !forever)) {
413 do_fork();
414/*
415 * Added the next line to stop forking a second time
416 * Fraser Gardiner - Sun Microsystems Australia
417 */
418 forever = 1;
419 }
420
421 memset( (char *)&whereto, 0, sizeof(struct sockaddr_in) );
422 to->sin_family = AF_INET;
423 to->sin_addr.s_addr = inet_addr(sendaddress);
424
425 memset( (char *)&joinaddr, 0, sizeof(struct sockaddr_in) );
426 joinaddr.sin_family = AF_INET;
427 joinaddr.sin_addr.s_addr = inet_addr(recvaddress);
428
429#ifdef RDISC_SERVER
430 if (responder)
431 srandom((int)gethostid());
432#endif
433
434 if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
435 logperror("socket");
436 exit(5);
437 }
438
439 setlinebuf( stdout );
440
441 signal_setup(SIGINT, finish );
442 signal_setup(SIGTERM, graceful_finish );
443 signal_setup(SIGHUP, initifs );
444 signal_setup(SIGALRM, timer );
445
446 sigemptyset(&sset);
447 sigemptyset(&sset_empty);
448 sigaddset(&sset, SIGALRM);
449 sigaddset(&sset, SIGHUP);
450 sigaddset(&sset, SIGTERM);
451 sigaddset(&sset, SIGINT);
452
453 init();
454 if (join(s, &joinaddr) < 0) {
455 logerr("Failed joining addresses\n");
456 exit (2);
457 }
458
459 timer(); /* start things going */
460
461 for (;;) {
462 u_char packet[MAXPACKET];
463 int len = sizeof (packet);
464 socklen_t fromlen = sizeof (from);
465 int cc;
466
467 cc=recvfrom(s, (char *)packet, len, 0,
468 (struct sockaddr *)&from, &fromlen);
469 if (cc<0) {
470 if (errno == EINTR)
471 continue;
472 logperror("recvfrom");
473 continue;
474 }
475
476 sigprocmask(SIG_SETMASK, &sset, NULL);
477 pr_pack( (char *)packet, cc, &from );
478 sigprocmask(SIG_SETMASK, &sset_empty, NULL);
479 }
480 /*NOTREACHED*/
481}
482
483#define TIMER_INTERVAL 3
484#define GETIFCONF_TIMER 30
485
486static int left_until_advertise;
487
488/* Called every TIMER_INTERVAL */
489void timer()
490{
491 static int time;
492 static int left_until_getifconf;
493 static int left_until_solicit;
494
495
496 time += TIMER_INTERVAL;
497
498 left_until_getifconf -= TIMER_INTERVAL;
499 left_until_advertise -= TIMER_INTERVAL;
500 left_until_solicit -= TIMER_INTERVAL;
501
502 if (left_until_getifconf < 0) {
503 initifs();
504 left_until_getifconf = GETIFCONF_TIMER;
505 }
506#ifdef RDISC_SERVER
507 if (responder && left_until_advertise <= 0) {
508 ntransmitted++;
509 advertise(&whereto, lifetime);
510 if (ntransmitted < initial_advertisements)
511 left_until_advertise = initial_advert_interval;
512 else
513 left_until_advertise = min_adv_int +
514 ((max_adv_int - min_adv_int) *
515 (random() % 1000)/1000);
516 } else
517#endif
518 if (solicit && left_until_solicit <= 0) {
519 ntransmitted++;
520 solicitor(&whereto);
521 if (ntransmitted < max_solicitations)
522 left_until_solicit = solicitation_interval;
523 else {
524 solicit = 0;
525 if (!forever && nreceived == 0)
526 exit(5);
527 }
528 }
529 age_table(TIMER_INTERVAL);
530 alarm(TIMER_INTERVAL);
531}
532
533/*
534 * S O L I C I T O R
535 *
536 * Compose and transmit an ICMP ROUTER SOLICITATION REQUEST packet.
537 * The IP packet will be added on by the kernel.
538 */
539void
540solicitor(struct sockaddr_in *sin)
541{
542 static u_char outpack[MAXPACKET];
543 struct icmphdr *icp = (struct icmphdr *) ALLIGN(outpack);
544 int packetlen, i;
545
546 if (verbose) {
547 logtrace("Sending solicitation to %s\n",
548 pr_name(sin->sin_addr));
549 }
550 icp->type = ICMP_ROUTER_SOLICITATION;
551 icp->code = 0;
552 icp->checksum = 0;
553 icp->un.gateway = 0; /* Reserved */
554 packetlen = 8;
555
556 /* Compute ICMP checksum here */
557 icp->checksum = in_cksum( (u_short *)icp, packetlen );
558
559 if (isbroadcast(sin))
560 i = sendbcast(s, (char *)outpack, packetlen);
561 else if (ismulticast(sin))
562 i = sendmcast(s, (char *)outpack, packetlen, sin);
563 else
564 i = sendto( s, (char *)outpack, packetlen, 0,
565 (struct sockaddr *)sin, sizeof(struct sockaddr));
566
567 if( i < 0 || i != packetlen ) {
568 if( i<0 ) {
569 logperror("solicitor:sendto");
570 }
571 logerr("wrote %s %d chars, ret=%d\n",
572 sendaddress, packetlen, i );
573 }
574}
575
576#ifdef RDISC_SERVER
577/*
578 * A V E R T I S E
579 *
580 * Compose and transmit an ICMP ROUTER ADVERTISEMENT packet.
581 * The IP packet will be added on by the kernel.
582 */
583void
584advertise(struct sockaddr_in *sin, int lft)
585{
586 static u_char outpack[MAXPACKET];
587 struct icmp_ra *rap = (struct icmp_ra *) ALLIGN(outpack);
588 struct icmp_ra_addr *ap;
589 int packetlen, i, cc;
590
591 if (verbose) {
592 logtrace("Sending advertisement to %s\n",
593 pr_name(sin->sin_addr));
594 }
595
596 for (i = 0; i < num_interfaces; i++) {
597 rap->icmp_type = ICMP_ROUTER_ADVERTISEMENT;
598 rap->icmp_code = 0;
599 rap->icmp_cksum = 0;
600 rap->icmp_num_addrs = 0;
601 rap->icmp_wpa = 2;
602 rap->icmp_lifetime = htons(lft);
603 packetlen = 8;
604
605 /*
606 * TODO handle multiple logical interfaces per
607 * physical interface. (increment with rap->icmp_wpa * 4 for
608 * each address.)
609 */
610 ap = (struct icmp_ra_addr *)ALLIGN(outpack + ICMP_MINLEN);
611 ap->ira_addr = interfaces[i].localaddr.s_addr;
612 ap->ira_preference = htonl(interfaces[i].preference);
613 packetlen += rap->icmp_wpa * 4;
614 rap->icmp_num_addrs++;
615
616 /* Compute ICMP checksum here */
617 rap->icmp_cksum = in_cksum( (u_short *)rap, packetlen );
618
619 if (isbroadcast(sin))
620 cc = sendbcastif(s, (char *)outpack, packetlen,
621 &interfaces[i]);
622 else if (ismulticast(sin))
623 cc = sendmcastif( s, (char *)outpack, packetlen, sin,
624 &interfaces[i]);
625 else {
626 struct interface *ifp = &interfaces[i];
627 /*
628 * Verify that the interface matches the destination
629 * address.
630 */
631 if ((sin->sin_addr.s_addr & ifp->netmask.s_addr) ==
632 (ifp->address.s_addr & ifp->netmask.s_addr)) {
633 if (debug) {
634 logdebug("Unicast to %s ",
635 pr_name(sin->sin_addr));
636 logdebug("on interface %s, %s\n",
637 ifp->name,
638 pr_name(ifp->address));
639 }
640 cc = sendto( s, (char *)outpack, packetlen, 0,
641 (struct sockaddr *)sin,
642 sizeof(struct sockaddr));
643 } else
644 cc = packetlen;
645 }
646 if( cc < 0 || cc != packetlen ) {
647 if (cc < 0) {
648 logperror("sendto");
649 } else {
650 logerr("wrote %s %d chars, ret=%d\n",
651 sendaddress, packetlen, cc );
652 }
653 }
654 }
655}
656#endif
657
658/*
659 * P R _ T Y P E
660 *
661 * Convert an ICMP "type" field to a printable string.
662 */
663char *
664pr_type(int t)
665{
666 static char *ttab[] = {
667 "Echo Reply",
668 "ICMP 1",
669 "ICMP 2",
670 "Dest Unreachable",
671 "Source Quench",
672 "Redirect",
673 "ICMP 6",
674 "ICMP 7",
675 "Echo",
676 "Router Advertise",
677 "Router Solicitation",
678 "Time Exceeded",
679 "Parameter Problem",
680 "Timestamp",
681 "Timestamp Reply",
682 "Info Request",
683 "Info Reply",
684 "Netmask Request",
685 "Netmask Reply"
686 };
687
688 if ( t < 0 || t > 16 )
689 return("OUT-OF-RANGE");
690
691 return(ttab[t]);
692}
693
694/*
695 * P R _ N A M E
696 *
697 * Return a string name for the given IP address.
698 */
699char *pr_name(struct in_addr addr)
700{
701 struct hostent *phe;
702 static char buf[80];
703
704 phe = gethostbyaddr((char *)&addr.s_addr, 4, AF_INET);
705 if (phe == NULL)
706 return( inet_ntoa(addr));
707 snprintf(buf, sizeof(buf), "%s (%s)", phe->h_name, inet_ntoa(addr));
708 return(buf);
709}
710
711/*
712 * P R _ P A C K
713 *
714 * Print out the packet, if it came from us. This logic is necessary
715 * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
716 * which arrive ('tis only fair). This permits multiple copies of this
717 * program to be run without having intermingled output (or statistics!).
718 */
719void
720pr_pack(char *buf, int cc, struct sockaddr_in *from)
721{
722 struct iphdr *ip;
723 struct icmphdr *icp;
724 int i;
725 int hlen;
726
727 ip = (struct iphdr *) ALLIGN(buf);
728 hlen = ip->ihl << 2;
729 if (cc < hlen + 8) {
730 if (verbose)
731 logtrace("packet too short (%d bytes) from %s\n", cc,
732 pr_name(from->sin_addr));
733 return;
734 }
735 cc -= hlen;
736 icp = (struct icmphdr *)ALLIGN(buf + hlen);
737
738 switch (icp->type) {
739 case ICMP_ROUTER_ADVERTISEMENT:
740 {
741 struct icmp_ra *rap = (struct icmp_ra *)ALLIGN(icp);
742 struct icmp_ra_addr *ap;
743
744#ifdef RDISC_SERVER
745 if (responder)
746 break;
747#endif
748
749 /* TBD verify that the link is multicast or broadcast */
750 /* XXX Find out the link it came in over? */
751 if (in_cksum((u_short *)ALLIGN(buf+hlen), cc)) {
752 if (verbose)
753 logtrace("ICMP %s from %s: Bad checksum\n",
754 pr_type((int)rap->icmp_type),
755 pr_name(from->sin_addr));
756 return;
757 }
758 if (rap->icmp_code != 0) {
759 if (verbose)
760 logtrace("ICMP %s from %s: Code = %d\n",
761 pr_type((int)rap->icmp_type),
762 pr_name(from->sin_addr),
763 rap->icmp_code);
764 return;
765 }
766 if (rap->icmp_num_addrs < 1) {
767 if (verbose)
768 logtrace("ICMP %s from %s: No addresses\n",
769 pr_type((int)rap->icmp_type),
770 pr_name(from->sin_addr));
771 return;
772 }
773 if (rap->icmp_wpa < 2) {
774 if (verbose)
775 logtrace("ICMP %s from %s: Words/addr = %d\n",
776 pr_type((int)rap->icmp_type),
777 pr_name(from->sin_addr),
778 rap->icmp_wpa);
779 return;
780 }
781 if ((unsigned)cc <
782 8 + rap->icmp_num_addrs * rap->icmp_wpa * 4) {
783 if (verbose)
784 logtrace("ICMP %s from %s: Too short %d, %d\n",
785 pr_type((int)rap->icmp_type),
786 pr_name(from->sin_addr),
787 cc,
788 8 + rap->icmp_num_addrs * rap->icmp_wpa * 4);
789 return;
790 }
791
792 if (verbose)
793 logtrace("ICMP %s from %s, lifetime %d\n",
794 pr_type((int)rap->icmp_type),
795 pr_name(from->sin_addr),
796 ntohs(rap->icmp_lifetime));
797
798 /* Check that at least one router address is a neighboor
799 * on the arriving link.
800 */
801 for (i = 0; (unsigned)i < rap->icmp_num_addrs; i++) {
802 struct in_addr ina;
803 ap = (struct icmp_ra_addr *)
804 ALLIGN(buf + hlen + 8 +
805 i * rap->icmp_wpa * 4);
806 ina.s_addr = ap->ira_addr;
807 if (verbose)
808 logtrace("\taddress %s, preference 0x%x\n",
809 pr_name(ina),
810 (unsigned int)ntohl(ap->ira_preference));
811 if (is_directly_connected(ina))
812 record_router(ina,
813 ntohl(ap->ira_preference),
814 ntohs(rap->icmp_lifetime));
815 }
816 nreceived++;
817 if (!forever) {
818 do_fork();
819 forever = 1;
820/*
821 * The next line was added so that the alarm is set for the new procces
822 * Fraser Gardiner Sun Microsystems Australia
823 */
824 (void) alarm(TIMER_INTERVAL);
825 }
826 break;
827 }
828
829#ifdef RDISC_SERVER
830 case ICMP_ROUTER_SOLICITATION:
831 {
832 struct sockaddr_in sin;
833
834 if (!responder)
835 break;
836
837 /* TBD verify that the link is multicast or broadcast */
838 /* XXX Find out the link it came in over? */
839
840 if (in_cksum((u_short *)ALLIGN(buf+hlen), cc)) {
841 if (verbose)
842 logtrace("ICMP %s from %s: Bad checksum\n",
843 pr_type((int)icp->type),
844 pr_name(from->sin_addr));
845 return;
846 }
847 if (icp->code != 0) {
848 if (verbose)
849 logtrace("ICMP %s from %s: Code = %d\n",
850 pr_type((int)icp->type),
851 pr_name(from->sin_addr),
852 icp->code);
853 return;
854 }
855
856 if (cc < ICMP_MINLEN) {
857 if (verbose)
858 logtrace("ICMP %s from %s: Too short %d, %d\n",
859 pr_type((int)icp->type),
860 pr_name(from->sin_addr),
861 cc,
862 ICMP_MINLEN);
863 return;
864 }
865
866 if (verbose)
867 logtrace("ICMP %s from %s\n",
868 pr_type((int)icp->type),
869 pr_name(from->sin_addr));
870
871 /* Check that ip_src is either a neighboor
872 * on the arriving link or 0.
873 */
874 sin.sin_family = AF_INET;
875 if (ip->saddr == 0) {
876 /* If it was sent to the broadcast address we respond
877 * to the broadcast address.
878 */
879 if (IN_CLASSD(ntohl(ip->daddr)))
880 sin.sin_addr.s_addr = htonl(0xe0000001);
881 else
882 sin.sin_addr.s_addr = INADDR_BROADCAST;
883 /* Restart the timer when we broadcast */
884 left_until_advertise = min_adv_int +
885 ((max_adv_int - min_adv_int)
886 * (random() % 1000)/1000);
887 } else {
888 sin.sin_addr.s_addr = ip->saddr;
889 if (!is_directly_connected(sin.sin_addr)) {
890 if (verbose)
891 logtrace("ICMP %s from %s: source not directly connected\n",
892 pr_type((int)icp->type),
893 pr_name(from->sin_addr));
894 break;
895 }
896 }
897 nreceived++;
898 ntransmitted++;
899 advertise(&sin, lifetime);
900 break;
901 }
902#endif
903 }
904}
905
906
907/*
908 * I N _ C K S U M
909 *
910 * Checksum routine for Internet Protocol family headers (C Version)
911 *
912 */
913#if BYTE_ORDER == LITTLE_ENDIAN
914# define ODDBYTE(v) (v)
915#elif BYTE_ORDER == BIG_ENDIAN
916# define ODDBYTE(v) ((u_short)(v) << 8)
917#else
918# define ODDBYTE(v) htons((u_short)(v) << 8)
919#endif
920
921u_short in_cksum(u_short *addr, int len)
922{
923 register int nleft = len;
924 register u_short *w = addr;
925 register u_short answer;
926 register int sum = 0;
927
928 /*
929 * Our algorithm is simple, using a 32 bit accumulator (sum),
930 * we add sequential 16 bit words to it, and at the end, fold
931 * back all the carry bits from the top 16 bits into the lower
932 * 16 bits.
933 */
934 while( nleft > 1 ) {
935 sum += *w++;
936 nleft -= 2;
937 }
938
939 /* mop up an odd byte, if necessary */
940 if( nleft == 1 )
941 sum += ODDBYTE(*(u_char *)w); /* le16toh() may be unavailable on old systems */
942
943 /*
944 * add back carry outs from top 16 bits to low 16 bits
945 */
946 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
947 sum += (sum >> 16); /* add carry */
948 answer = ~sum; /* truncate to 16 bits */
949 return (answer);
950}
951
952/*
953 * F I N I S H
954 *
955 * Print out statistics, and give up.
956 * Heavily buffered STDIO is used here, so that all the statistics
957 * will be written with 1 sys-write call. This is nice when more
958 * than one copy of the program is running on a terminal; it prevents
959 * the statistics output from becomming intermingled.
960 */
961void
962finish()
963{
964#ifdef RDISC_SERVER
965 if (responder) {
966 /* Send out a packet with a preference so that all
967 * hosts will know that we are dead.
968 *
969 * Wrong comment, wrong code.
970 * ttl must be set to 0 instead. --ANK
971 */
972 logerr("terminated\n");
973 ntransmitted++;
974 advertise(&whereto, 0);
975 }
976#endif
977 logtrace("\n----%s rdisc Statistics----\n", sendaddress );
978 logtrace("%d packets transmitted, ", ntransmitted );
979 logtrace("%d packets received, ", nreceived );
980 logtrace("\n");
981 (void) fflush(stdout);
982 exit(0);
983}
984
985void
986graceful_finish()
987{
988 discard_table();
989 finish();
990 exit(0);
991}
992
993
994/* From libc/rpc/pmap_rmt.c */
995
996int
997sendbcast(int s, char *packet, int packetlen)
998{
999 int i, cc;
1000
1001 for (i = 0; i < num_interfaces; i++) {
1002 if ((interfaces[i].flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0)
1003 continue;
1004 cc = sendbcastif(s, packet, packetlen, &interfaces[i]);
1005 if (cc!= packetlen) {
1006 return (cc);
1007 }
1008 }
1009 return (packetlen);
1010}
1011
1012int
1013sendbcastif(int s, char *packet, int packetlen, struct interface *ifp)
1014{
1015 int on;
1016 int cc;
1017 struct sockaddr_in baddr;
1018
1019 baddr.sin_family = AF_INET;
1020 baddr.sin_addr = ifp->bcastaddr;
1021 if (debug)
1022 logdebug("Broadcast to %s\n",
1023 pr_name(baddr.sin_addr));
1024 on = 1;
1025 setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on));
1026 cc = sendto(s, packet, packetlen, 0,
1027 (struct sockaddr *)&baddr, sizeof (struct sockaddr));
1028 if (cc!= packetlen) {
1029 logperror("sendbcast: sendto");
1030 logerr("Cannot send broadcast packet to %s\n",
1031 pr_name(baddr.sin_addr));
1032 }
1033 on = 0;
1034 setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on));
1035 return (cc);
1036}
1037
1038int
1039sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *sin)
1040{
1041 int i, cc;
1042
1043 for (i = 0; i < num_interfaces; i++) {
1044 if ((interfaces[i].flags & (IFF_BROADCAST|IFF_POINTOPOINT|IFF_MULTICAST)) == 0)
1045 continue;
1046 cc = sendmcastif(s, packet, packetlen, sin, &interfaces[i]);
1047 if (cc!= packetlen) {
1048 return (cc);
1049 }
1050 }
1051 return (packetlen);
1052}
1053
1054int
1055sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin,
1056 struct interface *ifp)
1057{
1058 int cc;
1059 struct ip_mreqn mreq;
1060
1061 memset(&mreq, 0, sizeof(mreq));
1062 mreq.imr_ifindex = ifp->ifindex;
1063 mreq.imr_address = ifp->localaddr;
1064 if (debug)
1065 logdebug("Multicast to interface %s, %s\n",
1066 ifp->name,
1067 pr_name(mreq.imr_address));
1068 if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
1069 (char *)&mreq,
1070 sizeof(mreq)) < 0) {
1071 logperror("setsockopt (IP_MULTICAST_IF)");
1072 logerr("Cannot send multicast packet over interface %s, %s\n",
1073 ifp->name,
1074 pr_name(mreq.imr_address));
1075 return (-1);
1076 }
1077 cc = sendto(s, packet, packetlen, 0,
1078 (struct sockaddr *)sin, sizeof (struct sockaddr));
1079 if (cc!= packetlen) {
1080 logperror("sendmcast: sendto");
1081 logerr("Cannot send multicast packet over interface %s, %s\n",
1082 ifp->name, pr_name(mreq.imr_address));
1083 }
1084 return (cc);
1085}
1086
1087void
1088init()
1089{
1090 initifs();
1091#ifdef RDISC_SERVER
1092 {
1093 int i;
1094 for (i = 0; i < interfaces_size; i++)
1095 interfaces[i].preference = preference;
1096 }
1097#endif
1098}
1099
1100void
1101initifs()
1102{
1103 int sock;
1104 struct ifconf ifc;
1105 struct ifreq ifreq, *ifr;
1106 struct sockaddr_in *sin;
1107 int n, i;
1108 char *buf;
1109 int numifs;
1110 unsigned bufsize;
1111
1112 sock = socket(AF_INET, SOCK_DGRAM, 0);
1113 if (sock < 0) {
1114 logperror("initifs: socket");
1115 return;
1116 }
1117#ifdef SIOCGIFNUM
1118 if (ioctl(sock, SIOCGIFNUM, (char *)&numifs) < 0) {
1119 numifs = MAXIFS;
1120 }
1121#else
1122 numifs = MAXIFS;
1123#endif
1124 bufsize = numifs * sizeof(struct ifreq);
1125 buf = (char *)malloc(bufsize);
1126 if (buf == NULL) {
1127 logerr("out of memory\n");
1128 (void) close(sock);
1129 return;
1130 }
1131 if (interfaces != NULL)
1132 (void) free(interfaces);
1133 interfaces = (struct interface *)ALLIGN(malloc(numifs *
1134 sizeof(struct interface)));
1135 if (interfaces == NULL) {
1136 logerr("out of memory\n");
1137 (void) close(sock);
1138 (void) free(buf);
1139 return;
1140 }
1141 interfaces_size = numifs;
1142
1143 ifc.ifc_len = bufsize;
1144 ifc.ifc_buf = buf;
1145 if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
1146 logperror("initifs: ioctl (get interface configuration)");
1147 (void) close(sock);
1148 (void) free(buf);
1149 return;
1150 }
1151 ifr = ifc.ifc_req;
1152 for (i = 0, n = ifc.ifc_len/sizeof (struct ifreq); n > 0; n--, ifr++) {
1153 ifreq = *ifr;
1154 if (strlen(ifreq.ifr_name) >= IFNAMSIZ)
1155 continue;
1156 if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
1157 logperror("initifs: ioctl (get interface flags)");
1158 continue;
1159 }
1160 if (ifr->ifr_addr.sa_family != AF_INET)
1161 continue;
1162 if ((ifreq.ifr_flags & IFF_UP) == 0)
1163 continue;
1164 if (ifreq.ifr_flags & IFF_LOOPBACK)
1165 continue;
1166 if ((ifreq.ifr_flags & (IFF_MULTICAST|IFF_BROADCAST|IFF_POINTOPOINT)) == 0)
1167 continue;
1168 strncpy(interfaces[i].name, ifr->ifr_name, IFNAMSIZ-1);
1169
1170 sin = (struct sockaddr_in *)ALLIGN(&ifr->ifr_addr);
1171 interfaces[i].localaddr = sin->sin_addr;
1172 interfaces[i].flags = ifreq.ifr_flags;
1173 interfaces[i].netmask.s_addr = (__u32)0xffffffff;
1174 if (ioctl(sock, SIOCGIFINDEX, (char *)&ifreq) < 0) {
1175 logperror("initifs: ioctl (get ifindex)");
1176 continue;
1177 }
1178 interfaces[i].ifindex = ifreq.ifr_ifindex;
1179 if (ifreq.ifr_flags & IFF_POINTOPOINT) {
1180 if (ioctl(sock, SIOCGIFDSTADDR, (char *)&ifreq) < 0) {
1181 logperror("initifs: ioctl (get destination addr)");
1182 continue;
1183 }
1184 sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr);
1185 /* A pt-pt link is identified by the remote address */
1186 interfaces[i].address = sin->sin_addr;
1187 interfaces[i].remoteaddr = sin->sin_addr;
1188 /* Simulate broadcast for pt-pt */
1189 interfaces[i].bcastaddr = sin->sin_addr;
1190 interfaces[i].flags |= IFF_BROADCAST;
1191 } else {
1192 /* Non pt-pt links are identified by the local address */
1193 interfaces[i].address = interfaces[i].localaddr;
1194 interfaces[i].remoteaddr = interfaces[i].address;
1195 if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
1196 logperror("initifs: ioctl (get netmask)");
1197 continue;
1198 }
1199 sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr);
1200 interfaces[i].netmask = sin->sin_addr;
1201 if (ifreq.ifr_flags & IFF_BROADCAST) {
1202 if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
1203 logperror("initifs: ioctl (get broadcast address)");
1204 continue;
1205 }
1206 sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr);
1207 interfaces[i].bcastaddr = sin->sin_addr;
1208 }
1209 }
1210#ifdef notdef
1211 if (debug)
1212 logdebug("Found interface %s, flags 0x%x\n",
1213 pr_name(interfaces[i].localaddr),
1214 interfaces[i].flags);
1215#endif
1216 i++;
1217 }
1218 num_interfaces = i;
1219#ifdef notdef
1220 if (debug)
1221 logdebug("Found %d interfaces\n", num_interfaces);
1222#endif
1223 (void) close(sock);
1224 (void) free(buf);
1225}
1226
1227int
1228join(int sock, struct sockaddr_in *sin)
1229{
1230 int i, j;
1231 struct ip_mreqn mreq;
1232 int joined[num_interfaces];
1233
1234 memset(joined, 0, sizeof(joined));
1235
1236 if (isbroadcast(sin))
1237 return (0);
1238
1239 mreq.imr_multiaddr = sin->sin_addr;
1240 for (i = 0; i < num_interfaces; i++) {
1241 for (j = 0; j < i; j++) {
1242 if (joined[j] == interfaces[i].ifindex)
1243 break;
1244 }
1245 if (j != i)
1246 continue;
1247
1248 mreq.imr_ifindex = interfaces[i].ifindex;
1249 mreq.imr_address.s_addr = 0;
1250
1251 if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
1252 (char *)&mreq, sizeof(mreq)) < 0) {
1253 logperror("setsockopt (IP_ADD_MEMBERSHIP)");
1254 return (-1);
1255 }
1256
1257 joined[i] = interfaces[i].ifindex;
1258 }
1259 return (0);
1260}
1261
1262int support_multicast()
1263{
1264 int sock;
1265 u_char ttl = 1;
1266
1267 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1268 if (sock < 0) {
1269 logperror("support_multicast: socket");
1270 return (0);
1271 }
1272
1273 if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL,
1274 (char *)&ttl, sizeof(ttl)) < 0) {
1275 (void) close(sock);
1276 return (0);
1277 }
1278 (void) close(sock);
1279 return (1);
1280}
1281
1282int
1283is_directly_connected(struct in_addr in)
1284{
1285 int i;
1286
1287 for (i = 0; i < num_interfaces; i++) {
1288 /* Check that the subnetwork numbers match */
1289
1290 if ((in.s_addr & interfaces[i].netmask.s_addr ) ==
1291 (interfaces[i].remoteaddr.s_addr & interfaces[i].netmask.s_addr))
1292 return (1);
1293 }
1294 return (0);
1295}
1296
1297/*
1298 * TABLES
1299 */
1300struct table {
1301 struct in_addr router;
1302 int preference;
1303 int remaining_time;
1304 int in_kernel;
1305 struct table *next;
1306};
1307
1308struct table *table;
1309
1310struct table *
1311find_router(struct in_addr addr)
1312{
1313 struct table *tp;
1314
1315 tp = table;
1316 while (tp) {
1317 if (tp->router.s_addr == addr.s_addr)
1318 return (tp);
1319 tp = tp->next;
1320 }
1321 return (NULL);
1322}
1323
1324int max_preference(void)
1325{
1326 struct table *tp;
1327 int max = (int)INELIGIBLE_PREF;
1328
1329 tp = table;
1330 while (tp) {
1331 if (tp->preference > max)
1332 max = tp->preference;
1333 tp = tp->next;
1334 }
1335 return (max);
1336}
1337
1338
1339/* Note: this might leave the kernel with no default route for a short time. */
1340void
1341age_table(int time)
1342{
1343 struct table **tpp, *tp;
1344 int recalculate_max = 0;
1345 int max = max_preference();
1346
1347 tpp = &table;
1348 while (*tpp != NULL) {
1349 tp = *tpp;
1350 tp->remaining_time -= time;
1351 if (tp->remaining_time <= 0) {
1352 *tpp = tp->next;
1353 if (tp->in_kernel)
1354 del_route(tp->router);
1355 if (best_preference &&
1356 tp->preference == max)
1357 recalculate_max++;
1358 free((char *)tp);
1359 } else {
1360 tpp = &tp->next;
1361 }
1362 }
1363 if (recalculate_max) {
1364 int max = max_preference();
1365
1366 if (max != INELIGIBLE_PREF) {
1367 tp = table;
1368 while (tp) {
1369 if (tp->preference == max && !tp->in_kernel) {
1370 add_route(tp->router);
1371 tp->in_kernel++;
1372 }
1373 tp = tp->next;
1374 }
1375 }
1376 }
1377}
1378
1379void discard_table(void)
1380{
1381 struct table **tpp, *tp;
1382
1383 tpp = &table;
1384 while (*tpp != NULL) {
1385 tp = *tpp;
1386 *tpp = tp->next;
1387 if (tp->in_kernel)
1388 del_route(tp->router);
1389 free((char *)tp);
1390 }
1391}
1392
1393
1394void
1395record_router(struct in_addr router, int preference, int ttl)
1396{
1397 struct table *tp;
1398 int old_max = max_preference();
1399 int changed_up = 0; /* max preference could have increased */
1400 int changed_down = 0; /* max preference could have decreased */
1401
1402 if (ttl < 4)
1403 preference = INELIGIBLE_PREF;
1404
1405 if (debug)
1406 logdebug("Recording %s, ttl %d, preference 0x%x\n",
1407 pr_name(router),
1408 ttl,
1409 preference);
1410 tp = find_router(router);
1411 if (tp) {
1412 if (tp->preference > preference &&
1413 tp->preference == old_max)
1414 changed_down++;
1415 else if (preference > tp->preference)
1416 changed_up++;
1417 tp->preference = preference;
1418 tp->remaining_time = ttl;
1419 } else {
1420 if (preference > old_max)
1421 changed_up++;
1422 tp = (struct table *)ALLIGN(malloc(sizeof(struct table)));
1423 if (tp == NULL) {
1424 logerr("Out of memory\n");
1425 return;
1426 }
1427 tp->router = router;
1428 tp->preference = preference;
1429 tp->remaining_time = ttl;
1430 tp->in_kernel = 0;
1431 tp->next = table;
1432 table = tp;
1433 }
1434 if (!tp->in_kernel &&
1435 (!best_preference || tp->preference == max_preference()) &&
1436 tp->preference != INELIGIBLE_PREF) {
1437 add_route(tp->router);
1438 tp->in_kernel++;
1439 }
1440 if (tp->preference == INELIGIBLE_PREF && tp->in_kernel) {
1441 del_route(tp->router);
1442 tp->in_kernel = 0;
1443 }
1444 if (best_preference && changed_down) {
1445 /* Check if we should add routes */
1446 int new_max = max_preference();
1447 if (new_max != INELIGIBLE_PREF) {
1448 tp = table;
1449 while (tp) {
1450 if (tp->preference == new_max &&
1451 !tp->in_kernel) {
1452 add_route(tp->router);
1453 tp->in_kernel++;
1454 }
1455 tp = tp->next;
1456 }
1457 }
1458 }
1459 if (best_preference && (changed_up || changed_down)) {
1460 /* Check if we should remove routes already in the kernel */
1461 int new_max = max_preference();
1462 tp = table;
1463 while (tp) {
1464 if (tp->preference < new_max && tp->in_kernel) {
1465 del_route(tp->router);
1466 tp->in_kernel = 0;
1467 }
1468 tp = tp->next;
1469 }
1470 }
1471}
1472
1473void
1474add_route(struct in_addr addr)
1475{
1476 if (debug)
1477 logdebug("Add default route to %s\n", pr_name(addr));
1478 rtioctl(addr, SIOCADDRT);
1479}
1480
1481void
1482del_route(struct in_addr addr)
1483{
1484 if (debug)
1485 logdebug("Delete default route to %s\n", pr_name(addr));
1486 rtioctl(addr, SIOCDELRT);
1487}
1488
1489void
1490rtioctl(struct in_addr addr, int op)
1491{
1492 int sock;
1493 struct rtentry rt;
1494 struct sockaddr_in *sin;
1495
1496 memset((char *)&rt, 0, sizeof(struct rtentry));
1497 rt.rt_dst.sa_family = AF_INET;
1498 rt.rt_gateway.sa_family = AF_INET;
1499 rt.rt_genmask.sa_family = AF_INET;
1500 sin = (struct sockaddr_in *)ALLIGN(&rt.rt_gateway);
1501 sin->sin_addr = addr;
1502 rt.rt_flags = RTF_UP | RTF_GATEWAY;
1503
1504 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1505 if (sock < 0) {
1506 logperror("rtioctl: socket");
1507 return;
1508 }
1509 if (ioctl(sock, op, (char *)&rt) < 0) {
1510 if (!(op == SIOCADDRT && errno == EEXIST))
1511 logperror("ioctl (add/delete route)");
1512 }
1513 (void) close(sock);
1514}
1515
1516/*
1517 * LOGGER
1518 */
1519
1520void initlog(void)
1521{
1522 logging++;
1523 openlog("in.rdiscd", LOG_PID | LOG_CONS, LOG_DAEMON);
1524}
1525
1526
1527void
1528logperror(char *str)
1529{
1530 if (logging)
1531 syslog(LOG_ERR, "%s: %m", str);
1532 else
1533 (void) fprintf(stderr, "%s: %s\n", str, strerror(errno));
1534}