rdisc.c revision 190716
1106266Sjulian/* 2106266Sjulian * Copyright (c) 1995 3139823Simp * The Regents of the University of California. All rights reserved. 4139823Simp * 5139823Simp * Redistribution and use in source and binary forms, with or without 6144674Sglebius * modification, are permitted provided that the following conditions 7106266Sjulian * are met: 8106266Sjulian * 1. Redistributions of source code must retain the above copyright 9106266Sjulian * notice, this list of conditions and the following disclaimer. 10106266Sjulian * 2. Redistributions in binary form must reproduce the above copyright 11106266Sjulian * notice, this list of conditions and the following disclaimer in the 12106319Sjulian * documentation and/or other materials provided with the distribution. 13106266Sjulian * 4. Neither the name of the University nor the names of its contributors 14106319Sjulian * may be used to endorse or promote products derived from this software 15106319Sjulian * without specific prior written permission. 16106266Sjulian * 17106319Sjulian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18106319Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19106266Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20106266Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21106266Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22106319Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23106319Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24106319Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25106266Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26106266Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27106266Sjulian * SUCH DAMAGE. 28106266Sjulian * 29106266Sjulian * $FreeBSD: head/sbin/routed/rdisc.c 190716 2009-04-05 16:01:56Z phk $ 30106319Sjulian */ 31106266Sjulian 32106266Sjulian#include "defs.h" 33106266Sjulian#include <netinet/in_systm.h> 34106266Sjulian#include <netinet/ip.h> 35106266Sjulian#include <netinet/ip_icmp.h> 36106266Sjulian 37106266Sjulian#ifdef __NetBSD__ 38106266Sjulian__RCSID("$NetBSD$"); 39106266Sjulian#elif defined(__FreeBSD__) 40106266Sjulian__RCSID("$FreeBSD: head/sbin/routed/rdisc.c 190716 2009-04-05 16:01:56Z phk $"); 41125077Sharti#else 42125077Sharti__RCSID("$Revision: 2.27 $"); 43125077Sharti#ident "$Revision: 2.27 $" 44106266Sjulian#endif 45106266Sjulian 46143387Sbmilekic/* router advertisement ICMP packet */ 47143387Sbmilekicstruct icmp_ad { 48143387Sbmilekic u_int8_t icmp_type; /* type of message */ 49143387Sbmilekic u_int8_t icmp_code; /* type sub code */ 50106266Sjulian u_int16_t icmp_cksum; /* ones complement cksum of struct */ 51106266Sjulian u_int8_t icmp_ad_num; /* # of following router addresses */ 52106266Sjulian u_int8_t icmp_ad_asize; /* 2--words in each advertisement */ 53106266Sjulian u_int16_t icmp_ad_life; /* seconds of validity */ 54106266Sjulian struct icmp_ad_info { 55106266Sjulian n_long icmp_ad_addr; 56143387Sbmilekic n_long icmp_ad_pref; 57106266Sjulian } icmp_ad_info[1]; 58106266Sjulian}; 59106266Sjulian 60106266Sjulian/* router solicitation ICMP packet */ 61106266Sjulianstruct icmp_so { 62106266Sjulian u_int8_t icmp_type; /* type of message */ 63106266Sjulian u_int8_t icmp_code; /* type sub code */ 64106266Sjulian u_int16_t icmp_cksum; /* ones complement cksum of struct */ 65106266Sjulian n_long icmp_so_rsvd; 66106266Sjulian}; 67106266Sjulian 68144674Sglebiusunion ad_u { 69106266Sjulian struct icmp icmp; 70106266Sjulian struct icmp_ad ad; 71106266Sjulian struct icmp_so so; 72106266Sjulian}; 73106266Sjulian 74106266Sjulian 75106266Sjulianint rdisc_sock = -1; /* router-discovery raw socket */ 76106266Sjulianstatic const struct interface *rdisc_sock_mcast; /* current multicast interface */ 77106266Sjulian 78106266Sjulianstruct timeval rdisc_timer; 79106266Sjulianint rdisc_ok; /* using solicited route */ 80106266Sjulian 81106266Sjulian 82106266Sjulian#define MAX_ADS 16 /* at least one per interface */ 83144674Sglebiusstruct dr { /* accumulated advertisements */ 84144674Sglebius struct interface *dr_ifp; 85106266Sjulian naddr dr_gate; /* gateway */ 86106319Sjulian time_t dr_ts; /* when received */ 87106266Sjulian time_t dr_life; /* lifetime in host byte order */ 88137138Sglebius n_long dr_recv_pref; /* received but biased preference */ 89144674Sglebius n_long dr_pref; /* preference adjusted by metric */ 90144674Sglebius}; 91106266Sjulianstatic const struct dr *cur_drp; 92106266Sjulianstatic struct dr drs[MAX_ADS]; 93106266Sjulian 94106266Sjulian/* convert between signed, balanced around zero, 95106266Sjulian * and unsigned zero-based preferences */ 96106266Sjulian#define SIGN_PREF(p) ((p) ^ MIN_PreferenceLevel) 97106266Sjulian#define UNSIGN_PREF(p) SIGN_PREF(p) 98106266Sjulian/* adjust unsigned preference by interface metric, 99106266Sjulian * without driving it to infinity */ 100106266Sjulian#define PREF(p, ifp) ((int)(p) <= ((ifp)->int_metric+(ifp)->int_adj_outmetric)\ 101106266Sjulian ? ((p) != 0 ? 1 : 0) \ 102144674Sglebius : (p) - ((ifp)->int_metric+(ifp)->int_adj_outmetric)) 103106266Sjulian 104106266Sjulianstatic void rdisc_sort(void); 105106266Sjulian 106106266Sjulian 107125243Sharti/* dump an ICMP Router Discovery Advertisement Message 108106266Sjulian */ 109144674Sglebiusstatic void 110106266Sjuliantrace_rdisc(const char *act, 111106266Sjulian naddr from, 112144674Sglebius naddr to, 113106266Sjulian struct interface *ifp, 114106266Sjulian union ad_u *p, 115125077Sharti u_int len) 116106266Sjulian{ 117106266Sjulian int i; 118106266Sjulian n_long *wp, *lim; 119106266Sjulian 120106266Sjulian 121106266Sjulian if (!TRACEPACKETS || ftrace == 0) 122106266Sjulian return; 123106266Sjulian 124106266Sjulian lastlog(); 125106266Sjulian 126106266Sjulian if (p->icmp.icmp_type == ICMP_ROUTERADVERT) { 127106266Sjulian (void)fprintf(ftrace, "%s Router Ad" 128106266Sjulian " from %s to %s via %s life=%d\n", 129106266Sjulian act, naddr_ntoa(from), naddr_ntoa(to), 130106266Sjulian ifp ? ifp->int_name : "?", 131106266Sjulian ntohs(p->ad.icmp_ad_life)); 132106266Sjulian if (!TRACECONTENTS) 133106266Sjulian return; 134106266Sjulian 135106266Sjulian wp = &p->ad.icmp_ad_info[0].icmp_ad_addr; 136106266Sjulian lim = &wp[(len - sizeof(p->ad)) / sizeof(*wp)]; 137106266Sjulian for (i = 0; i < p->ad.icmp_ad_num && wp <= lim; i++) { 138106266Sjulian (void)fprintf(ftrace, "\t%s preference=%d", 139106266Sjulian naddr_ntoa(wp[0]), (int)ntohl(wp[1])); 140106266Sjulian wp += p->ad.icmp_ad_asize; 141106266Sjulian } 142106266Sjulian (void)fputc('\n',ftrace); 143106266Sjulian 144106266Sjulian } else { 145106266Sjulian trace_act("%s Router Solic. from %s to %s via %s value=%#x", 146106266Sjulian act, naddr_ntoa(from), naddr_ntoa(to), 147106266Sjulian ifp ? ifp->int_name : "?", 148106266Sjulian (int)ntohl(p->so.icmp_so_rsvd)); 149106266Sjulian } 150106266Sjulian} 151106266Sjulian 152106266Sjulian/* prepare Router Discovery socket. 153106266Sjulian */ 154106266Sjulianstatic void 155106266Sjulianget_rdisc_sock(void) 156106266Sjulian{ 157106266Sjulian if (rdisc_sock < 0) { 158106266Sjulian rdisc_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 159106266Sjulian if (rdisc_sock < 0) 160106266Sjulian BADERR(1,"rdisc_sock = socket()"); 161106266Sjulian fix_sock(rdisc_sock,"rdisc_sock"); 162106266Sjulian fix_select(); 163106266Sjulian } 164106266Sjulian} 165106266Sjulian 166106266Sjulian 167106266Sjulian/* Pick multicast group for router-discovery socket 168106266Sjulian */ 169106266Sjulianvoid 170106266Sjulianset_rdisc_mg(struct interface *ifp, 171106266Sjulian int on) /* 0=turn it off */ 172106266Sjulian{ 173106266Sjulian struct group_req gr; 174106266Sjulian struct sockaddr_in *sin; 175106266Sjulian 176106266Sjulian if (rdisc_sock < 0) { 177125033Sharti /* Create the raw socket so that we can hear at least 178125033Sharti * broadcast router discovery packets. 179144674Sglebius */ 180144674Sglebius if ((ifp->int_state & IS_NO_RDISC) == IS_NO_RDISC 181144674Sglebius || !on) 182125033Sharti return; 183125033Sharti get_rdisc_sock(); 184153690Sglebius } 185153690Sglebius 186153690Sglebius if (!(ifp->int_if_flags & IFF_MULTICAST)) { 187153690Sglebius ifp->int_state &= ~(IS_ALL_HOSTS | IS_ALL_ROUTERS); 188153690Sglebius return; 189153690Sglebius } 190153690Sglebius 191106266Sjulian memset(&gr, 0, sizeof(gr)); 192106266Sjulian gr.gr_interface = ifp->int_index; 193106266Sjulian sin = (struct sockaddr_in *)&gr.gr_group; 194106266Sjulian sin->sin_family = AF_INET; 195106266Sjulian#ifdef _HAVE_SIN_LEN 196129823Sjulian sin->sin_len = sizeof(struct sockaddr_in); 197129823Sjulian#endif 198129823Sjulian 199129823Sjulian if (supplier 200129823Sjulian || (ifp->int_state & IS_NO_ADV_IN) 201129823Sjulian || !on) { 202144674Sglebius /* stop listening to advertisements 203129823Sjulian */ 204129823Sjulian if (ifp->int_state & IS_ALL_HOSTS) { 205129823Sjulian sin->sin_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); 206106266Sjulian if (setsockopt(rdisc_sock, IPPROTO_IP, 207106266Sjulian MCAST_LEAVE_GROUP, 208106266Sjulian &gr, sizeof(gr)) < 0) 209144674Sglebius LOGERR("MCAST_LEAVE_GROUP ALLHOSTS"); 210125032Sharti ifp->int_state &= ~IS_ALL_HOSTS; 211106266Sjulian } 212106266Sjulian 213106266Sjulian } else if (!(ifp->int_state & IS_ALL_HOSTS)) { 214106266Sjulian /* start listening to advertisements 215106321Sjulian */ 216106266Sjulian sin->sin_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); 217106266Sjulian if (setsockopt(rdisc_sock, IPPROTO_IP, MCAST_JOIN_GROUP, 218106266Sjulian &gr, sizeof(gr)) < 0) { 219125030Sharti LOGERR("MCAST_JOIN_GROUP ALLHOSTS"); 220106266Sjulian } else { 221106266Sjulian ifp->int_state |= IS_ALL_HOSTS; 222106266Sjulian } 223106321Sjulian } 224106321Sjulian 225106266Sjulian if (!supplier 226137138Sglebius || (ifp->int_state & IS_NO_ADV_OUT) 227137138Sglebius || !on) { 228106266Sjulian /* stop listening to solicitations 229106266Sjulian */ 230106266Sjulian if (ifp->int_state & IS_ALL_ROUTERS) { 231106266Sjulian sin->sin_addr.s_addr = htonl(INADDR_ALLROUTERS_GROUP); 232106266Sjulian if (setsockopt(rdisc_sock, IPPROTO_IP, 233106266Sjulian MCAST_LEAVE_GROUP, 234106266Sjulian &gr, sizeof(gr)) < 0) 235106266Sjulian LOGERR("MCAST_LEAVE_GROUP ALLROUTERS"); 236106266Sjulian ifp->int_state &= ~IS_ALL_ROUTERS; 237144674Sglebius } 238106266Sjulian 239106266Sjulian } else if (!(ifp->int_state & IS_ALL_ROUTERS)) { 240144674Sglebius /* start hearing solicitations 241106266Sjulian */ 242144674Sglebius sin->sin_addr.s_addr = htonl(INADDR_ALLROUTERS_GROUP); 243106266Sjulian if (setsockopt(rdisc_sock, IPPROTO_IP, MCAST_JOIN_GROUP, 244106266Sjulian &gr, sizeof(gr)) < 0) { 245106266Sjulian LOGERR("MCAST_JOIN_GROUP ALLROUTERS"); 246106266Sjulian } else { 247144674Sglebius ifp->int_state |= IS_ALL_ROUTERS; 248106266Sjulian } 249106266Sjulian } 250106266Sjulian} 251106266Sjulian 252144674Sglebius 253144674Sglebius/* start supplying routes 254144674Sglebius */ 255144674Sglebiusvoid 256144674Sglebiusset_supplier(void) 257144674Sglebius{ 258144674Sglebius struct interface *ifp; 259144674Sglebius struct dr *drp; 260144674Sglebius 261144674Sglebius if (supplier_set) 262144674Sglebius return; 263144674Sglebius 264144674Sglebius trace_act("start supplying routes"); 265144674Sglebius 266144674Sglebius /* Forget discovered routes. 267144674Sglebius */ 268144674Sglebius for (drp = drs; drp < &drs[MAX_ADS]; drp++) { 269144674Sglebius drp->dr_recv_pref = 0; 270144674Sglebius drp->dr_life = 0; 271144674Sglebius } 272144674Sglebius rdisc_age(0); 273144674Sglebius 274144674Sglebius supplier_set = 1; 275144674Sglebius supplier = 1; 276144674Sglebius 277144674Sglebius /* Do not start advertising until we have heard some RIP routes */ 278144674Sglebius LIM_SEC(rdisc_timer, now.tv_sec+MIN_WAITTIME); 279144674Sglebius 280144674Sglebius /* Switch router discovery multicast groups from soliciting 281144674Sglebius * to advertising. 282144674Sglebius */ 283106266Sjulian LIST_FOREACH(ifp, &ifnet, int_list) { 284106266Sjulian if (ifp->int_state & IS_BROKE) 285106266Sjulian continue; 286106321Sjulian ifp->int_rdisc_cnt = 0; 287106266Sjulian ifp->int_rdisc_timer.tv_usec = rdisc_timer.tv_usec; 288144674Sglebius ifp->int_rdisc_timer.tv_sec = now.tv_sec+MIN_WAITTIME; 289144674Sglebius set_rdisc_mg(ifp, 1); 290106266Sjulian } 291106266Sjulian 292106321Sjulian /* get rid of any redirects */ 293144674Sglebius del_redirects(0,0); 294106266Sjulian} 295106266Sjulian 296106435Sjulian 297106435Sjulian/* age discovered routes and find the best one 298106435Sjulian */ 299106435Sjulianvoid 300106266Sjulianrdisc_age(naddr bad_gate) 301106266Sjulian{ 302106266Sjulian time_t sec; 303106266Sjulian struct dr *drp; 304106266Sjulian 305106266Sjulian 306106266Sjulian /* If only advertising, then do only that. */ 307106266Sjulian if (supplier) { 308106266Sjulian /* If switching from client to server, get rid of old 309106266Sjulian * default routes. 310106266Sjulian */ 311106266Sjulian if (cur_drp != 0) 312106266Sjulian rdisc_sort(); 313106266Sjulian rdisc_adv(); 314106266Sjulian return; 315106319Sjulian } 316106321Sjulian 317106266Sjulian /* If we are being told about a bad router, 318106319Sjulian * then age the discovered default route, and if there is 319106266Sjulian * no alternative, solicit a replacement. 320106319Sjulian */ 321106266Sjulian if (bad_gate != 0) { 322106319Sjulian /* Look for the bad discovered default route. 323106266Sjulian * Age it and note its interface. 324106266Sjulian */ 325106266Sjulian for (drp = drs; drp < &drs[MAX_ADS]; drp++) { 326106266Sjulian if (drp->dr_ts == 0) 327106266Sjulian continue; 328106266Sjulian 329106266Sjulian /* When we find the bad router, then age the route 330106266Sjulian * to at most SUPPLY_INTERVAL. 331144674Sglebius * This is contrary to RFC 1256, but defends against 332144674Sglebius * black holes. 333144674Sglebius */ 334106266Sjulian if (drp->dr_gate == bad_gate) { 335106266Sjulian sec = (now.tv_sec - drp->dr_life 336106266Sjulian + SUPPLY_INTERVAL); 337125033Sharti if (drp->dr_ts > sec) { 338144674Sglebius trace_act("age 0.0.0.0 --> %s via %s", 339144674Sglebius naddr_ntoa(drp->dr_gate), 340144674Sglebius drp->dr_ifp->int_name); 341144674Sglebius drp->dr_ts = sec; 342144674Sglebius } 343125033Sharti break; 344106266Sjulian } 345106266Sjulian } 346106266Sjulian } 347106266Sjulian 348106266Sjulian rdisc_sol(); 349106266Sjulian rdisc_sort(); 350144674Sglebius 351144674Sglebius /* Delete old redirected routes to keep the kernel table small, 352144674Sglebius * and to prevent black holes. Check that the kernel table 353144674Sglebius * matches the daemon table (i.e. has the default route). 354144674Sglebius * But only if RIP is not running and we are not dealing with 355144674Sglebius * a bad gateway, since otherwise age() will be called. 356144674Sglebius */ 357144674Sglebius if (rip_sock < 0 && bad_gate == 0) 358144674Sglebius age(0); 359144674Sglebius} 360144674Sglebius 361144674Sglebius 362153690Sglebius/* Zap all routes discovered via an interface that has gone bad 363153690Sglebius * This should only be called when !(ifp->int_state & IS_ALIAS) 364153690Sglebius */ 365153690Sglebiusvoid 366153690Sglebiusif_bad_rdisc(struct interface *ifp) 367153690Sglebius{ 368153690Sglebius struct dr *drp; 369153690Sglebius 370153690Sglebius for (drp = drs; drp < &drs[MAX_ADS]; drp++) { 371153690Sglebius if (drp->dr_ifp != ifp) 372153690Sglebius continue; 373153690Sglebius drp->dr_recv_pref = 0; 374153690Sglebius drp->dr_ts = 0; 375153690Sglebius drp->dr_life = 0; 376153690Sglebius } 377106266Sjulian 378106266Sjulian /* make a note to re-solicit, turn RIP on or off, etc. */ 379106266Sjulian rdisc_timer.tv_sec = 0; 380106266Sjulian} 381106266Sjulian 382106435Sjulian 383106435Sjulian/* mark an interface ok for router discovering. 384106435Sjulian */ 385106435Sjulianvoid 386106435Sjulianif_ok_rdisc(struct interface *ifp) 387106435Sjulian{ 388144674Sglebius set_rdisc_mg(ifp, 1); 389144674Sglebius 390144674Sglebius ifp->int_rdisc_cnt = 0; 391144674Sglebius ifp->int_rdisc_timer.tv_sec = now.tv_sec + (supplier 392144674Sglebius ? MIN_WAITTIME 393144674Sglebius : MAX_SOLICITATION_DELAY); 394144674Sglebius if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) 395144674Sglebius rdisc_timer = ifp->int_rdisc_timer; 396144674Sglebius} 397144674Sglebius 398106435Sjulian 399106435Sjulian/* get rid of a dead discovered router 400144674Sglebius */ 401106435Sjulianstatic void 402106435Sjuliandel_rdisc(struct dr *drp) 403106435Sjulian{ 404106435Sjulian struct interface *ifp; 405106266Sjulian naddr gate; 406106266Sjulian int i; 407106266Sjulian 408106266Sjulian 409106266Sjulian del_redirects(gate = drp->dr_gate, 0); 410106266Sjulian drp->dr_ts = 0; 411144674Sglebius drp->dr_life = 0; 412106321Sjulian 413144674Sglebius 414106321Sjulian /* Count the other discovered routes on the interface. 415106266Sjulian */ 416106266Sjulian i = 0; 417106266Sjulian ifp = drp->dr_ifp; 418106266Sjulian for (drp = drs; drp < &drs[MAX_ADS]; drp++) { 419106266Sjulian if (drp->dr_ts != 0 420106266Sjulian && drp->dr_ifp == ifp) 421106266Sjulian i++; 422106266Sjulian } 423106266Sjulian 424106266Sjulian /* If that was the last good discovered router on the interface, 425106321Sjulian * then solicit a new one. 426106266Sjulian * This is contrary to RFC 1256, but defends against black holes. 427144674Sglebius */ 428144674Sglebius if (i != 0) { 429106266Sjulian trace_act("discovered router %s via %s" 430106266Sjulian " is bad--have %d remaining", 431106321Sjulian naddr_ntoa(gate), ifp->int_name, i); 432106321Sjulian } else if (ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) { 433106266Sjulian trace_act("last discovered router %s via %s" 434106266Sjulian " is bad--re-solicit", 435144674Sglebius naddr_ntoa(gate), ifp->int_name); 436106266Sjulian ifp->int_rdisc_cnt = 0; 437106321Sjulian ifp->int_rdisc_timer.tv_sec = 0; 438106266Sjulian rdisc_sol(); 439106266Sjulian } else { 440144674Sglebius trace_act("last discovered router %s via %s" 441106266Sjulian " is bad--wait to solicit", 442144674Sglebius naddr_ntoa(gate), ifp->int_name); 443106266Sjulian } 444125031Sharti} 445106266Sjulian 446106266Sjulian 447106266Sjulian/* Find the best discovered route, 448106266Sjulian * and discard stale routers. 449106266Sjulian */ 450106266Sjulianstatic void 451106266Sjulianrdisc_sort(void) 452106266Sjulian{ 453106266Sjulian struct dr *drp, *new_drp; 454106266Sjulian struct rt_entry *rt; 455106266Sjulian struct rt_spare new; 456144674Sglebius struct interface *ifp; 457106266Sjulian u_int new_st = 0; 458106266Sjulian n_long new_pref = 0; 459106266Sjulian 460106321Sjulian 461106321Sjulian /* Find the best discovered route. 462125077Sharti */ 463144674Sglebius new_drp = 0; 464106266Sjulian for (drp = drs; drp < &drs[MAX_ADS]; drp++) { 465106266Sjulian if (drp->dr_ts == 0) 466106266Sjulian continue; 467106266Sjulian ifp = drp->dr_ifp; 468106266Sjulian 469106266Sjulian /* Get rid of expired discovered routers. 470106266Sjulian */ 471106266Sjulian if (drp->dr_ts + drp->dr_life <= now.tv_sec) { 472106266Sjulian del_rdisc(drp); 473106319Sjulian continue; 474106266Sjulian } 475106321Sjulian 476125077Sharti LIM_SEC(rdisc_timer, drp->dr_ts+drp->dr_life+1); 477144674Sglebius 478106321Sjulian /* Update preference with possibly changed interface 479106266Sjulian * metric. 480106266Sjulian */ 481106266Sjulian drp->dr_pref = PREF(drp->dr_recv_pref, ifp); 482106266Sjulian 483106435Sjulian /* Prefer the current route to prevent thrashing. 484106435Sjulian * Prefer shorter lifetimes to speed the detection of 485106435Sjulian * bad routers. 486106435Sjulian * Avoid sick interfaces. 487144674Sglebius */ 488106435Sjulian if (new_drp == 0 489106435Sjulian || (!((new_st ^ drp->dr_ifp->int_state) & IS_SICK) 490106435Sjulian && (new_pref < drp->dr_pref 491106266Sjulian || (new_pref == drp->dr_pref 492144674Sglebius && (drp == cur_drp 493106266Sjulian || (new_drp != cur_drp 494106266Sjulian && new_drp->dr_life > drp->dr_life))))) 495125077Sharti || ((new_st & IS_SICK) 496106266Sjulian && !(drp->dr_ifp->int_state & IS_SICK))) { 497106266Sjulian new_drp = drp; 498106266Sjulian new_st = drp->dr_ifp->int_state; 499106266Sjulian new_pref = drp->dr_pref; 500106266Sjulian } 501106266Sjulian } 502106266Sjulian 503106266Sjulian /* switch to a better default route 504106266Sjulian */ 505106266Sjulian if (new_drp != cur_drp) { 506106266Sjulian rt = rtget(RIP_DEFAULT, 0); 507125077Sharti 508106266Sjulian /* Stop using discovered routes if they are all bad 509106319Sjulian */ 510106266Sjulian if (new_drp == 0) { 511106266Sjulian trace_act("turn off Router Discovery client"); 512106266Sjulian rdisc_ok = 0; 513106266Sjulian 514106266Sjulian if (rt != 0 515106266Sjulian && (rt->rt_state & RS_RDISC)) { 516106266Sjulian new = rt->rt_spares[0]; 517106266Sjulian new.rts_metric = HOPCNT_INFINITY; 518106266Sjulian new.rts_time = now.tv_sec - GARBAGE_TIME; 519106266Sjulian rtchange(rt, rt->rt_state & ~RS_RDISC, 520106266Sjulian &new, 0); 521144674Sglebius rtswitch(rt, 0); 522106266Sjulian } 523106266Sjulian 524106266Sjulian } else { 525106266Sjulian if (cur_drp == 0) { 526106266Sjulian trace_act("turn on Router Discovery client" 527144674Sglebius " using %s via %s", 528106266Sjulian naddr_ntoa(new_drp->dr_gate), 529106266Sjulian new_drp->dr_ifp->int_name); 530106266Sjulian rdisc_ok = 1; 531144674Sglebius 532144674Sglebius } else { 533106266Sjulian trace_act("switch Router Discovery from" 534106266Sjulian " %s via %s to %s via %s", 535106266Sjulian naddr_ntoa(cur_drp->dr_gate), 536106266Sjulian cur_drp->dr_ifp->int_name, 537106266Sjulian naddr_ntoa(new_drp->dr_gate), 538106266Sjulian new_drp->dr_ifp->int_name); 539106266Sjulian } 540106266Sjulian 541106266Sjulian memset(&new, 0, sizeof(new)); 542106266Sjulian new.rts_ifp = new_drp->dr_ifp; 543106266Sjulian new.rts_gate = new_drp->dr_gate; 544106266Sjulian new.rts_router = new_drp->dr_gate; 545125031Sharti new.rts_metric = HOPCNT_INFINITY-1; 546106266Sjulian new.rts_time = now.tv_sec; 547106266Sjulian if (rt != 0) { 548106266Sjulian rtchange(rt, rt->rt_state | RS_RDISC, &new, 0); 549106266Sjulian } else { 550106266Sjulian rtadd(RIP_DEFAULT, 0, RS_RDISC, &new); 551106266Sjulian } 552106266Sjulian } 553106266Sjulian 554106266Sjulian cur_drp = new_drp; 555106266Sjulian } 556144674Sglebius 557144674Sglebius /* turn RIP on or off */ 558106266Sjulian if (!rdisc_ok || rip_interfaces > 1) { 559144674Sglebius rip_on(0); 560144674Sglebius } else { 561144674Sglebius rip_off(); 562144674Sglebius } 563144674Sglebius} 564144674Sglebius 565144674Sglebius 566144674Sglebius/* handle a single address in an advertisement 567144674Sglebius */ 568144674Sglebiusstatic void 569144674Sglebiusparse_ad(naddr from, 570144674Sglebius naddr gate, 571144674Sglebius n_long pref, /* signed and in network order */ 572144674Sglebius u_short life, /* in host byte order */ 573153690Sglebius struct interface *ifp) 574144674Sglebius{ 575144674Sglebius static struct msg_limit bad_gate; 576144674Sglebius struct dr *drp, *new_drp; 577144674Sglebius 578106266Sjulian 579106266Sjulian if (gate == RIP_DEFAULT 580106266Sjulian || !check_dst(gate)) { 581106266Sjulian msglim(&bad_gate, from,"router %s advertising bad gateway %s", 582106266Sjulian naddr_ntoa(from), 583106266Sjulian naddr_ntoa(gate)); 584144674Sglebius return; 585106266Sjulian } 586144674Sglebius 587144674Sglebius /* ignore pointers to ourself and routes via unreachable networks 588144674Sglebius */ 589144674Sglebius if (ifwithaddr(gate, 1, 0) != 0) { 590144674Sglebius trace_pkt(" discard Router Discovery Ad pointing at us"); 591106266Sjulian return; 592106266Sjulian } 593106266Sjulian if (!on_net(gate, ifp->int_net, ifp->int_mask)) { 594106266Sjulian trace_pkt(" discard Router Discovery Ad" 595106266Sjulian " toward unreachable net"); 596106266Sjulian return; 597106266Sjulian } 598106266Sjulian 599125243Sharti /* Convert preference to an unsigned value 600106266Sjulian * and later bias it by the metric of the interface. 601125243Sharti */ 602106266Sjulian pref = UNSIGN_PREF(ntohl(pref)); 603106266Sjulian 604106266Sjulian if (pref == 0 || life < MinMaxAdvertiseInterval) { 605125077Sharti pref = 0; 606106266Sjulian life = 0; 607144674Sglebius } 608106321Sjulian 609106266Sjulian for (new_drp = 0, drp = drs; drp < &drs[MAX_ADS]; drp++) { 610106266Sjulian /* accept new info for a familiar entry 611106266Sjulian */ 612106266Sjulian if (drp->dr_gate == gate) { 613125033Sharti new_drp = drp; 614141745Sru break; 615125033Sharti } 616125033Sharti 617125033Sharti if (life == 0) 618125033Sharti continue; /* do not worry about dead ads */ 619153690Sglebius 620153690Sglebius if (drp->dr_ts == 0) { 621153690Sglebius new_drp = drp; /* use unused entry */ 622153690Sglebius 623153690Sglebius } else if (new_drp == 0) { 624153690Sglebius /* look for an entry worse than the new one to 625153690Sglebius * reuse. 626153690Sglebius */ 627153690Sglebius if ((!(ifp->int_state & IS_SICK) 628153690Sglebius && (drp->dr_ifp->int_state & IS_SICK)) 629153690Sglebius || (pref > drp->dr_pref 630153690Sglebius && !((ifp->int_state ^ drp->dr_ifp->int_state) 631153690Sglebius & IS_SICK))) 632153690Sglebius new_drp = drp; 633153690Sglebius 634106266Sjulian } else if (new_drp->dr_ts != 0) { 635125243Sharti /* look for the least valuable entry to reuse 636106266Sjulian */ 637125243Sharti if ((!(new_drp->dr_ifp->int_state & IS_SICK) 638138268Sglebius && (drp->dr_ifp->int_state & IS_SICK)) 639137136Sglebius || (new_drp->dr_pref > drp->dr_pref 640106266Sjulian && !((new_drp->dr_ifp->int_state 641106266Sjulian ^ drp->dr_ifp->int_state) 642106266Sjulian & IS_SICK))) 643154707Sglebius new_drp = drp; 644106266Sjulian } 645106266Sjulian } 646154707Sglebius 647106266Sjulian /* forget it if all of the current entries are better */ 648106266Sjulian if (new_drp == 0) 649154707Sglebius return; 650106266Sjulian 651106266Sjulian new_drp->dr_ifp = ifp; 652125077Sharti new_drp->dr_gate = gate; 653106321Sjulian new_drp->dr_ts = now.tv_sec; 654125077Sharti new_drp->dr_life = life; 655106266Sjulian new_drp->dr_recv_pref = pref; 656144674Sglebius /* bias functional preference by metric of the interface */ 657106266Sjulian new_drp->dr_pref = PREF(pref,ifp); 658106266Sjulian 659154707Sglebius /* after hearing a good advertisement, stop asking 660106266Sjulian */ 661125031Sharti if (!(ifp->int_state & IS_SICK)) 662106266Sjulian ifp->int_rdisc_cnt = MAX_SOLICITATIONS; 663106266Sjulian} 664106266Sjulian 665154707Sglebius 666111119Simp/* Compute the IP checksum 667106266Sjulian * This assumes the packet is less than 32K long. 668125031Sharti */ 669106266Sjulianstatic u_short 670106266Sjulianin_cksum(u_short *p, 671106266Sjulian u_int len) 672106266Sjulian{ 673144674Sglebius u_int sum = 0; 674125031Sharti int nwords = len >> 1; 675106266Sjulian 676154707Sglebius while (nwords-- != 0) 677154707Sglebius sum += *p++; 678154707Sglebius 679154707Sglebius if (len & 1) 680106266Sjulian sum += *(u_char *)p; 681106266Sjulian 682106266Sjulian /* end-around-carry */ 683106266Sjulian sum = (sum >> 16) + (sum & 0xffff); 684106266Sjulian sum += (sum >> 16); 685106266Sjulian return (~sum); 686106266Sjulian} 687106266Sjulian 688 689/* Send a router discovery advertisement or solicitation ICMP packet. 690 */ 691static void 692send_rdisc(union ad_u *p, 693 int p_size, 694 struct interface *ifp, 695 naddr dst, /* 0 or unicast destination */ 696 int type) /* 0=unicast, 1=bcast, 2=mcast */ 697{ 698 struct sockaddr_in rsin; 699 int flags; 700 const char *msg; 701 702 703 memset(&rsin, 0, sizeof(rsin)); 704 rsin.sin_addr.s_addr = dst; 705 rsin.sin_family = AF_INET; 706#ifdef _HAVE_SIN_LEN 707 rsin.sin_len = sizeof(rsin); 708#endif 709 flags = MSG_DONTROUTE; 710 711 switch (type) { 712 case 0: /* unicast */ 713 default: 714 msg = "Send"; 715 break; 716 717 case 1: /* broadcast */ 718 if (ifp->int_if_flags & IFF_POINTOPOINT) { 719 msg = "Send pt-to-pt"; 720 rsin.sin_addr.s_addr = ifp->int_dstaddr; 721 } else { 722 msg = "Send broadcast"; 723 rsin.sin_addr.s_addr = ifp->int_brdaddr; 724 } 725 break; 726 727 case 2: /* multicast */ 728 msg = "Send multicast"; 729 if (ifp->int_state & IS_DUP) { 730 trace_act("abort multicast output via %s" 731 " with duplicate address", 732 ifp->int_name); 733 return; 734 } 735 if (rdisc_sock_mcast != ifp) { 736 /* select the right interface. */ 737 struct ip_mreqn mreqn; 738 739 memset(&mreqn, 0, sizeof(struct ip_mreqn)); 740 mreqn.imr_ifindex = ifp->int_index; 741 if (0 > setsockopt(rdisc_sock, 742 IPPROTO_IP, IP_MULTICAST_IF, 743 &mreqn, 744 sizeof(mreqn))) { 745 LOGERR("setsockopt(rdisc_sock," 746 "IP_MULTICAST_IF)"); 747 rdisc_sock_mcast = 0; 748 return; 749 } 750 rdisc_sock_mcast = ifp; 751 } 752 flags = 0; 753 break; 754 } 755 756 if (rdisc_sock < 0) 757 get_rdisc_sock(); 758 759 trace_rdisc(msg, ifp->int_addr, rsin.sin_addr.s_addr, ifp, 760 p, p_size); 761 762 if (0 > sendto(rdisc_sock, p, p_size, flags, 763 (struct sockaddr *)&rsin, sizeof(rsin))) { 764 if (ifp == 0 || !(ifp->int_state & IS_BROKE)) 765 msglog("sendto(%s%s%s): %s", 766 ifp != 0 ? ifp->int_name : "", 767 ifp != 0 ? ", " : "", 768 inet_ntoa(rsin.sin_addr), 769 strerror(errno)); 770 if (ifp != 0) 771 if_sick(ifp); 772 } 773} 774 775 776/* Send an advertisement 777 */ 778static void 779send_adv(struct interface *ifp, 780 naddr dst, /* 0 or unicast destination */ 781 int type) /* 0=unicast, 1=bcast, 2=mcast */ 782{ 783 union ad_u u; 784 n_long pref; 785 786 787 memset(&u, 0, sizeof(u.ad)); 788 789 u.ad.icmp_type = ICMP_ROUTERADVERT; 790 u.ad.icmp_ad_num = 1; 791 u.ad.icmp_ad_asize = sizeof(u.ad.icmp_ad_info[0])/4; 792 793 u.ad.icmp_ad_life = stopint ? 0 : htons(ifp->int_rdisc_int*3); 794 795 /* Convert the configured preference to an unsigned value, 796 * bias it by the interface metric, and then send it as a 797 * signed, network byte order value. 798 */ 799 pref = UNSIGN_PREF(ifp->int_rdisc_pref); 800 u.ad.icmp_ad_info[0].icmp_ad_pref = htonl(SIGN_PREF(PREF(pref, ifp))); 801 802 u.ad.icmp_ad_info[0].icmp_ad_addr = ifp->int_addr; 803 804 u.ad.icmp_cksum = in_cksum((u_short*)&u.ad, sizeof(u.ad)); 805 806 send_rdisc(&u, sizeof(u.ad), ifp, dst, type); 807} 808 809 810/* Advertise for Router Discovery 811 */ 812void 813rdisc_adv(void) 814{ 815 struct interface *ifp; 816 817 if (!supplier) 818 return; 819 820 rdisc_timer.tv_sec = now.tv_sec + NEVER; 821 822 LIST_FOREACH(ifp, &ifnet, int_list) { 823 if (0 != (ifp->int_state & (IS_NO_ADV_OUT | IS_BROKE))) 824 continue; 825 826 if (!timercmp(&ifp->int_rdisc_timer, &now, >) 827 || stopint) { 828 send_adv(ifp, htonl(INADDR_ALLHOSTS_GROUP), 829 (ifp->int_state&IS_BCAST_RDISC) ? 1 : 2); 830 ifp->int_rdisc_cnt++; 831 832 intvl_random(&ifp->int_rdisc_timer, 833 (ifp->int_rdisc_int*3)/4, 834 ifp->int_rdisc_int); 835 if (ifp->int_rdisc_cnt < MAX_INITIAL_ADVERTS 836 && (ifp->int_rdisc_timer.tv_sec 837 > MAX_INITIAL_ADVERT_INTERVAL)) { 838 ifp->int_rdisc_timer.tv_sec 839 = MAX_INITIAL_ADVERT_INTERVAL; 840 } 841 timevaladd(&ifp->int_rdisc_timer, &now); 842 } 843 844 if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) 845 rdisc_timer = ifp->int_rdisc_timer; 846 } 847} 848 849 850/* Solicit for Router Discovery 851 */ 852void 853rdisc_sol(void) 854{ 855 struct interface *ifp; 856 union ad_u u; 857 858 859 if (supplier) 860 return; 861 862 rdisc_timer.tv_sec = now.tv_sec + NEVER; 863 864 LIST_FOREACH(ifp, &ifnet, int_list) { 865 if (0 != (ifp->int_state & (IS_NO_SOL_OUT | IS_BROKE)) 866 || ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) 867 continue; 868 869 if (!timercmp(&ifp->int_rdisc_timer, &now, >)) { 870 memset(&u, 0, sizeof(u.so)); 871 u.so.icmp_type = ICMP_ROUTERSOLICIT; 872 u.so.icmp_cksum = in_cksum((u_short*)&u.so, 873 sizeof(u.so)); 874 send_rdisc(&u, sizeof(u.so), ifp, 875 htonl(INADDR_ALLROUTERS_GROUP), 876 ((ifp->int_state&IS_BCAST_RDISC) ? 1 : 2)); 877 878 if (++ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) 879 continue; 880 881 ifp->int_rdisc_timer.tv_sec = SOLICITATION_INTERVAL; 882 ifp->int_rdisc_timer.tv_usec = 0; 883 timevaladd(&ifp->int_rdisc_timer, &now); 884 } 885 886 if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) 887 rdisc_timer = ifp->int_rdisc_timer; 888 } 889} 890 891 892/* check the IP header of a possible Router Discovery ICMP packet */ 893static struct interface * /* 0 if bad */ 894ck_icmp(const char *act, 895 naddr from, 896 struct interface *ifp, 897 naddr to, 898 union ad_u *p, 899 u_int len) 900{ 901 const char *type; 902 903 904 if (p->icmp.icmp_type == ICMP_ROUTERADVERT) { 905 type = "advertisement"; 906 } else if (p->icmp.icmp_type == ICMP_ROUTERSOLICIT) { 907 type = "solicitation"; 908 } else { 909 return 0; 910 } 911 912 if (p->icmp.icmp_code != 0) { 913 trace_pkt("unrecognized ICMP Router %s code=%d from %s to %s", 914 type, p->icmp.icmp_code, 915 naddr_ntoa(from), naddr_ntoa(to)); 916 return 0; 917 } 918 919 trace_rdisc(act, from, to, ifp, p, len); 920 921 if (ifp == 0) 922 trace_pkt("unknown interface for router-discovery %s" 923 " from %s to %s", 924 type, naddr_ntoa(from), naddr_ntoa(to)); 925 926 return ifp; 927} 928 929 930/* read packets from the router discovery socket 931 */ 932void 933read_d(void) 934{ 935 static struct msg_limit bad_asize, bad_len; 936#ifdef USE_PASSIFNAME 937 static struct msg_limit bad_name; 938#endif 939 struct sockaddr_in from; 940 int n, fromlen, cc, hlen; 941 struct { 942#ifdef USE_PASSIFNAME 943 char ifname[IFNAMSIZ]; 944#endif 945 union { 946 struct ip ip; 947 u_short s[512/2]; 948 u_char b[512]; 949 } pkt; 950 } buf; 951 union ad_u *p; 952 n_long *wp; 953 struct interface *ifp; 954 955 956 for (;;) { 957 fromlen = sizeof(from); 958 cc = recvfrom(rdisc_sock, &buf, sizeof(buf), 0, 959 (struct sockaddr*)&from, 960 &fromlen); 961 if (cc <= 0) { 962 if (cc < 0 && errno != EWOULDBLOCK) 963 LOGERR("recvfrom(rdisc_sock)"); 964 break; 965 } 966 if (fromlen != sizeof(struct sockaddr_in)) 967 logbad(1,"impossible recvfrom(rdisc_sock) fromlen=%d", 968 fromlen); 969#ifdef USE_PASSIFNAME 970 if ((cc -= sizeof(buf.ifname)) < 0) 971 logbad(0,"missing USE_PASSIFNAME; only %d bytes", 972 cc+sizeof(buf.ifname)); 973#endif 974 975 hlen = buf.pkt.ip.ip_hl << 2; 976 if (cc < hlen + ICMP_MINLEN) 977 continue; 978 p = (union ad_u *)&buf.pkt.b[hlen]; 979 cc -= hlen; 980 981#ifdef USE_PASSIFNAME 982 ifp = ifwithname(buf.ifname, 0); 983 if (ifp == 0) 984 msglim(&bad_name, from.sin_addr.s_addr, 985 "impossible rdisc if_ name %.*s", 986 IFNAMSIZ, buf.ifname); 987#else 988 /* If we could tell the interface on which a packet from 989 * address 0 arrived, we could deal with such solicitations. 990 */ 991 ifp = ((from.sin_addr.s_addr == 0) 992 ? 0 : iflookup(from.sin_addr.s_addr)); 993#endif 994 ifp = ck_icmp("Recv", from.sin_addr.s_addr, ifp, 995 buf.pkt.ip.ip_dst.s_addr, p, cc); 996 if (ifp == 0) 997 continue; 998 if (ifwithaddr(from.sin_addr.s_addr, 0, 0)) { 999 trace_pkt(" " 1000 "discard our own Router Discovery message"); 1001 continue; 1002 } 1003 1004 switch (p->icmp.icmp_type) { 1005 case ICMP_ROUTERADVERT: 1006 if (p->ad.icmp_ad_asize*4 1007 < (int)sizeof(p->ad.icmp_ad_info[0])) { 1008 msglim(&bad_asize, from.sin_addr.s_addr, 1009 "intolerable rdisc address size=%d", 1010 p->ad.icmp_ad_asize); 1011 continue; 1012 } 1013 if (p->ad.icmp_ad_num == 0) { 1014 trace_pkt(" empty?"); 1015 continue; 1016 } 1017 if (cc != (int)(sizeof(p->ad) 1018 - sizeof(p->ad.icmp_ad_info) 1019 + (p->ad.icmp_ad_num 1020 * sizeof(p->ad.icmp_ad_info[0])))) { 1021 msglim(&bad_len, from.sin_addr.s_addr, 1022 "rdisc length %d does not match ad_num" 1023 " %d", cc, p->ad.icmp_ad_num); 1024 continue; 1025 } 1026 if (supplier) 1027 continue; 1028 if (ifp->int_state & IS_NO_ADV_IN) 1029 continue; 1030 1031 wp = &p->ad.icmp_ad_info[0].icmp_ad_addr; 1032 for (n = 0; n < p->ad.icmp_ad_num; n++) { 1033 parse_ad(from.sin_addr.s_addr, 1034 wp[0], wp[1], 1035 ntohs(p->ad.icmp_ad_life), 1036 ifp); 1037 wp += p->ad.icmp_ad_asize; 1038 } 1039 break; 1040 1041 1042 case ICMP_ROUTERSOLICIT: 1043 if (!supplier) 1044 continue; 1045 if (ifp->int_state & IS_NO_ADV_OUT) 1046 continue; 1047 if (stopint) 1048 continue; 1049 1050 /* XXX 1051 * We should handle messages from address 0. 1052 */ 1053 1054 /* Respond with a point-to-point advertisement */ 1055 send_adv(ifp, from.sin_addr.s_addr, 0); 1056 break; 1057 } 1058 } 1059 1060 rdisc_sort(); 1061} 1062