Ditch the sysvinit stuff
[profile/ivi/iputils.git] / rdisc.c
1 /*
2  * Rdisc (this program) was developed by Sun Microsystems, Inc. and is
3  * provided for unrestricted use provided that this legend is included on
4  * all tape media and as a part of the software program in whole or part.
5  * Users may copy or modify Rdisc without charge, and they may freely
6  * distribute it.
7  *
8  * RDISC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
9  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
10  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
11  *
12  * Rdisc is provided with no support and without any obligation on the
13  * part of Sun Microsystems, Inc. to assist in its use, correction,
14  * modification or enhancement.
15  *
16  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
17  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY RDISC
18  * OR ANY PART THEREOF.
19  *
20  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
21  * or profits or other special, indirect and consequential damages, even if
22  * Sun has been advised of the possibility of such damages.
23  *
24  * Sun Microsystems, Inc.
25  * 2550 Garcia Avenue
26  * Mountain View, California  94043
27  */
28 #include <stdio.h>
29 #include <errno.h>
30 #include <signal.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <sys/types.h>
34 #include <sys/time.h>
35 /* Do not use "improved" glibc version! */
36 #include <linux/limits.h>
37
38 #include <sys/param.h>
39 #include <sys/socket.h>
40 #include <sys/file.h>
41 #include <malloc.h>
42
43 #include <sys/ioctl.h>
44 #include <linux/if.h>
45 #include <linux/route.h>
46
47 #include <netinet/in.h>
48 #include <netinet/ip.h>
49 #include <netinet/ip_icmp.h>
50
51 /*
52  * The next include contains all defs and structures for multicast
53  * that are not in SunOS 4.1.x. On a SunOS 4.1.x system none of this code
54  * is ever used because it does not support multicast
55  * Fraser Gardiner - Sun Microsystems Australia
56  */
57
58 #include <netdb.h>
59 #include <arpa/inet.h>
60
61 #include <string.h>
62 #include <syslog.h>
63
64 #include "SNAPSHOT.h"
65
66 struct interface
67 {
68         struct in_addr  address;        /* Used to identify the interface */
69         struct in_addr  localaddr;      /* Actual address if the interface */
70         int             preference;
71         int             flags;
72         struct in_addr  bcastaddr;
73         struct in_addr  remoteaddr;
74         struct in_addr  netmask;
75         int             ifindex;
76         char            name[IFNAMSIZ];
77 };
78
79 /*
80  * TBD
81  *      Use 255.255.255.255 for broadcasts - not the interface broadcast
82  *      address.
83  */
84
85 #define ALLIGN(ptr)     (ptr)
86
87 static int join(int sock, struct sockaddr_in *sin);
88 static void solicitor(struct sockaddr_in *);
89 #ifdef RDISC_SERVER
90 static void advertise(struct sockaddr_in *, int lft);
91 #endif
92 static char *pr_name(struct in_addr addr);
93 static void pr_pack(char *buf, int cc, struct sockaddr_in *from);
94 static void age_table(int time);
95 static void record_router(struct in_addr router, int preference, int ttl);
96 static void add_route(struct in_addr addr);
97 static void del_route(struct in_addr addr);
98 static void rtioctl(struct in_addr addr, int op);
99 static int support_multicast(void);
100 static int sendbcast(int s, char *packet, int packetlen);
101 static int sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *);
102 static int sendbcastif(int s, char *packet, int packetlen, struct interface *ifp);
103 static int sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin, struct interface *ifp);
104 static int is_directly_connected(struct in_addr in);
105 static void initlog(void);
106 static void discard_table(void);
107 static void init(void);
108
109 #define ICMP_ROUTER_ADVERTISEMENT       9
110 #define ICMP_ROUTER_SOLICITATION        10
111
112 #define ALL_HOSTS_ADDRESS               "224.0.0.1"
113 #define ALL_ROUTERS_ADDRESS             "224.0.0.2"
114
115 #define MAXIFS 32
116
117 #if !defined(__GLIBC__) || __GLIBC__ < 2
118 /* For router advertisement */
119 struct icmp_ra
120 {
121         u_char  icmp_type;              /* type of message, see below */
122         u_char  icmp_code;              /* type sub code */
123         u_short icmp_cksum;             /* ones complement cksum of struct */
124         u_char  icmp_num_addrs;
125         u_char  icmp_wpa;               /* Words per address */
126         short   icmp_lifetime;
127 };
128
129 struct icmp_ra_addr
130 {
131         __u32   ira_addr;
132         __u32   ira_preference;
133 };
134 #else
135 #define icmp_ra icmp
136 #endif
137
138 /* Router constants */
139 #define MAX_INITIAL_ADVERT_INTERVAL     16
140 #define MAX_INITIAL_ADVERTISEMENTS      3
141 #define MAX_RESPONSE_DELAY              2       /* Not used */
142
143 /* Host constants */
144 #define MAX_SOLICITATIONS               3
145 #define SOLICITATION_INTERVAL           3
146 #define MAX_SOLICITATION_DELAY          1       /* Not used */
147
148 #define INELIGIBLE_PREF                 0x80000000      /* Maximum negative */
149
150 #define MAX_ADV_INT 600
151
152 /* Statics */
153 static int num_interfaces;
154
155 static struct interface *interfaces;
156 static int interfaces_size;                     /* Number of elements in interfaces */
157
158
159 #define MAXPACKET       4096    /* max packet size */
160
161 /* fraser */
162 int debugfile;
163
164 char usage[] =
165 "Usage: rdisc [-b] [-d] [-s] [-v] [-f] [-a] [-V] [send_address] [receive_address]\n"
166 #ifdef RDISC_SERVER
167 "       rdisc -r [-b] [-d] [-s] [-v] [-f] [-a] [-V] [-p <preference>] [-T <secs>] \n"
168 "                [send_address] [receive_address]\n"
169 #endif
170 ;
171
172
173 int s;                  /* Socket file descriptor */
174 struct sockaddr_in whereto;/* Address to send to */
175
176 /* Common variables */
177 int verbose = 0;
178 int debug = 0;
179 int trace = 0;
180 int solicit = 0;
181 int ntransmitted = 0;
182 int nreceived = 0;
183 int forever = 0;        /* Never give up on host. If 0 defer fork until
184                          * first response.
185                          */
186
187 #ifdef RDISC_SERVER
188 /* Router variables */
189 int responder;
190 int max_adv_int = MAX_ADV_INT;
191 int min_adv_int;
192 int lifetime;
193 int initial_advert_interval = MAX_INITIAL_ADVERT_INTERVAL;
194 int initial_advertisements = MAX_INITIAL_ADVERTISEMENTS;
195 int preference = 0;             /* Setable with -p option */
196 #endif
197
198 /* Host variables */
199 int max_solicitations = MAX_SOLICITATIONS;
200 unsigned int solicitation_interval = SOLICITATION_INTERVAL;
201 int best_preference = 1;        /* Set to record only the router(s) with the
202                                    best preference in the kernel. Not set
203                                    puts all routes in the kernel. */
204
205
206 static void graceful_finish(void);
207 static void finish(void);
208 static void timer(void);
209 static void initifs(void);
210 static u_short in_cksum(u_short *addr, int len);
211
212 static int logging = 0;
213
214 #define logerr(fmt...) ({ if (logging) syslog(LOG_ERR, fmt); \
215                           else fprintf(stderr, fmt); })
216 #define logtrace(fmt...) ({ if (logging) syslog(LOG_INFO, fmt); \
217                           else fprintf(stderr, fmt); })
218 #define logdebug(fmt...) ({ if (logging) syslog(LOG_DEBUG, fmt); \
219                           else fprintf(stderr, fmt); })
220 static void logperror(char *str);
221
222 static __inline__ int isbroadcast(struct sockaddr_in *sin)
223 {
224         return (sin->sin_addr.s_addr == INADDR_BROADCAST);
225 }
226
227 static __inline__ int ismulticast(struct sockaddr_in *sin)
228 {
229         return IN_CLASSD(ntohl(sin->sin_addr.s_addr));
230 }
231
232 static void prusage(void)
233 {
234         (void) fprintf(stderr, "%s", usage);
235         exit(1);
236 }
237
238 void do_fork(void)
239 {
240         int t;
241         pid_t pid;
242         long open_max;
243
244         if (trace)
245                 return;
246         if ((open_max = sysconf(_SC_OPEN_MAX)) == -1) {
247                 if (errno == 0) {
248                         (void) fprintf(stderr, "OPEN_MAX is not supported\n");
249                 } 
250                 else {
251                         (void) fprintf(stderr, "sysconf() error\n");
252                 }
253                 exit(1);
254         }
255
256
257         if ((pid=fork()) != 0)
258                 exit(0);
259
260         for (t = 0; t < open_max; t++)
261                 if (t != s)
262                         close(t);
263
264         setsid();
265         initlog();
266 }
267
268 void signal_setup(int signo, void (*handler)(void))
269 {
270         struct sigaction sa;
271
272         memset(&sa, 0, sizeof(sa));
273
274         sa.sa_handler = (void (*)(int))handler;
275 #ifdef SA_INTERRUPT
276         sa.sa_flags = SA_INTERRUPT;
277 #endif
278         sigaction(signo, &sa, NULL);
279 }
280
281 /*
282  *                      M A I N
283  */
284 char    *sendaddress, *recvaddress;
285
286 int main(int argc, char **argv)
287 {
288         struct sockaddr_in from;
289         char **av = argv;
290         struct sockaddr_in *to = &whereto;
291         struct sockaddr_in joinaddr;
292         sigset_t sset, sset_empty;
293 #ifdef RDISC_SERVER
294         int val;
295
296         min_adv_int =( max_adv_int * 3 / 4);
297         lifetime = (3*max_adv_int);
298 #endif
299
300         argc--, av++;
301         while (argc > 0 && *av[0] == '-') {
302                 while (*++av[0]) {
303                         switch (*av[0]) {
304                         case 'd':
305                                 debug = 1;
306                                 break;
307                         case 't':
308                                 trace = 1;
309                                 break;
310                         case 'v':
311                                 verbose++;
312                                 break;
313                         case 's':
314                                 solicit = 1;
315                                 break;
316 #ifdef RDISC_SERVER
317                         case 'r':
318                                 responder = 1;
319                                 break;
320 #endif
321                         case 'a':
322                                 best_preference = 0;
323                                 break;
324                         case 'b':
325                                 best_preference = 1;
326                                 break;
327                         case 'f':
328                                 forever = 1;
329                                 break;
330                         case 'V':
331                                 printf("rdisc utility, iputils-ss%s\n", SNAPSHOT);
332                                 exit(0);
333 #ifdef RDISC_SERVER
334                         case 'T':
335                                 argc--, av++;
336                                 if (argc != 0) {
337                                         val = strtol(av[0], (char **)NULL, 0);
338                                         if (val < 4 || val > 1800) {
339                                                 (void) fprintf(stderr,
340                                                                "Bad Max Advertizement Interval\n");
341                                                 exit(1);
342                                         }
343                                         max_adv_int = val;
344                                         min_adv_int =( max_adv_int * 3 / 4);
345                                         lifetime = (3*max_adv_int);
346                                 } else {
347                                         prusage();
348                                         /* NOTREACHED*/
349                                 }
350                                 goto next;
351                         case 'p':
352                                 argc--, av++;
353                                 if (argc != 0) {
354                                         val = strtol(av[0], (char **)NULL, 0);
355                                         preference = val;
356                                 } else {
357                                         prusage();
358                                         /* NOTREACHED*/
359                                 }
360                                 goto next;
361 #endif
362                         default:
363                                 prusage();
364                                 /* NOTREACHED*/
365                         }
366                 }
367 #ifdef RDISC_SERVER
368 next:
369 #endif
370                 argc--, av++;
371         }
372         if( argc < 1)  {
373                 if (support_multicast()) {
374                         sendaddress = ALL_ROUTERS_ADDRESS;
375 #ifdef RDISC_SERVER
376                         if (responder)
377                                 sendaddress = ALL_HOSTS_ADDRESS;
378 #endif
379                 } else
380                         sendaddress = "255.255.255.255";
381         } else {
382                 sendaddress = av[0];
383                 argc--;
384         }
385
386         if (argc < 1) {
387                 if (support_multicast()) {
388                         recvaddress = ALL_HOSTS_ADDRESS;
389 #ifdef RDISC_SERVER
390                         if (responder)
391                                 recvaddress = ALL_ROUTERS_ADDRESS;
392 #endif
393                 } else
394                         recvaddress = "255.255.255.255";
395         } else {
396                 recvaddress = av[0];
397                 argc--;
398         }
399         if (argc != 0) {
400                 (void) fprintf(stderr, "Extra parameters\n");
401                 prusage();
402                 /* NOTREACHED */
403         }
404
405 #ifdef RDISC_SERVER
406         if (solicit && responder) {
407                 prusage();
408                 /* NOTREACHED */
409         }
410 #endif
411
412         if (!(solicit && !forever)) {
413                 do_fork();
414 /*
415  * Added the next line to stop forking a second time
416  * Fraser Gardiner - Sun Microsystems Australia
417  */
418                 forever = 1;
419         }
420
421         memset( (char *)&whereto, 0, sizeof(struct sockaddr_in) );
422         to->sin_family = AF_INET;
423         to->sin_addr.s_addr = inet_addr(sendaddress);
424
425         memset( (char *)&joinaddr, 0, sizeof(struct sockaddr_in) );
426         joinaddr.sin_family = AF_INET;
427         joinaddr.sin_addr.s_addr = inet_addr(recvaddress);
428
429 #ifdef RDISC_SERVER
430         if (responder)
431                 srandom((int)gethostid());
432 #endif
433
434         if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
435                 logperror("socket");
436                 exit(5);
437         }
438
439         setlinebuf( stdout );
440
441         signal_setup(SIGINT, finish );
442         signal_setup(SIGTERM, graceful_finish );
443         signal_setup(SIGHUP, initifs );
444         signal_setup(SIGALRM, timer );
445
446         sigemptyset(&sset);
447         sigemptyset(&sset_empty);
448         sigaddset(&sset, SIGALRM);
449         sigaddset(&sset, SIGHUP);
450         sigaddset(&sset, SIGTERM);
451         sigaddset(&sset, SIGINT);
452
453         init();
454         if (join(s, &joinaddr) < 0) {
455                 logerr("Failed joining addresses\n");
456                 exit (2);
457         }
458
459         timer();        /* start things going */
460
461         for (;;) {
462                 u_char  packet[MAXPACKET];
463                 int len = sizeof (packet);
464                 socklen_t fromlen = sizeof (from);
465                 int cc;
466
467                 cc=recvfrom(s, (char *)packet, len, 0,
468                             (struct sockaddr *)&from, &fromlen);
469                 if (cc<0) {
470                         if (errno == EINTR)
471                                 continue;
472                         logperror("recvfrom");
473                         continue;
474                 }
475
476                 sigprocmask(SIG_SETMASK, &sset, NULL);
477                 pr_pack( (char *)packet, cc, &from );
478                 sigprocmask(SIG_SETMASK, &sset_empty, NULL);
479         }
480         /*NOTREACHED*/
481 }
482
483 #define TIMER_INTERVAL  3
484 #define GETIFCONF_TIMER 30
485
486 static int left_until_advertise;
487
488 /* Called every TIMER_INTERVAL */
489 void timer()
490 {
491         static int time;
492         static int left_until_getifconf;
493         static int left_until_solicit;
494
495
496         time += TIMER_INTERVAL;
497
498         left_until_getifconf -= TIMER_INTERVAL;
499         left_until_advertise -= TIMER_INTERVAL;
500         left_until_solicit -= TIMER_INTERVAL;
501
502         if (left_until_getifconf < 0) {
503                 initifs();
504                 left_until_getifconf = GETIFCONF_TIMER;
505         }
506 #ifdef RDISC_SERVER
507         if (responder && left_until_advertise <= 0) {
508                 ntransmitted++;
509                 advertise(&whereto, lifetime);
510                 if (ntransmitted < initial_advertisements)
511                         left_until_advertise = initial_advert_interval;
512                 else
513                         left_until_advertise = min_adv_int +
514                                 ((max_adv_int - min_adv_int) *
515                                  (random() % 1000)/1000);
516         } else
517 #endif
518         if (solicit && left_until_solicit <= 0) {
519                 ntransmitted++;
520                 solicitor(&whereto);
521                 if (ntransmitted < max_solicitations)
522                         left_until_solicit = solicitation_interval;
523                 else {
524                         solicit = 0;
525                         if (!forever && nreceived == 0)
526                                 exit(5);
527                 }
528         }
529         age_table(TIMER_INTERVAL);
530         alarm(TIMER_INTERVAL);
531 }
532
533 /*
534  *                      S O L I C I T O R
535  *
536  * Compose and transmit an ICMP ROUTER SOLICITATION REQUEST packet.
537  * The IP packet will be added on by the kernel.
538  */
539 void
540 solicitor(struct sockaddr_in *sin)
541 {
542         static u_char outpack[MAXPACKET];
543         struct icmphdr *icp = (struct icmphdr *) ALLIGN(outpack);
544         int packetlen, i;
545
546         if (verbose) {
547                 logtrace("Sending solicitation to %s\n",
548                          pr_name(sin->sin_addr));
549         }
550         icp->type = ICMP_ROUTER_SOLICITATION;
551         icp->code = 0;
552         icp->checksum = 0;
553         icp->un.gateway = 0; /* Reserved */
554         packetlen = 8;
555
556         /* Compute ICMP checksum here */
557         icp->checksum = in_cksum( (u_short *)icp, packetlen );
558
559         if (isbroadcast(sin))
560                 i = sendbcast(s, (char *)outpack, packetlen);
561         else if (ismulticast(sin))
562                 i = sendmcast(s, (char *)outpack, packetlen, sin);
563         else
564                 i = sendto( s, (char *)outpack, packetlen, 0,
565                            (struct sockaddr *)sin, sizeof(struct sockaddr));
566
567         if( i < 0 || i != packetlen )  {
568                 if( i<0 ) {
569                     logperror("solicitor:sendto");
570                 }
571                 logerr("wrote %s %d chars, ret=%d\n",
572                         sendaddress, packetlen, i );
573         }
574 }
575
576 #ifdef RDISC_SERVER
577 /*
578  *                      A V E R T I S E
579  *
580  * Compose and transmit an ICMP ROUTER ADVERTISEMENT packet.
581  * The IP packet will be added on by the kernel.
582  */
583 void
584 advertise(struct sockaddr_in *sin, int lft)
585 {
586         static u_char outpack[MAXPACKET];
587         struct icmp_ra *rap = (struct icmp_ra *) ALLIGN(outpack);
588         struct icmp_ra_addr *ap;
589         int packetlen, i, cc;
590
591         if (verbose) {
592                 logtrace("Sending advertisement to %s\n",
593                          pr_name(sin->sin_addr));
594         }
595
596         for (i = 0; i < num_interfaces; i++) {
597                 rap->icmp_type = ICMP_ROUTER_ADVERTISEMENT;
598                 rap->icmp_code = 0;
599                 rap->icmp_cksum = 0;
600                 rap->icmp_num_addrs = 0;
601                 rap->icmp_wpa = 2;
602                 rap->icmp_lifetime = htons(lft);
603                 packetlen = 8;
604
605                 /*
606                  * TODO handle multiple logical interfaces per
607                  * physical interface. (increment with rap->icmp_wpa * 4 for
608                  * each address.)
609                  */
610                 ap = (struct icmp_ra_addr *)ALLIGN(outpack + ICMP_MINLEN);
611                 ap->ira_addr = interfaces[i].localaddr.s_addr;
612                 ap->ira_preference = htonl(interfaces[i].preference);
613                 packetlen += rap->icmp_wpa * 4;
614                 rap->icmp_num_addrs++;
615
616                 /* Compute ICMP checksum here */
617                 rap->icmp_cksum = in_cksum( (u_short *)rap, packetlen );
618
619                 if (isbroadcast(sin))
620                         cc = sendbcastif(s, (char *)outpack, packetlen,
621                                         &interfaces[i]);
622                 else if (ismulticast(sin))
623                         cc = sendmcastif( s, (char *)outpack, packetlen, sin,
624                                         &interfaces[i]);
625                 else {
626                         struct interface *ifp = &interfaces[i];
627                         /*
628                          * Verify that the interface matches the destination
629                          * address.
630                          */
631                         if ((sin->sin_addr.s_addr & ifp->netmask.s_addr) ==
632                             (ifp->address.s_addr & ifp->netmask.s_addr)) {
633                                 if (debug) {
634                                         logdebug("Unicast to %s ",
635                                                  pr_name(sin->sin_addr));
636                                         logdebug("on interface %s, %s\n",
637                                                  ifp->name,
638                                                  pr_name(ifp->address));
639                                 }
640                                 cc = sendto( s, (char *)outpack, packetlen, 0,
641                                             (struct sockaddr *)sin,
642                                             sizeof(struct sockaddr));
643                         } else
644                                 cc = packetlen;
645                 }
646                 if( cc < 0 || cc != packetlen )  {
647                         if (cc < 0) {
648                                 logperror("sendto");
649                         } else {
650                                 logerr("wrote %s %d chars, ret=%d\n",
651                                        sendaddress, packetlen, cc );
652                         }
653                 }
654         }
655 }
656 #endif
657
658 /*
659  *                      P R _ T Y P E
660  *
661  * Convert an ICMP "type" field to a printable string.
662  */
663 char *
664 pr_type(int t)
665 {
666         static char *ttab[] = {
667                 "Echo Reply",
668                 "ICMP 1",
669                 "ICMP 2",
670                 "Dest Unreachable",
671                 "Source Quench",
672                 "Redirect",
673                 "ICMP 6",
674                 "ICMP 7",
675                 "Echo",
676                 "Router Advertise",
677                 "Router Solicitation",
678                 "Time Exceeded",
679                 "Parameter Problem",
680                 "Timestamp",
681                 "Timestamp Reply",
682                 "Info Request",
683                 "Info Reply",
684                 "Netmask Request",
685                 "Netmask Reply"
686         };
687
688         if ( t < 0 || t > 16 )
689                 return("OUT-OF-RANGE");
690
691         return(ttab[t]);
692 }
693
694 /*
695  *                      P R _ N A M E
696  *
697  * Return a string name for the given IP address.
698  */
699 char *pr_name(struct in_addr addr)
700 {
701         struct hostent *phe;
702         static char buf[80];
703
704         phe = gethostbyaddr((char *)&addr.s_addr, 4, AF_INET);
705         if (phe == NULL)
706                 return( inet_ntoa(addr));
707         snprintf(buf, sizeof(buf), "%s (%s)", phe->h_name, inet_ntoa(addr));
708         return(buf);
709 }
710
711 /*
712  *                      P R _ P A C K
713  *
714  * Print out the packet, if it came from us.  This logic is necessary
715  * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
716  * which arrive ('tis only fair).  This permits multiple copies of this
717  * program to be run without having intermingled output (or statistics!).
718  */
719 void
720 pr_pack(char *buf, int cc, struct sockaddr_in *from)
721 {
722         struct iphdr *ip;
723         struct icmphdr *icp;
724         int i;
725         int hlen;
726
727         ip = (struct iphdr *) ALLIGN(buf);
728         hlen = ip->ihl << 2;
729         if (cc < hlen + 8) {
730                 if (verbose)
731                         logtrace("packet too short (%d bytes) from %s\n", cc,
732                                  pr_name(from->sin_addr));
733                 return;
734         }
735         cc -= hlen;
736         icp = (struct icmphdr *)ALLIGN(buf + hlen);
737
738         switch (icp->type) {
739         case ICMP_ROUTER_ADVERTISEMENT:
740         {
741                 struct icmp_ra *rap = (struct icmp_ra *)ALLIGN(icp);
742                 struct icmp_ra_addr *ap;
743
744 #ifdef RDISC_SERVER
745                 if (responder)
746                         break;
747 #endif
748
749                 /* TBD verify that the link is multicast or broadcast */
750                 /* XXX Find out the link it came in over? */
751                 if (in_cksum((u_short *)ALLIGN(buf+hlen), cc)) {
752                         if (verbose)
753                                 logtrace("ICMP %s from %s: Bad checksum\n",
754                                          pr_type((int)rap->icmp_type),
755                                          pr_name(from->sin_addr));
756                         return;
757                 }
758                 if (rap->icmp_code != 0) {
759                         if (verbose)
760                                 logtrace("ICMP %s from %s: Code = %d\n",
761                                          pr_type((int)rap->icmp_type),
762                                          pr_name(from->sin_addr),
763                                          rap->icmp_code);
764                         return;
765                 }
766                 if (rap->icmp_num_addrs < 1) {
767                         if (verbose)
768                                 logtrace("ICMP %s from %s: No addresses\n",
769                                          pr_type((int)rap->icmp_type),
770                                          pr_name(from->sin_addr));
771                         return;
772                 }
773                 if (rap->icmp_wpa < 2) {
774                         if (verbose)
775                                 logtrace("ICMP %s from %s: Words/addr = %d\n",
776                                          pr_type((int)rap->icmp_type),
777                                          pr_name(from->sin_addr),
778                                          rap->icmp_wpa);
779                         return;
780                 }
781                 if ((unsigned)cc <
782                     8 + rap->icmp_num_addrs * rap->icmp_wpa * 4) {
783                         if (verbose)
784                                 logtrace("ICMP %s from %s: Too short %d, %d\n",
785                                               pr_type((int)rap->icmp_type),
786                                               pr_name(from->sin_addr),
787                                               cc,
788                                               8 + rap->icmp_num_addrs * rap->icmp_wpa * 4);
789                         return;
790                 }
791
792                 if (verbose)
793                         logtrace("ICMP %s from %s, lifetime %d\n",
794                                       pr_type((int)rap->icmp_type),
795                                       pr_name(from->sin_addr),
796                                       ntohs(rap->icmp_lifetime));
797
798                 /* Check that at least one router address is a neighboor
799                  * on the arriving link.
800                  */
801                 for (i = 0; (unsigned)i < rap->icmp_num_addrs; i++) {
802                         struct in_addr ina;
803                         ap = (struct icmp_ra_addr *)
804                                 ALLIGN(buf + hlen + 8 +
805                                        i * rap->icmp_wpa * 4);
806                         ina.s_addr = ap->ira_addr;
807                         if (verbose)
808                                 logtrace("\taddress %s, preference 0x%x\n",
809                                               pr_name(ina),
810                                               (unsigned int)ntohl(ap->ira_preference));
811                         if (is_directly_connected(ina))
812                                 record_router(ina,
813                                               ntohl(ap->ira_preference),
814                                               ntohs(rap->icmp_lifetime));
815                 }
816                 nreceived++;
817                 if (!forever) {
818                         do_fork();
819                         forever = 1;
820 /*
821  * The next line was added so that the alarm is set for the new procces
822  * Fraser Gardiner Sun Microsystems Australia
823  */
824                         (void) alarm(TIMER_INTERVAL);
825                 }
826                 break;
827         }
828
829 #ifdef RDISC_SERVER
830         case ICMP_ROUTER_SOLICITATION:
831         {
832                 struct sockaddr_in sin;
833
834                 if (!responder)
835                         break;
836
837                 /* TBD verify that the link is multicast or broadcast */
838                 /* XXX Find out the link it came in over? */
839
840                 if (in_cksum((u_short *)ALLIGN(buf+hlen), cc)) {
841                         if (verbose)
842                                 logtrace("ICMP %s from %s: Bad checksum\n",
843                                               pr_type((int)icp->type),
844                                               pr_name(from->sin_addr));
845                         return;
846                 }
847                 if (icp->code != 0) {
848                         if (verbose)
849                                 logtrace("ICMP %s from %s: Code = %d\n",
850                                               pr_type((int)icp->type),
851                                               pr_name(from->sin_addr),
852                                               icp->code);
853                         return;
854                 }
855
856                 if (cc < ICMP_MINLEN) {
857                         if (verbose)
858                                 logtrace("ICMP %s from %s: Too short %d, %d\n",
859                                               pr_type((int)icp->type),
860                                               pr_name(from->sin_addr),
861                                               cc,
862                                               ICMP_MINLEN);
863                         return;
864                 }
865
866                 if (verbose)
867                         logtrace("ICMP %s from %s\n",
868                                       pr_type((int)icp->type),
869                                       pr_name(from->sin_addr));
870
871                 /* Check that ip_src is either a neighboor
872                  * on the arriving link or 0.
873                  */
874                 sin.sin_family = AF_INET;
875                 if (ip->saddr == 0) {
876                         /* If it was sent to the broadcast address we respond
877                          * to the broadcast address.
878                          */
879                         if (IN_CLASSD(ntohl(ip->daddr)))
880                                 sin.sin_addr.s_addr = htonl(0xe0000001);
881                         else
882                                 sin.sin_addr.s_addr = INADDR_BROADCAST;
883                         /* Restart the timer when we broadcast */
884                         left_until_advertise = min_adv_int +
885                                 ((max_adv_int - min_adv_int)
886                                  * (random() % 1000)/1000);
887                 } else {
888                         sin.sin_addr = ip->saddr;
889                         if (!is_directly_connected(sin.sin_addr)) {
890                                 if (verbose)
891                                         logtrace("ICMP %s from %s: source not directly connected\n",
892                                                       pr_type((int)icp->type),
893                                                       pr_name(from->sin_addr));
894                                 break;
895                         }
896                 }
897                 nreceived++;
898                 ntransmitted++;
899                 advertise(&sin, lifetime);
900                 break;
901         }
902 #endif
903         }
904 }
905
906
907 /*
908  *                      I N _ C K S U M
909  *
910  * Checksum routine for Internet Protocol family headers (C Version)
911  *
912  */
913 u_short in_cksum(u_short *addr, int len)
914 {
915         register int nleft = len;
916         register u_short *w = addr;
917         register u_short answer;
918         register int sum = 0;
919
920         /*
921          *  Our algorithm is simple, using a 32 bit accumulator (sum),
922          *  we add sequential 16 bit words to it, and at the end, fold
923          *  back all the carry bits from the top 16 bits into the lower
924          *  16 bits.
925          */
926         while( nleft > 1 )  {
927                 sum += *w++;
928                 nleft -= 2;
929         }
930
931         /* mop up an odd byte, if necessary */
932         if( nleft == 1 )
933                 sum += htons(*(u_char *)w<<8);
934
935         /*
936          * add back carry outs from top 16 bits to low 16 bits
937          */
938         sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
939         sum += (sum >> 16);                     /* add carry */
940         answer = ~sum;                          /* truncate to 16 bits */
941         return (answer);
942 }
943
944 /*
945  *                      F I N I S H
946  *
947  * Print out statistics, and give up.
948  * Heavily buffered STDIO is used here, so that all the statistics
949  * will be written with 1 sys-write call.  This is nice when more
950  * than one copy of the program is running on a terminal;  it prevents
951  * the statistics output from becomming intermingled.
952  */
953 void
954 finish()
955 {
956 #ifdef RDISC_SERVER
957         if (responder) {
958                 int i;
959
960                 /* Send out a packet with a preference so that all
961                  * hosts will know that we are dead.
962                  *
963                  * Wrong comment, wrong code.
964                  *      ttl must be set to 0 instead. --ANK
965                  */
966                 logerr("terminated\n");
967                 ntransmitted++;
968                 advertise(&whereto, 0);
969         }
970 #endif
971         logtrace("\n----%s rdisc Statistics----\n", sendaddress );
972         logtrace("%d packets transmitted, ", ntransmitted );
973         logtrace("%d packets received, ", nreceived );
974         logtrace("\n");
975         (void) fflush(stdout);
976         exit(0);
977 }
978
979 void
980 graceful_finish()
981 {
982         discard_table();
983         finish();
984         exit(0);
985 }
986
987
988 /* From libc/rpc/pmap_rmt.c */
989
990 int
991 sendbcast(int s, char *packet, int packetlen)
992 {
993         int i, cc;
994
995         for (i = 0; i < num_interfaces; i++) {
996                 if ((interfaces[i].flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0)
997                         continue;
998                 cc = sendbcastif(s, packet, packetlen, &interfaces[i]);
999                 if (cc!= packetlen) {
1000                         return (cc);
1001                 }
1002         }
1003         return (packetlen);
1004 }
1005
1006 int
1007 sendbcastif(int s, char *packet, int packetlen, struct interface *ifp)
1008 {
1009         int on;
1010         int cc;
1011         struct sockaddr_in baddr;
1012
1013         baddr.sin_family = AF_INET;
1014         baddr.sin_addr = ifp->bcastaddr;
1015         if (debug)
1016                 logdebug("Broadcast to %s\n",
1017                          pr_name(baddr.sin_addr));
1018         on = 1;
1019         setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on));
1020         cc = sendto(s, packet, packetlen, 0,
1021                     (struct sockaddr *)&baddr, sizeof (struct sockaddr));
1022         if (cc!= packetlen) {
1023                 logperror("sendbcast: sendto");
1024                 logerr("Cannot send broadcast packet to %s\n",
1025                        pr_name(baddr.sin_addr));
1026         }
1027         on = 0;
1028         setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on));
1029         return (cc);
1030 }
1031
1032 int
1033 sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *sin)
1034 {
1035         int i, cc;
1036
1037         for (i = 0; i < num_interfaces; i++) {
1038                 if ((interfaces[i].flags & (IFF_BROADCAST|IFF_POINTOPOINT|IFF_MULTICAST)) == 0)
1039                         continue;
1040                 cc = sendmcastif(s, packet, packetlen, sin, &interfaces[i]);
1041                 if (cc!= packetlen) {
1042                         return (cc);
1043                 }
1044         }
1045         return (packetlen);
1046 }
1047
1048 int
1049 sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin,
1050             struct interface *ifp)
1051 {
1052         int cc;
1053         struct ip_mreqn mreq;
1054
1055         memset(&mreq, 0, sizeof(mreq));
1056         mreq.imr_ifindex = ifp->ifindex;
1057         mreq.imr_address = ifp->localaddr;
1058         if (debug)
1059                 logdebug("Multicast to interface %s, %s\n",
1060                          ifp->name,
1061                          pr_name(mreq.imr_address));
1062         if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
1063                        (char *)&mreq,
1064                        sizeof(mreq)) < 0) {
1065                 logperror("setsockopt (IP_MULTICAST_IF)");
1066                 logerr("Cannot send multicast packet over interface %s, %s\n",
1067                        ifp->name,
1068                        pr_name(mreq.imr_address));
1069                 return (-1);
1070         }
1071         cc = sendto(s, packet, packetlen, 0,
1072                     (struct sockaddr *)sin, sizeof (struct sockaddr));
1073         if (cc!= packetlen) {
1074                 logperror("sendmcast: sendto");
1075                 logerr("Cannot send multicast packet over interface %s, %s\n",
1076                        ifp->name, pr_name(mreq.imr_address));
1077         }
1078         return (cc);
1079 }
1080
1081 void
1082 init()
1083 {
1084         initifs();
1085 #ifdef RDISC_SERVER
1086         {
1087                 int i;
1088                 for (i = 0; i < interfaces_size; i++)
1089                         interfaces[i].preference = preference;
1090         }
1091 #endif
1092 }
1093
1094 void
1095 initifs()
1096 {
1097         int     sock;
1098         struct ifconf ifc;
1099         struct ifreq ifreq, *ifr;
1100         struct sockaddr_in *sin;
1101         int n, i;
1102         char *buf;
1103         int numifs;
1104         unsigned bufsize;
1105
1106         sock = socket(AF_INET, SOCK_DGRAM, 0);
1107         if (sock < 0) {
1108                 logperror("initifs: socket");
1109                 return;
1110         }
1111 #ifdef SIOCGIFNUM
1112         if (ioctl(sock, SIOCGIFNUM, (char *)&numifs) < 0) {
1113                 numifs = MAXIFS;
1114         }
1115 #else
1116         numifs = MAXIFS;
1117 #endif
1118         bufsize = numifs * sizeof(struct ifreq);
1119         buf = (char *)malloc(bufsize);
1120         if (buf == NULL) {
1121                 logerr("out of memory\n");
1122                 (void) close(sock);
1123                 return;
1124         }
1125         if (interfaces)
1126                 interfaces = (struct interface *)ALLIGN(realloc((char *)interfaces,
1127                                          numifs * sizeof(struct interface)));
1128         else
1129                 interfaces = (struct interface *)ALLIGN(malloc(numifs *
1130                                                 sizeof(struct interface)));
1131         if (interfaces == NULL) {
1132                 logerr("out of memory\n");
1133                 (void) close(sock);
1134                 (void) free(buf);
1135                 return;
1136         }
1137         interfaces_size = numifs;
1138
1139         ifc.ifc_len = bufsize;
1140         ifc.ifc_buf = buf;
1141         if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
1142                 logperror("initifs: ioctl (get interface configuration)");
1143                 (void) close(sock);
1144                 (void) free(buf);
1145                 return;
1146         }
1147         ifr = ifc.ifc_req;
1148         for (i = 0, n = ifc.ifc_len/sizeof (struct ifreq); n > 0; n--, ifr++) {
1149                 ifreq = *ifr;
1150                 if (strlen(ifreq.ifr_name) >= IFNAMSIZ)
1151                         continue;
1152                 if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
1153                         logperror("initifs: ioctl (get interface flags)");
1154                         continue;
1155                 }
1156                 if (ifr->ifr_addr.sa_family != AF_INET)
1157                         continue;
1158                 if ((ifreq.ifr_flags & IFF_UP) == 0)
1159                         continue;
1160                 if (ifreq.ifr_flags & IFF_LOOPBACK)
1161                         continue;
1162                 if ((ifreq.ifr_flags & (IFF_MULTICAST|IFF_BROADCAST|IFF_POINTOPOINT)) == 0)
1163                         continue;
1164                 strncpy(interfaces[i].name, ifr->ifr_name, IFNAMSIZ-1);
1165
1166                 sin = (struct sockaddr_in *)ALLIGN(&ifr->ifr_addr);
1167                 interfaces[i].localaddr = sin->sin_addr;
1168                 interfaces[i].flags = ifreq.ifr_flags;
1169                 interfaces[i].netmask.s_addr = (__u32)0xffffffff;
1170                 if (ioctl(sock, SIOCGIFINDEX, (char *)&ifreq) < 0) {
1171                         logperror("initifs: ioctl (get ifindex)");
1172                         continue;
1173                 }
1174                 interfaces[i].ifindex = ifreq.ifr_ifindex;
1175                 if (ifreq.ifr_flags & IFF_POINTOPOINT) {
1176                         if (ioctl(sock, SIOCGIFDSTADDR, (char *)&ifreq) < 0) {
1177                                 logperror("initifs: ioctl (get destination addr)");
1178                                 continue;
1179                         }
1180                         sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr);
1181                         /* A pt-pt link is identified by the remote address */
1182                         interfaces[i].address = sin->sin_addr;
1183                         interfaces[i].remoteaddr = sin->sin_addr;
1184                         /* Simulate broadcast for pt-pt */
1185                         interfaces[i].bcastaddr = sin->sin_addr;
1186                         interfaces[i].flags |= IFF_BROADCAST;
1187                 } else {
1188                         /* Non pt-pt links are identified by the local address */
1189                         interfaces[i].address = interfaces[i].localaddr;
1190                         interfaces[i].remoteaddr = interfaces[i].address;
1191                         if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
1192                                 logperror("initifs: ioctl (get netmask)");
1193                                 continue;
1194                         }
1195                         sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr);
1196                         interfaces[i].netmask = sin->sin_addr;
1197                         if (ifreq.ifr_flags & IFF_BROADCAST) {
1198                                 if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
1199                                         logperror("initifs: ioctl (get broadcast address)");
1200                                         continue;
1201                                 }
1202                                 sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr);
1203                                 interfaces[i].bcastaddr = sin->sin_addr;
1204                         }
1205                 }
1206 #ifdef notdef
1207                 if (debug)
1208                         logdebug("Found interface %s, flags 0x%x\n",
1209                                  pr_name(interfaces[i].localaddr),
1210                                  interfaces[i].flags);
1211 #endif
1212                 i++;
1213         }
1214         num_interfaces = i;
1215 #ifdef notdef
1216         if (debug)
1217                 logdebug("Found %d interfaces\n", num_interfaces);
1218 #endif
1219         (void) close(sock);
1220         (void) free(buf);
1221 }
1222
1223 int
1224 join(int sock, struct sockaddr_in *sin)
1225 {
1226         int i, j;
1227         struct ip_mreqn mreq;
1228         int joined[num_interfaces];
1229
1230         memset(joined, 0, sizeof(joined));
1231
1232         if (isbroadcast(sin))
1233                 return (0);
1234
1235         mreq.imr_multiaddr = sin->sin_addr;
1236         for (i = 0; i < num_interfaces; i++) {
1237                 for (j = 0; j < i; j++) {
1238                         if (joined[j] == interfaces[i].ifindex)
1239                                 break;
1240                 }
1241                 if (j != i)
1242                         continue;
1243
1244                 mreq.imr_ifindex = interfaces[i].ifindex;
1245                 mreq.imr_address.s_addr = 0;
1246
1247                 if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
1248                                (char *)&mreq, sizeof(mreq)) < 0) {
1249                         logperror("setsockopt (IP_ADD_MEMBERSHIP)");
1250                         return (-1);
1251                 }
1252
1253                 joined[i] = interfaces[i].ifindex;
1254         }
1255         return (0);
1256 }
1257
1258 int support_multicast()
1259 {
1260         int sock;
1261         u_char ttl = 1;
1262
1263         sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1264         if (sock < 0) {
1265                 logperror("support_multicast: socket");
1266                 return (0);
1267         }
1268
1269         if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL,
1270                        (char *)&ttl, sizeof(ttl)) < 0) {
1271                 (void) close(sock);
1272                 return (0);
1273         }
1274         (void) close(sock);
1275         return (1);
1276 }
1277
1278 int
1279 is_directly_connected(struct in_addr in)
1280 {
1281         int i;
1282
1283         for (i = 0; i < num_interfaces; i++) {
1284                 /* Check that the subnetwork numbers match */
1285
1286                 if ((in.s_addr & interfaces[i].netmask.s_addr ) ==
1287                     (interfaces[i].remoteaddr.s_addr & interfaces[i].netmask.s_addr))
1288                         return (1);
1289         }
1290         return (0);
1291 }
1292
1293 /*
1294  * TABLES
1295  */
1296 struct table {
1297         struct in_addr  router;
1298         int             preference;
1299         int             remaining_time;
1300         int             in_kernel;
1301         struct table    *next;
1302 };
1303
1304 struct table *table;
1305
1306 struct table *
1307 find_router(struct in_addr addr)
1308 {
1309         struct table *tp;
1310
1311         tp = table;
1312         while (tp) {
1313                 if (tp->router.s_addr == addr.s_addr)
1314                         return (tp);
1315                 tp = tp->next;
1316         }
1317         return (NULL);
1318 }
1319
1320 int max_preference(void)
1321 {
1322         struct table *tp;
1323         int max = (int)INELIGIBLE_PREF;
1324
1325         tp = table;
1326         while (tp) {
1327                 if (tp->preference > max)
1328                         max = tp->preference;
1329                 tp = tp->next;
1330         }
1331         return (max);
1332 }
1333
1334
1335 /* Note: this might leave the kernel with no default route for a short time. */
1336 void
1337 age_table(int time)
1338 {
1339         struct table **tpp, *tp;
1340         int recalculate_max = 0;
1341         int max = max_preference();
1342
1343         tpp = &table;
1344         while (*tpp != NULL) {
1345                 tp = *tpp;
1346                 tp->remaining_time -= time;
1347                 if (tp->remaining_time <= 0) {
1348                         *tpp = tp->next;
1349                         if (tp->in_kernel)
1350                                 del_route(tp->router);
1351                         if (best_preference &&
1352                             tp->preference == max)
1353                                 recalculate_max++;
1354                         free((char *)tp);
1355                 } else {
1356                         tpp = &tp->next;
1357                 }
1358         }
1359         if (recalculate_max) {
1360                 int max = max_preference();
1361
1362                 if (max != INELIGIBLE_PREF) {
1363                         tp = table;
1364                         while (tp) {
1365                                 if (tp->preference == max && !tp->in_kernel) {
1366                                         add_route(tp->router);
1367                                         tp->in_kernel++;
1368                                 }
1369                                 tp = tp->next;
1370                         }
1371                 }
1372         }
1373 }
1374
1375 void discard_table(void)
1376 {
1377         struct table **tpp, *tp;
1378
1379         tpp = &table;
1380         while (*tpp != NULL) {
1381                 tp = *tpp;
1382                 *tpp = tp->next;
1383                 if (tp->in_kernel)
1384                         del_route(tp->router);
1385                 free((char *)tp);
1386         }
1387 }
1388
1389
1390 void
1391 record_router(struct in_addr router, int preference, int ttl)
1392 {
1393         struct table *tp;
1394         int old_max = max_preference();
1395         int changed_up = 0;     /* max preference could have increased */
1396         int changed_down = 0;   /* max preference could have decreased */
1397
1398         if (ttl < 4)
1399                 preference = INELIGIBLE_PREF;
1400
1401         if (debug)
1402                 logdebug("Recording %s, ttl %d, preference 0x%x\n",
1403                          pr_name(router),
1404                          ttl,
1405                          preference);
1406         tp = find_router(router);
1407         if (tp) {
1408                 if (tp->preference > preference &&
1409                     tp->preference == old_max)
1410                         changed_down++;
1411                 else if (preference > tp->preference)
1412                         changed_up++;
1413                 tp->preference = preference;
1414                 tp->remaining_time = ttl;
1415         } else {
1416                 if (preference > old_max)
1417                         changed_up++;
1418                 tp = (struct table *)ALLIGN(malloc(sizeof(struct table)));
1419                 if (tp == NULL) {
1420                         logerr("Out of memory\n");
1421                         return;
1422                 }
1423                 tp->router = router;
1424                 tp->preference = preference;
1425                 tp->remaining_time = ttl;
1426                 tp->in_kernel = 0;
1427                 tp->next = table;
1428                 table = tp;
1429         }
1430         if (!tp->in_kernel &&
1431             (!best_preference || tp->preference == max_preference()) &&
1432             tp->preference != INELIGIBLE_PREF) {
1433                 add_route(tp->router);
1434                 tp->in_kernel++;
1435         }
1436         if (tp->preference == INELIGIBLE_PREF && tp->in_kernel) {
1437                 del_route(tp->router);
1438                 tp->in_kernel = 0;
1439         }
1440         if (best_preference && changed_down) {
1441                 /* Check if we should add routes */
1442                 int new_max = max_preference();
1443                 if (new_max != INELIGIBLE_PREF) {
1444                         tp = table;
1445                         while (tp) {
1446                                 if (tp->preference == new_max &&
1447                                     !tp->in_kernel) {
1448                                         add_route(tp->router);
1449                                         tp->in_kernel++;
1450                                 }
1451                                 tp = tp->next;
1452                         }
1453                 }
1454         }
1455         if (best_preference && (changed_up || changed_down)) {
1456                 /* Check if we should remove routes already in the kernel */
1457                 int new_max = max_preference();
1458                 tp = table;
1459                 while (tp) {
1460                         if (tp->preference < new_max && tp->in_kernel) {
1461                                 del_route(tp->router);
1462                                 tp->in_kernel = 0;
1463                         }
1464                         tp = tp->next;
1465                 }
1466         }
1467 }
1468
1469 void
1470 add_route(struct in_addr addr)
1471 {
1472         if (debug)
1473                 logdebug("Add default route to %s\n", pr_name(addr));
1474         rtioctl(addr, SIOCADDRT);
1475 }
1476
1477 void
1478 del_route(struct in_addr addr)
1479 {
1480         if (debug)
1481                 logdebug("Delete default route to %s\n", pr_name(addr));
1482         rtioctl(addr, SIOCDELRT);
1483 }
1484
1485 void
1486 rtioctl(struct in_addr addr, int op)
1487 {
1488         int sock;
1489         struct rtentry rt;
1490         struct sockaddr_in *sin;
1491
1492         memset((char *)&rt, 0, sizeof(struct rtentry));
1493         rt.rt_dst.sa_family = AF_INET;
1494         rt.rt_gateway.sa_family = AF_INET;
1495         rt.rt_genmask.sa_family = AF_INET;
1496         sin = (struct sockaddr_in *)ALLIGN(&rt.rt_gateway);
1497         sin->sin_addr = addr;
1498         rt.rt_flags = RTF_UP | RTF_GATEWAY;
1499
1500         sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1501         if (sock < 0) {
1502                 logperror("rtioctl: socket");
1503                 return;
1504         }
1505         if (ioctl(sock, op, (char *)&rt) < 0) {
1506                 if (!(op == SIOCADDRT && errno == EEXIST))
1507                         logperror("ioctl (add/delete route)");
1508         }
1509         (void) close(sock);
1510 }
1511
1512 /*
1513  * LOGGER
1514  */
1515
1516 void initlog(void)
1517 {
1518         logging++;
1519         openlog("in.rdiscd", LOG_PID | LOG_CONS, LOG_DAEMON);
1520 }
1521
1522
1523 void
1524 logperror(char *str)
1525 {
1526         if (logging)
1527                 syslog(LOG_ERR, "%s: %m", str);
1528         else
1529                 (void) fprintf(stderr, "%s: %s\n", str, strerror(errno));
1530 }