You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1535 lines
34 KiB
1535 lines
34 KiB
/*
|
|
* 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 <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
/* Do not use "improved" glibc version! */
|
|
#include <linux/limits.h>
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/file.h>
|
|
#include <malloc.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <linux/if.h>
|
|
#include <linux/route.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 <netdb.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
|
|
#include "SNAPSHOT.h"
|
|
|
|
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;
|
|
int ifindex;
|
|
char name[IFNAMSIZ];
|
|
};
|
|
|
|
/*
|
|
* TBD
|
|
* Use 255.255.255.255 for broadcasts - not the interface broadcast
|
|
* address.
|
|
*/
|
|
|
|
#define ALLIGN(ptr) (ptr)
|
|
|
|
static int join(int sock, struct sockaddr_in *sin);
|
|
static void solicitor(struct sockaddr_in *);
|
|
#ifdef RDISC_SERVER
|
|
static void advertise(struct sockaddr_in *, int lft);
|
|
#endif
|
|
static char *pr_name(struct in_addr addr);
|
|
static void pr_pack(char *buf, int cc, struct sockaddr_in *from);
|
|
static void age_table(int time);
|
|
static void record_router(struct in_addr router, int preference, int ttl);
|
|
static void add_route(struct in_addr addr);
|
|
static void del_route(struct in_addr addr);
|
|
static void rtioctl(struct in_addr addr, int op);
|
|
static int support_multicast(void);
|
|
static int sendbcast(int s, char *packet, int packetlen);
|
|
static int sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *);
|
|
static int sendbcastif(int s, char *packet, int packetlen, struct interface *ifp);
|
|
static int sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin, struct interface *ifp);
|
|
static int is_directly_connected(struct in_addr in);
|
|
static void initlog(void);
|
|
static void discard_table(void);
|
|
static void init(void);
|
|
|
|
#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
|
|
|
|
#if !defined(__GLIBC__) || __GLIBC__ < 2
|
|
/* 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
|
|
{
|
|
__u32 ira_addr;
|
|
__u32 ira_preference;
|
|
};
|
|
#else
|
|
#define icmp_ra icmp
|
|
#endif
|
|
|
|
/* 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 INELIGIBLE_PREF 0x80000000 /* Maximum negative */
|
|
|
|
#define MAX_ADV_INT 600
|
|
|
|
/* Statics */
|
|
static int num_interfaces;
|
|
|
|
static struct interface *interfaces;
|
|
static int interfaces_size; /* Number of elements in interfaces */
|
|
|
|
|
|
#define MAXPACKET 4096 /* max packet size */
|
|
|
|
/* fraser */
|
|
int debugfile;
|
|
|
|
const char usage[] =
|
|
"Usage: rdisc [-b] [-d] [-s] [-v] [-f] [-a] [-V] [send_address] [receive_address]\n"
|
|
#ifdef RDISC_SERVER
|
|
" rdisc -r [-b] [-d] [-s] [-v] [-f] [-a] [-V] [-p <preference>] [-T <secs>]\n"
|
|
" [send_address] [receive_address]\n"
|
|
#endif
|
|
;
|
|
|
|
|
|
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 ntransmitted = 0;
|
|
int nreceived = 0;
|
|
int forever = 0; /* Never give up on host. If 0 defer fork until
|
|
* first response.
|
|
*/
|
|
|
|
#ifdef RDISC_SERVER
|
|
/* Router variables */
|
|
int responder;
|
|
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 */
|
|
#endif
|
|
|
|
/* 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. */
|
|
|
|
|
|
static void graceful_finish(void);
|
|
static void finish(void);
|
|
static void timer(void);
|
|
static void initifs(void);
|
|
static u_short in_cksum(u_short *addr, int len);
|
|
|
|
static int logging = 0;
|
|
|
|
#define logerr(fmt...) ({ if (logging) syslog(LOG_ERR, fmt); \
|
|
else fprintf(stderr, fmt); })
|
|
#define logtrace(fmt...) ({ if (logging) syslog(LOG_INFO, fmt); \
|
|
else fprintf(stderr, fmt); })
|
|
#define logdebug(fmt...) ({ if (logging) syslog(LOG_DEBUG, fmt); \
|
|
else fprintf(stderr, fmt); })
|
|
static void logperror(char *str);
|
|
|
|
static __inline__ int isbroadcast(struct sockaddr_in *sin)
|
|
{
|
|
return (sin->sin_addr.s_addr == INADDR_BROADCAST);
|
|
}
|
|
|
|
static __inline__ int ismulticast(struct sockaddr_in *sin)
|
|
{
|
|
return IN_CLASSD(ntohl(sin->sin_addr.s_addr));
|
|
}
|
|
|
|
static void prusage(void)
|
|
{
|
|
fputs(usage, stderr);
|
|
exit(1);
|
|
}
|
|
|
|
void do_fork(void)
|
|
{
|
|
int t;
|
|
pid_t pid;
|
|
long open_max;
|
|
|
|
if (trace)
|
|
return;
|
|
if ((open_max = sysconf(_SC_OPEN_MAX)) == -1) {
|
|
if (errno == 0) {
|
|
(void) fprintf(stderr, "OPEN_MAX is not supported\n");
|
|
}
|
|
else {
|
|
(void) fprintf(stderr, "sysconf() error\n");
|
|
}
|
|
exit(1);
|
|
}
|
|
|
|
|
|
if ((pid=fork()) != 0)
|
|
exit(0);
|
|
|
|
for (t = 0; t < open_max; t++)
|
|
if (t != s)
|
|
close(t);
|
|
|
|
setsid();
|
|
initlog();
|
|
}
|
|
|
|
void signal_setup(int signo, void (*handler)(void))
|
|
{
|
|
struct sigaction sa;
|
|
|
|
memset(&sa, 0, sizeof(sa));
|
|
|
|
sa.sa_handler = (void (*)(int))handler;
|
|
#ifdef SA_INTERRUPT
|
|
sa.sa_flags = SA_INTERRUPT;
|
|
#endif
|
|
sigaction(signo, &sa, NULL);
|
|
}
|
|
|
|
/*
|
|
* M A I N
|
|
*/
|
|
char *sendaddress, *recvaddress;
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
struct sockaddr_in from;
|
|
char **av = argv;
|
|
struct sockaddr_in *to = &whereto;
|
|
struct sockaddr_in joinaddr;
|
|
sigset_t sset, sset_empty;
|
|
#ifdef RDISC_SERVER
|
|
int val;
|
|
|
|
min_adv_int =( max_adv_int * 3 / 4);
|
|
lifetime = (3*max_adv_int);
|
|
#endif
|
|
|
|
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;
|
|
#ifdef RDISC_SERVER
|
|
case 'r':
|
|
responder = 1;
|
|
break;
|
|
#endif
|
|
case 'a':
|
|
best_preference = 0;
|
|
break;
|
|
case 'b':
|
|
best_preference = 1;
|
|
break;
|
|
case 'f':
|
|
forever = 1;
|
|
break;
|
|
case 'V':
|
|
printf("rdisc utility, iputils-%s\n", SNAPSHOT);
|
|
exit(0);
|
|
#ifdef RDISC_SERVER
|
|
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;
|
|
#endif
|
|
default:
|
|
prusage();
|
|
/* NOTREACHED*/
|
|
}
|
|
}
|
|
#ifdef RDISC_SERVER
|
|
next:
|
|
#endif
|
|
argc--, av++;
|
|
}
|
|
if( argc < 1) {
|
|
if (support_multicast()) {
|
|
sendaddress = ALL_ROUTERS_ADDRESS;
|
|
#ifdef RDISC_SERVER
|
|
if (responder)
|
|
sendaddress = ALL_HOSTS_ADDRESS;
|
|
#endif
|
|
} else
|
|
sendaddress = "255.255.255.255";
|
|
} else {
|
|
sendaddress = av[0];
|
|
argc--;
|
|
}
|
|
|
|
if (argc < 1) {
|
|
if (support_multicast()) {
|
|
recvaddress = ALL_HOSTS_ADDRESS;
|
|
#ifdef RDISC_SERVER
|
|
if (responder)
|
|
recvaddress = ALL_ROUTERS_ADDRESS;
|
|
#endif
|
|
} else
|
|
recvaddress = "255.255.255.255";
|
|
} else {
|
|
recvaddress = av[0];
|
|
argc--;
|
|
}
|
|
if (argc != 0) {
|
|
(void) fprintf(stderr, "Extra parameters\n");
|
|
prusage();
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
#ifdef RDISC_SERVER
|
|
if (solicit && responder) {
|
|
prusage();
|
|
/* NOTREACHED */
|
|
}
|
|
#endif
|
|
|
|
if (!(solicit && !forever)) {
|
|
do_fork();
|
|
/*
|
|
* Added the next line to stop forking a second time
|
|
* Fraser Gardiner - Sun Microsystems Australia
|
|
*/
|
|
forever = 1;
|
|
}
|
|
|
|
memset( (char *)&whereto, 0, sizeof(struct sockaddr_in) );
|
|
to->sin_family = AF_INET;
|
|
to->sin_addr.s_addr = inet_addr(sendaddress);
|
|
|
|
memset( (char *)&joinaddr, 0, sizeof(struct sockaddr_in) );
|
|
joinaddr.sin_family = AF_INET;
|
|
joinaddr.sin_addr.s_addr = inet_addr(recvaddress);
|
|
|
|
#ifdef RDISC_SERVER
|
|
if (responder)
|
|
srandom((int)gethostid());
|
|
#endif
|
|
|
|
if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
|
|
logperror("socket");
|
|
exit(5);
|
|
}
|
|
|
|
setlinebuf( stdout );
|
|
|
|
signal_setup(SIGINT, finish );
|
|
signal_setup(SIGTERM, graceful_finish );
|
|
signal_setup(SIGHUP, initifs );
|
|
signal_setup(SIGALRM, timer );
|
|
|
|
sigemptyset(&sset);
|
|
sigemptyset(&sset_empty);
|
|
sigaddset(&sset, SIGALRM);
|
|
sigaddset(&sset, SIGHUP);
|
|
sigaddset(&sset, SIGTERM);
|
|
sigaddset(&sset, SIGINT);
|
|
|
|
init();
|
|
if (join(s, &joinaddr) < 0) {
|
|
logerr("Failed joining addresses\n");
|
|
exit (2);
|
|
}
|
|
|
|
timer(); /* start things going */
|
|
|
|
for (;;) {
|
|
u_char packet[MAXPACKET];
|
|
int len = sizeof (packet);
|
|
socklen_t fromlen = sizeof (from);
|
|
int cc;
|
|
|
|
cc=recvfrom(s, (char *)packet, len, 0,
|
|
(struct sockaddr *)&from, &fromlen);
|
|
if (cc<0) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
logperror("recvfrom");
|
|
continue;
|
|
}
|
|
|
|
sigprocmask(SIG_SETMASK, &sset, NULL);
|
|
pr_pack( (char *)packet, cc, &from );
|
|
sigprocmask(SIG_SETMASK, &sset_empty, NULL);
|
|
}
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
#define TIMER_INTERVAL 3
|
|
#define GETIFCONF_TIMER 30
|
|
|
|
static int left_until_advertise;
|
|
|
|
/* Called every TIMER_INTERVAL */
|
|
void timer()
|
|
{
|
|
static int time;
|
|
static int left_until_getifconf;
|
|
static int 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;
|
|
}
|
|
#ifdef RDISC_SERVER
|
|
if (responder && left_until_advertise <= 0) {
|
|
ntransmitted++;
|
|
advertise(&whereto, lifetime);
|
|
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
|
|
#endif
|
|
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);
|
|
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(struct sockaddr_in *sin)
|
|
{
|
|
static u_char outpack[MAXPACKET];
|
|
struct icmphdr *icp = (struct icmphdr *) ALLIGN(outpack);
|
|
int packetlen, i;
|
|
|
|
if (verbose) {
|
|
logtrace("Sending solicitation to %s\n",
|
|
pr_name(sin->sin_addr));
|
|
}
|
|
icp->type = ICMP_ROUTER_SOLICITATION;
|
|
icp->code = 0;
|
|
icp->checksum = 0;
|
|
icp->un.gateway = 0; /* Reserved */
|
|
packetlen = 8;
|
|
|
|
/* Compute ICMP checksum here */
|
|
icp->checksum = 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("solicitor:sendto");
|
|
}
|
|
logerr("wrote %s %d chars, ret=%d\n",
|
|
sendaddress, packetlen, i );
|
|
}
|
|
}
|
|
|
|
#ifdef RDISC_SERVER
|
|
/*
|
|
* 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(struct sockaddr_in *sin, int lft)
|
|
{
|
|
static u_char outpack[MAXPACKET];
|
|
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 = htons(lft);
|
|
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 = htonl(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, %s\n",
|
|
ifp->name,
|
|
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 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* P R _ T Y P E
|
|
*
|
|
* Convert an ICMP "type" field to a printable string.
|
|
*/
|
|
char *
|
|
pr_type(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(struct in_addr addr)
|
|
{
|
|
struct hostent *phe;
|
|
static char buf[80];
|
|
|
|
phe = gethostbyaddr((char *)&addr.s_addr, 4, AF_INET);
|
|
if (phe == NULL)
|
|
return( inet_ntoa(addr));
|
|
snprintf(buf, sizeof(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(char *buf, int cc, struct sockaddr_in *from)
|
|
{
|
|
struct iphdr *ip;
|
|
struct icmphdr *icp;
|
|
int i;
|
|
int hlen;
|
|
|
|
ip = (struct iphdr *) ALLIGN(buf);
|
|
hlen = ip->ihl << 2;
|
|
if (cc < hlen + 8) {
|
|
if (verbose)
|
|
logtrace("packet too short (%d bytes) from %s\n", cc,
|
|
pr_name(from->sin_addr));
|
|
return;
|
|
}
|
|
cc -= hlen;
|
|
icp = (struct icmphdr *)ALLIGN(buf + hlen);
|
|
|
|
switch (icp->type) {
|
|
case ICMP_ROUTER_ADVERTISEMENT:
|
|
{
|
|
struct icmp_ra *rap = (struct icmp_ra *)ALLIGN(icp);
|
|
struct icmp_ra_addr *ap;
|
|
|
|
#ifdef RDISC_SERVER
|
|
if (responder)
|
|
break;
|
|
#endif
|
|
|
|
/* TBD verify that the link is multicast or broadcast */
|
|
/* XXX Find out the link it came in over? */
|
|
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 <
|
|
8 + 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,
|
|
8 + rap->icmp_num_addrs * rap->icmp_wpa * 4);
|
|
return;
|
|
}
|
|
|
|
if (verbose)
|
|
logtrace("ICMP %s from %s, lifetime %d\n",
|
|
pr_type((int)rap->icmp_type),
|
|
pr_name(from->sin_addr),
|
|
ntohs(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 + 8 +
|
|
i * rap->icmp_wpa * 4);
|
|
ina.s_addr = ap->ira_addr;
|
|
if (verbose)
|
|
logtrace("\taddress %s, preference 0x%x\n",
|
|
pr_name(ina),
|
|
(unsigned int)ntohl(ap->ira_preference));
|
|
if (is_directly_connected(ina))
|
|
record_router(ina,
|
|
ntohl(ap->ira_preference),
|
|
ntohs(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;
|
|
}
|
|
|
|
#ifdef RDISC_SERVER
|
|
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? */
|
|
|
|
if (in_cksum((u_short *)ALLIGN(buf+hlen), cc)) {
|
|
if (verbose)
|
|
logtrace("ICMP %s from %s: Bad checksum\n",
|
|
pr_type((int)icp->type),
|
|
pr_name(from->sin_addr));
|
|
return;
|
|
}
|
|
if (icp->code != 0) {
|
|
if (verbose)
|
|
logtrace("ICMP %s from %s: Code = %d\n",
|
|
pr_type((int)icp->type),
|
|
pr_name(from->sin_addr),
|
|
icp->code);
|
|
return;
|
|
}
|
|
|
|
if (cc < ICMP_MINLEN) {
|
|
if (verbose)
|
|
logtrace("ICMP %s from %s: Too short %d, %d\n",
|
|
pr_type((int)icp->type),
|
|
pr_name(from->sin_addr),
|
|
cc,
|
|
ICMP_MINLEN);
|
|
return;
|
|
}
|
|
|
|
if (verbose)
|
|
logtrace("ICMP %s from %s\n",
|
|
pr_type((int)icp->type),
|
|
pr_name(from->sin_addr));
|
|
|
|
/* Check that ip_src is either a neighboor
|
|
* on the arriving link or 0.
|
|
*/
|
|
sin.sin_family = AF_INET;
|
|
if (ip->saddr == 0) {
|
|
/* If it was sent to the broadcast address we respond
|
|
* to the broadcast address.
|
|
*/
|
|
if (IN_CLASSD(ntohl(ip->daddr)))
|
|
sin.sin_addr.s_addr = htonl(0xe0000001);
|
|
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 {
|
|
sin.sin_addr.s_addr = ip->saddr;
|
|
if (!is_directly_connected(sin.sin_addr)) {
|
|
if (verbose)
|
|
logtrace("ICMP %s from %s: source not directly connected\n",
|
|
pr_type((int)icp->type),
|
|
pr_name(from->sin_addr));
|
|
break;
|
|
}
|
|
}
|
|
nreceived++;
|
|
ntransmitted++;
|
|
advertise(&sin, lifetime);
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* I N _ C K S U M
|
|
*
|
|
* Checksum routine for Internet Protocol family headers (C Version)
|
|
*
|
|
*/
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
# define ODDBYTE(v) (v)
|
|
#elif BYTE_ORDER == BIG_ENDIAN
|
|
# define ODDBYTE(v) ((u_short)(v) << 8)
|
|
#else
|
|
# define ODDBYTE(v) htons((u_short)(v) << 8)
|
|
#endif
|
|
|
|
u_short in_cksum(u_short *addr, int len)
|
|
{
|
|
register int nleft = len;
|
|
register u_short *w = addr;
|
|
register u_short answer;
|
|
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 )
|
|
sum += ODDBYTE(*(u_char *)w); /* le16toh() may be unavailable on old systems */
|
|
|
|
/*
|
|
* 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()
|
|
{
|
|
#ifdef RDISC_SERVER
|
|
if (responder) {
|
|
/* Send out a packet with a preference so that all
|
|
* hosts will know that we are dead.
|
|
*
|
|
* Wrong comment, wrong code.
|
|
* ttl must be set to 0 instead. --ANK
|
|
*/
|
|
logerr("terminated\n");
|
|
ntransmitted++;
|
|
advertise(&whereto, 0);
|
|
}
|
|
#endif
|
|
logtrace("\n----%s rdisc Statistics----\n", sendaddress );
|
|
logtrace("%d packets transmitted, ", ntransmitted );
|
|
logtrace("%d packets received, ", nreceived );
|
|
logtrace("\n");
|
|
(void) fflush(stdout);
|
|
exit(0);
|
|
}
|
|
|
|
void
|
|
graceful_finish()
|
|
{
|
|
discard_table();
|
|
finish();
|
|
exit(0);
|
|
}
|
|
|
|
|
|
/* From libc/rpc/pmap_rmt.c */
|
|
|
|
int
|
|
sendbcast(int s, char *packet, int packetlen)
|
|
{
|
|
int i, cc;
|
|
|
|
for (i = 0; i < num_interfaces; i++) {
|
|
if ((interfaces[i].flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0)
|
|
continue;
|
|
cc = sendbcastif(s, packet, packetlen, &interfaces[i]);
|
|
if (cc!= packetlen) {
|
|
return (cc);
|
|
}
|
|
}
|
|
return (packetlen);
|
|
}
|
|
|
|
int
|
|
sendbcastif(int s, char *packet, int packetlen, struct interface *ifp)
|
|
{
|
|
int on;
|
|
int cc;
|
|
struct sockaddr_in baddr;
|
|
|
|
baddr.sin_family = AF_INET;
|
|
baddr.sin_addr = ifp->bcastaddr;
|
|
if (debug)
|
|
logdebug("Broadcast to %s\n",
|
|
pr_name(baddr.sin_addr));
|
|
on = 1;
|
|
setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on));
|
|
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));
|
|
}
|
|
on = 0;
|
|
setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on));
|
|
return (cc);
|
|
}
|
|
|
|
int
|
|
sendmcast(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_BROADCAST|IFF_POINTOPOINT|IFF_MULTICAST)) == 0)
|
|
continue;
|
|
cc = sendmcastif(s, packet, packetlen, sin, &interfaces[i]);
|
|
if (cc!= packetlen) {
|
|
return (cc);
|
|
}
|
|
}
|
|
return (packetlen);
|
|
}
|
|
|
|
int
|
|
sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin,
|
|
struct interface *ifp)
|
|
{
|
|
int cc;
|
|
struct ip_mreqn mreq;
|
|
|
|
memset(&mreq, 0, sizeof(mreq));
|
|
mreq.imr_ifindex = ifp->ifindex;
|
|
mreq.imr_address = ifp->localaddr;
|
|
if (debug)
|
|
logdebug("Multicast to interface %s, %s\n",
|
|
ifp->name,
|
|
pr_name(mreq.imr_address));
|
|
if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
|
|
(char *)&mreq,
|
|
sizeof(mreq)) < 0) {
|
|
logperror("setsockopt (IP_MULTICAST_IF)");
|
|
logerr("Cannot send multicast packet over interface %s, %s\n",
|
|
ifp->name,
|
|
pr_name(mreq.imr_address));
|
|
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, %s\n",
|
|
ifp->name, pr_name(mreq.imr_address));
|
|
}
|
|
return (cc);
|
|
}
|
|
|
|
void
|
|
init()
|
|
{
|
|
initifs();
|
|
#ifdef RDISC_SERVER
|
|
{
|
|
int i;
|
|
for (i = 0; i < interfaces_size; i++)
|
|
interfaces[i].preference = preference;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
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 != NULL)
|
|
(void) free(interfaces);
|
|
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 (strlen(ifreq.ifr_name) >= IFNAMSIZ)
|
|
continue;
|
|
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|IFF_POINTOPOINT)) == 0)
|
|
continue;
|
|
strncpy(interfaces[i].name, ifr->ifr_name, IFNAMSIZ-1);
|
|
|
|
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 = (__u32)0xffffffff;
|
|
if (ioctl(sock, SIOCGIFINDEX, (char *)&ifreq) < 0) {
|
|
logperror("initifs: ioctl (get ifindex)");
|
|
continue;
|
|
}
|
|
interfaces[i].ifindex = ifreq.ifr_ifindex;
|
|
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(int sock, struct sockaddr_in *sin)
|
|
{
|
|
int i, j;
|
|
struct ip_mreqn mreq;
|
|
int joined[num_interfaces];
|
|
|
|
memset(joined, 0, sizeof(joined));
|
|
|
|
if (isbroadcast(sin))
|
|
return (0);
|
|
|
|
mreq.imr_multiaddr = sin->sin_addr;
|
|
for (i = 0; i < num_interfaces; i++) {
|
|
for (j = 0; j < i; j++) {
|
|
if (joined[j] == interfaces[i].ifindex)
|
|
break;
|
|
}
|
|
if (j != i)
|
|
continue;
|
|
|
|
mreq.imr_ifindex = interfaces[i].ifindex;
|
|
mreq.imr_address.s_addr = 0;
|
|
|
|
if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
|
|
(char *)&mreq, sizeof(mreq)) < 0) {
|
|
logperror("setsockopt (IP_ADD_MEMBERSHIP)");
|
|
return (-1);
|
|
}
|
|
|
|
joined[i] = interfaces[i].ifindex;
|
|
}
|
|
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(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(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(void)
|
|
{
|
|
struct table *tp;
|
|
int max = (int)INELIGIBLE_PREF;
|
|
|
|
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(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 != INELIGIBLE_PREF) {
|
|
tp = table;
|
|
while (tp) {
|
|
if (tp->preference == max && !tp->in_kernel) {
|
|
add_route(tp->router);
|
|
tp->in_kernel++;
|
|
}
|
|
tp = tp->next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void discard_table(void)
|
|
{
|
|
struct table **tpp, *tp;
|
|
|
|
tpp = &table;
|
|
while (*tpp != NULL) {
|
|
tp = *tpp;
|
|
*tpp = tp->next;
|
|
if (tp->in_kernel)
|
|
del_route(tp->router);
|
|
free((char *)tp);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
record_router(struct in_addr router, int 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 (ttl < 4)
|
|
preference = INELIGIBLE_PREF;
|
|
|
|
if (debug)
|
|
logdebug("Recording %s, ttl %d, preference 0x%x\n",
|
|
pr_name(router),
|
|
ttl,
|
|
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 != INELIGIBLE_PREF) {
|
|
add_route(tp->router);
|
|
tp->in_kernel++;
|
|
}
|
|
if (tp->preference == INELIGIBLE_PREF && 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 != INELIGIBLE_PREF) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
add_route(struct in_addr addr)
|
|
{
|
|
if (debug)
|
|
logdebug("Add default route to %s\n", pr_name(addr));
|
|
rtioctl(addr, SIOCADDRT);
|
|
}
|
|
|
|
void
|
|
del_route(struct in_addr addr)
|
|
{
|
|
if (debug)
|
|
logdebug("Delete default route to %s\n", pr_name(addr));
|
|
rtioctl(addr, SIOCDELRT);
|
|
}
|
|
|
|
void
|
|
rtioctl(struct in_addr addr, int op)
|
|
{
|
|
int sock;
|
|
struct rtentry rt;
|
|
struct sockaddr_in *sin;
|
|
|
|
memset((char *)&rt, 0, sizeof(struct rtentry));
|
|
rt.rt_dst.sa_family = AF_INET;
|
|
rt.rt_gateway.sa_family = AF_INET;
|
|
rt.rt_genmask.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
|
|
*/
|
|
|
|
void initlog(void)
|
|
{
|
|
logging++;
|
|
openlog("in.rdiscd", LOG_PID | LOG_CONS, LOG_DAEMON);
|
|
}
|
|
|
|
|
|
void
|
|
logperror(char *str)
|
|
{
|
|
if (logging)
|
|
syslog(LOG_ERR, "%s: %m", str);
|
|
else
|
|
(void) fprintf(stderr, "%s: %s\n", str, strerror(errno));
|
|
}
|