]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/routed/rdisc.c
This commit was generated by cvs2svn to compensate for changes in r31087,
[FreeBSD/FreeBSD.git] / sbin / routed / rdisc.c
1 /*
2  * Copyright (c) 1995
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #if !defined(lint) && !defined(sgi) && !defined(__NetBSD__)
35 static char sccsid[] = "@(#)rdisc.c     8.1 (Berkeley) x/y/95";
36 #elif defined(__NetBSD__)
37 static char rcsid[] = "$NetBSD$";
38 #endif
39 #ident "$Revision: 1.20 $"
40
41 #include "defs.h"
42 #include <netinet/in_systm.h>
43 #include <netinet/ip.h>
44 #include <netinet/ip_icmp.h>
45
46 /* router advertisement ICMP packet */
47 struct icmp_ad {
48         u_int8_t    icmp_type;          /* type of message */
49         u_int8_t    icmp_code;          /* type sub code */
50         u_int16_t   icmp_cksum;         /* ones complement cksum of struct */
51         u_int8_t    icmp_ad_num;        /* # of following router addresses */
52         u_int8_t    icmp_ad_asize;      /* 2--words in each advertisement */
53         u_int16_t   icmp_ad_life;       /* seconds of validity */
54         struct icmp_ad_info {
55             n_long  icmp_ad_addr;
56             n_long  icmp_ad_pref;
57         } icmp_ad_info[1];
58 };
59
60 /* router solicitation ICMP packet */
61 struct icmp_so {
62         u_int8_t    icmp_type;          /* type of message */
63         u_int8_t    icmp_code;          /* type sub code */
64         u_int16_t   icmp_cksum;         /* ones complement cksum of struct */
65         n_long      icmp_so_rsvd;
66 };
67
68 union ad_u {
69         struct icmp icmp;
70         struct icmp_ad ad;
71         struct icmp_so so;
72 };
73
74
75 int     rdisc_sock = -1;                /* router-discovery raw socket */
76 struct interface *rdisc_sock_mcast;     /* current multicast interface */
77
78 struct timeval rdisc_timer;
79 int rdisc_ok;                           /* using solicited route */
80
81
82 #define MAX_ADS 5
83 struct dr {                             /* accumulated advertisements */
84     struct interface *dr_ifp;
85     naddr   dr_gate;                    /* gateway */
86     time_t  dr_ts;                      /* when received */
87     time_t  dr_life;                    /* lifetime */
88     n_long  dr_recv_pref;               /* received but biased preference */
89     n_long  dr_pref;                    /* preference adjusted by metric */
90 } *cur_drp, drs[MAX_ADS];
91
92 /* adjust preference by interface metric without driving it to infinity */
93 #define PREF(p, ifp) ((p) <= (ifp)->int_metric ? ((p) != 0 ? 1 : 0) \
94                       : (p) - ((ifp)->int_metric))
95
96 static void rdisc_sort(void);
97
98
99 /* dump an ICMP Router Discovery Advertisement Message
100  */
101 static void
102 trace_rdisc(char        *act,
103             naddr       from,
104             naddr       to,
105             struct interface *ifp,
106             union ad_u  *p,
107             u_int       len)
108 {
109         int i;
110         n_long *wp, *lim;
111
112
113         if (!TRACEPACKETS || ftrace == 0)
114                 return;
115
116         lastlog();
117
118         if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
119                 (void)fprintf(ftrace, "%s Router Ad"
120                               " from %s to %s via %s life=%d\n",
121                               act, naddr_ntoa(from), naddr_ntoa(to),
122                               ifp ? ifp->int_name : "?",
123                               ntohs(p->ad.icmp_ad_life));
124                 if (!TRACECONTENTS)
125                         return;
126
127                 wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
128                 lim = &wp[(len - sizeof(p->ad)) / sizeof(*wp)];
129                 for (i = 0; i < p->ad.icmp_ad_num && wp <= lim; i++) {
130                         (void)fprintf(ftrace, "\t%s preference=%d",
131                                       naddr_ntoa(wp[0]), (int)ntohl(wp[1]));
132                         wp += p->ad.icmp_ad_asize;
133                 }
134                 (void)fputc('\n',ftrace);
135
136         } else {
137                 trace_act("%s Router Solic. from %s to %s via %s value=%#x",
138                           act, naddr_ntoa(from), naddr_ntoa(to),
139                           ifp ? ifp->int_name : "?",
140                           ntohl(p->so.icmp_so_rsvd));
141         }
142 }
143
144 /* prepare Router Discovery socket.
145  */
146 static void
147 get_rdisc_sock(void)
148 {
149         if (rdisc_sock < 0) {
150                 rdisc_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
151                 if (rdisc_sock < 0)
152                         BADERR(1,"rdisc_sock = socket()");
153                 fix_sock(rdisc_sock,"rdisc_sock");
154                 fix_select();
155         }
156 }
157
158
159 /* Pick multicast group for router-discovery socket
160  */
161 void
162 set_rdisc_mg(struct interface *ifp,
163              int on)                    /* 0=turn it off */
164 {
165         struct ip_mreq m;
166
167         if (rdisc_sock < 0) {
168                 /* Create the raw socket so that we can hear at least
169                  * broadcast router discovery packets.
170                  */
171                 if ((ifp->int_state & IS_NO_RDISC) == IS_NO_RDISC
172                     || !on)
173                         return;
174                 get_rdisc_sock();
175         }
176
177         if (!(ifp->int_if_flags & IFF_MULTICAST)) {
178                 ifp->int_state &= ~(IS_ALL_HOSTS | IS_ALL_ROUTERS);
179                 return;
180         }
181
182 #ifdef MCAST_PPP_BUG
183         if (ifp->int_if_flags & IFF_POINTOPOINT)
184                 return;
185 #endif
186         bzero(&m, sizeof(m));
187         m.imr_interface.s_addr = ((ifp->int_if_flags & IFF_POINTOPOINT)
188                                   ? ifp->int_dstaddr
189                                   : ifp->int_addr);
190         if (supplier
191             || (ifp->int_state & IS_NO_ADV_IN)
192             || !on) {
193                 /* stop listening to advertisements
194                  */
195                 if (ifp->int_state & IS_ALL_HOSTS) {
196                         m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
197                         if (setsockopt(rdisc_sock, IPPROTO_IP,
198                                        IP_DROP_MEMBERSHIP,
199                                        &m, sizeof(m)) < 0)
200                                 LOGERR("IP_DROP_MEMBERSHIP ALLHOSTS");
201                         ifp->int_state &= ~IS_ALL_HOSTS;
202                 }
203
204         } else if (!(ifp->int_state & IS_ALL_HOSTS)) {
205                 /* start listening to advertisements
206                  */
207                 m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
208                 if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
209                                &m, sizeof(m)) < 0) {
210                         LOGERR("IP_ADD_MEMBERSHIP ALLHOSTS");
211                 } else {
212                         ifp->int_state |= IS_ALL_HOSTS;
213                 }
214         }
215
216         if (!supplier
217             || (ifp->int_state & IS_NO_ADV_OUT)
218             || !on) {
219                 /* stop listening to solicitations
220                  */
221                 if (ifp->int_state & IS_ALL_ROUTERS) {
222                         m.imr_multiaddr.s_addr=htonl(INADDR_ALLROUTERS_GROUP);
223                         if (setsockopt(rdisc_sock, IPPROTO_IP,
224                                        IP_DROP_MEMBERSHIP,
225                                        &m, sizeof(m)) < 0)
226                                 LOGERR("IP_DROP_MEMBERSHIP ALLROUTERS");
227                         ifp->int_state &= ~IS_ALL_ROUTERS;
228                 }
229
230         } else if (!(ifp->int_state & IS_ALL_ROUTERS)) {
231                 /* start hearing solicitations
232                  */
233                 m.imr_multiaddr.s_addr=htonl(INADDR_ALLROUTERS_GROUP);
234                 if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
235                                &m, sizeof(m)) < 0) {
236                         LOGERR("IP_ADD_MEMBERSHIP ALLROUTERS");
237                 } else {
238                         ifp->int_state |= IS_ALL_ROUTERS;
239                 }
240         }
241 }
242
243
244 /* start supplying routes
245  */
246 void
247 set_supplier(void)
248 {
249         struct interface *ifp;
250         struct dr *drp;
251
252         if (supplier_set)
253                 return;
254
255         trace_act("start suppying routes");
256
257         /* Forget discovered routes.
258          */
259         for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
260                 drp->dr_recv_pref = 0;
261                 drp->dr_life = 0;
262         }
263         rdisc_age(0);
264
265         supplier_set = 1;
266         supplier = 1;
267
268         /* Do not start advertising until we have heard some RIP routes */
269         LIM_SEC(rdisc_timer, now.tv_sec+MIN_WAITTIME);
270
271         /* Switch router discovery multicast groups from soliciting
272          * to advertising.
273          */
274         for (ifp = ifnet; ifp; ifp = ifp->int_next) {
275                 if (ifp->int_state & IS_BROKE)
276                         continue;
277                 ifp->int_rdisc_cnt = 0;
278                 ifp->int_rdisc_timer.tv_usec = rdisc_timer.tv_usec;
279                 ifp->int_rdisc_timer.tv_sec = now.tv_sec+MIN_WAITTIME;
280                 set_rdisc_mg(ifp, 1);
281         }
282
283         /* get rid of any redirects */
284         del_redirects(0,0);
285 }
286
287
288 /* age discovered routes and find the best one
289  */
290 void
291 rdisc_age(naddr bad_gate)
292 {
293         time_t sec;
294         struct dr *drp;
295
296
297         /* If only adverising, then do only that. */
298         if (supplier) {
299                 /* if switching from client to server, get rid of old
300                  * default routes.
301                  */
302                 if (cur_drp != 0)
303                         rdisc_sort();
304                 rdisc_adv();
305                 return;
306         }
307
308         /* If we are being told about a bad router,
309          * then age the discovered default route, and if there is
310          * no alternative, solicite a replacement.
311          */
312         if (bad_gate != 0) {
313                 /* Look for the bad discovered default route.
314                  * Age it and note its interface.
315                  */
316                 for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
317                         if (drp->dr_ts == 0)
318                                 continue;
319
320                         /* When we find the bad router, then age the route
321                          * to at most SUPPLY_INTERVAL.
322                          * This is contrary to RFC 1256, but defends against
323                          * black holes.
324                          */
325                         if (drp->dr_gate == bad_gate) {
326                                 sec = (now.tv_sec - drp->dr_life
327                                        + SUPPLY_INTERVAL);
328                                 if (drp->dr_ts > sec) {
329                                         trace_act("age 0.0.0.0 --> %s via %s",
330                                                   naddr_ntoa(drp->dr_gate),
331                                                   drp->dr_ifp->int_name);
332                                         drp->dr_ts = sec;
333                                 }
334                                 break;
335                         }
336                 }
337         }
338
339         /* delete old redirected routes to keep the kernel table small
340          */
341         sec = (cur_drp == 0) ? MaxMaxAdvertiseInterval : cur_drp->dr_life;
342         del_redirects(bad_gate, now.tv_sec-sec);
343
344         rdisc_sol();
345
346         rdisc_sort();
347 }
348
349
350 /* Zap all routes discovered via an interface that has gone bad
351  *      This should only be called when !(ifp->int_state & IS_ALIAS)
352  */
353 void
354 if_bad_rdisc(struct interface *ifp)
355 {
356         struct dr *drp;
357
358         for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
359                 if (drp->dr_ifp != ifp)
360                         continue;
361                 drp->dr_recv_pref = 0;
362                 drp->dr_life = 0;
363         }
364
365         rdisc_sort();
366 }
367
368
369 /* mark an interface ok for router discovering.
370  */
371 void
372 if_ok_rdisc(struct interface *ifp)
373 {
374         set_rdisc_mg(ifp, 1);
375
376         ifp->int_rdisc_cnt = 0;
377         ifp->int_rdisc_timer.tv_sec = now.tv_sec + (supplier
378                                                     ? MIN_WAITTIME
379                                                     : MAX_SOLICITATION_DELAY);
380         if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
381                 rdisc_timer = ifp->int_rdisc_timer;
382 }
383
384
385 /* get rid of a dead discovered router
386  */
387 static void
388 del_rdisc(struct dr *drp)
389 {
390         struct interface *ifp;
391         int i;
392
393
394         del_redirects(drp->dr_gate, 0);
395         drp->dr_ts = 0;
396         drp->dr_life = 0;
397
398
399         /* Count the other discovered routes on the interface.
400          */
401         i = 0;
402         ifp = drp->dr_ifp;
403         for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
404                 if (drp->dr_ts != 0
405                     && drp->dr_ifp == ifp)
406                         i++;
407         }
408
409         /* If that was the last good discovered router on the interface,
410          * then solicit a new one.
411          * This is contrary to RFC 1256, but defends against black holes.
412          */
413         if (i == 0
414             && ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) {
415                 trace_act("discovered route is bad--re-solicit routers via %s",
416                           ifp->int_name);
417                 ifp->int_rdisc_cnt = 0;
418                 ifp->int_rdisc_timer.tv_sec = 0;
419                 rdisc_sol();
420         }
421 }
422
423
424 /* Find the best discovered route,
425  * and discard stale routers.
426  */
427 static void
428 rdisc_sort(void)
429 {
430         struct dr *drp, *new_drp;
431         struct rt_entry *rt;
432         struct interface *ifp;
433         u_int new_st;
434         n_long new_pref;
435
436
437         /* Find the best discovered route.
438          */
439         new_drp = 0;
440         for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
441                 if (drp->dr_ts == 0)
442                         continue;
443                 ifp = drp->dr_ifp;
444
445                 /* Get rid of expired discovered routers.
446                  */
447                 if (drp->dr_ts + drp->dr_life <= now.tv_sec) {
448                         del_rdisc(drp);
449                         continue;
450                 }
451
452                 LIM_SEC(rdisc_timer, drp->dr_ts+drp->dr_life+1);
453
454                 /* Update preference with possibly changed interface
455                  * metric.
456                  */
457                 drp->dr_pref = PREF(drp->dr_recv_pref, ifp);
458
459                 /* Prefer the current route to prevent thrashing.
460                  * Prefer shorter lifetimes to speed the detection of
461                  * bad routers.
462                  * Avoid sick interfaces.
463                  */
464                 if (new_drp == 0
465                     || (!((new_st ^ drp->dr_ifp->int_state) & IS_SICK)
466                         && (new_pref < drp->dr_pref
467                             || (new_pref == drp->dr_pref
468                                 && (drp == cur_drp
469                                     || (new_drp != cur_drp
470                                         && new_drp->dr_life > drp->dr_life)))))
471                     || ((new_st & IS_SICK)
472                         && !(drp->dr_ifp->int_state & IS_SICK))) {
473                             new_drp = drp;
474                             new_st = drp->dr_ifp->int_state;
475                             new_pref = drp->dr_pref;
476                 }
477         }
478
479         /* switch to a better default route
480          */
481         if (new_drp != cur_drp) {
482                 rt = rtget(RIP_DEFAULT, 0);
483
484                 /* Stop using discovered routes if they are all bad
485                  */
486                 if (new_drp == 0) {
487                         trace_act("turn off Router Discovery client");
488                         rdisc_ok = 0;
489
490                         if (rt != 0
491                             && (rt->rt_state & RS_RDISC)) {
492                                 rtchange(rt, rt->rt_state & ~RS_RDISC,
493                                          rt->rt_gate, rt->rt_router,
494                                          HOPCNT_INFINITY, 0, rt->rt_ifp,
495                                          now.tv_sec - GARBAGE_TIME, 0);
496                                 rtswitch(rt, 0);
497                         }
498
499                         /* turn on RIP if permitted */
500                         rip_on(0);
501
502                 } else {
503                         if (cur_drp == 0) {
504                                 trace_act("turn on Router Discovery client"
505                                           " using %s via %s",
506                                           naddr_ntoa(new_drp->dr_gate),
507                                           new_drp->dr_ifp->int_name);
508
509                                 rdisc_ok = 1;
510
511                         } else {
512                                 trace_act("switch Router Discovery from"
513                                           " %s via %s to %s via %s",
514                                           naddr_ntoa(cur_drp->dr_gate),
515                                           cur_drp->dr_ifp->int_name,
516                                           naddr_ntoa(new_drp->dr_gate),
517                                           new_drp->dr_ifp->int_name);
518                         }
519
520                         if (rt != 0) {
521                                 rtchange(rt, rt->rt_state | RS_RDISC,
522                                          new_drp->dr_gate, new_drp->dr_gate,
523                                          0,0, new_drp->dr_ifp,
524                                          now.tv_sec, 0);
525                         } else {
526                                 rtadd(RIP_DEFAULT, 0,
527                                       new_drp->dr_gate, new_drp->dr_gate,
528                                       HOPCNT_INFINITY-1, 0,
529                                       RS_RDISC, new_drp->dr_ifp);
530                         }
531
532                         /* Now turn off RIP and delete RIP routes,
533                          * which might otherwise include the default
534                          * we just modified.
535                          */
536                         rip_off();
537                 }
538
539                 cur_drp = new_drp;
540         }
541 }
542
543
544 /* handle a single address in an advertisement
545  */
546 static void
547 parse_ad(naddr from,
548          naddr gate,
549          n_long pref,
550          u_short life,
551          struct interface *ifp)
552 {
553         static struct msg_limit bad_gate;
554         struct dr *drp, *new_drp;
555
556
557         if (gate == RIP_DEFAULT
558             || !check_dst(gate)) {
559                 msglim(&bad_gate, from,"router %s advertising bad gateway %s",
560                        naddr_ntoa(from),
561                        naddr_ntoa(gate));
562                 return;
563         }
564
565         /* ignore pointers to ourself and routes via unreachable networks
566          */
567         if (ifwithaddr(gate, 1, 0) != 0) {
568                 trace_pkt("    discard Router Discovery Ad pointing at us");
569                 return;
570         }
571         if (!on_net(gate, ifp->int_net, ifp->int_mask)) {
572                 trace_pkt("    discard Router Discovery Ad"
573                           " toward unreachable net");
574                 return;
575         }
576
577         /* Convert preference to an unsigned value
578          * and later bias it by the metric of the interface.
579          */
580         pref = ntohl(pref) ^ MIN_PreferenceLevel;
581
582         if (pref == 0 || life == 0) {
583                 pref = 0;
584                 life = 0;
585         }
586
587         for (new_drp = 0, drp = drs; drp < &drs[MAX_ADS]; drp++) {
588                 /* accept new info for a familiar entry
589                  */
590                 if (drp->dr_gate == gate) {
591                         new_drp = drp;
592                         break;
593                 }
594
595                 if (life == 0)
596                         continue;       /* do not worry about dead ads */
597
598                 if (drp->dr_ts == 0) {
599                         new_drp = drp;  /* use unused entry */
600
601                 } else if (new_drp == 0) {
602                         /* look for an entry worse than the new one to
603                          * reuse.
604                          */
605                         if ((!(ifp->int_state & IS_SICK)
606                              && (drp->dr_ifp->int_state & IS_SICK))
607                             || (pref > drp->dr_pref
608                                 && !((ifp->int_state ^ drp->dr_ifp->int_state)
609                                      & IS_SICK)))
610                                 new_drp = drp;
611
612                 } else if (new_drp->dr_ts != 0) {
613                         /* look for the least valueable entry to reuse
614                          */
615                         if ((!(new_drp->dr_ifp->int_state & IS_SICK)
616                              && (drp->dr_ifp->int_state & IS_SICK))
617                             || (new_drp->dr_pref > drp->dr_pref
618                                 && !((new_drp->dr_ifp->int_state
619                                       ^ drp->dr_ifp->int_state)
620                                      & IS_SICK)))
621                                 new_drp = drp;
622                 }
623         }
624
625         /* forget it if all of the current entries are better */
626         if (new_drp == 0)
627                 return;
628
629         new_drp->dr_ifp = ifp;
630         new_drp->dr_gate = gate;
631         new_drp->dr_ts = now.tv_sec;
632         new_drp->dr_life = ntohs(life);
633         new_drp->dr_recv_pref = pref;
634         /* bias functional preference by metric of the interface */
635         new_drp->dr_pref = PREF(pref,ifp);
636
637         /* after hearing a good advertisement, stop asking
638          */
639         if (!(ifp->int_state & IS_SICK))
640                 ifp->int_rdisc_cnt = MAX_SOLICITATIONS;
641 }
642
643
644 /* Compute the IP checksum
645  *      This assumes the packet is less than 32K long.
646  */
647 static u_short
648 in_cksum(u_short *p,
649          u_int len)
650 {
651         u_int sum = 0;
652         int nwords = len >> 1;
653
654         while (nwords-- != 0)
655                 sum += *p++;
656
657         if (len & 1)
658                 sum += *(u_char *)p;
659
660         /* end-around-carry */
661         sum = (sum >> 16) + (sum & 0xffff);
662         sum += (sum >> 16);
663         return (~sum);
664 }
665
666
667 /* Send a router discovery advertisement or solicitation ICMP packet.
668  */
669 static void
670 send_rdisc(union ad_u *p,
671            int p_size,
672            struct interface *ifp,
673            naddr dst,                   /* 0 or unicast destination */
674            int  type)                   /* 0=unicast, 1=bcast, 2=mcast */
675 {
676         struct sockaddr_in sin;
677         int flags;
678         char *msg;
679         naddr tgt_mcast;
680
681
682         bzero(&sin, sizeof(sin));
683         sin.sin_addr.s_addr = dst;
684         sin.sin_family = AF_INET;
685 #ifdef _HAVE_SIN_LEN
686         sin.sin_len = sizeof(sin);
687 #endif
688         flags = MSG_DONTROUTE;
689
690         switch (type) {
691         case 0:                         /* unicast */
692         default:
693                 msg = "Send";
694                 break;
695
696         case 1:                         /* broadcast */
697                 if (ifp->int_if_flags & IFF_POINTOPOINT) {
698                         msg = "Send pt-to-pt";
699                         sin.sin_addr.s_addr = ifp->int_dstaddr;
700                 } else {
701                         msg = "Send broadcast";
702                         sin.sin_addr.s_addr = ifp->int_brdaddr;
703                 }
704                 break;
705
706         case 2:                         /* multicast */
707                 msg = "Send multicast";
708                 if (ifp->int_state & IS_DUP) {
709                         trace_act("abort multicast output via %s"
710                                   " with duplicate address",
711                                   ifp->int_name);
712                         return;
713                 }
714                 if (rdisc_sock_mcast != ifp) {
715                         /* select the right interface. */
716 #ifdef MCAST_PPP_BUG
717                         /* Do not specifiy the primary interface explicitly
718                          * if we have the multicast point-to-point kernel
719                          * bug, since the kernel will do the wrong thing
720                          * if the local address of a point-to-point link
721                          * is the same as the address of an ordinary
722                          * interface.
723                          */
724                         if (ifp->int_addr == myaddr) {
725                                 tgt_mcast = 0;
726                         } else
727 #endif
728                         tgt_mcast = ifp->int_addr;
729                         if (0 > setsockopt(rdisc_sock,
730                                            IPPROTO_IP, IP_MULTICAST_IF,
731                                            &tgt_mcast, sizeof(tgt_mcast))) {
732                                 LOGERR("setsockopt(rdisc_sock,"
733                                        "IP_MULTICAST_IF)");
734                                 rdisc_sock_mcast = 0;
735                                 return;
736                         }
737                         rdisc_sock_mcast = ifp;
738                 }
739                 flags = 0;
740                 break;
741         }
742
743         if (rdisc_sock < 0)
744                 get_rdisc_sock();
745
746         trace_rdisc(msg, ifp->int_addr, sin.sin_addr.s_addr, ifp,
747                     p, p_size);
748
749         if (0 > sendto(rdisc_sock, p, p_size, flags,
750                        (struct sockaddr *)&sin, sizeof(sin))) {
751                 if (ifp == 0 || !(ifp->int_state & IS_BROKE))
752                         msglog("sendto(%s%s%s): %s",
753                                ifp != 0 ? ifp->int_name : "",
754                                ifp != 0 ? ", " : "",
755                                inet_ntoa(sin.sin_addr),
756                                strerror(errno));
757                 if (ifp != 0)
758                         if_sick(ifp);
759         }
760 }
761
762
763 /* Send an advertisement
764  */
765 static void
766 send_adv(struct interface *ifp,
767          naddr  dst,                    /* 0 or unicast destination */
768          int    type)                   /* 0=unicast, 1=bcast, 2=mcast */
769 {
770         union ad_u u;
771         n_long pref;
772
773
774         bzero(&u,sizeof(u.ad));
775
776         u.ad.icmp_type = ICMP_ROUTERADVERT;
777         u.ad.icmp_ad_num = 1;
778         u.ad.icmp_ad_asize = sizeof(u.ad.icmp_ad_info[0])/4;
779
780         u.ad.icmp_ad_life = stopint ? 0 : htons(ifp->int_rdisc_int*3);
781         pref = ifp->int_rdisc_pref ^ MIN_PreferenceLevel;
782         pref = PREF(pref, ifp) ^ MIN_PreferenceLevel;
783         u.ad.icmp_ad_info[0].icmp_ad_pref = htonl(pref);
784
785         u.ad.icmp_ad_info[0].icmp_ad_addr = ifp->int_addr;
786
787         u.ad.icmp_cksum = in_cksum((u_short*)&u.ad, sizeof(u.ad));
788
789         send_rdisc(&u, sizeof(u.ad), ifp, dst, type);
790 }
791
792
793 /* Advertise for Router Discovery
794  */
795 void
796 rdisc_adv(void)
797 {
798         struct interface *ifp;
799
800         if (!supplier)
801                 return;
802
803         rdisc_timer.tv_sec = now.tv_sec + NEVER;
804
805         for (ifp = ifnet; ifp; ifp = ifp->int_next) {
806                 if (0 != (ifp->int_state & (IS_NO_ADV_OUT | IS_BROKE)))
807                         continue;
808
809                 if (!timercmp(&ifp->int_rdisc_timer, &now, >)
810                     || stopint) {
811                         send_adv(ifp, htonl(INADDR_ALLHOSTS_GROUP),
812                                  (ifp->int_state&IS_BCAST_RDISC) ? 1 : 2);
813                         ifp->int_rdisc_cnt++;
814
815                         intvl_random(&ifp->int_rdisc_timer,
816                                      (ifp->int_rdisc_int*3)/4,
817                                      ifp->int_rdisc_int);
818                         if (ifp->int_rdisc_cnt < MAX_INITIAL_ADVERTS
819                             && (ifp->int_rdisc_timer.tv_sec
820                                 > MAX_INITIAL_ADVERT_INTERVAL)) {
821                                 ifp->int_rdisc_timer.tv_sec
822                                 = MAX_INITIAL_ADVERT_INTERVAL;
823                         }
824                         timevaladd(&ifp->int_rdisc_timer, &now);
825                 }
826
827                 if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
828                         rdisc_timer = ifp->int_rdisc_timer;
829         }
830 }
831
832
833 /* Solicit for Router Discovery
834  */
835 void
836 rdisc_sol(void)
837 {
838         struct interface *ifp;
839         union ad_u u;
840
841
842         if (supplier)
843                 return;
844
845         rdisc_timer.tv_sec = now.tv_sec + NEVER;
846
847         for (ifp = ifnet; ifp; ifp = ifp->int_next) {
848                 if (0 != (ifp->int_state & (IS_NO_SOL_OUT | IS_BROKE))
849                     || ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
850                         continue;
851
852                 if (!timercmp(&ifp->int_rdisc_timer, &now, >)) {
853                         bzero(&u,sizeof(u.so));
854                         u.so.icmp_type = ICMP_ROUTERSOLICIT;
855                         u.so.icmp_cksum = in_cksum((u_short*)&u.so,
856                                                    sizeof(u.so));
857                         send_rdisc(&u, sizeof(u.so), ifp,
858                                    htonl(INADDR_ALLROUTERS_GROUP),
859                                    ((ifp->int_state&IS_BCAST_RDISC) ? 1 : 2));
860
861                         if (++ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
862                                 continue;
863
864                         ifp->int_rdisc_timer.tv_sec = SOLICITATION_INTERVAL;
865                         ifp->int_rdisc_timer.tv_usec = 0;
866                         timevaladd(&ifp->int_rdisc_timer, &now);
867                 }
868
869                 if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
870                         rdisc_timer = ifp->int_rdisc_timer;
871         }
872 }
873
874
875 /* check the IP header of a possible Router Discovery ICMP packet */
876 static struct interface *               /* 0 if bad */
877 ck_icmp(char    *act,
878         naddr   from,
879         struct interface *ifp,
880         naddr   to,
881         union ad_u *p,
882         u_int   len)
883 {
884         char *type;
885
886
887         if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
888                 type = "advertisement";
889         } else if (p->icmp.icmp_type == ICMP_ROUTERSOLICIT) {
890                 type = "solicitation";
891         } else {
892                 return 0;
893         }
894
895         if (p->icmp.icmp_code != 0) {
896                 trace_pkt("unrecognized ICMP Router %s code=%d from %s to %s",
897                           type, p->icmp.icmp_code,
898                           naddr_ntoa(from), naddr_ntoa(to));
899                 return 0;
900         }
901
902         trace_rdisc(act, from, to, ifp, p, len);
903
904         if (ifp == 0)
905                 trace_pkt("unknown interface for router-discovery %s"
906                           " from %s to %s",
907                           type, naddr_ntoa(from), naddr_ntoa(to));
908
909         return ifp;
910 }
911
912
913 /* read packets from the router discovery socket
914  */
915 void
916 read_d(void)
917 {
918         static struct msg_limit bad_asize, bad_len;
919 #ifdef USE_PASSIFNAME
920         static struct msg_limit  bad_name;
921 #endif
922         struct sockaddr_in from;
923         int n, fromlen, cc, hlen;
924         struct {
925 #ifdef USE_PASSIFNAME
926                 char    ifname[IFNAMSIZ];
927 #endif
928                 union {
929                         struct ip ip;
930                         u_short s[512/2];
931                         u_char  b[512];
932                 } pkt;
933         } buf;
934         union ad_u *p;
935         n_long *wp;
936         struct interface *ifp;
937
938
939         for (;;) {
940                 fromlen = sizeof(from);
941                 cc = recvfrom(rdisc_sock, &buf, sizeof(buf), 0,
942                               (struct sockaddr*)&from,
943                               &fromlen);
944                 if (cc <= 0) {
945                         if (cc < 0 && errno != EWOULDBLOCK)
946                                 LOGERR("recvfrom(rdisc_sock)");
947                         break;
948                 }
949                 if (fromlen != sizeof(struct sockaddr_in))
950                         logbad(1,"impossible recvfrom(rdisc_sock) fromlen=%d",
951                                fromlen);
952 #ifdef USE_PASSIFNAME
953                 if ((cc -= sizeof(buf.ifname)) < 0)
954                         logbad(0,"missing USE_PASSIFNAME; only %d bytes",
955                                cc+sizeof(buf.ifname));
956 #endif
957
958                 hlen = buf.pkt.ip.ip_hl << 2;
959                 if (cc < hlen + ICMP_MINLEN)
960                         continue;
961                 p = (union ad_u *)&buf.pkt.b[hlen];
962                 cc -= hlen;
963
964 #ifdef USE_PASSIFNAME
965                 ifp = ifwithname(buf.ifname, 0);
966                 if (ifp == 0)
967                         msglim(&bad_name, from.sin_addr.s_addr,
968                                "impossible rdisc if_ name %.*s",
969                                IFNAMSIZ, buf.ifname);
970 #else
971                 /* If we could tell the interface on which a packet from
972                  * address 0 arrived, we could deal with such solicitations.
973                  */
974                 ifp = ((from.sin_addr.s_addr == 0)
975                        ? 0 : iflookup(from.sin_addr.s_addr));
976 #endif
977                 ifp = ck_icmp("Recv", from.sin_addr.s_addr, ifp,
978                               buf.pkt.ip.ip_dst.s_addr, p, cc);
979                 if (ifp == 0)
980                         continue;
981                 if (ifwithaddr(from.sin_addr.s_addr, 0, 0)) {
982                         trace_pkt("    "
983                                   "discard our own Router Discovery message");
984                         continue;
985                 }
986
987                 switch (p->icmp.icmp_type) {
988                 case ICMP_ROUTERADVERT:
989                         if (p->ad.icmp_ad_asize*4
990                             < sizeof(p->ad.icmp_ad_info[0])) {
991                                 msglim(&bad_asize, from.sin_addr.s_addr,
992                                        "intolerable rdisc address size=%d",
993                                        p->ad.icmp_ad_asize);
994                                 continue;
995                         }
996                         if (p->ad.icmp_ad_num == 0) {
997                                 trace_pkt("    empty?");
998                                 continue;
999                         }
1000                         if (cc != (sizeof(p->ad) - sizeof(p->ad.icmp_ad_info)
1001                                    + (p->ad.icmp_ad_num
1002                                       * sizeof(p->ad.icmp_ad_info[0])))) {
1003                                 msglim(&bad_len, from.sin_addr.s_addr,
1004                                        "rdisc length %d does not match ad_num"
1005                                        " %d", cc, p->ad.icmp_ad_num);
1006                                 continue;
1007                         }
1008                         if (supplier)
1009                                 continue;
1010                         if (ifp->int_state & IS_NO_ADV_IN)
1011                                 continue;
1012
1013                         wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
1014                         for (n = 0; n < p->ad.icmp_ad_num; n++) {
1015                                 parse_ad(from.sin_addr.s_addr,
1016                                          wp[0], wp[1],
1017                                          ntohs(p->ad.icmp_ad_life),
1018                                          ifp);
1019                                 wp += p->ad.icmp_ad_asize;
1020                         }
1021                         break;
1022
1023
1024                 case ICMP_ROUTERSOLICIT:
1025                         if (!supplier)
1026                                 continue;
1027                         if (ifp->int_state & IS_NO_ADV_OUT)
1028                                 continue;
1029
1030                         /* XXX
1031                          * We should handle messages from address 0.
1032                          */
1033
1034                         /* Respond with a point-to-point advertisement */
1035                         send_adv(ifp, from.sin_addr.s_addr, 0);
1036                         break;
1037                 }
1038         }
1039
1040         rdisc_sort();
1041 }