dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / usr.sbin / in.rdisc / in.rdisc.c
blob73ba820f820fa465ad50e0626f34bcf7084ed0c6
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 #define ALIGN(ptr) (ptr)
54 #ifdef SYSV
55 #define signal(s, f) sigset(s, (void (*)(int))f)
56 #define random() rand()
57 #endif
59 #define ALL_HOSTS_ADDRESS "224.0.0.1"
60 #define ALL_ROUTERS_ADDRESS "224.0.0.2"
62 #define MAXIFS 256
64 /* For router advertisement */
65 struct icmp_ra {
66 uchar_t icmp_type; /* type of message, see below */
67 uchar_t icmp_code; /* type sub code */
68 ushort_t icmp_cksum; /* ones complement cksum of struct */
69 uchar_t icmp_num_addrs;
70 uchar_t icmp_wpa; /* Words per address */
71 short icmp_lifetime;
74 struct icmp_ra_addr {
75 ulong_t addr;
76 ulong_t preference;
79 /* Router constants */
80 #define MAX_INITIAL_ADVERT_INTERVAL 16
81 #define MAX_INITIAL_ADVERTISEMENTS 3
82 #define MAX_RESPONSE_DELAY 2 /* Not used */
84 /* Host constants */
85 #define MAX_SOLICITATIONS 3
86 #define SOLICITATION_INTERVAL 3
87 #define MAX_SOLICITATION_DELAY 1 /* Not used */
89 #define IGNORE_PREFERENCE 0x80000000 /* Maximum negative */
91 #define MAX_ADV_INT 600
95 * A doubly linked list of all physical interfaces that each contain a
96 * doubly linked list of logical interfaces aka IP addresses.
98 struct phyint {
99 char pi_name[IFNAMSIZ]; /* Used to identify it */
100 int pi_state; /* See below */
101 struct logint *pi_logical_first;
102 struct logint *pi_logical_last;
103 struct phyint *pi_next;
104 struct phyint *pi_prev;
107 struct logint {
108 char li_name[IFNAMSIZ]; /* Used to identify it */
109 int li_state; /* See below */
110 struct in_addr li_address; /* Used to identify the interface */
111 struct in_addr li_localaddr; /* Actual address of the interface */
112 int li_preference;
113 int li_index; /* interface index (SIOCGLIFINDEX) */
114 uint64_t li_flags;
115 struct in_addr li_bcastaddr;
116 struct in_addr li_remoteaddr;
117 struct in_addr li_netmask;
118 struct logint *li_next; /* Next logical for this physical */
119 struct logint *li_prev; /* Prev logical for this physical */
120 struct phyint *li_physical; /* Back pointer */
123 struct phyint *phyint;
124 int num_usable_interfaces; /* Num used for sending/receiving */
127 * State bits
129 #define ST_MARKED 0x01 /* To determine removed interfaces */
130 #define ST_JOINED 0x02 /* Joined multicast group */
131 #define ST_DELETED 0x04 /* Interface should be ignored */
133 /* Function prototypes */
134 static void solicitor(struct sockaddr_in *sin);
135 static void advertise(struct sockaddr_in *sin);
137 static void age_table(int time);
138 static void flush_unreachable_routers(void);
139 static void record_router(struct in_addr router, long preference, int ttl);
141 static void add_route(struct in_addr addr);
142 static void del_route(struct in_addr addr);
143 static void rtioctl(struct in_addr addr, int op);
145 static int support_multicast(void);
146 static int sendbcast(int s, char *packet, int packetlen);
147 static int sendbcastif(int s, char *packet, int packetlen,
148 struct logint *li);
149 static int sendmcast(int s, char *packet, int packetlen,
150 struct sockaddr_in *sin);
151 static int sendmcastif(int s, char *packet, int packetlen,
152 struct sockaddr_in *sin, struct logint *li);
154 static int ismulticast(struct sockaddr_in *sin);
155 static int isbroadcast(struct sockaddr_in *sin);
156 int in_cksum(ushort_t *addr, int len);
157 static struct logint *find_directly_connected_logint(struct in_addr in,
158 struct phyint *pi);
159 static void force_preference(int preference);
161 static void timer(void);
162 static void finish(void);
163 static void report(void);
164 static void report_interfaces(void);
165 static void report_routes(void);
166 static void reinitifs(void);
168 static struct phyint *find_phyint(char *name);
169 static struct phyint *add_phyint(char *name);
170 static void free_phyint(struct phyint *pi);
171 static struct logint *find_logint(struct phyint *pi, char *name);
172 static struct logint *add_logint(struct phyint *pi, char *name);
173 static void free_logint(struct logint *li);
175 static void deleted_phyint(struct phyint *pi, int s,
176 struct sockaddr_in *joinaddr);
177 static void added_logint(struct logint *li, int s,
178 struct sockaddr_in *joinaddr);
179 static void deleted_logint(struct logint *li, struct logint *newli, int s,
180 struct sockaddr_in *joinaddr);
182 static int initifs(int s, struct sockaddr_in *joinaddr, int preference);
183 static boolean_t getconfig(int sock, uint64_t if_flags, struct sockaddr *addr,
184 struct ifreq *ifr, struct logint *li);
186 static void pr_pack(char *buf, int cc, struct sockaddr_in *from);
187 char *pr_name(struct in_addr addr);
188 char *pr_type(int t);
190 static void initlog(void);
191 void logerr(), logtrace(), logdebug(), logperror();
193 /* Local variables */
195 #define MAXPACKET 4096 /* max packet size */
196 uchar_t packet[MAXPACKET];
198 char usage[] =
199 "Usage: rdisc [-s] [-v] [-f] [-a] [send_address] [receive_address]\n"
200 " rdisc -r [-v] [-p <preference>] [-T <secs>] \n"
201 " [send_address] [receive_address]\n";
204 int s; /* Socket file descriptor */
205 struct sockaddr_in whereto; /* Address to send to */
206 struct sockaddr_in g_joinaddr; /* Address to receive on */
207 char *sendaddress, *recvaddress; /* For logging purposes only */
209 /* Common variables */
210 int verbose = 0;
211 int debug = 0;
212 int trace = 0;
213 int start_solicit = 0; /* -s parameter set */
214 int solicit = 0; /* Are we currently sending solicitations? */
215 int responder;
216 int ntransmitted = 0;
217 int nreceived = 0;
218 int forever = 0; /* Never give up on host. If 0 defer fork until */
219 /* first response. */
221 /* Router variables */
222 int max_adv_int = MAX_ADV_INT;
223 int min_adv_int;
224 int lifetime;
225 int initial_advert_interval = MAX_INITIAL_ADVERT_INTERVAL;
226 int initial_advertisements = MAX_INITIAL_ADVERTISEMENTS;
227 ulong_t g_preference = 0; /* Setable with -p option */
229 /* Host variables */
230 int max_solicitations = MAX_SOLICITATIONS;
231 unsigned int solicitation_interval = SOLICITATION_INTERVAL;
232 int best_preference = 1; /* Set to record only the router(s) with the */
233 /* best preference in the kernel. Not set */
234 /* puts all routes in the kernel. */
237 static void
238 prusage()
240 (void) fprintf(stderr, usage);
241 exit(1);
244 static int sock = -1;
246 static void
247 do_fork()
249 int t;
251 if (trace)
252 return;
254 if (fork())
255 exit(0);
256 for (t = 0; t < 20; t++)
257 if (t != s)
258 (void) close(t);
259 sock = -1;
260 (void) open("/", O_RDONLY);
261 (void) dup2(0, 1);
262 (void) dup2(0, 2);
263 #ifndef SYSV
264 t = open("/dev/tty", O_RDWR);
265 if (t >= 0) {
266 (void) ioctl(t, TIOCNOTTY, (char *)0);
267 (void) close(t);
269 #else
270 (void) setpgrp();
271 #endif
272 initlog();
276 * M A I N
279 main(int argc, char *argv[])
281 #ifndef SYSV
282 struct sigvec sv;
283 #endif
284 struct sockaddr_in from;
285 char **av = argv;
286 struct sockaddr_in *to = &whereto;
287 ulong_t val;
289 min_adv_int = (max_adv_int * 3 / 4);
290 lifetime = (3*max_adv_int);
292 argc--, av++;
293 while (argc > 0 && *av[0] == '-') {
294 while (*++av[0])
295 switch (*av[0]) {
296 case 'd':
297 debug = 1;
298 break;
299 case 't':
300 trace = 1;
301 break;
302 case 'v':
303 verbose++;
304 break;
305 case 's':
306 start_solicit = solicit = 1;
307 break;
308 case 'r':
309 responder = 1;
310 break;
311 case 'a':
312 best_preference = 0;
313 break;
314 case 'b':
315 best_preference = 1;
316 break;
317 case 'f':
318 forever = 1;
319 break;
320 case 'T':
321 argc--, av++;
322 if (argc != 0) {
323 val = strtol(av[0], (char **)NULL, 0);
324 if (val < 4 || val > 1800) {
325 (void) fprintf(stderr,
326 "Bad Max Advertisement Interval\n");
327 exit(1);
329 max_adv_int = val;
330 min_adv_int = (max_adv_int * 3 / 4);
331 lifetime = (3*max_adv_int);
332 } else {
333 prusage();
334 /* NOTREACHED */
336 goto next;
337 case 'p':
338 argc--, av++;
339 if (argc != 0) {
340 val = strtoul(av[0], (char **)NULL, 0);
341 g_preference = val;
342 } else {
343 prusage();
344 /* NOTREACHED */
346 goto next;
347 default:
348 prusage();
349 /* NOTREACHED */
351 next:
352 argc--, av++;
354 if (argc < 1) {
355 if (support_multicast()) {
356 if (responder)
357 sendaddress = ALL_HOSTS_ADDRESS;
358 else
359 sendaddress = ALL_ROUTERS_ADDRESS;
360 } else
361 sendaddress = "255.255.255.255";
362 } else {
363 sendaddress = av[0];
364 argc--;
366 if (argc < 1) {
367 if (support_multicast()) {
368 if (responder)
369 recvaddress = ALL_ROUTERS_ADDRESS;
370 else
371 recvaddress = ALL_HOSTS_ADDRESS;
372 } else
373 recvaddress = "255.255.255.255";
374 } else {
375 recvaddress = av[0];
376 argc--;
378 if (argc != 0) {
379 (void) fprintf(stderr, "Extra paramaters\n");
380 prusage();
381 /* NOTREACHED */
384 if (solicit && responder) {
385 prusage();
386 /* NOTREACHED */
389 if (!(solicit && !forever)) {
390 do_fork();
393 bzero((char *)&whereto, sizeof (struct sockaddr_in));
394 to->sin_family = AF_INET;
395 to->sin_addr.s_addr = inet_addr(sendaddress);
396 if (to->sin_addr.s_addr == (unsigned long)-1) {
397 logerr("in.rdisc: bad address %s\n", sendaddress);
398 exit(1);
401 bzero((char *)&g_joinaddr, sizeof (struct sockaddr_in));
402 g_joinaddr.sin_family = AF_INET;
403 g_joinaddr.sin_addr.s_addr = inet_addr(recvaddress);
404 if (g_joinaddr.sin_addr.s_addr == (unsigned long)-1) {
405 logerr("in.rdisc: bad address %s\n", recvaddress);
406 exit(1);
409 if (responder) {
410 #ifdef SYSV
411 srand((int)gethostid());
412 #else
413 srandom((int)gethostid());
414 #endif
417 if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
418 logperror("socket");
419 exit(5);
422 #ifdef SYSV
423 setvbuf(stdout, NULL, _IOLBF, 0);
424 #else
425 setlinebuf(stdout);
426 #endif
428 (void) signal(SIGINT, finish);
429 (void) signal(SIGTERM, finish);
430 (void) signal(SIGHUP, reinitifs);
431 (void) signal(SIGUSR1, report);
433 if (initifs(s, &g_joinaddr, g_preference) < 0) {
434 logerr("Failed initializing interfaces\n");
435 exit(2);
439 * If there are no usable interfaces and we are soliciting
440 * waiting for to return an exit code (i.e. forever isn't set)
441 * give up immediately.
443 if (num_usable_interfaces == 0 && solicit && !forever) {
444 logerr("in.rdisc: No interfaces up\n");
445 exit(5);
448 #ifdef SYSV
449 (void) signal(SIGALRM, timer);
450 #else
452 * Make sure that this signal actually interrupts (rather than
453 * restarts) the recvfrom call below.
455 sv.sv_handler = timer;
456 sv.sv_mask = 0;
457 sv.sv_flags = SV_INTERRUPT;
458 (void) sigvec(SIGALRM, &sv, NULL);
459 #endif
460 timer(); /* start things going */
462 for (;;) {
463 int len = sizeof (packet);
464 socklen_t fromlen = (socklen_t)sizeof (from);
465 int cc;
466 sigset_t newmask, oldmask;
468 if ((cc = recvfrom(s, (char *)packet, len, 0,
469 (struct sockaddr *)&from,
470 &fromlen)) < 0) {
471 if (errno == EINTR)
472 continue;
473 logperror("recvfrom");
474 continue;
476 /* Block all signals while processing */
477 (void) sigfillset(&newmask);
478 (void) sigprocmask(SIG_SETMASK, &newmask, &oldmask);
479 pr_pack((char *)packet, cc, &from);
480 (void) sigprocmask(SIG_SETMASK, &oldmask, NULL);
482 /* NOTREACHED */
485 static void
486 report(void)
488 report_interfaces();
489 report_routes();
492 #define TIMER_INTERVAL 6
493 #define GETIFCONF_TIMER 30
495 static int left_until_advertise;
497 /* Called every TIMER_INTERVAL */
498 static void
499 timer(void)
501 static int time;
502 static int left_until_getifconf;
503 static int left_until_solicit;
505 time += TIMER_INTERVAL;
507 left_until_getifconf -= TIMER_INTERVAL;
508 left_until_advertise -= TIMER_INTERVAL;
509 left_until_solicit -= TIMER_INTERVAL;
511 if (left_until_getifconf < 0) {
512 (void) initifs(s, &g_joinaddr, g_preference);
513 left_until_getifconf = GETIFCONF_TIMER;
515 if (responder && left_until_advertise <= 0) {
516 ntransmitted++;
517 advertise(&whereto);
518 if (ntransmitted < initial_advertisements)
519 left_until_advertise = initial_advert_interval;
520 else
521 left_until_advertise = min_adv_int +
522 ((max_adv_int - min_adv_int) *
523 (random() % 1000)/1000);
524 } else if (solicit && left_until_solicit <= 0) {
525 if (ntransmitted < max_solicitations) {
526 ntransmitted++;
527 solicitor(&whereto);
528 left_until_solicit = solicitation_interval;
529 } else {
530 solicit = 0;
531 if (!forever && nreceived == 0)
532 exit(5);
535 age_table(TIMER_INTERVAL);
536 (void) alarm(TIMER_INTERVAL);
540 * S O L I C I T O R
542 * Compose and transmit an ICMP ROUTER SOLICITATION REQUEST packet.
543 * The IP packet will be added on by the kernel.
545 static void
546 solicitor(struct sockaddr_in *sin)
548 static uchar_t outpack[MAXPACKET];
549 register struct icmp *icp = (struct icmp *)ALIGN(outpack);
550 int packetlen, i;
552 if (verbose) {
553 logtrace("Sending solicitation to %s\n",
554 pr_name(sin->sin_addr));
556 icp->icmp_type = ICMP_ROUTERSOLICIT;
557 icp->icmp_code = 0;
558 icp->icmp_cksum = 0;
559 icp->icmp_void = 0; /* Reserved */
560 packetlen = 8;
562 /* Compute ICMP checksum here */
563 icp->icmp_cksum = in_cksum((ushort_t *)icp, packetlen);
565 if (isbroadcast(sin))
566 i = sendbcast(s, (char *)outpack, packetlen);
567 else if (ismulticast(sin))
568 i = sendmcast(s, (char *)outpack, packetlen, sin);
569 else {
570 struct logint *li;
572 li = find_directly_connected_logint(sin->sin_addr, NULL);
573 if (li != NULL && (li->li_flags & IFF_NORTEXCH)) {
574 if (verbose) {
575 logtrace("Suppressing sending %s on %s "
576 "(no route exchange on interface)\n",
577 pr_type((int)icp->icmp_type), li->li_name);
579 return;
580 } else {
581 i = sendto(s, (char *)outpack, packetlen, 0,
582 (struct sockaddr *)sin, sizeof (struct sockaddr));
586 if (i < 0 || i != packetlen) {
587 if (i < 0) {
588 logperror("sendto");
590 logerr("wrote %s %d chars, ret=%d\n",
591 sendaddress, packetlen, i);
596 * A D V E R T I S E
598 * Compose and transmit an ICMP ROUTER ADVERTISEMENT packet.
599 * The IP packet will be added on by the kernel.
601 static void
602 advertise(struct sockaddr_in *sin)
604 struct phyint *pi;
605 struct logint *li, *li_tmp;
606 static uchar_t outpack[MAXPACKET];
607 register struct icmp_ra *rap = (struct icmp_ra *)ALIGN(outpack);
608 struct icmp_ra_addr *ap;
609 int packetlen, cc;
611 if (verbose) {
612 logtrace("Sending advertisement to %s\n",
613 pr_name(sin->sin_addr));
616 for (pi = phyint; pi != NULL; pi = pi->pi_next) {
617 rap->icmp_type = ICMP_ROUTERADVERT;
618 rap->icmp_code = 0;
619 rap->icmp_cksum = 0;
620 rap->icmp_num_addrs = 0;
621 rap->icmp_wpa = 2;
622 rap->icmp_lifetime = htons(lifetime);
623 packetlen = ICMP_MINLEN;
625 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
626 if (li->li_state & ST_DELETED)
627 continue;
630 * XXX Just truncate the list of addresses.
631 * Should probably send multiple packets.
633 if (packetlen + rap->icmp_wpa * 4 > sizeof (outpack)) {
634 if (debug)
635 logdebug("full packet: %d addresses\n",
636 rap->icmp_num_addrs);
637 break;
639 ap = (struct icmp_ra_addr *)ALIGN(outpack + packetlen);
640 ap->addr = li->li_localaddr.s_addr;
641 ap->preference = htonl(li->li_preference);
642 packetlen += rap->icmp_wpa * 4;
643 rap->icmp_num_addrs++;
646 if (rap->icmp_num_addrs == 0)
647 continue;
649 /* Compute ICMP checksum here */
650 rap->icmp_cksum = in_cksum((ushort_t *)rap, packetlen);
652 if (isbroadcast(sin))
653 cc = sendbcastif(s, (char *)outpack, packetlen,
654 pi->pi_logical_first);
655 else if (ismulticast(sin))
656 cc = sendmcastif(s, (char *)outpack, packetlen, sin,
657 pi->pi_logical_first);
658 else {
660 * Verify that the physical interface matches the
661 * destination address.
663 li_tmp = find_directly_connected_logint(sin->sin_addr,
664 pi);
665 if (li_tmp == NULL)
666 continue;
667 if (li_tmp->li_flags & IFF_NORTEXCH) {
668 if (verbose) {
669 logtrace("Suppressing sending %s on %s "
670 "(no route exchange on "
671 "interface)\n",
672 pr_type((int)rap->icmp_type),
673 li_tmp->li_name);
675 continue;
677 if (debug) {
678 logdebug("Unicast to %s ",
679 pr_name(sin->sin_addr));
680 logdebug("on interface %s\n", pi->pi_name);
682 cc = sendto(s, (char *)outpack, packetlen, 0,
683 (struct sockaddr *)sin, sizeof (struct sockaddr));
685 if (cc < 0 || cc != packetlen) {
686 if (cc < 0) {
687 logperror("sendto");
688 } else {
689 logerr("wrote %s %d chars, ret=%d\n",
690 sendaddress, packetlen, cc);
697 * P R _ T Y P E
699 * Convert an ICMP "type" field to a printable string.
701 char *
702 pr_type(int t)
704 static char *ttab[] = {
705 "Echo Reply",
706 "ICMP 1",
707 "ICMP 2",
708 "Dest Unreachable",
709 "Source Quench",
710 "Redirect",
711 "ICMP 6",
712 "ICMP 7",
713 "Echo",
714 "Router Advertise",
715 "Router Solicitation",
716 "Time Exceeded",
717 "Parameter Problem",
718 "Timestamp",
719 "Timestamp Reply",
720 "Info Request",
721 "Info Reply",
722 "Netmask Request",
723 "Netmask Reply"
726 if (t < 0 || t > 16)
727 return ("OUT-OF-RANGE");
729 return (ttab[t]);
733 * P R _ N A M E
735 * Return a string name for the given IP address.
737 char *
738 pr_name(struct in_addr addr)
740 struct hostent *phe;
741 static char buf[256];
743 phe = gethostbyaddr((char *)&addr.s_addr, 4, AF_INET);
744 if (phe == NULL)
745 return (inet_ntoa(addr));
746 (void) sprintf(buf, "%s (%s)", phe->h_name, inet_ntoa(addr));
747 return (buf);
751 * P R _ P A C K
753 * Print out the packet, if it came from us. This logic is necessary
754 * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
755 * which arrive ('tis only fair). This permits multiple copies of this
756 * program to be run without having intermingled output (or statistics!).
758 static void
759 pr_pack(char *buf, int cc, struct sockaddr_in *from)
761 struct ip *ip;
762 register struct icmp *icp;
763 register int i;
764 int hlen;
765 struct logint *li;
767 ip = (struct ip *)ALIGN(buf);
768 hlen = ip->ip_hl << 2;
769 if (cc < hlen + ICMP_MINLEN) {
770 if (verbose)
771 logtrace("packet too short (%d bytes) from %s\n", cc,
772 pr_name(from->sin_addr));
773 return;
776 cc -= hlen;
777 icp = (struct icmp *)ALIGN(buf + hlen);
780 * Let's check if IFF_NORTEXCH flag is set on the interface which
781 * recevied this packet.
782 * TODO: this code can be re-written using one socket per interface
783 * to determine which interface the packet is recevied.
785 li = find_directly_connected_logint(ip->ip_src, NULL);
786 if (li != NULL && (li->li_flags & IFF_NORTEXCH)) {
787 if (verbose) {
788 logtrace("Ignoring received %s on %s "
789 "(no route exchange on interface)",
790 pr_type((int)icp->icmp_type), li->li_name);
792 return;
795 if (ip->ip_p == 0) {
797 * Assume that we are running on a pre-4.3BSD system
798 * such as SunOS before 4.0
800 icp = (struct icmp *)ALIGN(buf);
802 switch (icp->icmp_type) {
803 case ICMP_ROUTERADVERT: {
804 struct icmp_ra *rap = (struct icmp_ra *)ALIGN(icp);
805 struct icmp_ra_addr *ap;
807 if (responder)
808 break;
810 /* TBD verify that the link is multicast or broadcast */
811 /* XXX Find out the link it came in over? */
812 #ifdef notdef
813 if (debug) {
814 logdebug("ROUTER_ADVERTISEMENT: \n");
815 pr_hex(buf+hlen, cc);
817 #endif /* notdef */
818 if (in_cksum((ushort_t *)ALIGN(buf+hlen), cc)) {
819 if (verbose)
820 logtrace("ICMP %s from %s: Bad checksum\n",
821 pr_type((int)rap->icmp_type),
822 pr_name(from->sin_addr));
823 return;
825 if (rap->icmp_code != 0) {
826 if (verbose)
827 logtrace("ICMP %s from %s: Code = %d\n",
828 pr_type((int)rap->icmp_type),
829 pr_name(from->sin_addr),
830 rap->icmp_code);
831 return;
833 if (rap->icmp_num_addrs < 1) {
834 if (verbose)
835 logtrace("ICMP %s from %s: No addresses\n",
836 pr_type((int)rap->icmp_type),
837 pr_name(from->sin_addr));
838 return;
840 if (rap->icmp_wpa < 2) {
841 if (verbose)
842 logtrace("ICMP %s from %s: Words/addr = %d\n",
843 pr_type((int)rap->icmp_type),
844 pr_name(from->sin_addr),
845 rap->icmp_wpa);
846 return;
848 if ((unsigned)cc <
849 ICMP_MINLEN + rap->icmp_num_addrs * rap->icmp_wpa * 4) {
850 if (verbose)
851 logtrace("ICMP %s from %s: Too short %d, %d\n",
852 pr_type((int)rap->icmp_type),
853 pr_name(from->sin_addr),
855 ICMP_MINLEN +
856 rap->icmp_num_addrs *
857 rap->icmp_wpa * 4);
858 return;
860 rap->icmp_lifetime = ntohs(rap->icmp_lifetime);
861 if ((rap->icmp_lifetime < 4 && rap->icmp_lifetime != 0) ||
862 rap->icmp_lifetime > 9000) {
863 if (verbose)
864 logtrace("ICMP %s from %s: Invalid lifetime %d\n",
865 pr_type((int)rap->icmp_type),
866 pr_name(from->sin_addr),
867 rap->icmp_lifetime);
868 return;
870 if (verbose)
871 logtrace("ICMP %s from %s, lifetime %d\n",
872 pr_type((int)rap->icmp_type),
873 pr_name(from->sin_addr),
874 rap->icmp_lifetime);
877 * Check that at least one router address is a neighbor
878 * on the arriving link.
880 for (i = 0; (unsigned)i < rap->icmp_num_addrs; i++) {
881 struct in_addr ina;
882 ap = (struct icmp_ra_addr *)
883 ALIGN(buf + hlen + ICMP_MINLEN +
884 i * rap->icmp_wpa * 4);
885 ap->preference = ntohl(ap->preference);
886 ina.s_addr = ap->addr;
887 if (verbose)
888 logtrace("\taddress %s, preference 0x%x\n",
889 pr_name(ina),
890 ap->preference);
891 if (!responder) {
892 if (find_directly_connected_logint(ina, NULL) !=
893 NULL) {
894 record_router(ina,
895 (long)ap->preference,
896 rap->icmp_lifetime);
900 nreceived++;
901 if (!forever) {
902 (void) alarm(0);
903 do_fork();
904 forever = 1;
905 (void) alarm(TIMER_INTERVAL);
907 break;
910 case ICMP_ROUTERSOLICIT: {
911 struct sockaddr_in sin;
913 if (!responder)
914 break;
916 /* TBD verify that the link is multicast or broadcast */
917 /* XXX Find out the link it came in over? */
918 #ifdef notdef
919 if (debug) {
920 logdebug("ROUTER_SOLICITATION: \n");
921 pr_hex(buf+hlen, cc);
923 #endif /* notdef */
924 if (in_cksum((ushort_t *)ALIGN(buf+hlen), cc)) {
925 if (verbose)
926 logtrace("ICMP %s from %s: Bad checksum\n",
927 pr_type((int)icp->icmp_type),
928 pr_name(from->sin_addr));
929 return;
931 if (icp->icmp_code != 0) {
932 if (verbose)
933 logtrace("ICMP %s from %s: Code = %d\n",
934 pr_type((int)icp->icmp_type),
935 pr_name(from->sin_addr),
936 icp->icmp_code);
937 return;
940 if (cc < ICMP_MINLEN) {
941 if (verbose)
942 logtrace("ICMP %s from %s: Too short %d, %d\n",
943 pr_type((int)icp->icmp_type),
944 pr_name(from->sin_addr),
946 ICMP_MINLEN);
947 return;
950 if (verbose)
951 logtrace("ICMP %s from %s\n",
952 pr_type((int)icp->icmp_type),
953 pr_name(from->sin_addr));
955 if (!responder)
956 break;
959 * Check that ip_src is either a neighbor
960 * on the arriving link or 0.
962 sin.sin_family = AF_INET;
963 if (ip->ip_src.s_addr == 0) {
965 * If it was sent to the broadcast address we respond
966 * to the broadcast address.
968 if (IN_CLASSD(ntohl(ip->ip_dst.s_addr))) {
969 sin.sin_addr.s_addr =
970 htonl(INADDR_ALLHOSTS_GROUP);
971 } else
972 sin.sin_addr.s_addr = htonl(INADDR_BROADCAST);
973 /* Restart the timer when we broadcast */
974 left_until_advertise = min_adv_int +
975 ((max_adv_int - min_adv_int)
976 * (random() % 1000)/1000);
977 } else {
978 if (li == NULL) {
979 if (verbose)
980 logtrace("ICMP %s from %s: %s\n",
981 pr_type((int)icp->icmp_type),
982 pr_name(from->sin_addr),
983 "source not directly connected");
984 break;
986 sin.sin_addr.s_addr = ip->ip_src.s_addr;
988 nreceived++;
989 ntransmitted++;
990 advertise(&sin);
991 break;
998 * I N _ C K S U M
1000 * Checksum routine for Internet Protocol family headers (C Version)
1004 in_cksum(ushort_t *addr, int len)
1006 register int nleft = len;
1007 register ushort_t *w = addr;
1008 register ushort_t answer;
1009 ushort_t odd_byte = 0;
1010 register int sum = 0;
1013 * Our algorithm is simple, using a 32 bit accumulator (sum),
1014 * we add sequential 16 bit words to it, and at the end, fold
1015 * back all the carry bits from the top 16 bits into the lower
1016 * 16 bits.
1018 while (nleft > 1) {
1019 sum += *w++;
1020 nleft -= 2;
1023 /* mop up an odd byte, if necessary */
1024 if (nleft == 1) {
1025 *(uchar_t *)(&odd_byte) = *(uchar_t *)w;
1026 sum += odd_byte;
1030 * add back carry outs from top 16 bits to low 16 bits
1032 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
1033 sum += (sum >> 16); /* add carry */
1034 answer = ~sum; /* truncate to 16 bits */
1035 return (answer);
1039 * F I N I S H
1041 * Print out statistics, and give up.
1042 * Heavily buffered stdio is used here, so that all the statistics
1043 * will be written with 1 sys-write call. This is nice when more
1044 * than one copy of the program is running on a terminal; it prevents
1045 * the statistics output from becoming intermingled.
1047 static void
1048 finish(void)
1050 if (responder) {
1052 * Send out a packet with a preference so that all
1053 * hosts will know that we are dead.
1055 logerr("terminated\n");
1056 force_preference(IGNORE_PREFERENCE);
1057 ntransmitted++;
1058 advertise(&whereto);
1060 if (verbose) {
1061 logtrace("\n----%s rdisc Statistics----\n", sendaddress);
1062 logtrace("%d packets transmitted, ", ntransmitted);
1063 logtrace("%d packets received, ", nreceived);
1064 logtrace("\n");
1066 (void) fflush(stdout);
1067 exit(0);
1070 #include <ctype.h>
1072 #ifdef notdef
1074 pr_hex(unsigned char *data, int len)
1076 FILE *out;
1078 out = stdout;
1080 while (len) {
1081 register int i;
1082 char charstring[17];
1084 (void) strcpy(charstring, " "); /* 16 spaces */
1085 for (i = 0; i < 16; i++) {
1087 * output the bytes one at a time,
1088 * not going past "len" bytes
1090 if (len) {
1091 char ch = *data & 0x7f; /* strip parity */
1092 if (!isprint((uchar_t)ch))
1093 ch = ' '; /* ensure printable */
1094 charstring[i] = ch;
1095 (void) fprintf(out, "%02x ", *data++);
1096 len--;
1097 } else
1098 (void) fprintf(out, " ");
1099 if (i == 7)
1100 (void) fprintf(out, " ");
1103 (void) fprintf(out, " *%s*\n", charstring);
1106 #endif /* notdef */
1108 static int
1109 isbroadcast(struct sockaddr_in *sin)
1111 return (sin->sin_addr.s_addr == htonl(INADDR_BROADCAST));
1114 static int
1115 ismulticast(struct sockaddr_in *sin)
1117 return (IN_CLASSD(ntohl(sin->sin_addr.s_addr)));
1120 /* From libc/rpc/pmap_rmt.c */
1123 /* Only send once per physical interface */
1124 static int
1125 sendbcast(int s, char *packet, int packetlen)
1127 struct phyint *pi;
1128 struct logint *li;
1129 boolean_t bcast;
1130 int cc;
1132 for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1133 bcast = B_FALSE;
1134 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1135 if (li->li_state & ST_DELETED)
1136 continue;
1138 if (li->li_flags & IFF_BROADCAST) {
1139 bcast = B_TRUE;
1140 break;
1143 if (!bcast)
1144 continue;
1145 cc = sendbcastif(s, packet, packetlen, li);
1146 if (cc != packetlen) {
1147 return (cc);
1150 return (packetlen);
1153 static int
1154 sendbcastif(int s, char *packet, int packetlen, struct logint *li)
1156 int cc;
1157 struct sockaddr_in baddr;
1158 struct icmp *icp = (struct icmp *)ALIGN(packet);
1160 baddr.sin_family = AF_INET;
1162 if ((li->li_flags & IFF_BROADCAST) == 0) {
1163 if (verbose) {
1164 logtrace("Suppressing sending %s on %s "
1165 "(interface is not broadcast capable)\n",
1166 pr_type((int)icp->icmp_type), li->li_name);
1168 return (packetlen);
1170 if (li->li_flags & IFF_NORTEXCH) {
1171 if (verbose) {
1172 logtrace("Suppressing sending %s on %s "
1173 "(no route exchange on interface)\n",
1174 pr_type((int)icp->icmp_type), li->li_name);
1176 return (packetlen);
1179 baddr.sin_addr = li->li_bcastaddr;
1180 if (debug)
1181 logdebug("Broadcast to %s\n",
1182 pr_name(baddr.sin_addr));
1183 cc = sendto(s, packet, packetlen, 0,
1184 (struct sockaddr *)&baddr, sizeof (struct sockaddr));
1185 if (cc != packetlen) {
1186 logperror("sendbcast: sendto");
1187 logerr("Cannot send broadcast packet to %s\n",
1188 pr_name(baddr.sin_addr));
1190 return (cc);
1193 static int
1194 sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *sin)
1196 struct phyint *pi;
1197 struct logint *li;
1198 boolean_t mcast;
1199 int cc;
1201 for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1202 mcast = B_FALSE;
1203 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1204 if (li->li_state & ST_DELETED)
1205 continue;
1207 if (li->li_flags & IFF_MULTICAST) {
1208 mcast = B_TRUE;
1209 break;
1212 if (!mcast)
1213 continue;
1214 cc = sendmcastif(s, packet, packetlen, sin, li);
1215 if (cc != packetlen) {
1216 return (cc);
1219 return (packetlen);
1222 static int
1223 sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin,
1224 struct logint *li)
1226 int cc;
1227 struct sockaddr_in ifaddr;
1228 struct icmp *icp = (struct icmp *)ALIGN(packet);
1230 ifaddr.sin_family = AF_INET;
1232 if ((li->li_flags & IFF_MULTICAST) == 0) {
1233 if (verbose) {
1234 logtrace("Suppressing sending %s on %s "
1235 "(interface is not multicast capable)\n",
1236 pr_type((int)icp->icmp_type), li->li_name);
1238 return (packetlen);
1240 if (li->li_flags & IFF_NORTEXCH) {
1241 if (verbose) {
1242 logtrace("Suppressing sending %s on %s "
1243 "(no route exchange on interface)\n",
1244 pr_type((int)icp->icmp_type), li->li_name);
1246 return (packetlen);
1249 ifaddr.sin_addr = li->li_address;
1250 if (debug)
1251 logdebug("Multicast to interface %s\n",
1252 pr_name(ifaddr.sin_addr));
1253 if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
1254 (char *)&ifaddr.sin_addr,
1255 sizeof (ifaddr.sin_addr)) < 0) {
1256 logperror("setsockopt (IP_MULTICAST_IF)");
1257 logerr("Cannot send multicast packet over interface %s\n",
1258 pr_name(ifaddr.sin_addr));
1259 return (-1);
1261 cc = sendto(s, packet, packetlen, 0,
1262 (struct sockaddr *)sin, sizeof (struct sockaddr));
1263 if (cc != packetlen) {
1264 logperror("sendmcast: sendto");
1265 logerr("Cannot send multicast packet over interface %s\n",
1266 pr_name(ifaddr.sin_addr));
1268 return (cc);
1271 static void
1272 reinitifs(void)
1274 (void) initifs(s, &g_joinaddr, g_preference);
1277 static void
1278 force_preference(int preference)
1280 struct phyint *pi;
1281 struct logint *li;
1283 for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1284 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1285 if (li->li_state & ST_DELETED)
1286 continue;
1288 li->li_preference = preference;
1294 * Returns -1 on failure.
1296 static int
1297 initifs(int s, struct sockaddr_in *joinaddr, int preference)
1299 struct ifconf ifc;
1300 struct ifreq ifreq, *ifr;
1301 struct lifreq lifreq;
1302 int n;
1303 char *buf;
1304 int numifs;
1305 unsigned bufsize;
1306 struct phyint *pi;
1307 struct logint *li;
1308 int num_deletions;
1309 char phyintname[IFNAMSIZ];
1310 char *cp;
1311 int old_num_usable_interfaces = num_usable_interfaces;
1314 * Mark all interfaces so that we can determine which ones
1315 * have gone away.
1317 for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1318 pi->pi_state |= ST_MARKED;
1319 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1320 li->li_state |= ST_MARKED;
1324 if (sock < 0) {
1325 sock = socket(AF_INET, SOCK_DGRAM, 0);
1326 if (sock < 0) {
1327 logperror("initifs: socket");
1328 return (-1);
1331 #ifdef SIOCGIFNUM
1332 if (ioctl(sock, SIOCGIFNUM, (char *)&numifs) < 0) {
1333 logperror("initifs: SIOCGIFNUM");
1334 return (-1);
1336 #else
1337 numifs = MAXIFS;
1338 #endif
1339 bufsize = numifs * sizeof (struct ifreq);
1340 buf = (char *)malloc(bufsize);
1341 if (buf == NULL) {
1342 logerr("out of memory\n");
1343 (void) close(sock);
1344 sock = -1;
1345 return (-1);
1347 ifc.ifc_len = bufsize;
1348 ifc.ifc_buf = buf;
1349 if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
1350 logperror("initifs: ioctl (get interface configuration)");
1351 (void) close(sock);
1352 sock = -1;
1353 (void) free(buf);
1354 return (-1);
1356 ifr = ifc.ifc_req;
1357 for (n = ifc.ifc_len/sizeof (struct ifreq); n > 0; n--, ifr++) {
1358 ifreq = *ifr;
1360 * We need to use new interface ioctls to get 64-bit flags.
1362 (void) strncpy(lifreq.lifr_name, ifr->ifr_name,
1363 sizeof (ifr->ifr_name));
1364 if (ioctl(sock, SIOCGLIFFLAGS, (char *)&lifreq) < 0) {
1365 logperror("initifs: ioctl (get interface flags)");
1366 continue;
1368 if (ifr->ifr_addr.sa_family != AF_INET)
1369 continue;
1370 if ((lifreq.lifr_flags & IFF_UP) == 0)
1371 continue;
1372 if (lifreq.lifr_flags & IFF_LOOPBACK)
1373 continue;
1374 if ((lifreq.lifr_flags & (IFF_MULTICAST | IFF_BROADCAST)) == 0)
1375 continue;
1377 /* Create the physical name by truncating at the ':' */
1378 strncpy(phyintname, ifreq.ifr_name, sizeof (phyintname));
1379 if ((cp = strchr(phyintname, ':')) != NULL)
1380 *cp = '\0';
1382 pi = find_phyint(phyintname);
1383 if (pi == NULL) {
1384 pi = add_phyint(phyintname);
1385 if (pi == NULL) {
1386 logerr("out of memory\n");
1387 (void) close(sock);
1388 sock = -1;
1389 (void) free(buf);
1390 return (-1);
1393 pi->pi_state &= ~ST_MARKED;
1395 li = find_logint(pi, ifreq.ifr_name);
1396 if (li != NULL) {
1398 * Detect significant changes.
1399 * We treat netmask changes as insignificant but all
1400 * other changes cause a delete plus add of the
1401 * logical interface.
1402 * Note: if the flags and localaddr are unchanged
1403 * then nothing but the netmask and the broadcast
1404 * address could have changed since the other addresses
1405 * are derived from the flags and the localaddr.
1407 struct logint newli;
1409 if (!getconfig(sock, lifreq.lifr_flags, &ifr->ifr_addr,
1410 &ifreq, &newli)) {
1411 free_logint(li);
1412 continue;
1415 if (newli.li_flags != li->li_flags ||
1416 newli.li_localaddr.s_addr !=
1417 li->li_localaddr.s_addr || newli.li_index !=
1418 li->li_index) {
1419 /* Treat as an interface deletion + addition */
1420 li->li_state |= ST_DELETED;
1421 deleted_logint(li, &newli, s, joinaddr);
1422 free_logint(li);
1423 li = NULL; /* li recreated below */
1424 } else {
1426 * No significant changes.
1427 * Just update the netmask, and broadcast.
1429 li->li_netmask = newli.li_netmask;
1430 li->li_bcastaddr = newli.li_bcastaddr;
1433 if (li == NULL) {
1434 li = add_logint(pi, ifreq.ifr_name);
1435 if (li == NULL) {
1436 logerr("out of memory\n");
1437 (void) close(sock);
1438 sock = -1;
1439 (void) free(buf);
1440 return (-1);
1443 /* init li */
1444 if (!getconfig(sock, lifreq.lifr_flags, &ifr->ifr_addr,
1445 &ifreq, li)) {
1446 free_logint(li);
1447 continue;
1449 li->li_preference = preference;
1450 added_logint(li, s, joinaddr);
1452 li->li_state &= ~ST_MARKED;
1454 (void) free(buf);
1457 * Determine which interfaces have gone away.
1458 * The deletion is done in three phases:
1459 * 1. Mark ST_DELETED
1460 * 2. Inform using the deleted_* function.
1461 * 3. Unlink and free the actual memory.
1462 * Note that for #3 the physical interface must be deleted after
1463 * the logical ones.
1464 * Also count the number of physical interfaces.
1466 num_usable_interfaces = 0;
1467 num_deletions = 0;
1468 for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1469 if (pi->pi_state & ST_MARKED) {
1470 num_deletions++;
1471 pi->pi_state |= ST_DELETED;
1473 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1474 if (li->li_state & ST_MARKED) {
1475 num_deletions++;
1476 li->li_state |= ST_DELETED;
1479 if (!(pi->pi_state & ST_DELETED))
1480 num_usable_interfaces++;
1482 if (num_deletions != 0) {
1483 struct phyint *nextpi;
1484 struct logint *nextli;
1486 for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1487 if (pi->pi_state & ST_DELETED) {
1489 * By deleting the physical interface pi, all of
1490 * the corresponding logical interfaces will
1491 * also be deleted so there is no need to delete
1492 * them individually.
1494 deleted_phyint(pi, s, joinaddr);
1495 } else {
1496 for (li = pi->pi_logical_first; li != NULL;
1497 li = li->li_next) {
1498 if (li->li_state & ST_DELETED) {
1499 deleted_logint(li, NULL, s,
1500 joinaddr);
1505 /* Do the actual linked list update + free */
1506 for (pi = phyint; pi != NULL; pi = nextpi) {
1507 nextpi = pi->pi_next;
1508 for (li = pi->pi_logical_first; li != NULL;
1509 li = nextli) {
1510 nextli = li->li_next;
1511 if (li->li_state & ST_DELETED)
1512 free_logint(li);
1514 if (pi->pi_state & ST_DELETED)
1515 free_phyint(pi);
1519 * When the set of available interfaces goes from zero to
1520 * non-zero we restart solicitations if '-s' was specified.
1522 if (old_num_usable_interfaces == 0 && num_usable_interfaces > 0 &&
1523 start_solicit && !solicit) {
1524 if (debug)
1525 logdebug("switching to solicitations: num if %d\n",
1526 num_usable_interfaces);
1527 solicit = start_solicit;
1528 ntransmitted = 0;
1529 ntransmitted++;
1530 solicitor(&whereto);
1532 return (0);
1535 static boolean_t
1536 getconfig(int sock, uint64_t if_flags, struct sockaddr *addr,
1537 struct ifreq *ifr, struct logint *li)
1539 struct ifreq ifreq;
1540 struct sockaddr_in *sin;
1541 struct lifreq lifreq;
1543 ifreq = *ifr; /* Copy name etc */
1545 li->li_flags = if_flags;
1546 sin = (struct sockaddr_in *)ALIGN(addr);
1547 li->li_localaddr = sin->sin_addr;
1549 (void) strlcpy(lifreq.lifr_name, ifr->ifr_name,
1550 sizeof (lifreq.lifr_name));
1551 if (ioctl(sock, SIOCGLIFINDEX, &lifreq) < 0) {
1552 logperror("initifs: ioctl (get if index)");
1553 /* Continue with 0; a safe value never used for interfaces */
1554 li->li_index = 0;
1555 } else {
1556 li->li_index = lifreq.lifr_index;
1559 if (if_flags & IFF_POINTOPOINT) {
1560 li->li_netmask.s_addr = (unsigned long)0xffffffff;
1561 if (ioctl(sock, SIOCGIFDSTADDR, (char *)&ifreq) < 0) {
1562 logperror("initifs: ioctl (get dest addr)");
1563 return (B_FALSE);
1565 /* A pt-pt link is identified by the remote address */
1566 sin = (struct sockaddr_in *)ALIGN(&ifreq.ifr_addr);
1567 li->li_address = sin->sin_addr;
1568 li->li_remoteaddr = sin->sin_addr;
1569 /* Simulate broadcast for pt-pt */
1570 li->li_bcastaddr = sin->sin_addr;
1571 li->li_flags |= IFF_BROADCAST;
1572 } else {
1574 * Non pt-pt links are identified by the local
1575 * address
1577 li->li_address = li->li_localaddr;
1578 li->li_remoteaddr = li->li_address;
1579 if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
1580 logperror("initifs: ioctl (get netmask)");
1581 return (B_FALSE);
1583 sin = (struct sockaddr_in *)ALIGN(&ifreq.ifr_addr);
1584 li->li_netmask = sin->sin_addr;
1585 if (if_flags & IFF_BROADCAST) {
1586 if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
1587 logperror(
1588 "initifs: ioctl (get broadcast address)");
1589 return (B_FALSE);
1591 sin = (struct sockaddr_in *)ALIGN(&ifreq.ifr_addr);
1592 li->li_bcastaddr = sin->sin_addr;
1595 return (B_TRUE);
1599 static int
1600 support_multicast(void)
1602 int sock;
1603 uchar_t ttl = 1;
1605 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1606 if (sock < 0) {
1607 logperror("support_multicast: socket");
1608 return (0);
1611 if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL,
1612 (char *)&ttl, sizeof (ttl)) < 0) {
1613 (void) close(sock);
1614 return (0);
1616 (void) close(sock);
1617 return (1);
1621 * For a given destination address, find the logical interface to use.
1622 * If opi is NULL check all interfaces. Otherwise just match against
1623 * the specified physical interface.
1624 * Return logical interface if there's a match, NULL otherwise.
1626 static struct logint *
1627 find_directly_connected_logint(struct in_addr in, struct phyint *opi)
1629 struct phyint *pi;
1630 struct logint *li;
1632 if (opi == NULL)
1633 pi = phyint;
1634 else
1635 pi = opi;
1637 for (; pi != NULL; pi = pi->pi_next) {
1638 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1639 if (li->li_state & ST_DELETED)
1640 continue;
1642 /* Check that the subnetwork numbers match */
1643 if ((in.s_addr & li->li_netmask.s_addr) ==
1644 (li->li_remoteaddr.s_addr &
1645 li->li_netmask.s_addr))
1646 return (li);
1648 if (opi != NULL)
1649 break;
1651 return (NULL);
1655 * INTERFACES - physical and logical identified by name
1659 static void
1660 report_interfaces(void)
1662 struct phyint *pi;
1663 struct logint *li;
1665 logdebug("\nInterfaces:\n\n");
1666 for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1667 logdebug("Phyint %s state 0x%x\n",
1668 pi->pi_name, pi->pi_state);
1669 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1670 logdebug("IF %s state 0x%x, flags 0x%x, addr %s\n",
1671 li->li_name, li->li_state, li->li_flags,
1672 pr_name(li->li_address));
1673 logdebug("\tlocal %s pref 0x%x ",
1674 pr_name(li->li_localaddr), li->li_preference);
1675 logdebug("bcast %s\n",
1676 pr_name(li->li_bcastaddr));
1677 logdebug("\tremote %s ",
1678 pr_name(li->li_remoteaddr));
1679 logdebug("netmask %s\n",
1680 pr_name(li->li_netmask));
1685 static struct phyint *
1686 find_phyint(char *name)
1688 struct phyint *pi;
1690 for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1691 if (strcmp(pi->pi_name, name) == 0)
1692 return (pi);
1694 return (NULL);
1697 /* Assumes that the entry does not exist - caller must use find_* */
1698 static struct phyint *
1699 add_phyint(char *name)
1701 struct phyint *pi;
1703 pi = malloc(sizeof (*pi));
1704 if (pi == NULL)
1705 return (NULL);
1706 bzero((char *)pi, sizeof (*pi));
1708 strncpy(pi->pi_name, name, sizeof (pi->pi_name));
1709 /* Link into list */
1710 pi->pi_next = phyint;
1711 pi->pi_prev = NULL;
1712 if (phyint != NULL)
1713 phyint->pi_prev = pi;
1714 phyint = pi;
1715 return (pi);
1718 static void
1719 free_phyint(struct phyint *pi)
1721 assert(pi->pi_logical_first == NULL);
1722 assert(pi->pi_logical_last == NULL);
1724 if (pi->pi_prev == NULL) {
1725 /* Delete first */
1726 assert(phyint == pi);
1727 phyint = pi->pi_next;
1728 } else {
1729 assert(pi->pi_prev->pi_next == pi);
1730 pi->pi_prev->pi_next = pi->pi_next;
1732 if (pi->pi_next != NULL) {
1733 assert(pi->pi_next->pi_prev == pi);
1734 pi->pi_next->pi_prev = pi->pi_prev;
1736 free(pi);
1739 static struct logint *
1740 find_logint(struct phyint *pi, char *name)
1742 struct logint *li;
1744 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1745 if (strcmp(li->li_name, name) == 0)
1746 return (li);
1748 return (NULL);
1752 * Assumes that the entry does not exist - caller must use find_*
1753 * Tail insertion.
1755 static struct logint *
1756 add_logint(struct phyint *pi, char *name)
1758 struct logint *li;
1760 li = malloc(sizeof (*li));
1761 if (li == NULL)
1762 return (NULL);
1763 bzero((char *)li, sizeof (*li));
1765 strncpy(li->li_name, name, sizeof (li->li_name));
1766 /* Link into list */
1767 li->li_prev = pi->pi_logical_last;
1768 if (pi->pi_logical_last == NULL) {
1769 /* First one */
1770 assert(pi->pi_logical_first == NULL);
1771 pi->pi_logical_first = li;
1772 } else {
1773 pi->pi_logical_last->li_next = li;
1775 li->li_next = NULL;
1776 li->li_physical = pi;
1777 pi->pi_logical_last = li;
1778 return (li);
1782 static void
1783 free_logint(struct logint *li)
1785 struct phyint *pi;
1787 pi = li->li_physical;
1788 if (li->li_prev == NULL) {
1789 /* Delete first */
1790 assert(pi->pi_logical_first == li);
1791 pi->pi_logical_first = li->li_next;
1792 } else {
1793 assert(li->li_prev->li_next == li);
1794 li->li_prev->li_next = li->li_next;
1796 if (li->li_next == NULL) {
1797 /* Delete last */
1798 assert(pi->pi_logical_last == li);
1799 pi->pi_logical_last = li->li_prev;
1800 } else {
1801 assert(li->li_next->li_prev == li);
1802 li->li_next->li_prev = li->li_prev;
1804 free(li);
1808 /* Tell all the logical interfaces that they are going away */
1809 static void
1810 deleted_phyint(struct phyint *pi, int s,
1811 struct sockaddr_in *joinaddr)
1813 struct logint *li;
1815 if (debug)
1816 logdebug("Deleting physical interface %s\n", pi->pi_name);
1818 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1819 li->li_state |= ST_DELETED;
1821 for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1822 deleted_logint(li, NULL, s, joinaddr);
1827 * Join the multicast address if no other logical interface has done
1828 * so for this physical interface.
1830 static void
1831 added_logint(struct logint *li, int s,
1832 struct sockaddr_in *joinaddr)
1834 if (debug)
1835 logdebug("Adding logical interface %s\n", li->li_name);
1837 if ((!(li->li_physical->pi_state & ST_JOINED)) &&
1838 (!isbroadcast(joinaddr))) {
1839 struct ip_mreq mreq;
1841 mreq.imr_multiaddr = joinaddr->sin_addr;
1842 mreq.imr_interface = li->li_address;
1844 if (debug)
1845 logdebug("Joining MC on interface %s\n", li->li_name);
1847 if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
1848 (char *)&mreq, sizeof (mreq)) < 0) {
1849 logperror("setsockopt (IP_ADD_MEMBERSHIP)");
1850 } else {
1851 li->li_physical->pi_state |= ST_JOINED;
1852 li->li_state |= ST_JOINED;
1858 * Leave the multicast address if this logical interface joined it.
1859 * Look for a replacement logical interface for the same physical interface.
1860 * Remove any routes which are no longer reachable.
1862 * If newli is non-NULL, then it is likely that the address of a logical
1863 * interface has changed. In this case, the membership should be dropped using
1864 * the new address of the interface in question.
1866 * XXX When a physical interface is being deleted by deleted_phyint(), this
1867 * routine will be called for each logical interface associated with the
1868 * physical one. This should be made more efficient as there is no point in
1869 * searching for an alternate logical interface to add group membership to as
1870 * they all are marked ST_DELETED.
1872 static void
1873 deleted_logint(struct logint *li, struct logint *newli, int s,
1874 struct sockaddr_in *joinaddr)
1876 struct phyint *pi;
1877 struct logint *oli;
1879 if (debug)
1880 logdebug("Deleting logical interface %s\n", li->li_name);
1882 assert(li->li_state & ST_DELETED);
1884 if (li->li_state & ST_JOINED) {
1885 struct ip_mreq mreq;
1887 pi = li->li_physical;
1888 assert(pi->pi_state & ST_JOINED);
1889 assert(!isbroadcast(joinaddr));
1891 mreq.imr_multiaddr = joinaddr->sin_addr;
1892 if (newli != NULL)
1893 mreq.imr_interface = newli->li_address;
1894 else
1895 mreq.imr_interface = li->li_address;
1897 if (debug)
1898 logdebug("Leaving MC on interface %s\n", li->li_name);
1900 if (setsockopt(s, IPPROTO_IP, IP_DROP_MEMBERSHIP,
1901 (char *)&mreq, sizeof (mreq)) < 0) {
1903 * EADDRNOTAVAIL will be returned if the interface has
1904 * been unplumbed or if the interface no longer has
1905 * IFF_MULTICAST set. The former is the common case
1906 * while the latter is rare so don't log the error
1907 * unless some other error was returned or if debug is
1908 * set.
1910 if (errno != EADDRNOTAVAIL) {
1911 logperror("setsockopt (IP_DROP_MEMBERSHIP)");
1912 } else if (debug) {
1913 logdebug("%s: %s\n",
1914 "setsockopt (IP_DROP_MEMBERSHIP)",
1915 strerror(errno));
1918 li->li_physical->pi_state &= ~ST_JOINED;
1919 li->li_state &= ~ST_JOINED;
1921 /* Is there another interface that can join? */
1922 for (oli = pi->pi_logical_first; oli != NULL;
1923 oli = oli->li_next) {
1924 if (oli->li_state & ST_DELETED)
1925 continue;
1927 mreq.imr_multiaddr = joinaddr->sin_addr;
1928 mreq.imr_interface = oli->li_address;
1930 if (debug)
1931 logdebug("Joining MC on interface %s\n",
1932 oli->li_name);
1934 if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
1935 (char *)&mreq, sizeof (mreq)) < 0) {
1936 logperror("setsockopt (IP_ADD_MEMBERSHIP)");
1937 } else {
1938 pi->pi_state |= ST_JOINED;
1939 oli->li_state |= ST_JOINED;
1940 break;
1945 flush_unreachable_routers();
1951 * TABLES
1953 struct table {
1954 struct in_addr router;
1955 int preference;
1956 int remaining_time;
1957 int in_kernel;
1958 struct table *next;
1961 struct table *table;
1963 static void
1964 report_routes(void)
1966 struct table *tp;
1968 logdebug("\nRoutes:\n\n");
1969 tp = table;
1970 while (tp) {
1971 logdebug("Router %s, pref 0x%x, time %d, %s kernel\n",
1972 pr_name(tp->router), tp->preference,
1973 tp->remaining_time,
1974 (tp->in_kernel ? "in" : "not in"));
1975 tp = tp->next;
1979 static struct table *
1980 find_router(struct in_addr addr)
1982 struct table *tp;
1984 tp = table;
1985 while (tp) {
1986 if (tp->router.s_addr == addr.s_addr)
1987 return (tp);
1988 tp = tp->next;
1990 return (NULL);
1993 static int
1994 max_preference(void)
1996 struct table *tp;
1997 int max = (int)IGNORE_PREFERENCE;
1999 tp = table;
2000 while (tp) {
2001 if (tp->preference > max)
2002 max = tp->preference;
2003 tp = tp->next;
2005 return (max);
2009 /* Note: this might leave the kernel with no default route for a short time. */
2010 static void
2011 age_table(int time)
2013 struct table **tpp, *tp;
2014 int recalculate_max = 0;
2015 int max = max_preference();
2017 tpp = &table;
2018 while (*tpp != NULL) {
2019 tp = *tpp;
2020 tp->remaining_time -= time;
2021 if (tp->remaining_time <= 0) {
2022 *tpp = tp->next;
2023 if (debug) {
2024 logdebug("Timed out router %s\n",
2025 pr_name(tp->router));
2027 if (tp->in_kernel)
2028 del_route(tp->router);
2029 if (best_preference &&
2030 tp->preference == max)
2031 recalculate_max++;
2032 free((char *)tp);
2033 } else {
2034 tpp = &tp->next;
2037 if (recalculate_max) {
2038 int max = max_preference();
2040 if (max != IGNORE_PREFERENCE) {
2041 tp = table;
2042 while (tp) {
2043 if (tp->preference == max && !tp->in_kernel) {
2044 add_route(tp->router);
2045 tp->in_kernel++;
2047 tp = tp->next;
2054 * Remove any routes which are no longer directly connected.
2056 static void
2057 flush_unreachable_routers(void)
2059 struct table **tpp, *tp;
2060 int recalculate_max = 0;
2061 int max = max_preference();
2063 tpp = &table;
2064 while (*tpp != NULL) {
2065 tp = *tpp;
2066 if (find_directly_connected_logint(tp->router, NULL) == NULL) {
2067 *tpp = tp->next;
2068 if (debug) {
2069 logdebug("Unreachable router %s\n",
2070 pr_name(tp->router));
2072 if (tp->in_kernel)
2073 del_route(tp->router);
2074 if (best_preference &&
2075 tp->preference == max)
2076 recalculate_max++;
2077 free((char *)tp);
2078 } else {
2079 tpp = &tp->next;
2082 if (recalculate_max) {
2083 int max = max_preference();
2085 if (max != IGNORE_PREFERENCE) {
2086 tp = table;
2087 while (tp) {
2088 if (tp->preference == max && !tp->in_kernel) {
2089 add_route(tp->router);
2090 tp->in_kernel++;
2092 tp = tp->next;
2098 static void
2099 record_router(struct in_addr router, long preference, int ttl)
2101 struct table *tp;
2102 int old_max = max_preference();
2103 int changed_up = 0; /* max preference could have increased */
2104 int changed_down = 0; /* max preference could have decreased */
2106 if (debug)
2107 logdebug("Recording %s, preference 0x%x\n",
2108 pr_name(router),
2109 preference);
2110 tp = find_router(router);
2111 if (tp) {
2112 if (tp->preference > preference &&
2113 tp->preference == old_max)
2114 changed_down++;
2115 else if (preference > tp->preference)
2116 changed_up++;
2117 tp->preference = preference;
2118 tp->remaining_time = ttl;
2119 } else {
2120 if (preference > old_max)
2121 changed_up++;
2122 tp = (struct table *)ALIGN(malloc(sizeof (struct table)));
2123 if (tp == NULL) {
2124 logerr("Out of memory\n");
2125 return;
2127 tp->router = router;
2128 tp->preference = preference;
2129 tp->remaining_time = ttl;
2130 tp->in_kernel = 0;
2131 tp->next = table;
2132 table = tp;
2134 if (!tp->in_kernel &&
2135 (!best_preference || tp->preference == max_preference()) &&
2136 tp->preference != IGNORE_PREFERENCE) {
2137 add_route(tp->router);
2138 tp->in_kernel++;
2140 if (tp->preference == IGNORE_PREFERENCE && tp->in_kernel) {
2141 del_route(tp->router);
2142 tp->in_kernel = 0;
2144 if (best_preference && changed_down) {
2145 /* Check if we should add routes */
2146 int new_max = max_preference();
2147 if (new_max != IGNORE_PREFERENCE) {
2148 tp = table;
2149 while (tp) {
2150 if (tp->preference == new_max &&
2151 !tp->in_kernel) {
2152 add_route(tp->router);
2153 tp->in_kernel++;
2155 tp = tp->next;
2159 if (best_preference && (changed_up || changed_down)) {
2160 /* Check if we should remove routes already in the kernel */
2161 int new_max = max_preference();
2162 tp = table;
2163 while (tp) {
2164 if (tp->preference < new_max && tp->in_kernel) {
2165 del_route(tp->router);
2166 tp->in_kernel = 0;
2168 tp = tp->next;
2174 #include <net/route.h>
2176 static void
2177 add_route(struct in_addr addr)
2179 if (debug)
2180 logdebug("Add default route to %s\n", pr_name(addr));
2181 rtioctl(addr, SIOCADDRT);
2184 static void
2185 del_route(struct in_addr addr)
2187 if (debug)
2188 logdebug("Delete default route to %s\n", pr_name(addr));
2189 rtioctl(addr, SIOCDELRT);
2192 static void
2193 rtioctl(struct in_addr addr, int op)
2195 int sock;
2196 struct rtentry rt;
2197 struct sockaddr_in *sin;
2198 bzero((char *)&rt, sizeof (struct rtentry));
2199 rt.rt_dst.sa_family = AF_INET;
2200 rt.rt_gateway.sa_family = AF_INET;
2201 sin = (struct sockaddr_in *)ALIGN(&rt.rt_gateway);
2202 sin->sin_addr = addr;
2203 rt.rt_flags = RTF_UP | RTF_GATEWAY;
2205 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
2206 if (sock < 0) {
2207 logperror("rtioctl: socket");
2208 return;
2210 if (ioctl(sock, op, (char *)&rt) < 0) {
2211 if (!(op == SIOCADDRT && errno == EEXIST))
2212 logperror("ioctl (add/delete route)");
2214 (void) close(sock);
2220 * LOGGER
2223 #include <syslog.h>
2225 static int logging = 0;
2227 static void
2228 initlog(void)
2230 logging++;
2231 openlog("in.rdisc", LOG_PID | LOG_CONS, LOG_DAEMON);
2234 /* VARARGS1 */
2235 void
2236 logerr(fmt, a, b, c, d, e, f, g, h)
2237 char *fmt;
2239 if (logging)
2240 syslog(LOG_ERR, fmt, a, b, c, d, e, f, g, h);
2241 else
2242 (void) fprintf(stderr, fmt, a, b, c, d, e, f, g, h);
2245 /* VARARGS1 */
2246 void
2247 logtrace(fmt, a, b, c, d, e, f, g, h)
2248 char *fmt;
2250 if (logging)
2251 syslog(LOG_INFO, fmt, a, b, c, d, e, f, g, h);
2252 else
2253 (void) fprintf(stdout, fmt, a, b, c, d, e, f, g, h);
2256 /* VARARGS1 */
2257 void
2258 logdebug(fmt, a, b, c, d, e, f, g, h)
2259 char *fmt;
2261 if (logging)
2262 syslog(LOG_DEBUG, fmt, a, b, c, d, e, f, g, h);
2263 else
2264 (void) fprintf(stdout, fmt, a, b, c, d, e, f, g, h);
2267 void
2268 logperror(str)
2269 char *str;
2271 if (logging)
2272 syslog(LOG_ERR, "%s: %s\n", str, strerror(errno));
2273 else
2274 (void) fprintf(stderr, "%s: %s\n", str, strerror(errno));