| /* | |
| * 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)); | |
| } |