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