aboutsummaryrefslogtreecommitdiff
path: root/sbin/rdisc/rdisc.c
diff options
context:
space:
mode:
authorPaul Traina <pst@FreeBSD.org>1994-09-30 21:16:09 +0000
committerPaul Traina <pst@FreeBSD.org>1994-09-30 21:16:09 +0000
commit109f505bfbe08bf5e54056eed39a1a7577552b40 (patch)
tree4ba9e6c4da6be666470bc80549e10cbd326cc005 /sbin/rdisc/rdisc.c
parentbafea7d8a333cd2b2df3ec59b9f4641ab9d965c3 (diff)
downloadsrc-109f505bfbe08bf5e54056eed39a1a7577552b40.tar.gz
src-109f505bfbe08bf5e54056eed39a1a7577552b40.zip
Router Discovery from Sun Microsystems
Notes
Notes: svn path=/cvs2svn/branches/SUN/; revision=3248
Diffstat (limited to 'sbin/rdisc/rdisc.c')
-rw-r--r--sbin/rdisc/rdisc.c1576
1 files changed, 1576 insertions, 0 deletions
diff --git a/sbin/rdisc/rdisc.c b/sbin/rdisc/rdisc.c
new file mode 100644
index 000000000000..b9c674cbd7fa
--- /dev/null
+++ b/sbin/rdisc/rdisc.c
@@ -0,0 +1,1576 @@
+/*
+ * Rdisc (this program) was developed by Sun Microsystems, Inc. and is
+ * provided for unrestricted use provided that this legend is included on
+ * all tape media and as a part of the software program in whole or part.
+ * Users may copy or modify Rdisc without charge, and they may freely
+ * distribute it.
+ *
+ * RDISC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Rdisc is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY RDISC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+/*
+ * The next include contains all defs and structures for multicast
+ * that are not in SunOS 4.1.x. On a SunOS 4.1.x system none of this code
+ * is ever used because it does not support multicast
+ * Fraser Gardiner - Sun Microsystems Australia
+ */
+
+#include "rdisc.h"
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include <string.h>
+#include <syslog.h>
+
+/*
+ * TBD
+ * Use 255.255.255.255 for broadcasts - not the interface broadcast
+ * address.
+ */
+
+char *malloc();
+void exit();
+long gethostid();
+long random();
+time_t time();
+long strtol();
+
+#ifdef lint
+#define ALLIGN(ptr) (ptr ? 0 : 0)
+#else
+#define ALLIGN(ptr) (ptr)
+#endif
+
+#ifdef SYSV
+#define signal(s,f) sigset(s,f)
+#define random() rand()
+#endif
+
+void solicitor(), advertise(), pr_pack(), init();
+void bzero(), initlog(), logerr(), logtrace(), logdebug(), logperror();
+void age_table(), record_router(), add_route(), del_route(), rtioctl();
+int support_multicast(), sendbcast(), sendmcast(), sendbcastif(), sendmcastif();
+int ismulticast(), isbroadcast(), in_cksum(), is_directly_connected();
+static int join();
+
+#define ICMP_ROUTER_ADVERTISEMENT 9
+#define ICMP_ROUTER_SOLICITATION 10
+
+#define ALL_HOSTS_ADDRESS "224.0.0.1"
+#define ALL_ROUTERS_ADDRESS "224.0.0.2"
+
+#define MAXIFS 32
+
+/* For router advertisement */
+struct icmp_ra {
+ u_char icmp_type; /* type of message, see below */
+ u_char icmp_code; /* type sub code */
+ u_short icmp_cksum; /* ones complement cksum of struct */
+ u_char icmp_num_addrs;
+ u_char icmp_wpa; /* Words per address */
+ short icmp_lifetime;
+};
+
+struct icmp_ra_addr {
+ u_long addr;
+ u_long preference;
+};
+
+/* Router constants */
+#define MAX_INITIAL_ADVERT_INTERVAL 16
+#define MAX_INITIAL_ADVERTISEMENTS 3
+#define MAX_RESPONSE_DELAY 2 /* Not used */
+
+/* Host constants */
+#define MAX_SOLICITATIONS 3
+#define SOLICITATION_INTERVAL 3
+#define MAX_SOLICITATION_DELAY 1 /* Not used */
+
+#define IGNORE_PREFERENCE 0x80000000 /* Maximum negative */
+
+#define MAX_ADV_INT 600
+
+/* Statics */
+int num_interfaces;
+struct interface {
+ struct in_addr address; /* Used to identify the interface */
+ struct in_addr localaddr; /* Actual address if the interface */
+ int preference;
+ int flags;
+ struct in_addr bcastaddr;
+ struct in_addr remoteaddr;
+ struct in_addr netmask;
+};
+struct interface *interfaces;
+int interfaces_size; /* Number of elements in interfaces */
+
+
+#define MAXPACKET 4096 /* max packet size */
+u_char packet[MAXPACKET];
+
+/* fraser */
+int debugfile;
+char usage[] =
+"Usage: rdisc [-s] [-v] [-f] [-a] [send_address] [receive_address]\n\
+ rdisc -r [-v] [-p <preference>] [-T <secs>] \n\
+ [send_address] [receive_address]\n";
+
+
+int s; /* Socket file descriptor */
+struct sockaddr_in whereto;/* Address to send to */
+
+/* Common variables */
+int verbose = 0;
+int debug = 0;
+int trace = 0;
+int solicit = 0;
+int responder;
+int ntransmitted = 0;
+int nreceived = 0;
+int forever = 0; /* Never give up on host. If 0 defer fork until
+ * first response.
+ */
+
+/* Router variables */
+int max_adv_int = MAX_ADV_INT;
+int min_adv_int;
+int lifetime;
+int initial_advert_interval = MAX_INITIAL_ADVERT_INTERVAL;
+int initial_advertisements = MAX_INITIAL_ADVERTISEMENTS;
+int preference = 0; /* Setable with -p option */
+
+/* Host variables */
+int max_solicitations = MAX_SOLICITATIONS;
+unsigned int solicitation_interval = SOLICITATION_INTERVAL;
+int best_preference = 1; /* Set to record only the router(s) with the
+ best preference in the kernel. Not set
+ puts all routes in the kernel. */
+
+
+void finish(), catcher(), timer(), initifs();
+char *pr_name();
+
+static void prusage()
+{
+ (void) fprintf(stderr, usage);
+ exit(1);
+}
+
+void
+do_fork()
+{
+ int t;
+ long pid;
+
+ if (trace)
+ return;
+
+ if (pid =fork())
+ exit(0);
+
+ for (t = 0; t < 20; t++)
+ if (t != s)
+ (void) close(t);
+
+#ifndef SYSV
+ t = open("/dev/tty", 2);
+ if (t >= 0) {
+ (void) ioctl(t, TIOCNOTTY, (char *)0);
+ (void) close(t);
+ }
+#else
+ (void) setpgrp ();
+#endif
+ initlog();
+}
+
+/*
+ * M A I N
+ */
+char *sendaddress, *recvaddress;
+
+main(argc, argv)
+char *argv[];
+{
+#ifndef SYSV
+ struct sigvec sv;
+#endif
+ struct sockaddr_in from;
+ char **av = argv;
+ struct sockaddr_in *to = &whereto;
+ struct sockaddr_in joinaddr;
+ int val;
+
+ min_adv_int =( max_adv_int * 3 / 4);
+ lifetime = (3*max_adv_int);
+
+ argc--, av++;
+ while (argc > 0 && *av[0] == '-') {
+ while (*++av[0]) switch (*av[0]) {
+ case 'd':
+ debug = 1;
+ break;
+ case 't':
+ trace = 1;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 's':
+ solicit = 1;
+ break;
+ case 'r':
+ responder = 1;
+ break;
+ case 'a':
+ best_preference = 0;
+ break;
+ case 'b':
+ best_preference = 1;
+ break;
+ case 'f':
+ forever = 1;
+ break;
+ case 'T':
+ argc--, av++;
+ if (argc != 0) {
+ val = strtol(av[0], (char **)NULL, 0);
+ if (val < 4 || val > 1800) {
+ (void) fprintf(stderr,
+ "Bad Max Advertizement Interval\n");
+ exit(1);
+ }
+ max_adv_int = val;
+ min_adv_int =( max_adv_int * 3 / 4);
+ lifetime = (3*max_adv_int);
+ } else {
+ prusage();
+ /* NOTREACHED*/
+ }
+ goto next;
+ case 'p':
+ argc--, av++;
+ if (argc != 0) {
+ val = strtol(av[0], (char **)NULL, 0);
+ preference = val;
+ } else {
+ prusage();
+ /* NOTREACHED*/
+ }
+ goto next;
+ default:
+ prusage();
+ /* NOTREACHED*/
+ }
+ next:
+ argc--, av++;
+ }
+ if( argc < 1) {
+ if (support_multicast()) {
+ if (responder)
+ sendaddress = ALL_HOSTS_ADDRESS;
+ else
+ sendaddress = ALL_ROUTERS_ADDRESS;
+ } else
+ sendaddress = "255.255.255.255";
+ } else {
+ sendaddress = av[0];
+ argc--;
+ }
+
+ if (argc < 1) {
+ if (support_multicast()) {
+ if (responder)
+ recvaddress = ALL_ROUTERS_ADDRESS;
+ else
+ recvaddress = ALL_HOSTS_ADDRESS;
+ } else
+ recvaddress = "255.255.255.255";
+ } else {
+ recvaddress = av[0];
+ argc--;
+ }
+ if (argc != 0) {
+ (void) fprintf(stderr, "Extra paramaters\n");
+ prusage();
+ /* NOTREACHED */
+ }
+
+ if (solicit && responder) {
+ prusage();
+ /* NOTREACHED */
+ }
+
+ if (!(solicit && !forever)) {
+ do_fork();
+/*
+ * Added the next line to stop forking a second time
+ * Fraser Gardiner - Sun Microsystems Australia
+ */
+ forever = 1;
+ }
+
+ bzero( (char *)&whereto, sizeof(struct sockaddr_in) );
+ to->sin_family = AF_INET;
+ to->sin_addr.s_addr = inet_addr(sendaddress);
+
+ bzero( (char *)&joinaddr, sizeof(struct sockaddr_in) );
+ joinaddr.sin_family = AF_INET;
+ joinaddr.sin_addr.s_addr = inet_addr(recvaddress);
+
+ if (responder) {
+#ifdef SYSV
+ /* TBD fix this to be more random */
+ srand((u_int)time((time_t *)NULL));
+#else
+ srandom((int)gethostid());
+#endif
+ }
+
+ if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
+ logperror("socket");
+ exit(5);
+ }
+
+#ifdef SYSV
+ setvbuf( stdout, NULL, _IOLBF, 0 );
+#else
+ setlinebuf( stdout );
+#endif
+
+ (void) signal( SIGINT, finish );
+ (void) signal( SIGTERM, finish );
+ (void) signal( SIGHUP, initifs );
+
+ init();
+ if (join(s, &joinaddr) < 0) {
+ logerr("Failed joining addresses\n");
+ exit (2);
+ }
+
+
+#ifdef SYSV
+ (void) sigset(SIGALRM, timer);
+#else
+ /*
+ * Make sure that this signal actually interrupts (rather than
+ * restarts) the recvfrom call below.
+ */
+ sv.sv_handler = timer;
+ sv.sv_mask = 0;
+ sv.sv_flags = SV_INTERRUPT;
+ (void) sigvec(SIGALRM, &sv, (struct sigvec *)NULL);
+#endif
+ timer(); /* start things going */
+
+ for (;;) {
+ int len = sizeof (packet);
+ int fromlen = sizeof (from);
+ int cc;
+
+ if ( (cc=recvfrom(s, (char *)packet, len, 0,
+ (struct sockaddr *)&from, &fromlen)) < 0) {
+ if( errno == EINTR )
+ continue;
+ logperror("recvfrom");
+ continue;
+ }
+ pr_pack( (char *)packet, cc, &from );
+ }
+ /*NOTREACHED*/
+}
+
+#define TIMER_INTERVAL 3
+#define GETIFCONF_TIMER 30
+
+static left_until_advertise;
+
+/* Called every TIMER_INTERVAL */
+void timer()
+{
+ static time;
+ static left_until_getifconf;
+ static left_until_solicit;
+
+
+ time += TIMER_INTERVAL;
+
+ left_until_getifconf -= TIMER_INTERVAL;
+ left_until_advertise -= TIMER_INTERVAL;
+ left_until_solicit -= TIMER_INTERVAL;
+
+ if (left_until_getifconf < 0) {
+ initifs();
+ left_until_getifconf = GETIFCONF_TIMER;
+ }
+ if (responder && left_until_advertise <= 0) {
+ ntransmitted++;
+ advertise(&whereto);
+ if (ntransmitted < initial_advertisements)
+ left_until_advertise = initial_advert_interval;
+ else
+ left_until_advertise = min_adv_int +
+ ((max_adv_int - min_adv_int) *
+ (random() % 1000)/1000);
+ } else if (solicit && left_until_solicit <= 0) {
+ ntransmitted++;
+ solicitor(&whereto);
+ if (ntransmitted < max_solicitations)
+ left_until_solicit = solicitation_interval;
+ else {
+ solicit = 0;
+ if (!forever && nreceived == 0)
+ exit(5);
+ }
+ }
+ age_table(TIMER_INTERVAL);
+ (void) alarm(TIMER_INTERVAL);
+}
+
+/*
+ * S O L I C I T O R
+ *
+ * Compose and transmit an ICMP ROUTER SOLICITATION REQUEST packet.
+ * The IP packet will be added on by the kernel.
+ */
+void
+solicitor(sin)
+ struct sockaddr_in *sin;
+{
+ static u_char outpack[MAXPACKET];
+ register struct icmp *icp = (struct icmp *) ALLIGN(outpack);
+ int packetlen, i;
+
+ if (verbose) {
+ logtrace("Sending solicitation to %s\n",
+ pr_name(sin->sin_addr));
+ }
+ icp->icmp_type = ICMP_ROUTER_SOLICITATION;
+ icp->icmp_code = 0;
+ icp->icmp_cksum = 0;
+ icp->icmp_void = 0; /* Reserved */
+ packetlen = 8;
+
+ /* Compute ICMP checksum here */
+ icp->icmp_cksum = in_cksum( (u_short *)icp, packetlen );
+
+ if (isbroadcast(sin))
+ i = sendbcast(s, (char *)outpack, packetlen);
+ else if (ismulticast(sin))
+ i = sendmcast( s, (char *)outpack, packetlen, sin);
+ else
+ i = sendto( s, (char *)outpack, packetlen, 0,
+ (struct sockaddr *)sin, sizeof(struct sockaddr));
+
+ if( i < 0 || i != packetlen ) {
+ if( i<0 ) {
+ logperror("sendto");
+ }
+ logerr("wrote %s %d chars, ret=%d\n",
+ sendaddress, packetlen, i );
+ }
+}
+
+/*
+ * A V E R T I S E
+ *
+ * Compose and transmit an ICMP ROUTER ADVERTISEMENT packet.
+ * The IP packet will be added on by the kernel.
+ */
+void
+advertise(sin)
+ struct sockaddr_in *sin;
+{
+ static u_char outpack[MAXPACKET];
+ register struct icmp_ra *rap = (struct icmp_ra *) ALLIGN(outpack);
+ struct icmp_ra_addr *ap;
+ int packetlen, i, cc;
+
+ if (verbose) {
+ logtrace("Sending advertisement to %s\n",
+ pr_name(sin->sin_addr));
+ }
+
+ for (i = 0; i < num_interfaces; i++) {
+ rap->icmp_type = ICMP_ROUTER_ADVERTISEMENT;
+ rap->icmp_code = 0;
+ rap->icmp_cksum = 0;
+ rap->icmp_num_addrs = 0;
+ rap->icmp_wpa = 2;
+ rap->icmp_lifetime = lifetime;
+ packetlen = 8;
+
+ /*
+ * TODO handle multiple logical interfaces per
+ * physical interface. (increment with rap->icmp_wpa * 4 for
+ * each address.)
+ */
+ ap = (struct icmp_ra_addr *)ALLIGN(outpack + ICMP_MINLEN);
+ ap->addr = interfaces[i].localaddr.s_addr;
+ ap->preference = interfaces[i].preference;
+ packetlen += rap->icmp_wpa * 4;
+ rap->icmp_num_addrs++;
+
+
+ /* Compute ICMP checksum here */
+ rap->icmp_cksum = in_cksum( (u_short *)rap, packetlen );
+
+ if (isbroadcast(sin))
+ cc = sendbcastif(s, (char *)outpack, packetlen,
+ &interfaces[i]);
+ else if (ismulticast(sin))
+ cc = sendmcastif( s, (char *)outpack, packetlen, sin,
+ &interfaces[i]);
+ else {
+ struct interface *ifp = &interfaces[i];
+ /*
+ * Verify that the interface matches the destination
+ * address.
+ */
+ if ((sin->sin_addr.s_addr & ifp->netmask.s_addr) ==
+ (ifp->address.s_addr & ifp->netmask.s_addr)) {
+ if (debug) {
+ logdebug("Unicast to %s ",
+ pr_name(sin->sin_addr));
+ logdebug("on interface %s\n",
+ pr_name(ifp->address));
+ }
+ cc = sendto( s, (char *)outpack, packetlen, 0,
+ (struct sockaddr *)sin,
+ sizeof(struct sockaddr));
+ } else
+ cc = packetlen;
+ }
+ if( cc < 0 || cc != packetlen ) {
+ if (cc < 0) {
+ logperror("sendto");
+ } else {
+ logerr("wrote %s %d chars, ret=%d\n",
+ sendaddress, packetlen, cc );
+ }
+ }
+ }
+}
+
+/*
+ * P R _ T Y P E
+ *
+ * Convert an ICMP "type" field to a printable string.
+ */
+char *
+pr_type( t )
+register int t;
+{
+ static char *ttab[] = {
+ "Echo Reply",
+ "ICMP 1",
+ "ICMP 2",
+ "Dest Unreachable",
+ "Source Quench",
+ "Redirect",
+ "ICMP 6",
+ "ICMP 7",
+ "Echo",
+ "Router Advertise",
+ "Router Solicitation",
+ "Time Exceeded",
+ "Parameter Problem",
+ "Timestamp",
+ "Timestamp Reply",
+ "Info Request",
+ "Info Reply",
+ "Netmask Request",
+ "Netmask Reply"
+ };
+
+ if ( t < 0 || t > 16 )
+ return("OUT-OF-RANGE");
+
+ return(ttab[t]);
+}
+
+/*
+ * P R _ N A M E
+ *
+ * Return a string name for the given IP address.
+ */
+char *pr_name(addr)
+ struct in_addr addr;
+{
+ char *inet_ntoa();
+ struct hostent *phe;
+ static char buf[256];
+
+ phe = gethostbyaddr((char *)&addr.s_addr, 4, AF_INET);
+ if (phe == NULL)
+ return( inet_ntoa(addr));
+ (void) sprintf(buf, "%s (%s)", phe->h_name, inet_ntoa(addr));
+ return(buf);
+}
+
+/*
+ * P R _ P A C K
+ *
+ * Print out the packet, if it came from us. This logic is necessary
+ * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
+ * which arrive ('tis only fair). This permits multiple copies of this
+ * program to be run without having intermingled output (or statistics!).
+ */
+void
+pr_pack( buf, cc, from )
+char *buf;
+int cc;
+struct sockaddr_in *from;
+{
+ struct ip *ip;
+ register struct icmp *icp;
+ register int i;
+ int hlen;
+
+ ip = (struct ip *) ALLIGN(buf);
+ hlen = ip->ip_hl << 2;
+ if (cc < hlen + ICMP_MINLEN) {
+ if (verbose)
+ logtrace("packet too short (%d bytes) from %s\n", cc,
+ pr_name(from->sin_addr));
+ return;
+ }
+ cc -= hlen;
+ icp = (struct icmp *)ALLIGN(buf + hlen);
+ if (ip->ip_p == 0) {
+ /*
+ * Assume that we are running on a pre-4.3BSD system
+ * such as SunOS before 4.0
+ */
+ icp = (struct icmp *)ALLIGN(buf);
+ }
+ switch (icp->icmp_type) {
+ case ICMP_ROUTER_ADVERTISEMENT: {
+ struct icmp_ra *rap = (struct icmp_ra *)ALLIGN(icp);
+ struct icmp_ra_addr *ap;
+
+ if (responder)
+ break;
+
+ /* TBD verify that the link is multicast or broadcast */
+ /* XXX Find out the link it came in over? */
+#ifdef notdef
+ if (debug) {
+ logdebug("ROUTER_ADVERTISEMENT: \n");
+ pr_hex(buf+hlen, cc);
+ }
+#endif notdef
+ if (in_cksum((u_short *)ALLIGN(buf+hlen), cc)) {
+ if (verbose)
+ logtrace("ICMP %s from %s: Bad checksum\n",
+ pr_type((int)rap->icmp_type),
+ pr_name(from->sin_addr));
+ return;
+ }
+ if (rap->icmp_code != 0) {
+ if (verbose)
+ logtrace("ICMP %s from %s: Code = %d\n",
+ pr_type((int)rap->icmp_type),
+ pr_name(from->sin_addr),
+ rap->icmp_code);
+ return;
+ }
+ if (rap->icmp_num_addrs < 1) {
+ if (verbose)
+ logtrace("ICMP %s from %s: No addresses\n",
+ pr_type((int)rap->icmp_type),
+ pr_name(from->sin_addr));
+ return;
+ }
+ if (rap->icmp_wpa < 2) {
+ if (verbose)
+ logtrace("ICMP %s from %s: Words/addr = %d\n",
+ pr_type((int)rap->icmp_type),
+ pr_name(from->sin_addr),
+ rap->icmp_wpa);
+ return;
+ }
+ if ((unsigned)cc <
+ ICMP_MINLEN + rap->icmp_num_addrs * rap->icmp_wpa * 4) {
+ if (verbose)
+ logtrace("ICMP %s from %s: Too short %d, %d\n",
+ pr_type((int)rap->icmp_type),
+ pr_name(from->sin_addr),
+ cc,
+ ICMP_MINLEN +
+ rap->icmp_num_addrs * rap->icmp_wpa * 4);
+ return;
+ }
+ if (rap->icmp_lifetime < 4) {
+ if (verbose)
+ logtrace("ICMP %s from %s: Lifetime = %d\n",
+ pr_type((int)rap->icmp_type),
+ pr_name(from->sin_addr),
+ rap->icmp_lifetime);
+ return;
+ }
+ if (verbose)
+ logtrace("ICMP %s from %s, lifetime %d\n",
+ pr_type((int)rap->icmp_type),
+ pr_name(from->sin_addr),
+ rap->icmp_lifetime);
+
+ /* Check that at least one router address is a neighboor
+ * on the arriving link.
+ */
+ for (i = 0; (unsigned)i < rap->icmp_num_addrs; i++) {
+ struct in_addr ina;
+ ap = (struct icmp_ra_addr *)
+ ALLIGN(buf + hlen + ICMP_MINLEN +
+ i * rap->icmp_wpa * 4);
+ ina.s_addr = ntohl(ap->addr);
+ if (verbose)
+ logtrace("\taddress %s, preference 0x%x\n",
+ pr_name(ina),
+ ntohl(ap->preference));
+ if (!responder) {
+ if (is_directly_connected(ina))
+ record_router(ina,
+ (long)ntohl(ap->preference),
+ rap->icmp_lifetime);
+ }
+ }
+ nreceived++;
+ if (!forever) {
+ do_fork();
+ forever = 1;
+/*
+ * The next line was added so that the alarm is set for the new procces
+ * Fraser Gardiner Sun Microsystems Australia
+ */
+ (void) alarm(TIMER_INTERVAL);
+ }
+ break;
+ }
+
+ case ICMP_ROUTER_SOLICITATION: {
+ struct sockaddr_in sin;
+
+ if (!responder)
+ break;
+
+ /* TBD verify that the link is multicast or broadcast */
+ /* XXX Find out the link it came in over? */
+#ifdef notdef
+ if (debug) {
+ logdebug("ROUTER_SOLICITATION: \n");
+ pr_hex(buf+hlen, cc);
+ }
+#endif notdef
+ if (in_cksum((u_short *)ALLIGN(buf+hlen), cc)) {
+ if (verbose)
+ logtrace("ICMP %s from %s: Bad checksum\n",
+ pr_type((int)icp->icmp_type),
+ pr_name(from->sin_addr));
+ return;
+ }
+ if (icp->icmp_code != 0) {
+ if (verbose)
+ logtrace("ICMP %s from %s: Code = %d\n",
+ pr_type((int)icp->icmp_type),
+ pr_name(from->sin_addr),
+ icp->icmp_code);
+ return;
+ }
+
+ if (cc < ICMP_MINLEN) {
+ if (verbose)
+ logtrace("ICMP %s from %s: Too short %d, %d\n",
+ pr_type((int)icp->icmp_type),
+ pr_name(from->sin_addr),
+ cc,
+ ICMP_MINLEN);
+ return;
+ }
+
+ if (verbose)
+ logtrace("ICMP %s from %s\n",
+ pr_type((int)icp->icmp_type),
+ pr_name(from->sin_addr));
+
+ if (!responder)
+ break;
+
+ /* Check that ip_src is either a neighboor
+ * on the arriving link or 0.
+ */
+ sin.sin_family = AF_INET;
+ if (ip->ip_src.s_addr == 0) {
+ /* If it was sent to the broadcast address we respond
+ * to the broadcast address.
+ */
+ if (IN_CLASSD(ip->ip_dst.s_addr))
+ sin.sin_addr.s_addr = INADDR_ALLHOSTS_GROUP;
+ else
+ sin.sin_addr.s_addr = INADDR_BROADCAST;
+ /* Restart the timer when we broadcast */
+ left_until_advertise = min_adv_int +
+ ((max_adv_int - min_adv_int)
+ * (random() % 1000)/1000);
+ }
+ else {
+ if (!is_directly_connected(ip->ip_src)) {
+ if (verbose)
+ logtrace("ICMP %s from %s: source not directly connected\n",
+ pr_type((int)icp->icmp_type),
+ pr_name(from->sin_addr));
+ break;
+ }
+ sin.sin_addr.s_addr = ip->ip_src.s_addr;
+ }
+ nreceived++;
+ ntransmitted++;
+ advertise(&sin);
+ break;
+ }
+ }
+}
+
+
+/*
+ * I N _ C K S U M
+ *
+ * Checksum routine for Internet Protocol family headers (C Version)
+ *
+ */
+in_cksum(addr, len)
+u_short *addr;
+int len;
+{
+ register int nleft = len;
+ register u_short *w = addr;
+ register u_short answer;
+ u_short odd_byte = 0;
+ register int sum = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum),
+ * we add sequential 16 bit words to it, and at the end, fold
+ * back all the carry bits from the top 16 bits into the lower
+ * 16 bits.
+ */
+ while( nleft > 1 ) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if( nleft == 1 ) {
+ *(u_char *)(&odd_byte) = *(u_char *)w;
+ sum += odd_byte;
+ }
+
+ /*
+ * add back carry outs from top 16 bits to low 16 bits
+ */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return (answer);
+}
+
+/*
+ * F I N I S H
+ *
+ * Print out statistics, and give up.
+ * Heavily buffered STDIO is used here, so that all the statistics
+ * will be written with 1 sys-write call. This is nice when more
+ * than one copy of the program is running on a terminal; it prevents
+ * the statistics output from becomming intermingled.
+ */
+void
+finish()
+{
+ if (responder) {
+ int i;
+
+ /* Send out a packet with a preference so that all
+ * hosts will know that we are dead.
+ */
+ logerr("terminated\n");
+ for (i = 0; i < num_interfaces; i++)
+ interfaces[i].preference = IGNORE_PREFERENCE;
+ ntransmitted++;
+ advertise(&whereto);
+ }
+ logtrace("\n----%s rdisc Statistics----\n", sendaddress );
+ logtrace("%d packets transmitted, ", ntransmitted );
+ logtrace("%d packets received, ", nreceived );
+ logtrace("\n");
+ (void) fflush(stdout);
+ exit(0);
+}
+
+#include <ctype.h>
+
+#ifdef notdef
+pr_hex(data, len)
+int len;
+unsigned char *data;
+{
+ FILE *out;
+
+ out = stdout;
+
+ while (len) {
+ register int i;
+ char charstring[17];
+
+ (void)strcpy(charstring," "); /* 16 spaces */
+ for (i = 0; i < 16; i++) {
+ /* output the bytes one at a time,
+ * not going pas "len" bytes
+ */
+ if (len) {
+ char ch = *data & 0x7f; /* strip parity */
+ if (!isprint((u_char)ch))
+ ch = ' '; /* ensure printable */
+ charstring[i] = ch;
+ (void) fprintf(out,"%02x ",*data++);
+ len--;
+ } else
+ (void) fprintf(out," ");
+ if (i==7)
+ (void) fprintf(out," ");
+ }
+
+ (void) fprintf(out," *%s*\n",charstring);
+ }
+}
+#endif notdef
+
+isbroadcast(sin)
+ struct sockaddr_in *sin;
+{
+ return (sin->sin_addr.s_addr == INADDR_BROADCAST);
+}
+
+ismulticast(sin)
+ struct sockaddr_in *sin;
+{
+ return (IN_CLASSD(sin->sin_addr.s_addr));
+}
+
+/* From libc/rpc/pmap_rmt.c */
+
+int
+sendbcast(s, packet, packetlen)
+ int s;
+ char *packet;
+ int packetlen;
+{
+ int i, cc;
+
+ for (i = 0; i < num_interfaces; i++) {
+ if ((interfaces[i].flags & IFF_BROADCAST) == 0)
+ continue;
+ cc = sendbcastif(s, packet, packetlen, &interfaces[i]);
+ if (cc!= packetlen) {
+ return (cc);
+ }
+ }
+ return (packetlen);
+}
+
+int
+sendbcastif(s, packet, packetlen, ifp)
+ int s;
+ char *packet;
+ int packetlen;
+ struct interface *ifp;
+{
+ int cc;
+ struct sockaddr_in baddr;
+
+ baddr.sin_family = AF_INET;
+
+ if ((ifp->flags & IFF_BROADCAST) == 0)
+ return (packetlen);
+
+ baddr.sin_addr = ifp->bcastaddr;
+ if (debug)
+ logdebug("Broadcast to %s\n",
+ pr_name(baddr.sin_addr));
+ cc = sendto(s, packet, packetlen, 0,
+ (struct sockaddr *)&baddr, sizeof (struct sockaddr));
+ if (cc!= packetlen) {
+ logperror("sendbcast: sendto");
+ logerr("Cannot send broadcast packet to %s\n",
+ pr_name(baddr.sin_addr));
+ }
+ return (cc);
+}
+
+int
+sendmcast(s, packet, packetlen, sin)
+ int s;
+ char *packet;
+ int packetlen;
+ struct sockaddr_in *sin;
+{
+ int i, cc;
+
+ for (i = 0; i < num_interfaces; i++) {
+ if ((interfaces[i].flags & IFF_MULTICAST) == 0)
+ continue;
+ cc = sendmcastif(s, packet, packetlen, sin, &interfaces[i]);
+ if (cc!= packetlen) {
+ return (cc);
+ }
+ }
+ return (packetlen);
+}
+
+int
+sendmcastif(s, packet, packetlen, sin, ifp)
+ int s;
+ char *packet;
+ int packetlen;
+ struct sockaddr_in *sin;
+ struct interface *ifp;
+{
+ int cc;
+ struct sockaddr_in ifaddr;
+
+ ifaddr.sin_family = AF_INET;
+
+ if ((ifp->flags & IFF_MULTICAST) == 0)
+ return (packetlen);
+
+ ifaddr.sin_addr = ifp->address;
+ if (debug)
+ logdebug("Multicast to interface %s\n",
+ pr_name(ifaddr.sin_addr));
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
+ (char *)&ifaddr.sin_addr,
+ sizeof(ifaddr.sin_addr)) < 0) {
+ logperror("setsockopt (IP_MULTICAST_IF)");
+ logerr("Cannot send multicast packet over interface %s\n",
+ pr_name(ifaddr.sin_addr));
+ return (-1);
+ }
+ cc = sendto(s, packet, packetlen, 0,
+ (struct sockaddr *)sin, sizeof (struct sockaddr));
+ if (cc!= packetlen) {
+ logperror("sendmcast: sendto");
+ logerr("Cannot send multicast packet over interface %s\n",
+ pr_name(ifaddr.sin_addr));
+ }
+ return (cc);
+}
+
+void
+init()
+{
+ int i;
+
+ initifs();
+ for (i = 0; i < interfaces_size; i++)
+ interfaces[i].preference = preference;
+}
+
+void
+initifs()
+{
+ int sock;
+ struct ifconf ifc;
+ struct ifreq ifreq, *ifr;
+ struct sockaddr_in *sin;
+ int n, i;
+ char *buf;
+ int numifs;
+ unsigned bufsize;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ logperror("initifs: socket");
+ return;
+ }
+#ifdef SIOCGIFNUM
+ if (ioctl(sock, SIOCGIFNUM, (char *)&numifs) < 0) {
+ numifs = MAXIFS;
+ }
+#else
+ numifs = MAXIFS;
+#endif
+ bufsize = numifs * sizeof(struct ifreq);
+ buf = (char *)malloc(bufsize);
+ if (buf == NULL) {
+ logerr("out of memory\n");
+ (void) close(sock);
+ return;
+ }
+ if (interfaces)
+ interfaces = (struct interface *)ALLIGN(realloc((char *)interfaces,
+ numifs * sizeof(struct interface)));
+ else
+ interfaces = (struct interface *)ALLIGN(malloc(numifs *
+ sizeof(struct interface)));
+ if (interfaces == NULL) {
+ logerr("out of memory\n");
+ (void) close(sock);
+ (void) free(buf);
+ return;
+ }
+ interfaces_size = numifs;
+
+ ifc.ifc_len = bufsize;
+ ifc.ifc_buf = buf;
+ if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
+ logperror("initifs: ioctl (get interface configuration)");
+ (void) close(sock);
+ (void) free(buf);
+ return;
+ }
+ ifr = ifc.ifc_req;
+ for (i = 0, n = ifc.ifc_len/sizeof (struct ifreq); n > 0; n--, ifr++) {
+ ifreq = *ifr;
+ if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
+ logperror("initifs: ioctl (get interface flags)");
+ continue;
+ }
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+ if ((ifreq.ifr_flags & IFF_UP) == 0)
+ continue;
+ if (ifreq.ifr_flags & IFF_LOOPBACK)
+ continue;
+ if ((ifreq.ifr_flags & (IFF_MULTICAST | IFF_BROADCAST)) == 0)
+ continue;
+ sin = (struct sockaddr_in *)ALLIGN(&ifr->ifr_addr);
+ interfaces[i].localaddr = sin->sin_addr;
+ interfaces[i].flags = ifreq.ifr_flags;
+ interfaces[i].netmask.s_addr = (unsigned long)0xffffffff;
+ if (ifreq.ifr_flags & IFF_POINTOPOINT) {
+ if (ioctl(sock, SIOCGIFDSTADDR, (char *)&ifreq) < 0) {
+ logperror("initifs: ioctl (get destination addr)");
+ continue;
+ }
+ sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr);
+ /* A pt-pt link is identified by the remote address */
+ interfaces[i].address = sin->sin_addr;
+ interfaces[i].remoteaddr = sin->sin_addr;
+ /* Simulate broadcast for pt-pt */
+ interfaces[i].bcastaddr = sin->sin_addr;
+ interfaces[i].flags |= IFF_BROADCAST;
+ } else {
+ /* Non pt-pt links are identified by the local address */
+ interfaces[i].address = interfaces[i].localaddr;
+ interfaces[i].remoteaddr = interfaces[i].address;
+ if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
+ logperror("initifs: ioctl (get netmask)");
+ continue;
+ }
+ sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr);
+ interfaces[i].netmask = sin->sin_addr;
+ if (ifreq.ifr_flags & IFF_BROADCAST) {
+ if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
+ logperror("initifs: ioctl (get broadcast address)");
+ continue;
+ }
+ sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr);
+ interfaces[i].bcastaddr = sin->sin_addr;
+ }
+ }
+#ifdef notdef
+ if (debug)
+ logdebug("Found interface %s, flags 0x%x\n",
+ pr_name(interfaces[i].localaddr),
+ interfaces[i].flags);
+#endif
+ i++;
+ }
+ num_interfaces = i;
+#ifdef notdef
+ if (debug)
+ logdebug("Found %d interfaces\n", num_interfaces);
+#endif
+ (void) close(sock);
+ (void) free(buf);
+}
+
+static int
+join(sock, sin)
+ int sock;
+ struct sockaddr_in *sin;
+{
+ int i;
+ struct ip_mreq mreq;
+
+ if (isbroadcast(sin))
+ return (0);
+
+ mreq.imr_multiaddr = sin->sin_addr;
+ for (i = 0; i < num_interfaces; i++) {
+ mreq.imr_interface = interfaces[i].address;
+
+ if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq)) < 0) {
+ logperror("setsockopt (IP_ADD_MEMBERSHIP)");
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+int support_multicast()
+{
+ int sock;
+ u_char ttl = 1;
+
+ sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0) {
+ logperror("support_multicast: socket");
+ return (0);
+ }
+
+ if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL,
+ (char *)&ttl, sizeof(ttl)) < 0) {
+ (void) close(sock);
+ return (0);
+ }
+ (void) close(sock);
+ return (1);
+}
+
+int
+is_directly_connected(in)
+ struct in_addr in;
+{
+ int i;
+
+ for (i = 0; i < num_interfaces; i++) {
+ /* Check that the subnetwork numbers match */
+
+ if ((in.s_addr & interfaces[i].netmask.s_addr ) ==
+ (interfaces[i].remoteaddr.s_addr & interfaces[i].netmask.s_addr))
+ return (1);
+ }
+ return (0);
+}
+
+
+/*
+ * TABLES
+ */
+struct table {
+ struct in_addr router;
+ int preference;
+ int remaining_time;
+ int in_kernel;
+ struct table *next;
+};
+
+struct table *table;
+
+struct table *
+find_router(addr)
+ struct in_addr addr;
+{
+ struct table *tp;
+
+ tp = table;
+ while (tp) {
+ if (tp->router.s_addr == addr.s_addr)
+ return (tp);
+ tp = tp->next;
+ }
+ return (NULL);
+}
+
+int max_preference()
+{
+ struct table *tp;
+ int max = (int)IGNORE_PREFERENCE;
+
+ tp = table;
+ while (tp) {
+ if (tp->preference > max)
+ max = tp->preference;
+ tp = tp->next;
+ }
+ return (max);
+}
+
+
+/* Note: this might leave the kernel with no default route for a short time. */
+void
+age_table(time)
+ int time;
+{
+ struct table **tpp, *tp;
+ int recalculate_max = 0;
+ int max = max_preference();
+
+ tpp = &table;
+ while (*tpp != NULL) {
+ tp = *tpp;
+ tp->remaining_time -= time;
+ if (tp->remaining_time <= 0) {
+ *tpp = tp->next;
+ if (tp->in_kernel)
+ del_route(tp->router);
+ if (best_preference &&
+ tp->preference == max)
+ recalculate_max++;
+ free((char *)tp);
+ } else {
+ tpp = &tp->next;
+ }
+ }
+ if (recalculate_max) {
+ int max = max_preference();
+
+ if (max != IGNORE_PREFERENCE) {
+ tp = table;
+ while (tp) {
+ if (tp->preference == max && !tp->in_kernel) {
+ add_route(tp->router);
+ tp->in_kernel++;
+ }
+ tp = tp->next;
+ }
+ }
+ }
+}
+
+void
+record_router(router, preference, ttl)
+ struct in_addr router;
+ long preference;
+ int ttl;
+{
+ struct table *tp;
+ int old_max = max_preference();
+ int changed_up = 0; /* max preference could have increased */
+ int changed_down = 0; /* max preference could have decreased */
+
+ if (debug)
+ logdebug("Recording %s, preference 0x%x\n",
+ pr_name(router),
+ preference);
+ tp = find_router(router);
+ if (tp) {
+ if (tp->preference > preference &&
+ tp->preference == old_max)
+ changed_down++;
+ else if (preference > tp->preference)
+ changed_up++;
+ tp->preference = preference;
+ tp->remaining_time = ttl;
+ } else {
+ if (preference > old_max)
+ changed_up++;
+ tp = (struct table *)ALLIGN(malloc(sizeof(struct table)));
+ if (tp == NULL) {
+ logerr("Out of memory\n");
+ return;
+ }
+ tp->router = router;
+ tp->preference = preference;
+ tp->remaining_time = ttl;
+ tp->in_kernel = 0;
+ tp->next = table;
+ table = tp;
+ }
+ if (!tp->in_kernel &&
+ (!best_preference || tp->preference == max_preference()) &&
+ tp->preference != IGNORE_PREFERENCE) {
+ add_route(tp->router);
+ tp->in_kernel++;
+ }
+ if (tp->preference == IGNORE_PREFERENCE && tp->in_kernel) {
+ del_route(tp->router);
+ tp->in_kernel = 0;
+ }
+ if (best_preference && changed_down) {
+ /* Check if we should add routes */
+ int new_max = max_preference();
+ if (new_max != IGNORE_PREFERENCE) {
+ tp = table;
+ while (tp) {
+ if (tp->preference == new_max &&
+ !tp->in_kernel) {
+ add_route(tp->router);
+ tp->in_kernel++;
+ }
+ tp = tp->next;
+ }
+ }
+ }
+ if (best_preference && (changed_up || changed_down)) {
+ /* Check if we should remove routes already in the kernel */
+ int new_max = max_preference();
+ tp = table;
+ while (tp) {
+ if (tp->preference < new_max && tp->in_kernel) {
+ del_route(tp->router);
+ tp->in_kernel = 0;
+ }
+ tp = tp->next;
+ }
+ }
+}
+
+
+#include <net/route.h>
+
+void
+add_route(addr)
+ struct in_addr addr;
+{
+ if (debug)
+ logdebug("Add default route to %s\n", pr_name(addr));
+ rtioctl(addr, SIOCADDRT);
+}
+
+void
+del_route(addr)
+ struct in_addr addr;
+{
+ if (debug)
+ logdebug("Delete default route to %s\n", pr_name(addr));
+ rtioctl(addr, SIOCDELRT);
+}
+
+void
+rtioctl(addr, op)
+ struct in_addr addr;
+ int op;
+{
+ int sock;
+ struct rtentry rt;
+ struct sockaddr_in *sin;
+ bzero((char *)&rt, sizeof(struct rtentry));
+ rt.rt_dst.sa_family = AF_INET;
+ rt.rt_gateway.sa_family = AF_INET;
+ sin = (struct sockaddr_in *)ALLIGN(&rt.rt_gateway);
+ sin->sin_addr = addr;
+ rt.rt_flags = RTF_UP | RTF_GATEWAY;
+
+ sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0) {
+ logperror("rtioctl: socket");
+ return;
+ }
+ if (ioctl(sock, op, (char *)&rt) < 0) {
+ if (!(op == SIOCADDRT && errno == EEXIST))
+ logperror("ioctl (add/delete route)");
+ }
+ (void) close(sock);
+}
+
+
+
+/*
+ * LOGGER
+ */
+
+
+static logging = 0;
+
+void
+initlog()
+{
+ logging++;
+ openlog("in.rdiscd", LOG_PID | LOG_CONS, LOG_DAEMON);
+}
+
+/* VARARGS1 */
+void
+logerr(fmt, a,b,c,d,e,f,g,h)
+ char *fmt;
+{
+ if (logging)
+ syslog(LOG_ERR, fmt, a,b,c,d,e,f,g,h);
+ else
+ (void) fprintf(stderr, fmt, a,b,c,d,e,f,g,h);
+}
+
+/* VARARGS1 */
+void
+logtrace(fmt, a,b,c,d,e,f,g,h)
+ char *fmt;
+{
+ if (logging)
+ syslog(LOG_INFO, fmt, a,b,c,d,e,f,g,h);
+ else
+ (void) fprintf(stdout, fmt, a,b,c,d,e,f,g,h);
+}
+
+/* VARARGS1 */
+void
+logdebug(fmt, a,b,c,d,e,f,g,h)
+ char *fmt;
+{
+ if (logging)
+ syslog(LOG_DEBUG, fmt, a,b,c,d,e,f,g,h);
+ else
+ (void) fprintf(stdout, fmt, a,b,c,d,e,f,g,h);
+}
+
+extern char *sys_errlist[];
+extern int errno;
+
+void
+logperror(str)
+ char *str;
+{
+ if (logging)
+ syslog(LOG_ERR, "%s: %s\n", str, sys_errlist[errno]);
+ else
+ (void) fprintf(stderr, "%s: %s\n", str, sys_errlist[errno]);
+}
+
+
+#ifdef SYSV
+void
+bzero(cp, len)
+ char *cp;
+ int len;
+{
+ while (--len>=0)
+ *cp++ = 0;
+}
+#endif
+