summaryrefslogtreecommitdiff
path: root/sbin/rdisc
diff options
context:
space:
mode:
authorThorsten Lockert <tholo@cvs.openbsd.org>1996-08-02 23:39:09 +0000
committerThorsten Lockert <tholo@cvs.openbsd.org>1996-08-02 23:39:09 +0000
commit5f1927683736d7e95dabfb4f625f4a4eee4bd657 (patch)
treeedc47633ce0a7591baf6d50a431a7d9d2128e8fd /sbin/rdisc
parent62a91e93b27de03a1252d6167ebbd4ff8570f5fb (diff)
Router Discovery daemon; from Sun via FreeBSD
Diffstat (limited to 'sbin/rdisc')
-rw-r--r--sbin/rdisc/Makefile7
-rw-r--r--sbin/rdisc/rdisc.8191
-rw-r--r--sbin/rdisc/rdisc.c1614
3 files changed, 1812 insertions, 0 deletions
diff --git a/sbin/rdisc/Makefile b/sbin/rdisc/Makefile
new file mode 100644
index 00000000000..9ee24d378ae
--- /dev/null
+++ b/sbin/rdisc/Makefile
@@ -0,0 +1,7 @@
+# $OpenBSD: Makefile,v 1.1 1996/08/02 23:39:08 tholo Exp $
+
+PROG= rdisc
+MAN= rdisc.8
+BINMODE=555
+
+.include <bsd.prog.mk>
diff --git a/sbin/rdisc/rdisc.8 b/sbin/rdisc/rdisc.8
new file mode 100644
index 00000000000..15a128516b7
--- /dev/null
+++ b/sbin/rdisc/rdisc.8
@@ -0,0 +1,191 @@
+'\"macro stdmacro
+.\"
+.\" $OpenBSD: rdisc.8,v 1.1 1996/08/02 23:39:08 tholo Exp $
+.\"
+.\" 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
+.\"
+.nr X
+.TH rdisc 8C "22 Jan 1996"
+.SH NAME
+rdisc \- network router discovery daemon
+.SH SYNOPSIS
+.B rdisc
+[
+.B \-sfa
+]
+.LP
+.B rdisc
+.B \-r
+[
+.BI \-p " preference"
+] [
+.BI \-T " interval"
+]
+.SH DESCRIPTION
+.IX "rdisc" "" "\fLrdisc\fP \(em ICMP router discovery daemon"
+.IX "Internet" "ICMP router discovery daemon" "" "ICMP router discovery daemon \(em \fLrdisc\fP"
+.IX "Internet Control Message Protocol" "See ICMP"
+.IX "ICMP" "router discovery daemon" "" "router discovery daemon \(em \fLrdisc\fP"
+.LP
+\f4rdisc\f1
+implements the
+.SM ICMP
+router discover protocol.
+The first form of the command is used on hosts and the
+second form is used on routers.
+On a host \f4rdisc\f1 is invoked at
+boot time to populate the network routing tables with default routes.
+On a router it is also invoked at boot time in order to start
+advertising the router to all the hosts.
+.SS "Host (First Form)"
+.PP
+On a host \f4rdisc\f1
+listens on the \f4ALL_HOSTS\f1 (224.0.0.1) multicast address
+for \f4ROUTER_ADVERTISE\f1 messages from routers. The received
+messages are handled by first ignoring
+those listed router addresses with which the host does not share a network.
+Among the remaining addresses the ones with the highest preference are selected
+as default routers and a default route is entered in the kernel routing
+table for each one of them.
+.LP
+Optionally, \f4rdisc\f1 can avoid waiting for routers to announce
+themselves by sending out a few \f4ROUTER_SOLICITATION\f1 messages
+to the \f4ALL_ROUTERS\f1 (224.0.0.2) multicast address when it is started.
+.LP
+A timer is associated with each router address and the address will
+no longer be considered for inclusion in the the routing tables if the
+timer expires before a new
+.I advertise
+message is received from the router.
+The address will also be excluded from consideration if the host receives an
+.I advertise
+message with the preference being maximally negative.
+.SS "Router (Second Form)"
+.PP
+When
+\f4rdisc\f1
+is started on a router, it uses the
+\f4SIOCGIFCONF\f1
+.BR ioctl (2)
+to find the interfaces configured into the system and it starts
+listening on the \f4ALL_ROUTERS\f1 multicast address on all the interfaces
+that support multicast.
+It sends out
+.I advertise
+messages to the \f4ALL_HOSTS\f1 multicast address advertising all its
+.BM IP
+addresses.
+A few initial
+.I advertise
+messages are sent out during the first 30 seconds and after that it will
+transmit
+.I advertise
+messages approximately every 600 seconds.
+.LP
+When \f4rdisc\f1
+receives a
+.I solicitation
+message it sends an
+.I advertise
+message to the host that sent the
+.I solicitation
+message.
+.LP
+When
+.B rdisc
+is terminated by a signal it sends out an
+.I advertise
+message with the preference being maximally negative.
+.br
+.ne 1i
+.SH OPTIONS
+.TP 15
+.B \-a
+Accept all routers independently of the preference they have in their
+.I advertise
+messages.
+Normally
+.B rdisc
+only accepts (and enters in the kernel routing tables)
+the router or routers with the highest preference.
+.TP
+.B \-f
+Run
+.B rdisc
+forever even if no routers are found.
+Normally
+.B rdisc
+gives up if it has not received any
+.I advertise
+message after after soliciting three times,
+in which case it exits with a non-zero exit code.
+If
+.B \-f
+is not specified in the first form then
+.B \-s
+must be specified.
+.TP
+.BI \-p " preference"
+Set the preference transmitted in the
+.I solicitation
+messages.
+The default is zero.
+.TP
+.B \-r
+Act as a router as opposed to a host.
+.TP
+.B \-s
+Send three
+.I solicitation
+messages initially to quickly discover the routers
+when the system is booted.
+When
+.B \-s
+is specified
+.B rdisc
+exits with a non-zero exit code if it can not find any routers.
+This can be overridden with the
+.B \-f
+option.
+.TP
+.BI \-T " interval"
+Set the interval between transmitting the
+.I advertise
+messages.
+The default time is 600 seconds.
+.SH "SEE ALSO"
+.BR routed (8)
+.BR icmp (4),
+.BR inet (4)
+.LP
+Deering, S.E.,ed
+.RI `` "ICMP Router Discovery Messages" ,''
+.SM RFC
+1256, Network Information Center,
+.SM SRI
+International, Menlo Park, Calif.,
+September 1991.
diff --git a/sbin/rdisc/rdisc.c b/sbin/rdisc/rdisc.c
new file mode 100644
index 00000000000..ede60d3b63b
--- /dev/null
+++ b/sbin/rdisc/rdisc.c
@@ -0,0 +1,1614 @@
+/*
+ * $OpenBSD: rdisc.c,v 1.1 1996/08/02 23:39:08 tholo Exp $
+ *
+ * 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>
+
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include <string.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <sys/cdefs.h>
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+/*
+ * TBD
+ * Use 255.255.255.255 for broadcasts - not the interface broadcast
+ * address.
+ */
+
+#ifdef lint
+#define ALLIGN(ptr) (ptr ? 0 : 0)
+#else
+#define ALLIGN(ptr) (ptr)
+#endif
+
+
+#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
+
+/* 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];
+
+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. */
+
+/* Prototypes */
+void do_fork __P((void));
+int main __P((int, char **));
+void timer __P((int));
+void solicitor __P((struct sockaddr_in *));
+void advertise __P((struct sockaddr_in *));
+char * pr_type __P((int));
+char * pr_name __P((struct in_addr));
+void pr_pack __P((char *, int, struct sockaddr_in *));
+int in_cksum __P((u_short *, int));
+void finish __P((int));
+int isbroadcast __P((struct sockaddr_in *));
+int ismulticast __P((struct sockaddr_in *));
+int sendbcast __P((int, char *, int));
+int sendbcastif __P((int, char *, int, struct interface *));
+int sendmcast __P((int, char *, int, struct sockaddr_in *));
+int sendmcastif __P((int, char *, int, struct sockaddr_in *, struct interface *));
+void init __P((void));
+void initifs __P((int));
+int support_multicast __P((void));
+int is_directly_connected __P((struct in_addr));
+struct table * find_router __P((struct in_addr));
+int max_preference __P((void));
+void age_table __P((int));
+void record_router __P((struct in_addr, long, int));
+void add_route __P((struct in_addr));
+void del_route __P((struct in_addr));
+void rtioctl __P((struct in_addr, int));
+void initlog __P((void));
+void logerr __P((char *fmt, ...));
+void logtrace __P((char *fmt, ...));
+void logdebug __P((char *fmt, ...));
+void logperror __P((char *str));
+void prusage __P((void));
+int join __P((int sock, struct sockaddr_in *sin));
+
+
+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);
+
+ (void) setsid ();
+
+ initlog();
+}
+
+/*
+ * M A I N
+ */
+char *sendaddress, *recvaddress;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct sigaction sa;
+ struct sockaddr_in from;
+ struct sockaddr_in *to = &whereto;
+ struct sockaddr_in joinaddr;
+ int val;
+ int c;
+
+ min_adv_int =( max_adv_int * 3 / 4);
+ lifetime = (3*max_adv_int);
+
+ while ((c = getopt(argc, argv, "dtvsrabfT:p:")) != EOF) {
+ switch(c) {
+ 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':
+ val = strtol(optarg, (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);
+ break;
+ case 'p':
+ val = strtol(optarg, (char **)NULL, 0);
+ preference = val;
+ break;
+ default:
+ prusage();
+ /* NOTREACHED*/
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ 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 = argv[0];
+ argc--; argv++;
+ }
+
+ 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 = argv[0];
+ argc--; argv++;
+ }
+ 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) {
+ /* TBD fix this to be more random */
+ srandom((int)gethostid()+(int)time((time_t *)NULL));
+ }
+
+ if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
+ logperror("socket");
+ exit(5);
+ }
+
+ setvbuf( stdout, NULL, _IOLBF, 0 );
+
+ (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);
+ }
+
+
+ /*
+ * Make sure that this signal actually interrupts (rather than
+ * restarts) the recvfrom call below.
+ */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = timer;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0; /* note: no SA_RESTART */
+ (void) sigaction(SIGALRM, &sa, (struct sigaction *)NULL);
+ timer(0); /* 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
+
+int left_until_advertise;
+
+/* Called every TIMER_INTERVAL */
+void timer(sig)
+ int sig;
+{
+ static int left_until_getifconf;
+ static int left_until_solicit;
+
+ left_until_getifconf -= TIMER_INTERVAL;
+ left_until_advertise -= TIMER_INTERVAL;
+ left_until_solicit -= TIMER_INTERVAL;
+
+ if (left_until_getifconf < 0) {
+ initifs(0);
+ 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 *rap = (struct icmp *) 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->ira_addr = interfaces[i].localaddr.s_addr;
+ ap->ira_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 *rap = (struct icmp *)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;
+ }
+ rap->icmp_lifetime = ntohs(rap->icmp_lifetime);
+ 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 = ap->ira_addr;
+ if (verbose)
+ logtrace("\taddress %s, preference 0x%x\n",
+ pr_name(ina),
+ ntohl(ap->ira_preference));
+ if (!responder) {
+ if (is_directly_connected(ina))
+ record_router(ina,
+ (long)ntohl(ap->ira_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;
+ }
+ } /* end switch */
+}
+
+
+/*
+ * I N _ C K S U M
+ *
+ * Checksum routine for Internet Protocol family headers (C Version)
+ *
+ */
+int
+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(sig)
+ int sig;
+{
+ 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);
+ }
+ if (verbose) {
+ logtrace("\n----%s rdisc Statistics----\n", sendaddress );
+ logtrace("%d packets transmitted, ", ntransmitted );
+ logtrace("%d packets received, ", nreceived );
+ (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
+
+int
+isbroadcast(sin)
+ struct sockaddr_in *sin;
+{
+ return (sin->sin_addr.s_addr == INADDR_BROADCAST);
+}
+
+int
+ismulticast(sin)
+ struct sockaddr_in *sin;
+{
+ return (IN_CLASSD(ntohl(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(0);
+ for (i = 0; i < interfaces_size; i++)
+ interfaces[i].preference = preference;
+}
+
+void
+initifs(sig)
+ int sig;
+{
+ int sock;
+ struct ifconf ifc;
+ struct ifreq ifreq, *ifrp, *ifend;
+ 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;
+ }
+ bzero(buf, bufsize);
+ 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;
+ }
+ ifrp = (struct ifreq *)buf;
+ ifend = (struct ifreq *)(buf + ifc.ifc_len);
+ for (i = 0; ifrp < ifend; ifrp = (struct ifreq *)((char *)ifrp + n)) {
+ ifreq = *ifrp;
+ if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
+ logperror("initifs: ioctl (get interface flags)");
+ n = 0;
+ continue;
+ }
+ n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
+ if (n < sizeof(*ifrp))
+ n = sizeof(*ifrp);
+ if (ifrp->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(&ifrp->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);
+}
+
+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, RTM_ADD);
+}
+
+void
+del_route(addr)
+ struct in_addr addr;
+{
+ if (debug)
+ logdebug("Delete default route to %s\n", pr_name(addr));
+ rtioctl(addr, RTM_DELETE);
+}
+
+void
+rtioctl(addr, op)
+ struct in_addr addr;
+ int op;
+{
+ int sock;
+ struct {
+ struct rt_msghdr m_rtm;
+ struct sockaddr_in m_dst;
+ struct sockaddr_in m_gateway;
+ struct sockaddr_in m_netmask;
+ } m_rtmsg;
+ static int seq = 0;
+
+ bzero(&m_rtmsg, sizeof(m_rtmsg));
+
+ m_rtmsg.m_rtm.rtm_type = op;
+ m_rtmsg.m_rtm.rtm_flags = RTF_GATEWAY | RTF_UP; /* XXX more? */
+ m_rtmsg.m_rtm.rtm_version = RTM_VERSION;
+ m_rtmsg.m_rtm.rtm_seq = ++seq;
+ m_rtmsg.m_rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+
+ /* initialise route metrics to zero.. tcp may fill these in */
+ bzero(&m_rtmsg.m_rtm.rtm_rmx, sizeof(m_rtmsg.m_rtm.rtm_rmx));
+
+ m_rtmsg.m_rtm.rtm_inits = 0;
+ m_rtmsg.m_rtm.rtm_msglen = sizeof(m_rtmsg);
+
+ m_rtmsg.m_dst.sin_len = sizeof(struct sockaddr_in);
+ m_rtmsg.m_dst.sin_family = AF_INET;
+ m_rtmsg.m_dst.sin_addr.s_addr = 0; /* default */
+
+ /* XXX: is setting a zero length right? */
+ m_rtmsg.m_netmask.sin_len = 0;
+ m_rtmsg.m_netmask.sin_family = AF_INET;
+ m_rtmsg.m_netmask.sin_addr.s_addr = 0; /* default */
+
+ m_rtmsg.m_gateway.sin_len = sizeof(struct sockaddr_in);
+ m_rtmsg.m_gateway.sin_family = AF_INET;
+ m_rtmsg.m_gateway.sin_addr = addr; /* gateway */
+
+ sock = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (sock < 0) {
+ logperror("rtioctl: socket");
+ return;
+ }
+ if (write(sock, &m_rtmsg, sizeof(m_rtmsg)) != sizeof(m_rtmsg)) {
+ if (!(op == RTM_ADD && errno == EEXIST))
+ logperror("rtioctl: write");
+ }
+ close(sock);
+}
+
+
+
+/*
+ * LOGGER
+ */
+
+
+static int logging = 0;
+
+void
+initlog()
+{
+ logging++;
+ openlog("rdisc", LOG_PID | LOG_CONS, LOG_DAEMON);
+}
+
+/* VARARGS1 */
+void
+#if __STDC__
+logerr(char *fmt, ...)
+#else
+logerr(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ if (logging)
+ vsyslog(LOG_ERR, fmt, ap);
+ else
+ (void) vfprintf(stderr, fmt, ap);
+
+ va_end(ap);
+}
+
+/* VARARGS1 */
+void
+#if __STDC__
+logtrace(char *fmt, ...)
+#else
+logtrace(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ if (logging)
+ vsyslog(LOG_INFO, fmt, ap);
+ else
+ (void) vfprintf(stdout, fmt, ap);
+
+ va_end(ap);
+}
+
+/* VARARGS1 */
+void
+#if __STDC__
+logdebug(char *fmt, ...)
+#else
+logdebug(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ if (logging)
+ vsyslog(LOG_DEBUG, fmt, ap);
+ else
+ (void) vfprintf(stdout, fmt, ap);
+
+ va_end(ap);
+}
+
+void
+logperror(str)
+ char *str;
+{
+ if (logging)
+ syslog(LOG_ERR, "%s: %m", str);
+ else
+ (void) warn("%s", str);
+}