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