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.
1534 lines
34 KiB
1534 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)); |
|
}
|
|
|