8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / usr.sbin / in.rdisc / in.rdisc.c
blobbf0aec2212a0acdebcc2573db4832e69bb3077fc
1 /*
2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
6 /*
7 * Copyright (c) 1987 Regents of the University of California.
8 * All rights reserved.
10 * Redistribution and use in source and binary forms are permitted
11 * provided that the above copyright notice and this paragraph are
12 * duplicated in all such forms and that any documentation,
13 * advertising materials, and other materials related to such
14 * distribution and use acknowledge that the software was developed
15 * by the University of California, Berkeley. The name of the
16 * University may not be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
20 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23 #pragma ident "%Z%%M% %I% %E% SMI"
25 #include <stdio.h>
26 #include <errno.h>
27 #include <signal.h>
28 #include <sys/types.h>
29 #include <sys/time.h>
30 #include <sys/stat.h>
32 #include <sys/param.h>
33 #include <sys/socket.h>
34 #include <sys/file.h>
36 #include <sys/ioctl.h>
37 #include <net/if.h>
39 #include <netinet/in_systm.h>
40 #include <netinet/in.h>
41 #include <netinet/ip.h>
42 #include <netinet/ip_icmp.h>
43 #include <netdb.h>
44 #include <arpa/inet.h>
46 #include <fcntl.h>
47 #include <strings.h>
48 #include <stdlib.h>
49 #include <unistd.h>
50 #include <assert.h>
52 #ifdef lint
53 #define ALIGN(ptr) (ptr ? 0 : 0)
54 #else
55 #define ALIGN(ptr) (ptr)
56 #endif
58 #ifdef SYSV
59 #define signal(s, f) sigset(s, (void (*)(int))f)
60 #define random() rand()
61 #endif
63 #define ALL_HOSTS_ADDRESS "224.0.0.1"
64 #define ALL_ROUTERS_ADDRESS "224.0.0.2"
66 #define MAXIFS 256
68 /* For router advertisement */
69 struct icmp_ra {
70 uchar_t icmp_type; /* type of message, see below */
71 uchar_t icmp_code; /* type sub code */
72 ushort_t icmp_cksum; /* ones complement cksum of struct */
73 uchar_t icmp_num_addrs;
74 uchar_t icmp_wpa; /* Words per address */
75 short icmp_lifetime;
78 struct icmp_ra_addr {
79 ulong_t addr;
80 ulong_t preference;
83 /* Router constants */
84 #define MAX_INITIAL_ADVERT_INTERVAL 16
85 #define MAX_INITIAL_ADVERTISEMENTS 3
86 #define MAX_RESPONSE_DELAY 2 /* Not used */
88 /* Host constants */
89 #define MAX_SOLICITATIONS 3
90 #define SOLICITATION_INTERVAL 3
91 #define MAX_SOLICITATION_DELAY 1 /* Not used */
93 #define IGNORE_PREFERENCE 0x80000000 /* Maximum negative */
95 #define MAX_ADV_INT 600
99 * A doubly linked list of all physical interfaces that each contain a
100 * doubly linked list of logical interfaces aka IP addresses.
102 struct phyint {
103 char pi_name[IFNAMSIZ]; /* Used to identify it */
104 int pi_state; /* See below */
105 struct logint *pi_logical_first;
106 struct logint *pi_logical_last;
107 struct phyint *pi_next;
108 struct phyint *pi_prev;
111 struct logint {
112 char li_name[IFNAMSIZ]; /* Used to identify it */
113 int li_state; /* See below */
114 struct in_addr li_address; /* Used to identify the interface */
115 struct in_addr li_localaddr; /* Actual address of the interface */
116 int li_preference;
117 int li_index; /* interface index (SIOCGLIFINDEX) */
118 uint64_t li_flags;
119 struct in_addr li_bcastaddr;
120 struct in_addr li_remoteaddr;
121 struct in_addr li_netmask;
122 struct logint *li_next; /* Next logical for this physical */
123 struct logint *li_prev; /* Prev logical for this physical */
124 struct phyint *li_physical; /* Back pointer */
127 struct phyint *phyint;
128 int num_usable_interfaces; /* Num used for sending/receiving */
131 * State bits
133 #define ST_MARKED 0x01 /* To determine removed interfaces */
134 #define ST_JOINED 0x02 /* Joined multicast group */
135 #define ST_DELETED 0x04 /* Interface should be ignored */
137 /* Function prototypes */
138 static void solicitor(struct sockaddr_in *sin);
139 static void advertise(struct sockaddr_in *sin);
141 static void age_table(int time);
142 static void flush_unreachable_routers(void);
143 static void record_router(struct in_addr router, long preference, int ttl);
145 static void add_route(struct in_addr addr);
146 static void del_route(struct in_addr addr);
147 static void rtioctl(struct in_addr addr, int op);
149 static int support_multicast(void);
150 static int sendbcast(int s, char *packet, int packetlen);
151 static int sendbcastif(int s, char *packet, int packetlen,
152 struct logint *li);
153 static int sendmcast(int s, char *packet, int packetlen,
154 struct sockaddr_in *sin);
155 static int sendmcastif(int s, char *packet, int packetlen,
156 struct sockaddr_in *sin, struct logint *li);
158 static int ismulticast(struct sockaddr_in *sin);
159 static int isbroadcast(struct sockaddr_in *sin);
160 int in_cksum(ushort_t *addr, int len);
161 static struct logint *find_directly_connected_logint(struct in_addr in,
162 struct phyint *pi);
163 static void force_preference(int preference);
165 static void timer(void);
166 static void finish(void);
167 static void report(void);
168 static void report_interfaces(void);
169 static void report_routes(void);
170 static void reinitifs(void);
172 static struct phyint *find_phyint(char *name);
173 static struct phyint *add_phyint(char *name);
174 static void free_phyint(struct phyint *pi);
175 static struct logint *find_logint(struct phyint *pi, char *name);
176 static struct logint *add_logint(struct phyint *pi, char *name);
177 static void free_logint(struct logint *li);
179 static void deleted_phyint(struct phyint *pi, int s,
180 struct sockaddr_in *joinaddr);
181 static void added_logint(struct logint *li, int s,
182 struct sockaddr_in *joinaddr);
183 static void deleted_logint(struct logint *li, struct logint *newli, int s,
184 struct sockaddr_in *joinaddr);
186 static int initifs(int s, struct sockaddr_in *joinaddr, int preference);
187 static boolean_t getconfig(int sock, uint64_t if_flags, struct sockaddr *addr,
188 struct ifreq *ifr, struct logint *li);
190 static void pr_pack(char *buf, int cc, struct sockaddr_in *from);
191 char *pr_name(struct in_addr addr);
192 char *pr_type(int t);
194 static void initlog(void);
195 void logerr(), logtrace(), logdebug(), logperror();
197 /* Local variables */
199 #define MAXPACKET 4096 /* max packet size */
200 uchar_t packet[MAXPACKET];
202 char usage[] =
203 "Usage: rdisc [-s] [-v] [-f] [-a] [send_address] [receive_address]\n"
204 " rdisc -r [-v] [-p <preference>] [-T <secs>] \n"
205 " [send_address] [receive_address]\n";
208 int s; /* Socket file descriptor */
209 struct sockaddr_in whereto; /* Address to send to */
210 struct sockaddr_in g_joinaddr; /* Address to receive on */
211 char *sendaddress, *recvaddress; /* For logging purposes only */
213 /* Common variables */
214 int verbose = 0;
215 int debug = 0;
216 int trace = 0;
217 int start_solicit = 0; /* -s parameter set */
218 int solicit = 0; /* Are we currently sending solicitations? */
219 int responder;
220 int ntransmitted = 0;
221 int nreceived = 0;
222 int forever = 0; /* Never give up on host. If 0 defer fork until */
223 /* first response. */
225 /* Router variables */
226 int max_adv_int = MAX_ADV_INT;
227 int min_adv_int;
228 int lifetime;
229 int initial_advert_interval = MAX_INITIAL_ADVERT_INTERVAL;
230 int initial_advertisements = MAX_INITIAL_ADVERTISEMENTS;
231 ulong_t g_preference = 0; /* Setable with -p option */
233 /* Host variables */
234 int max_solicitations = MAX_SOLICITATIONS;
235 unsigned int solicitation_interval = SOLICITATION_INTERVAL;
236 int best_preference = 1; /* Set to record only the router(s) with the */
237 /* best preference in the kernel. Not set */
238 /* puts all routes in the kernel. */
241 static void
242 prusage()
244 (void) fprintf(stderr, usage);
245 exit(1);
248 static int sock = -1;
250 static void
251 do_fork()
253 int t;
255 if (trace)
256 return;
258 if (fork())
259 exit(0);
260 for (t = 0; t < 20; t++)
261 if (t != s)
262 (void) close(t);
263 sock = -1;
264 (void) open("/", 0);
265 (void) dup2(0, 1);
266 (void) dup2(0, 2);
267 #ifndef SYSV
268 t = open("/dev/tty", 2);
269 if (t >= 0) {
270 (void) ioctl(t, TIOCNOTTY, (char *)0);
271 (void) close(t);
273 #else
274 (void) setpgrp();
275 #endif
276 initlog();
280 * M A I N
283 main(int argc, char *argv[])
285 #ifndef SYSV
286 struct sigvec sv;
287 #endif
288 struct sockaddr_in from;
289 char **av = argv;
290 struct sockaddr_in *to = &whereto;
291 ulong_t val;
293 min_adv_int = (max_adv_int * 3 / 4);
294 lifetime = (3*max_adv_int);
296 argc--, av++;
297 while (argc > 0 && *av[0] == '-') {
298 while (*++av[0])
299 switch (*av[0]) {
300 case 'd':
301 debug = 1;
302 break;
303 case 't':
304 trace = 1;
305 break;
306 case 'v':
307 verbose++;
308 break;
309 case 's':
310 start_solicit = solicit = 1;
311 break;
312 case 'r':
313 responder = 1;
314 break;
315 case 'a':
316 best_preference = 0;
317 break;
318 case 'b':
319 best_preference = 1;
320 break;
321 case 'f':
322 forever = 1;
323 break;
324 case 'T':
325 argc--, av++;
326 if (argc != 0) {
327 val = strtol(av[0], (char **)NULL, 0);
328 if (val < 4 || val > 1800) {
329 (void) fprintf(stderr,
330 "Bad Max Advertisement Interval\n");
331 exit(1);
333 max_adv_int = val;
334 min_adv_int = (max_adv_int * 3 / 4);
335 lifetime = (3*max_adv_int);
336 } else {
337 prusage();
338 /* NOTREACHED */
340 goto next;
341 case 'p':
342 argc--, av++;
343 if (argc != 0) {
344 val = strtoul(av[0], (char **)NULL, 0);
345 g_preference = val;
346 } else {
347 prusage();
348 /* NOTREACHED */
350 goto next;
351 default:
352 prusage();
353 /* NOTREACHED */
355 next:
356 argc--, av++;
358 if (argc < 1) {
359 if (support_multicast()) {
360 if (responder)
361 sendaddress = ALL_HOSTS_ADDRESS;
362 else
363 sendaddress = ALL_ROUTERS_ADDRESS;
364 } else
365 sendaddress = "255.255.255.255";
366 } else {
367 sendaddress = av[0];
368 argc--;
370 if (argc < 1) {
371 if (support_multicast()) {
372 if (responder)
373 recvaddress = ALL_ROUTERS_ADDRESS;
374 else
375 recvaddress = ALL_HOSTS_ADDRESS;
376 } else
377 recvaddress = "255.255.255.255";
378 } else {
379 recvaddress = av[0];
380 argc--;
382 if (argc != 0) {
383 (void) fprintf(stderr, "Extra paramaters\n");
384 prusage();
385 /* NOTREACHED */
388 if (solicit && responder) {
389 prusage();
390 /* NOTREACHED */
393 if (!(solicit && !forever)) {
394 do_fork();
397 bzero((char *)&whereto, sizeof (struct sockaddr_in));
398 to->sin_family = AF_INET;
399 to->sin_addr.s_addr = inet_addr(sendaddress);
400 if (to->sin_addr.s_addr == (unsigned long)-1) {
401 logerr("in.rdisc: bad address %s\n", sendaddress);
402 exit(1);
405 bzero((char *)&g_joinaddr, sizeof (struct sockaddr_in));
406 g_joinaddr.sin_family = AF_INET;
407 g_joinaddr.sin_addr.s_addr = inet_addr(recvaddress);
408 if (g_joinaddr.sin_addr.s_addr == (unsigned long)-1) {
409 logerr("in.rdisc: bad address %s\n", recvaddress);
410 exit(1);
413 if (responder) {
414 #ifdef SYSV
415 srand((int)gethostid());
416 #else
417 srandom((int)gethostid());
418 #endif
421 if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
422 logperror("socket");
423 exit(5);
426 #ifdef SYSV
427 setvbuf(stdout, NULL, _IOLBF, 0);
428 #else
429 setlinebuf(stdout);
430 #endif
432 (void) signal(SIGINT, finish);
433 (void) signal(SIGTERM, finish);
434 (void) signal(SIGHUP, reinitifs);
435 (void) signal(SIGUSR1, report);
437 if (initifs(s, &g_joinaddr, g_preference) < 0) {
438 logerr("Failed initializing interfaces\n");
439 exit(2);
443 * If there are no usable interfaces and we are soliciting
444 * waiting for to return an exit code (i.e. forever isn't set)
445 * give up immediately.
447 if (num_usable_interfaces == 0 && solicit && !forever) {
448 logerr("in.rdisc: No interfaces up\n");
449 exit(5);
452 #ifdef SYSV
453 (void) signal(SIGALRM, timer);
454 #else
456 * Make sure that this signal actually interrupts (rather than
457 * restarts) the recvfrom call below.
459 sv.sv_handler = timer;
460 sv.sv_mask = 0;
461 sv.sv_flags = SV_INTERRUPT;
462 (void) sigvec(SIGALRM, &sv, (struct sigvec *)NULL);
463 #endif
464 timer(); /* start things going */
466 for (;;) {
467 int len = sizeof (packet);
468 socklen_t fromlen = (socklen_t)sizeof (from);
469 int cc;
470 sigset_t newmask, oldmask;
472 if ((cc = recvfrom(s, (char *)packet, len, 0,
473 (struct sockaddr *)&from,
474 &fromlen)) < 0) {
475 if (errno == EINTR)
476 continue;
477 logperror("recvfrom");
478 continue;
480 /* Block all signals while processing */
481 (void) sigfillset(&newmask);
482 (void) sigprocmask(SIG_SETMASK, &newmask, &oldmask);
483 pr_pack((char *)packet, cc, &from);
484 (void) sigprocmask(SIG_SETMASK, &oldmask, NULL);
486 /* NOTREACHED */
489 static void
490 report(void)
492 report_interfaces();
493 report_routes();
496 #define TIMER_INTERVAL 6
497 #define GETIFCONF_TIMER 30
499 static int left_until_advertise;
501 /* Called every TIMER_INTERVAL */
502 static void
503 timer(void)
505 static int time;
506 static int left_until_getifconf;
507 static int left_until_solicit;
509 time += TIMER_INTERVAL;
511 left_until_getifconf -= TIMER_INTERVAL;
512 left_until_advertise -= TIMER_INTERVAL;
513 left_until_solicit -= TIMER_INTERVAL;
515 if (left_until_getifconf < 0) {
516 (void) initifs(s, &g_joinaddr, g_preference);
517 left_until_getifconf = GETIFCONF_TIMER;
519 if (responder && left_until_advertise <= 0) {
520 ntransmitted++;
521 advertise(&whereto);
522 if (ntransmitted < initial_advertisements)
523 left_until_advertise = initial_advert_interval;
524 else
525 left_until_advertise = min_adv_int +
526 ((max_adv_int - min_adv_int) *
527 (random() % 1000)/1000);
528 } else if (solicit && left_until_solicit <= 0) {
529 if (ntransmitted < max_solicitations) {
530 ntransmitted++;
531 solicitor(&whereto);
532 left_until_solicit = solicitation_interval;
533 } else {
534 solicit = 0;
535 if (!forever && nreceived == 0)
536 exit(5);
539 age_table(TIMER_INTERVAL);
540 (void) alarm(TIMER_INTERVAL);
544 * S O L I C I T O R
546 * Compose and transmit an ICMP ROUTER SOLICITATION REQUEST packet.
547 * The IP packet will be added on by the kernel.
549 static void
550 solicitor(struct sockaddr_in *sin)
552 static uchar_t outpack[MAXPACKET];
553 register struct icmp *icp = (struct icmp *)ALIGN(outpack);
554 int packetlen, i;
556 if (verbose) {
557 logtrace("Sending solicitation to %s\n",
558 pr_name(sin->sin_addr));
560 icp->icmp_type = ICMP_ROUTERSOLICIT;
561 icp->icmp_code = 0;
562 icp->icmp_cksum = 0;
563 icp->icmp_void = 0; /* Reserved */
564 packetlen = 8;
566 /* Compute ICMP checksum here */
567 icp->icmp_cksum = in_cksum((ushort_t *)icp, packetlen);
569 if (isbroadcast(sin))
570 i = sendbcast(s, (char *)outpack, packetlen);
571 else if (ismulticast(sin))
572 i = sendmcast(s, (char *)outpack, packetlen, sin);
573 else {
574 struct logint *li;
576 li = find_directly_connected_logint(sin->sin_addr, NULL);
577 if (li != NULL && (li->li_flags & IFF_NORTEXCH)) {
578 if (verbose) {
579 logtrace("Suppressing sending %s on %s "
580 "(no route exchange on interface)\n",
581 pr_type((int)icp->icmp_type), li->li_name);
583 return;
584 } else {
585 i = sendto(s, (char *)outpack, packetlen, 0,
586 (struct sockaddr *)sin, sizeof (struct sockaddr));
590 if (i < 0 || i != packetlen) {
591 if (i < 0) {
592 logperror("sendto");
594 logerr("wrote %s %d chars, ret=%d\n",
595 sendaddress, packetlen, i);
600 * A D V E R T I S E
602 * Compose and transmit an ICMP ROUTER ADVERTISEMENT packet.
603 * The IP packet will be added on by the kernel.
605 static void
606 advertise(struct sockaddr_in *sin)
608 struct phyint *pi;
609 struct logint *li, *li_tmp;
610 static uchar_t outpack[MAXPACKET];
611 register struct icmp_ra *rap = (struct icmp_ra *)ALIGN(outpack);
612 struct icmp_ra_addr *ap;
613 int packetlen, cc;
615 if (verbose) {
616 logtrace("Sending advertisement to %s\n",
617 pr_name(sin->sin_addr));
620 for (pi = phyint; pi != NULL; pi = pi->pi_next) {
621 rap->icmp_type = ICMP_ROUTERADVERT;
622 rap->icmp_code = 0;
623 rap->icmp_cksum = 0;
624 rap->icmp_num_addrs = 0;
625 rap->icmp_wpa = 2;
626 rap->icmp_lifetime = htons(lifetime);
627 packetlen = ICMP_MINLEN;
629 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
630 if (li->li_state & ST_DELETED)
631 continue;
634 * XXX Just truncate the list of addresses.
635 * Should probably send multiple packets.
637 if (packetlen + rap->icmp_wpa * 4 > sizeof (outpack)) {
638 if (debug)
639 logdebug("full packet: %d addresses\n",
640 rap->icmp_num_addrs);
641 break;
643 ap = (struct icmp_ra_addr *)ALIGN(outpack + packetlen);
644 ap->addr = li->li_localaddr.s_addr;
645 ap->preference = htonl(li->li_preference);
646 packetlen += rap->icmp_wpa * 4;
647 rap->icmp_num_addrs++;
650 if (rap->icmp_num_addrs == 0)
651 continue;
653 /* Compute ICMP checksum here */
654 rap->icmp_cksum = in_cksum((ushort_t *)rap, packetlen);
656 if (isbroadcast(sin))
657 cc = sendbcastif(s, (char *)outpack, packetlen,
658 pi->pi_logical_first);
659 else if (ismulticast(sin))
660 cc = sendmcastif(s, (char *)outpack, packetlen, sin,
661 pi->pi_logical_first);
662 else {
664 * Verify that the physical interface matches the
665 * destination address.
667 li_tmp = find_directly_connected_logint(sin->sin_addr,
668 pi);
669 if (li_tmp == NULL)
670 continue;
671 if (li_tmp->li_flags & IFF_NORTEXCH) {
672 if (verbose) {
673 logtrace("Suppressing sending %s on %s "
674 "(no route exchange on "
675 "interface)\n",
676 pr_type((int)rap->icmp_type),
677 li_tmp->li_name);
679 continue;
681 if (debug) {
682 logdebug("Unicast to %s ",
683 pr_name(sin->sin_addr));
684 logdebug("on interface %s\n", pi->pi_name);
686 cc = sendto(s, (char *)outpack, packetlen, 0,
687 (struct sockaddr *)sin, sizeof (struct sockaddr));
689 if (cc < 0 || cc != packetlen) {
690 if (cc < 0) {
691 logperror("sendto");
692 } else {
693 logerr("wrote %s %d chars, ret=%d\n",
694 sendaddress, packetlen, cc);
701 * P R _ T Y P E
703 * Convert an ICMP "type" field to a printable string.
705 char *
706 pr_type(int t)
708 static char *ttab[] = {
709 "Echo Reply",
710 "ICMP 1",
711 "ICMP 2",
712 "Dest Unreachable",
713 "Source Quench",
714 "Redirect",
715 "ICMP 6",
716 "ICMP 7",
717 "Echo",
718 "Router Advertise",
719 "Router Solicitation",
720 "Time Exceeded",
721 "Parameter Problem",
722 "Timestamp",
723 "Timestamp Reply",
724 "Info Request",
725 "Info Reply",
726 "Netmask Request",
727 "Netmask Reply"
730 if (t < 0 || t > 16)
731 return ("OUT-OF-RANGE");
733 return (ttab[t]);
737 * P R _ N A M E
739 * Return a string name for the given IP address.
741 char *
742 pr_name(struct in_addr addr)
744 struct hostent *phe;
745 static char buf[256];
747 phe = gethostbyaddr((char *)&addr.s_addr, 4, AF_INET);
748 if (phe == NULL)
749 return (inet_ntoa(addr));
750 (void) sprintf(buf, "%s (%s)", phe->h_name, inet_ntoa(addr));
751 return (buf);
755 * P R _ P A C K
757 * Print out the packet, if it came from us. This logic is necessary
758 * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
759 * which arrive ('tis only fair). This permits multiple copies of this
760 * program to be run without having intermingled output (or statistics!).
762 static void
763 pr_pack(char *buf, int cc, struct sockaddr_in *from)
765 struct ip *ip;
766 register struct icmp *icp;
767 register int i;
768 int hlen;
769 struct logint *li;
771 ip = (struct ip *)ALIGN(buf);
772 hlen = ip->ip_hl << 2;
773 if (cc < hlen + ICMP_MINLEN) {
774 if (verbose)
775 logtrace("packet too short (%d bytes) from %s\n", cc,
776 pr_name(from->sin_addr));
777 return;
780 cc -= hlen;
781 icp = (struct icmp *)ALIGN(buf + hlen);
784 * Let's check if IFF_NORTEXCH flag is set on the interface which
785 * recevied this packet.
786 * TODO: this code can be re-written using one socket per interface
787 * to determine which interface the packet is recevied.
789 li = find_directly_connected_logint(ip->ip_src, NULL);
790 if (li != NULL && (li->li_flags & IFF_NORTEXCH)) {
791 if (verbose) {
792 logtrace("Ignoring received %s on %s "
793 "(no route exchange on interface)",
794 pr_type((int)icp->icmp_type), li->li_name);
796 return;
799 if (ip->ip_p == 0) {
801 * Assume that we are running on a pre-4.3BSD system
802 * such as SunOS before 4.0
804 icp = (struct icmp *)ALIGN(buf);
806 switch (icp->icmp_type) {
807 case ICMP_ROUTERADVERT: {
808 struct icmp_ra *rap = (struct icmp_ra *)ALIGN(icp);
809 struct icmp_ra_addr *ap;
811 if (responder)
812 break;
814 /* TBD verify that the link is multicast or broadcast */
815 /* XXX Find out the link it came in over? */
816 #ifdef notdef
817 if (debug) {
818 logdebug("ROUTER_ADVERTISEMENT: \n");
819 pr_hex(buf+hlen, cc);
821 #endif /* notdef */
822 if (in_cksum((ushort_t *)ALIGN(buf+hlen), cc)) {
823 if (verbose)
824 logtrace("ICMP %s from %s: Bad checksum\n",
825 pr_type((int)rap->icmp_type),
826 pr_name(from->sin_addr));
827 return;
829 if (rap->icmp_code != 0) {
830 if (verbose)
831 logtrace("ICMP %s from %s: Code = %d\n",
832 pr_type((int)rap->icmp_type),
833 pr_name(from->sin_addr),
834 rap->icmp_code);
835 return;
837 if (rap->icmp_num_addrs < 1) {
838 if (verbose)
839 logtrace("ICMP %s from %s: No addresses\n",
840 pr_type((int)rap->icmp_type),
841 pr_name(from->sin_addr));
842 return;
844 if (rap->icmp_wpa < 2) {
845 if (verbose)
846 logtrace("ICMP %s from %s: Words/addr = %d\n",
847 pr_type((int)rap->icmp_type),
848 pr_name(from->sin_addr),
849 rap->icmp_wpa);
850 return;
852 if ((unsigned)cc <
853 ICMP_MINLEN + rap->icmp_num_addrs * rap->icmp_wpa * 4) {
854 if (verbose)
855 logtrace("ICMP %s from %s: Too short %d, %d\n",
856 pr_type((int)rap->icmp_type),
857 pr_name(from->sin_addr),
859 ICMP_MINLEN +
860 rap->icmp_num_addrs *
861 rap->icmp_wpa * 4);
862 return;
864 rap->icmp_lifetime = ntohs(rap->icmp_lifetime);
865 if ((rap->icmp_lifetime < 4 && rap->icmp_lifetime != 0) ||
866 rap->icmp_lifetime > 9000) {
867 if (verbose)
868 logtrace("ICMP %s from %s: Invalid lifetime %d\n",
869 pr_type((int)rap->icmp_type),
870 pr_name(from->sin_addr),
871 rap->icmp_lifetime);
872 return;
874 if (verbose)
875 logtrace("ICMP %s from %s, lifetime %d\n",
876 pr_type((int)rap->icmp_type),
877 pr_name(from->sin_addr),
878 rap->icmp_lifetime);
881 * Check that at least one router address is a neighbor
882 * on the arriving link.
884 for (i = 0; (unsigned)i < rap->icmp_num_addrs; i++) {
885 struct in_addr ina;
886 ap = (struct icmp_ra_addr *)
887 ALIGN(buf + hlen + ICMP_MINLEN +
888 i * rap->icmp_wpa * 4);
889 ap->preference = ntohl(ap->preference);
890 ina.s_addr = ap->addr;
891 if (verbose)
892 logtrace("\taddress %s, preference 0x%x\n",
893 pr_name(ina),
894 ap->preference);
895 if (!responder) {
896 if (find_directly_connected_logint(ina, NULL) !=
897 NULL) {
898 record_router(ina,
899 (long)ap->preference,
900 rap->icmp_lifetime);
904 nreceived++;
905 if (!forever) {
906 (void) alarm(0);
907 do_fork();
908 forever = 1;
909 (void) alarm(TIMER_INTERVAL);
911 break;
914 case ICMP_ROUTERSOLICIT: {
915 struct sockaddr_in sin;
917 if (!responder)
918 break;
920 /* TBD verify that the link is multicast or broadcast */
921 /* XXX Find out the link it came in over? */
922 #ifdef notdef
923 if (debug) {
924 logdebug("ROUTER_SOLICITATION: \n");
925 pr_hex(buf+hlen, cc);
927 #endif /* notdef */
928 if (in_cksum((ushort_t *)ALIGN(buf+hlen), cc)) {
929 if (verbose)
930 logtrace("ICMP %s from %s: Bad checksum\n",
931 pr_type((int)icp->icmp_type),
932 pr_name(from->sin_addr));
933 return;
935 if (icp->icmp_code != 0) {
936 if (verbose)
937 logtrace("ICMP %s from %s: Code = %d\n",
938 pr_type((int)icp->icmp_type),
939 pr_name(from->sin_addr),
940 icp->icmp_code);
941 return;
944 if (cc < ICMP_MINLEN) {
945 if (verbose)
946 logtrace("ICMP %s from %s: Too short %d, %d\n",
947 pr_type((int)icp->icmp_type),
948 pr_name(from->sin_addr),
950 ICMP_MINLEN);
951 return;
954 if (verbose)
955 logtrace("ICMP %s from %s\n",
956 pr_type((int)icp->icmp_type),
957 pr_name(from->sin_addr));
959 if (!responder)
960 break;
963 * Check that ip_src is either a neighbor
964 * on the arriving link or 0.
966 sin.sin_family = AF_INET;
967 if (ip->ip_src.s_addr == 0) {
969 * If it was sent to the broadcast address we respond
970 * to the broadcast address.
972 if (IN_CLASSD(ntohl(ip->ip_dst.s_addr))) {
973 sin.sin_addr.s_addr =
974 htonl(INADDR_ALLHOSTS_GROUP);
975 } else
976 sin.sin_addr.s_addr = htonl(INADDR_BROADCAST);
977 /* Restart the timer when we broadcast */
978 left_until_advertise = min_adv_int +
979 ((max_adv_int - min_adv_int)
980 * (random() % 1000)/1000);
981 } else {
982 if (li == NULL) {
983 if (verbose)
984 logtrace("ICMP %s from %s: %s\n",
985 pr_type((int)icp->icmp_type),
986 pr_name(from->sin_addr),
987 "source not directly connected");
988 break;
990 sin.sin_addr.s_addr = ip->ip_src.s_addr;
992 nreceived++;
993 ntransmitted++;
994 advertise(&sin);
995 break;
1002 * I N _ C K S U M
1004 * Checksum routine for Internet Protocol family headers (C Version)
1008 in_cksum(ushort_t *addr, int len)
1010 register int nleft = len;
1011 register ushort_t *w = addr;
1012 register ushort_t answer;
1013 ushort_t odd_byte = 0;
1014 register int sum = 0;
1017 * Our algorithm is simple, using a 32 bit accumulator (sum),
1018 * we add sequential 16 bit words to it, and at the end, fold
1019 * back all the carry bits from the top 16 bits into the lower
1020 * 16 bits.
1022 while (nleft > 1) {
1023 sum += *w++;
1024 nleft -= 2;
1027 /* mop up an odd byte, if necessary */
1028 if (nleft == 1) {
1029 *(uchar_t *)(&odd_byte) = *(uchar_t *)w;
1030 sum += odd_byte;
1034 * add back carry outs from top 16 bits to low 16 bits
1036 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
1037 sum += (sum >> 16); /* add carry */
1038 answer = ~sum; /* truncate to 16 bits */
1039 return (answer);
1043 * F I N I S H
1045 * Print out statistics, and give up.
1046 * Heavily buffered stdio is used here, so that all the statistics
1047 * will be written with 1 sys-write call. This is nice when more
1048 * than one copy of the program is running on a terminal; it prevents
1049 * the statistics output from becoming intermingled.
1051 static void
1052 finish(void)
1054 if (responder) {
1056 * Send out a packet with a preference so that all
1057 * hosts will know that we are dead.
1059 logerr("terminated\n");
1060 force_preference(IGNORE_PREFERENCE);
1061 ntransmitted++;
1062 advertise(&whereto);
1064 if (verbose) {
1065 logtrace("\n----%s rdisc Statistics----\n", sendaddress);
1066 logtrace("%d packets transmitted, ", ntransmitted);
1067 logtrace("%d packets received, ", nreceived);
1068 logtrace("\n");
1070 (void) fflush(stdout);
1071 exit(0);
1074 #include <ctype.h>
1076 #ifdef notdef
1078 pr_hex(unsigned char *data, int len)
1080 FILE *out;
1082 out = stdout;
1084 while (len) {
1085 register int i;
1086 char charstring[17];
1088 (void) strcpy(charstring, " "); /* 16 spaces */
1089 for (i = 0; i < 16; i++) {
1091 * output the bytes one at a time,
1092 * not going past "len" bytes
1094 if (len) {
1095 char ch = *data & 0x7f; /* strip parity */
1096 if (!isprint((uchar_t)ch))
1097 ch = ' '; /* ensure printable */
1098 charstring[i] = ch;
1099 (void) fprintf(out, "%02x ", *data++);
1100 len--;
1101 } else
1102 (void) fprintf(out, " ");
1103 if (i == 7)
1104 (void) fprintf(out, " ");
1107 (void) fprintf(out, " *%s*\n", charstring);
1110 #endif /* notdef */
1112 static int
1113 isbroadcast(struct sockaddr_in *sin)
1115 return (sin->sin_addr.s_addr == htonl(INADDR_BROADCAST));
1118 static int
1119 ismulticast(struct sockaddr_in *sin)
1121 return (IN_CLASSD(ntohl(sin->sin_addr.s_addr)));
1124 /* From libc/rpc/pmap_rmt.c */
1127 /* Only send once per physical interface */
1128 static int
1129 sendbcast(int s, char *packet, int packetlen)
1131 struct phyint *pi;
1132 struct logint *li;
1133 boolean_t bcast;
1134 int cc;
1136 for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1137 bcast = B_FALSE;
1138 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1139 if (li->li_state & ST_DELETED)
1140 continue;
1142 if (li->li_flags & IFF_BROADCAST) {
1143 bcast = B_TRUE;
1144 break;
1147 if (!bcast)
1148 continue;
1149 cc = sendbcastif(s, packet, packetlen, li);
1150 if (cc != packetlen) {
1151 return (cc);
1154 return (packetlen);
1157 static int
1158 sendbcastif(int s, char *packet, int packetlen, struct logint *li)
1160 int cc;
1161 struct sockaddr_in baddr;
1162 struct icmp *icp = (struct icmp *)ALIGN(packet);
1164 baddr.sin_family = AF_INET;
1166 if ((li->li_flags & IFF_BROADCAST) == 0) {
1167 if (verbose) {
1168 logtrace("Suppressing sending %s on %s "
1169 "(interface is not broadcast capable)\n",
1170 pr_type((int)icp->icmp_type), li->li_name);
1172 return (packetlen);
1174 if (li->li_flags & IFF_NORTEXCH) {
1175 if (verbose) {
1176 logtrace("Suppressing sending %s on %s "
1177 "(no route exchange on interface)\n",
1178 pr_type((int)icp->icmp_type), li->li_name);
1180 return (packetlen);
1183 baddr.sin_addr = li->li_bcastaddr;
1184 if (debug)
1185 logdebug("Broadcast to %s\n",
1186 pr_name(baddr.sin_addr));
1187 cc = sendto(s, packet, packetlen, 0,
1188 (struct sockaddr *)&baddr, sizeof (struct sockaddr));
1189 if (cc != packetlen) {
1190 logperror("sendbcast: sendto");
1191 logerr("Cannot send broadcast packet to %s\n",
1192 pr_name(baddr.sin_addr));
1194 return (cc);
1197 static int
1198 sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *sin)
1200 struct phyint *pi;
1201 struct logint *li;
1202 boolean_t mcast;
1203 int cc;
1205 for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1206 mcast = B_FALSE;
1207 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1208 if (li->li_state & ST_DELETED)
1209 continue;
1211 if (li->li_flags & IFF_MULTICAST) {
1212 mcast = B_TRUE;
1213 break;
1216 if (!mcast)
1217 continue;
1218 cc = sendmcastif(s, packet, packetlen, sin, li);
1219 if (cc != packetlen) {
1220 return (cc);
1223 return (packetlen);
1226 static int
1227 sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin,
1228 struct logint *li)
1230 int cc;
1231 struct sockaddr_in ifaddr;
1232 struct icmp *icp = (struct icmp *)ALIGN(packet);
1234 ifaddr.sin_family = AF_INET;
1236 if ((li->li_flags & IFF_MULTICAST) == 0) {
1237 if (verbose) {
1238 logtrace("Suppressing sending %s on %s "
1239 "(interface is not multicast capable)\n",
1240 pr_type((int)icp->icmp_type), li->li_name);
1242 return (packetlen);
1244 if (li->li_flags & IFF_NORTEXCH) {
1245 if (verbose) {
1246 logtrace("Suppressing sending %s on %s "
1247 "(no route exchange on interface)\n",
1248 pr_type((int)icp->icmp_type), li->li_name);
1250 return (packetlen);
1253 ifaddr.sin_addr = li->li_address;
1254 if (debug)
1255 logdebug("Multicast to interface %s\n",
1256 pr_name(ifaddr.sin_addr));
1257 if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
1258 (char *)&ifaddr.sin_addr,
1259 sizeof (ifaddr.sin_addr)) < 0) {
1260 logperror("setsockopt (IP_MULTICAST_IF)");
1261 logerr("Cannot send multicast packet over interface %s\n",
1262 pr_name(ifaddr.sin_addr));
1263 return (-1);
1265 cc = sendto(s, packet, packetlen, 0,
1266 (struct sockaddr *)sin, sizeof (struct sockaddr));
1267 if (cc != packetlen) {
1268 logperror("sendmcast: sendto");
1269 logerr("Cannot send multicast packet over interface %s\n",
1270 pr_name(ifaddr.sin_addr));
1272 return (cc);
1275 static void
1276 reinitifs(void)
1278 (void) initifs(s, &g_joinaddr, g_preference);
1281 static void
1282 force_preference(int preference)
1284 struct phyint *pi;
1285 struct logint *li;
1287 for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1288 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1289 if (li->li_state & ST_DELETED)
1290 continue;
1292 li->li_preference = preference;
1298 * Returns -1 on failure.
1300 static int
1301 initifs(int s, struct sockaddr_in *joinaddr, int preference)
1303 struct ifconf ifc;
1304 struct ifreq ifreq, *ifr;
1305 struct lifreq lifreq;
1306 int n;
1307 char *buf;
1308 int numifs;
1309 unsigned bufsize;
1310 struct phyint *pi;
1311 struct logint *li;
1312 int num_deletions;
1313 char phyintname[IFNAMSIZ];
1314 char *cp;
1315 int old_num_usable_interfaces = num_usable_interfaces;
1318 * Mark all interfaces so that we can determine which ones
1319 * have gone away.
1321 for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1322 pi->pi_state |= ST_MARKED;
1323 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1324 li->li_state |= ST_MARKED;
1328 if (sock < 0) {
1329 sock = socket(AF_INET, SOCK_DGRAM, 0);
1330 if (sock < 0) {
1331 logperror("initifs: socket");
1332 return (-1);
1335 #ifdef SIOCGIFNUM
1336 if (ioctl(sock, SIOCGIFNUM, (char *)&numifs) < 0) {
1337 logperror("initifs: SIOCGIFNUM");
1338 return (-1);
1340 #else
1341 numifs = MAXIFS;
1342 #endif
1343 bufsize = numifs * sizeof (struct ifreq);
1344 buf = (char *)malloc(bufsize);
1345 if (buf == NULL) {
1346 logerr("out of memory\n");
1347 (void) close(sock);
1348 sock = -1;
1349 return (-1);
1351 ifc.ifc_len = bufsize;
1352 ifc.ifc_buf = buf;
1353 if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
1354 logperror("initifs: ioctl (get interface configuration)");
1355 (void) close(sock);
1356 sock = -1;
1357 (void) free(buf);
1358 return (-1);
1360 ifr = ifc.ifc_req;
1361 for (n = ifc.ifc_len/sizeof (struct ifreq); n > 0; n--, ifr++) {
1362 ifreq = *ifr;
1364 * We need to use new interface ioctls to get 64-bit flags.
1366 (void) strncpy(lifreq.lifr_name, ifr->ifr_name,
1367 sizeof (ifr->ifr_name));
1368 if (ioctl(sock, SIOCGLIFFLAGS, (char *)&lifreq) < 0) {
1369 logperror("initifs: ioctl (get interface flags)");
1370 continue;
1372 if (ifr->ifr_addr.sa_family != AF_INET)
1373 continue;
1374 if ((lifreq.lifr_flags & IFF_UP) == 0)
1375 continue;
1376 if (lifreq.lifr_flags & IFF_LOOPBACK)
1377 continue;
1378 if ((lifreq.lifr_flags & (IFF_MULTICAST | IFF_BROADCAST)) == 0)
1379 continue;
1381 /* Create the physical name by truncating at the ':' */
1382 strncpy(phyintname, ifreq.ifr_name, sizeof (phyintname));
1383 if ((cp = strchr(phyintname, ':')) != NULL)
1384 *cp = '\0';
1386 pi = find_phyint(phyintname);
1387 if (pi == NULL) {
1388 pi = add_phyint(phyintname);
1389 if (pi == NULL) {
1390 logerr("out of memory\n");
1391 (void) close(sock);
1392 sock = -1;
1393 (void) free(buf);
1394 return (-1);
1397 pi->pi_state &= ~ST_MARKED;
1399 li = find_logint(pi, ifreq.ifr_name);
1400 if (li != NULL) {
1402 * Detect significant changes.
1403 * We treat netmask changes as insignificant but all
1404 * other changes cause a delete plus add of the
1405 * logical interface.
1406 * Note: if the flags and localaddr are unchanged
1407 * then nothing but the netmask and the broadcast
1408 * address could have changed since the other addresses
1409 * are derived from the flags and the localaddr.
1411 struct logint newli;
1413 if (!getconfig(sock, lifreq.lifr_flags, &ifr->ifr_addr,
1414 &ifreq, &newli)) {
1415 free_logint(li);
1416 continue;
1419 if (newli.li_flags != li->li_flags ||
1420 newli.li_localaddr.s_addr !=
1421 li->li_localaddr.s_addr || newli.li_index !=
1422 li->li_index) {
1423 /* Treat as an interface deletion + addition */
1424 li->li_state |= ST_DELETED;
1425 deleted_logint(li, &newli, s, joinaddr);
1426 free_logint(li);
1427 li = NULL; /* li recreated below */
1428 } else {
1430 * No significant changes.
1431 * Just update the netmask, and broadcast.
1433 li->li_netmask = newli.li_netmask;
1434 li->li_bcastaddr = newli.li_bcastaddr;
1437 if (li == NULL) {
1438 li = add_logint(pi, ifreq.ifr_name);
1439 if (li == NULL) {
1440 logerr("out of memory\n");
1441 (void) close(sock);
1442 sock = -1;
1443 (void) free(buf);
1444 return (-1);
1447 /* init li */
1448 if (!getconfig(sock, lifreq.lifr_flags, &ifr->ifr_addr,
1449 &ifreq, li)) {
1450 free_logint(li);
1451 continue;
1453 li->li_preference = preference;
1454 added_logint(li, s, joinaddr);
1456 li->li_state &= ~ST_MARKED;
1458 (void) free(buf);
1461 * Determine which interfaces have gone away.
1462 * The deletion is done in three phases:
1463 * 1. Mark ST_DELETED
1464 * 2. Inform using the deleted_* function.
1465 * 3. Unlink and free the actual memory.
1466 * Note that for #3 the physical interface must be deleted after
1467 * the logical ones.
1468 * Also count the number of physical interfaces.
1470 num_usable_interfaces = 0;
1471 num_deletions = 0;
1472 for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1473 if (pi->pi_state & ST_MARKED) {
1474 num_deletions++;
1475 pi->pi_state |= ST_DELETED;
1477 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1478 if (li->li_state & ST_MARKED) {
1479 num_deletions++;
1480 li->li_state |= ST_DELETED;
1483 if (!(pi->pi_state & ST_DELETED))
1484 num_usable_interfaces++;
1486 if (num_deletions != 0) {
1487 struct phyint *nextpi;
1488 struct logint *nextli;
1490 for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1491 if (pi->pi_state & ST_DELETED) {
1493 * By deleting the physical interface pi, all of
1494 * the corresponding logical interfaces will
1495 * also be deleted so there is no need to delete
1496 * them individually.
1498 deleted_phyint(pi, s, joinaddr);
1499 } else {
1500 for (li = pi->pi_logical_first; li != NULL;
1501 li = li->li_next) {
1502 if (li->li_state & ST_DELETED) {
1503 deleted_logint(li, NULL, s,
1504 joinaddr);
1509 /* Do the actual linked list update + free */
1510 for (pi = phyint; pi != NULL; pi = nextpi) {
1511 nextpi = pi->pi_next;
1512 for (li = pi->pi_logical_first; li != NULL;
1513 li = nextli) {
1514 nextli = li->li_next;
1515 if (li->li_state & ST_DELETED)
1516 free_logint(li);
1518 if (pi->pi_state & ST_DELETED)
1519 free_phyint(pi);
1523 * When the set of available interfaces goes from zero to
1524 * non-zero we restart solicitations if '-s' was specified.
1526 if (old_num_usable_interfaces == 0 && num_usable_interfaces > 0 &&
1527 start_solicit && !solicit) {
1528 if (debug)
1529 logdebug("switching to solicitations: num if %d\n",
1530 num_usable_interfaces);
1531 solicit = start_solicit;
1532 ntransmitted = 0;
1533 ntransmitted++;
1534 solicitor(&whereto);
1536 return (0);
1539 static boolean_t
1540 getconfig(int sock, uint64_t if_flags, struct sockaddr *addr,
1541 struct ifreq *ifr, struct logint *li)
1543 struct ifreq ifreq;
1544 struct sockaddr_in *sin;
1545 struct lifreq lifreq;
1547 ifreq = *ifr; /* Copy name etc */
1549 li->li_flags = if_flags;
1550 sin = (struct sockaddr_in *)ALIGN(addr);
1551 li->li_localaddr = sin->sin_addr;
1553 (void) strlcpy(lifreq.lifr_name, ifr->ifr_name,
1554 sizeof (lifreq.lifr_name));
1555 if (ioctl(sock, SIOCGLIFINDEX, &lifreq) < 0) {
1556 logperror("initifs: ioctl (get if index)");
1557 /* Continue with 0; a safe value never used for interfaces */
1558 li->li_index = 0;
1559 } else {
1560 li->li_index = lifreq.lifr_index;
1563 if (if_flags & IFF_POINTOPOINT) {
1564 li->li_netmask.s_addr = (unsigned long)0xffffffff;
1565 if (ioctl(sock, SIOCGIFDSTADDR, (char *)&ifreq) < 0) {
1566 logperror("initifs: ioctl (get dest addr)");
1567 return (B_FALSE);
1569 /* A pt-pt link is identified by the remote address */
1570 sin = (struct sockaddr_in *)ALIGN(&ifreq.ifr_addr);
1571 li->li_address = sin->sin_addr;
1572 li->li_remoteaddr = sin->sin_addr;
1573 /* Simulate broadcast for pt-pt */
1574 li->li_bcastaddr = sin->sin_addr;
1575 li->li_flags |= IFF_BROADCAST;
1576 } else {
1578 * Non pt-pt links are identified by the local
1579 * address
1581 li->li_address = li->li_localaddr;
1582 li->li_remoteaddr = li->li_address;
1583 if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
1584 logperror("initifs: ioctl (get netmask)");
1585 return (B_FALSE);
1587 sin = (struct sockaddr_in *)ALIGN(&ifreq.ifr_addr);
1588 li->li_netmask = sin->sin_addr;
1589 if (if_flags & IFF_BROADCAST) {
1590 if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
1591 logperror(
1592 "initifs: ioctl (get broadcast address)");
1593 return (B_FALSE);
1595 sin = (struct sockaddr_in *)ALIGN(&ifreq.ifr_addr);
1596 li->li_bcastaddr = sin->sin_addr;
1599 return (B_TRUE);
1603 static int
1604 support_multicast(void)
1606 int sock;
1607 uchar_t ttl = 1;
1609 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1610 if (sock < 0) {
1611 logperror("support_multicast: socket");
1612 return (0);
1615 if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL,
1616 (char *)&ttl, sizeof (ttl)) < 0) {
1617 (void) close(sock);
1618 return (0);
1620 (void) close(sock);
1621 return (1);
1625 * For a given destination address, find the logical interface to use.
1626 * If opi is NULL check all interfaces. Otherwise just match against
1627 * the specified physical interface.
1628 * Return logical interface if there's a match, NULL otherwise.
1630 static struct logint *
1631 find_directly_connected_logint(struct in_addr in, struct phyint *opi)
1633 struct phyint *pi;
1634 struct logint *li;
1636 if (opi == NULL)
1637 pi = phyint;
1638 else
1639 pi = opi;
1641 for (; pi != NULL; pi = pi->pi_next) {
1642 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1643 if (li->li_state & ST_DELETED)
1644 continue;
1646 /* Check that the subnetwork numbers match */
1647 if ((in.s_addr & li->li_netmask.s_addr) ==
1648 (li->li_remoteaddr.s_addr &
1649 li->li_netmask.s_addr))
1650 return (li);
1652 if (opi != NULL)
1653 break;
1655 return (NULL);
1659 * INTERFACES - physical and logical identified by name
1663 static void
1664 report_interfaces(void)
1666 struct phyint *pi;
1667 struct logint *li;
1669 logdebug("\nInterfaces:\n\n");
1670 for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1671 logdebug("Phyint %s state 0x%x\n",
1672 pi->pi_name, pi->pi_state);
1673 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1674 logdebug("IF %s state 0x%x, flags 0x%x, addr %s\n",
1675 li->li_name, li->li_state, li->li_flags,
1676 pr_name(li->li_address));
1677 logdebug("\tlocal %s pref 0x%x ",
1678 pr_name(li->li_localaddr), li->li_preference);
1679 logdebug("bcast %s\n",
1680 pr_name(li->li_bcastaddr));
1681 logdebug("\tremote %s ",
1682 pr_name(li->li_remoteaddr));
1683 logdebug("netmask %s\n",
1684 pr_name(li->li_netmask));
1689 static struct phyint *
1690 find_phyint(char *name)
1692 struct phyint *pi;
1694 for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1695 if (strcmp(pi->pi_name, name) == 0)
1696 return (pi);
1698 return (NULL);
1701 /* Assumes that the entry does not exist - caller must use find_* */
1702 static struct phyint *
1703 add_phyint(char *name)
1705 struct phyint *pi;
1707 pi = malloc(sizeof (*pi));
1708 if (pi == NULL)
1709 return (NULL);
1710 bzero((char *)pi, sizeof (*pi));
1712 strncpy(pi->pi_name, name, sizeof (pi->pi_name));
1713 /* Link into list */
1714 pi->pi_next = phyint;
1715 pi->pi_prev = NULL;
1716 if (phyint != NULL)
1717 phyint->pi_prev = pi;
1718 phyint = pi;
1719 return (pi);
1722 static void
1723 free_phyint(struct phyint *pi)
1725 assert(pi->pi_logical_first == NULL);
1726 assert(pi->pi_logical_last == NULL);
1728 if (pi->pi_prev == NULL) {
1729 /* Delete first */
1730 assert(phyint == pi);
1731 phyint = pi->pi_next;
1732 } else {
1733 assert(pi->pi_prev->pi_next == pi);
1734 pi->pi_prev->pi_next = pi->pi_next;
1736 if (pi->pi_next != NULL) {
1737 assert(pi->pi_next->pi_prev == pi);
1738 pi->pi_next->pi_prev = pi->pi_prev;
1740 free(pi);
1743 static struct logint *
1744 find_logint(struct phyint *pi, char *name)
1746 struct logint *li;
1748 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1749 if (strcmp(li->li_name, name) == 0)
1750 return (li);
1752 return (NULL);
1756 * Assumes that the entry does not exist - caller must use find_*
1757 * Tail insertion.
1759 static struct logint *
1760 add_logint(struct phyint *pi, char *name)
1762 struct logint *li;
1764 li = malloc(sizeof (*li));
1765 if (li == NULL)
1766 return (NULL);
1767 bzero((char *)li, sizeof (*li));
1769 strncpy(li->li_name, name, sizeof (li->li_name));
1770 /* Link into list */
1771 li->li_prev = pi->pi_logical_last;
1772 if (pi->pi_logical_last == NULL) {
1773 /* First one */
1774 assert(pi->pi_logical_first == NULL);
1775 pi->pi_logical_first = li;
1776 } else {
1777 pi->pi_logical_last->li_next = li;
1779 li->li_next = NULL;
1780 li->li_physical = pi;
1781 pi->pi_logical_last = li;
1782 return (li);
1786 static void
1787 free_logint(struct logint *li)
1789 struct phyint *pi;
1791 pi = li->li_physical;
1792 if (li->li_prev == NULL) {
1793 /* Delete first */
1794 assert(pi->pi_logical_first == li);
1795 pi->pi_logical_first = li->li_next;
1796 } else {
1797 assert(li->li_prev->li_next == li);
1798 li->li_prev->li_next = li->li_next;
1800 if (li->li_next == NULL) {
1801 /* Delete last */
1802 assert(pi->pi_logical_last == li);
1803 pi->pi_logical_last = li->li_prev;
1804 } else {
1805 assert(li->li_next->li_prev == li);
1806 li->li_next->li_prev = li->li_prev;
1808 free(li);
1812 /* Tell all the logical interfaces that they are going away */
1813 static void
1814 deleted_phyint(struct phyint *pi, int s,
1815 struct sockaddr_in *joinaddr)
1817 struct logint *li;
1819 if (debug)
1820 logdebug("Deleting physical interface %s\n", pi->pi_name);
1822 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1823 li->li_state |= ST_DELETED;
1825 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1826 deleted_logint(li, NULL, s, joinaddr);
1831 * Join the multicast address if no other logical interface has done
1832 * so for this physical interface.
1834 static void
1835 added_logint(struct logint *li, int s,
1836 struct sockaddr_in *joinaddr)
1838 if (debug)
1839 logdebug("Adding logical interface %s\n", li->li_name);
1841 if ((!(li->li_physical->pi_state & ST_JOINED)) &&
1842 (!isbroadcast(joinaddr))) {
1843 struct ip_mreq mreq;
1845 mreq.imr_multiaddr = joinaddr->sin_addr;
1846 mreq.imr_interface = li->li_address;
1848 if (debug)
1849 logdebug("Joining MC on interface %s\n", li->li_name);
1851 if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
1852 (char *)&mreq, sizeof (mreq)) < 0) {
1853 logperror("setsockopt (IP_ADD_MEMBERSHIP)");
1854 } else {
1855 li->li_physical->pi_state |= ST_JOINED;
1856 li->li_state |= ST_JOINED;
1862 * Leave the multicast address if this logical interface joined it.
1863 * Look for a replacement logical interface for the same physical interface.
1864 * Remove any routes which are no longer reachable.
1866 * If newli is non-NULL, then it is likely that the address of a logical
1867 * interface has changed. In this case, the membership should be dropped using
1868 * the new address of the interface in question.
1870 * XXX When a physical interface is being deleted by deleted_phyint(), this
1871 * routine will be called for each logical interface associated with the
1872 * physical one. This should be made more efficient as there is no point in
1873 * searching for an alternate logical interface to add group membership to as
1874 * they all are marked ST_DELETED.
1876 static void
1877 deleted_logint(struct logint *li, struct logint *newli, int s,
1878 struct sockaddr_in *joinaddr)
1880 struct phyint *pi;
1881 struct logint *oli;
1883 if (debug)
1884 logdebug("Deleting logical interface %s\n", li->li_name);
1886 assert(li->li_state & ST_DELETED);
1888 if (li->li_state & ST_JOINED) {
1889 struct ip_mreq mreq;
1891 pi = li->li_physical;
1892 assert(pi->pi_state & ST_JOINED);
1893 assert(!isbroadcast(joinaddr));
1895 mreq.imr_multiaddr = joinaddr->sin_addr;
1896 if (newli != NULL)
1897 mreq.imr_interface = newli->li_address;
1898 else
1899 mreq.imr_interface = li->li_address;
1901 if (debug)
1902 logdebug("Leaving MC on interface %s\n", li->li_name);
1904 if (setsockopt(s, IPPROTO_IP, IP_DROP_MEMBERSHIP,
1905 (char *)&mreq, sizeof (mreq)) < 0) {
1907 * EADDRNOTAVAIL will be returned if the interface has
1908 * been unplumbed or if the interface no longer has
1909 * IFF_MULTICAST set. The former is the common case
1910 * while the latter is rare so don't log the error
1911 * unless some other error was returned or if debug is
1912 * set.
1914 if (errno != EADDRNOTAVAIL) {
1915 logperror("setsockopt (IP_DROP_MEMBERSHIP)");
1916 } else if (debug) {
1917 logdebug("%s: %s\n",
1918 "setsockopt (IP_DROP_MEMBERSHIP)",
1919 strerror(errno));
1922 li->li_physical->pi_state &= ~ST_JOINED;
1923 li->li_state &= ~ST_JOINED;
1925 /* Is there another interface that can join? */
1926 for (oli = pi->pi_logical_first; oli != NULL;
1927 oli = oli->li_next) {
1928 if (oli->li_state & ST_DELETED)
1929 continue;
1931 mreq.imr_multiaddr = joinaddr->sin_addr;
1932 mreq.imr_interface = oli->li_address;
1934 if (debug)
1935 logdebug("Joining MC on interface %s\n",
1936 oli->li_name);
1938 if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
1939 (char *)&mreq, sizeof (mreq)) < 0) {
1940 logperror("setsockopt (IP_ADD_MEMBERSHIP)");
1941 } else {
1942 pi->pi_state |= ST_JOINED;
1943 oli->li_state |= ST_JOINED;
1944 break;
1949 flush_unreachable_routers();
1955 * TABLES
1957 struct table {
1958 struct in_addr router;
1959 int preference;
1960 int remaining_time;
1961 int in_kernel;
1962 struct table *next;
1965 struct table *table;
1967 static void
1968 report_routes(void)
1970 struct table *tp;
1972 logdebug("\nRoutes:\n\n");
1973 tp = table;
1974 while (tp) {
1975 logdebug("Router %s, pref 0x%x, time %d, %s kernel\n",
1976 pr_name(tp->router), tp->preference,
1977 tp->remaining_time,
1978 (tp->in_kernel ? "in" : "not in"));
1979 tp = tp->next;
1983 static struct table *
1984 find_router(struct in_addr addr)
1986 struct table *tp;
1988 tp = table;
1989 while (tp) {
1990 if (tp->router.s_addr == addr.s_addr)
1991 return (tp);
1992 tp = tp->next;
1994 return (NULL);
1997 static int
1998 max_preference(void)
2000 struct table *tp;
2001 int max = (int)IGNORE_PREFERENCE;
2003 tp = table;
2004 while (tp) {
2005 if (tp->preference > max)
2006 max = tp->preference;
2007 tp = tp->next;
2009 return (max);
2013 /* Note: this might leave the kernel with no default route for a short time. */
2014 static void
2015 age_table(int time)
2017 struct table **tpp, *tp;
2018 int recalculate_max = 0;
2019 int max = max_preference();
2021 tpp = &table;
2022 while (*tpp != NULL) {
2023 tp = *tpp;
2024 tp->remaining_time -= time;
2025 if (tp->remaining_time <= 0) {
2026 *tpp = tp->next;
2027 if (debug) {
2028 logdebug("Timed out router %s\n",
2029 pr_name(tp->router));
2031 if (tp->in_kernel)
2032 del_route(tp->router);
2033 if (best_preference &&
2034 tp->preference == max)
2035 recalculate_max++;
2036 free((char *)tp);
2037 } else {
2038 tpp = &tp->next;
2041 if (recalculate_max) {
2042 int max = max_preference();
2044 if (max != IGNORE_PREFERENCE) {
2045 tp = table;
2046 while (tp) {
2047 if (tp->preference == max && !tp->in_kernel) {
2048 add_route(tp->router);
2049 tp->in_kernel++;
2051 tp = tp->next;
2058 * Remove any routes which are no longer directly connected.
2060 static void
2061 flush_unreachable_routers(void)
2063 struct table **tpp, *tp;
2064 int recalculate_max = 0;
2065 int max = max_preference();
2067 tpp = &table;
2068 while (*tpp != NULL) {
2069 tp = *tpp;
2070 if (find_directly_connected_logint(tp->router, NULL) == NULL) {
2071 *tpp = tp->next;
2072 if (debug) {
2073 logdebug("Unreachable router %s\n",
2074 pr_name(tp->router));
2076 if (tp->in_kernel)
2077 del_route(tp->router);
2078 if (best_preference &&
2079 tp->preference == max)
2080 recalculate_max++;
2081 free((char *)tp);
2082 } else {
2083 tpp = &tp->next;
2086 if (recalculate_max) {
2087 int max = max_preference();
2089 if (max != IGNORE_PREFERENCE) {
2090 tp = table;
2091 while (tp) {
2092 if (tp->preference == max && !tp->in_kernel) {
2093 add_route(tp->router);
2094 tp->in_kernel++;
2096 tp = tp->next;
2102 static void
2103 record_router(struct in_addr router, long preference, int ttl)
2105 struct table *tp;
2106 int old_max = max_preference();
2107 int changed_up = 0; /* max preference could have increased */
2108 int changed_down = 0; /* max preference could have decreased */
2110 if (debug)
2111 logdebug("Recording %s, preference 0x%x\n",
2112 pr_name(router),
2113 preference);
2114 tp = find_router(router);
2115 if (tp) {
2116 if (tp->preference > preference &&
2117 tp->preference == old_max)
2118 changed_down++;
2119 else if (preference > tp->preference)
2120 changed_up++;
2121 tp->preference = preference;
2122 tp->remaining_time = ttl;
2123 } else {
2124 if (preference > old_max)
2125 changed_up++;
2126 tp = (struct table *)ALIGN(malloc(sizeof (struct table)));
2127 if (tp == NULL) {
2128 logerr("Out of memory\n");
2129 return;
2131 tp->router = router;
2132 tp->preference = preference;
2133 tp->remaining_time = ttl;
2134 tp->in_kernel = 0;
2135 tp->next = table;
2136 table = tp;
2138 if (!tp->in_kernel &&
2139 (!best_preference || tp->preference == max_preference()) &&
2140 tp->preference != IGNORE_PREFERENCE) {
2141 add_route(tp->router);
2142 tp->in_kernel++;
2144 if (tp->preference == IGNORE_PREFERENCE && tp->in_kernel) {
2145 del_route(tp->router);
2146 tp->in_kernel = 0;
2148 if (best_preference && changed_down) {
2149 /* Check if we should add routes */
2150 int new_max = max_preference();
2151 if (new_max != IGNORE_PREFERENCE) {
2152 tp = table;
2153 while (tp) {
2154 if (tp->preference == new_max &&
2155 !tp->in_kernel) {
2156 add_route(tp->router);
2157 tp->in_kernel++;
2159 tp = tp->next;
2163 if (best_preference && (changed_up || changed_down)) {
2164 /* Check if we should remove routes already in the kernel */
2165 int new_max = max_preference();
2166 tp = table;
2167 while (tp) {
2168 if (tp->preference < new_max && tp->in_kernel) {
2169 del_route(tp->router);
2170 tp->in_kernel = 0;
2172 tp = tp->next;
2178 #include <net/route.h>
2180 static void
2181 add_route(struct in_addr addr)
2183 if (debug)
2184 logdebug("Add default route to %s\n", pr_name(addr));
2185 rtioctl(addr, SIOCADDRT);
2188 static void
2189 del_route(struct in_addr addr)
2191 if (debug)
2192 logdebug("Delete default route to %s\n", pr_name(addr));
2193 rtioctl(addr, SIOCDELRT);
2196 static void
2197 rtioctl(struct in_addr addr, int op)
2199 int sock;
2200 struct rtentry rt;
2201 struct sockaddr_in *sin;
2202 bzero((char *)&rt, sizeof (struct rtentry));
2203 rt.rt_dst.sa_family = AF_INET;
2204 rt.rt_gateway.sa_family = AF_INET;
2205 sin = (struct sockaddr_in *)ALIGN(&rt.rt_gateway);
2206 sin->sin_addr = addr;
2207 rt.rt_flags = RTF_UP | RTF_GATEWAY;
2209 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
2210 if (sock < 0) {
2211 logperror("rtioctl: socket");
2212 return;
2214 if (ioctl(sock, op, (char *)&rt) < 0) {
2215 if (!(op == SIOCADDRT && errno == EEXIST))
2216 logperror("ioctl (add/delete route)");
2218 (void) close(sock);
2224 * LOGGER
2227 #include <syslog.h>
2229 static int logging = 0;
2231 static void
2232 initlog(void)
2234 logging++;
2235 openlog("in.rdisc", LOG_PID | LOG_CONS, LOG_DAEMON);
2238 /* VARARGS1 */
2239 void
2240 logerr(fmt, a, b, c, d, e, f, g, h)
2241 char *fmt;
2243 if (logging)
2244 syslog(LOG_ERR, fmt, a, b, c, d, e, f, g, h);
2245 else
2246 (void) fprintf(stderr, fmt, a, b, c, d, e, f, g, h);
2249 /* VARARGS1 */
2250 void
2251 logtrace(fmt, a, b, c, d, e, f, g, h)
2252 char *fmt;
2254 if (logging)
2255 syslog(LOG_INFO, fmt, a, b, c, d, e, f, g, h);
2256 else
2257 (void) fprintf(stdout, fmt, a, b, c, d, e, f, g, h);
2260 /* VARARGS1 */
2261 void
2262 logdebug(fmt, a, b, c, d, e, f, g, h)
2263 char *fmt;
2265 if (logging)
2266 syslog(LOG_DEBUG, fmt, a, b, c, d, e, f, g, h);
2267 else
2268 (void) fprintf(stdout, fmt, a, b, c, d, e, f, g, h);
2271 void
2272 logperror(str)
2273 char *str;
2275 if (logging)
2276 syslog(LOG_ERR, "%s: %s\n", str, strerror(errno));
2277 else
2278 (void) fprintf(stderr, "%s: %s\n", str, strerror(errno));