1*53b39a0bSmpi /* $OpenBSD: nd6.c,v 1.186 2016/06/15 11:49:34 mpi Exp $ */ 2d8a7e3a7Sitojun /* $KAME: nd6.c,v 1.280 2002/06/08 19:52:07 itojun Exp $ */ 3287546eaSitojun 4287546eaSitojun /* 5287546eaSitojun * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 6287546eaSitojun * All rights reserved. 7287546eaSitojun * 8287546eaSitojun * Redistribution and use in source and binary forms, with or without 9287546eaSitojun * modification, are permitted provided that the following conditions 10287546eaSitojun * are met: 11287546eaSitojun * 1. Redistributions of source code must retain the above copyright 12287546eaSitojun * notice, this list of conditions and the following disclaimer. 13287546eaSitojun * 2. Redistributions in binary form must reproduce the above copyright 14287546eaSitojun * notice, this list of conditions and the following disclaimer in the 15287546eaSitojun * documentation and/or other materials provided with the distribution. 16287546eaSitojun * 3. Neither the name of the project nor the names of its contributors 17287546eaSitojun * may be used to endorse or promote products derived from this software 18287546eaSitojun * without specific prior written permission. 19287546eaSitojun * 20287546eaSitojun * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21287546eaSitojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22287546eaSitojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23287546eaSitojun * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24287546eaSitojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25287546eaSitojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26287546eaSitojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27287546eaSitojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28287546eaSitojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29287546eaSitojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30287546eaSitojun * SUCH DAMAGE. 31287546eaSitojun */ 32287546eaSitojun 33287546eaSitojun #include <sys/param.h> 34287546eaSitojun #include <sys/systm.h> 35b3c1e4c1Sitojun #include <sys/timeout.h> 36287546eaSitojun #include <sys/malloc.h> 37287546eaSitojun #include <sys/mbuf.h> 38287546eaSitojun #include <sys/socket.h> 39287546eaSitojun #include <sys/sockio.h> 40287546eaSitojun #include <sys/time.h> 41287546eaSitojun #include <sys/kernel.h> 4293fbd125Sbluhm #include <sys/pool.h> 43f4f4d166Sitojun #include <sys/protosw.h> 44287546eaSitojun #include <sys/errno.h> 45287546eaSitojun #include <sys/ioctl.h> 46287546eaSitojun #include <sys/syslog.h> 47287546eaSitojun #include <sys/queue.h> 48a09574ebSkettenis #include <sys/task.h> 49287546eaSitojun 50287546eaSitojun #include <net/if.h> 51287546eaSitojun #include <net/if_dl.h> 52287546eaSitojun #include <net/if_types.h> 53287546eaSitojun #include <net/route.h> 54287546eaSitojun 55287546eaSitojun #include <netinet/in.h> 56287546eaSitojun #include <netinet/if_ether.h> 57cb39d30aSangelos #include <netinet/ip_ipsp.h> 5858aa7d74Sangelos 59287546eaSitojun #include <netinet6/in6_var.h> 60fa86ee14Sitojun #include <netinet/ip6.h> 61287546eaSitojun #include <netinet6/ip6_var.h> 62287546eaSitojun #include <netinet6/nd6.h> 63fa86ee14Sitojun #include <netinet/icmp6.h> 64287546eaSitojun 65287546eaSitojun #define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */ 66287546eaSitojun #define ND6_RECALC_REACHTM_INTERVAL (60 * 120) /* 2 hours */ 67287546eaSitojun 68287546eaSitojun /* timer values */ 69287546eaSitojun int nd6_prune = 1; /* walk list every 1 seconds */ 70287546eaSitojun int nd6_delay = 5; /* delay first probe time 5 second */ 71287546eaSitojun int nd6_umaxtries = 3; /* maximum unicast query */ 72287546eaSitojun int nd6_mmaxtries = 3; /* maximum multicast query */ 73be4e9e12Sitojun int nd6_gctimer = (60 * 60 * 24); /* 1 day: garbage collection timer */ 74287546eaSitojun 75287546eaSitojun /* preventing too many loops in ND option parsing */ 76287546eaSitojun int nd6_maxndopt = 10; /* max # of ND options allowed */ 77287546eaSitojun 78f6e55599Sitojun int nd6_maxnudhint = 0; /* max # of subsequent upper layer hints */ 79f6e55599Sitojun 80b79da24aSitojun #ifdef ND6_DEBUG 81b79da24aSitojun int nd6_debug = 1; 82b79da24aSitojun #else 83b79da24aSitojun int nd6_debug = 0; 84b79da24aSitojun #endif 85b79da24aSitojun 8692c821ddSbluhm TAILQ_HEAD(llinfo_nd6_head, llinfo_nd6) nd6_list; 8793fbd125Sbluhm struct pool nd6_pool; /* pool for llinfo_nd6 structures */ 8892c821ddSbluhm int nd6_inuse, nd6_allocated; 89287546eaSitojun 90f4f4d166Sitojun struct nd_drhead nd_defrouter; 91287546eaSitojun struct nd_prhead nd_prefix = { 0 }; 92287546eaSitojun 93287546eaSitojun int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL; 94287546eaSitojun 95a0aa363cSjsing void nd6_slowtimo(void *); 96a0aa363cSjsing struct llinfo_nd6 *nd6_free(struct rtentry *, int); 97a0aa363cSjsing void nd6_llinfo_timer(void *); 98287546eaSitojun 99b3c1e4c1Sitojun struct timeout nd6_slowtimo_ch; 100b3c1e4c1Sitojun struct timeout nd6_timer_ch; 101a09574ebSkettenis struct task nd6_timer_task; 102e4195480Sdlg void nd6_timer_work(void *); 103b3c1e4c1Sitojun 104a0aa363cSjsing int fill_drlist(void *, size_t *, size_t); 105a0aa363cSjsing int fill_prlist(void *, size_t *, size_t); 106d8a7e3a7Sitojun 107287546eaSitojun void 108a0aa363cSjsing nd6_init(void) 109287546eaSitojun { 110287546eaSitojun static int nd6_init_done = 0; 111287546eaSitojun 112287546eaSitojun if (nd6_init_done) { 11335075f95Smpi log(LOG_NOTICE, "%s called more than once\n", __func__); 114287546eaSitojun return; 115287546eaSitojun } 116287546eaSitojun 11792c821ddSbluhm TAILQ_INIT(&nd6_list); 11893fbd125Sbluhm pool_init(&nd6_pool, sizeof(struct llinfo_nd6), 0, 0, 0, "nd6", NULL); 11993fbd125Sbluhm 120f4f4d166Sitojun /* initialization of the default router list */ 121f4f4d166Sitojun TAILQ_INIT(&nd_defrouter); 122f4f4d166Sitojun 123e4195480Sdlg task_set(&nd6_timer_task, nd6_timer_work, NULL); 124a09574ebSkettenis 125287546eaSitojun nd6_init_done = 1; 126287546eaSitojun 127287546eaSitojun /* start timer */ 128b3c1e4c1Sitojun timeout_set(&nd6_slowtimo_ch, nd6_slowtimo, NULL); 12929e86e5eSblambert timeout_add_sec(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL); 130110585f2Sflorian 13160968cbeSmpi nd6_rs_init(); 132287546eaSitojun } 133287546eaSitojun 134d6b9e9b9Sitojun struct nd_ifinfo * 135ee37ea65Smcbride nd6_ifattach(struct ifnet *ifp) 136287546eaSitojun { 137d6b9e9b9Sitojun struct nd_ifinfo *nd; 138287546eaSitojun 139393af863Skrw nd = malloc(sizeof(*nd), M_IP6NDP, M_WAITOK | M_ZERO); 140287546eaSitojun 141d6b9e9b9Sitojun nd->initialized = 1; 142287546eaSitojun 143d6b9e9b9Sitojun nd->basereachable = REACHABLE_TIME; 144d6b9e9b9Sitojun nd->reachable = ND_COMPUTE_RTIME(nd->basereachable); 145d6b9e9b9Sitojun nd->retrans = RETRANS_TIMER; 1462a4a63f1Shenning /* per-interface IFXF_AUTOCONF6 needs to be set too to accept RAs */ 147d8a7e3a7Sitojun nd->flags = (ND6_IFF_PERFORMNUD | ND6_IFF_ACCEPT_RTADV); 1481b5f410aSitojun 149d6b9e9b9Sitojun return nd; 150287546eaSitojun } 151287546eaSitojun 152d6b9e9b9Sitojun void 153ee37ea65Smcbride nd6_ifdetach(struct nd_ifinfo *nd) 154d6b9e9b9Sitojun { 155d374aaacSitojun 156dd168dc2Stedu free(nd, M_IP6NDP, 0); 157287546eaSitojun } 158287546eaSitojun 159287546eaSitojun void 160ee37ea65Smcbride nd6_option_init(void *opt, int icmp6len, union nd_opts *ndopts) 161287546eaSitojun { 162287546eaSitojun bzero(ndopts, sizeof(*ndopts)); 163287546eaSitojun ndopts->nd_opts_search = (struct nd_opt_hdr *)opt; 164287546eaSitojun ndopts->nd_opts_last 165287546eaSitojun = (struct nd_opt_hdr *)(((u_char *)opt) + icmp6len); 166287546eaSitojun 167287546eaSitojun if (icmp6len == 0) { 168287546eaSitojun ndopts->nd_opts_done = 1; 169287546eaSitojun ndopts->nd_opts_search = NULL; 170287546eaSitojun } 171287546eaSitojun } 172287546eaSitojun 173287546eaSitojun /* 174287546eaSitojun * Take one ND option. 175287546eaSitojun */ 176287546eaSitojun struct nd_opt_hdr * 177ee37ea65Smcbride nd6_option(union nd_opts *ndopts) 178287546eaSitojun { 179287546eaSitojun struct nd_opt_hdr *nd_opt; 180287546eaSitojun int olen; 181287546eaSitojun 182287546eaSitojun if (!ndopts) 183bc84bce2Skrw panic("ndopts == NULL in nd6_option"); 184287546eaSitojun if (!ndopts->nd_opts_last) 185bc84bce2Skrw panic("uninitialized ndopts in nd6_option"); 186287546eaSitojun if (!ndopts->nd_opts_search) 187287546eaSitojun return NULL; 188287546eaSitojun if (ndopts->nd_opts_done) 189287546eaSitojun return NULL; 190287546eaSitojun 191287546eaSitojun nd_opt = ndopts->nd_opts_search; 192287546eaSitojun 19315bd77d2Sitojun /* make sure nd_opt_len is inside the buffer */ 19415bd77d2Sitojun if ((caddr_t)&nd_opt->nd_opt_len >= (caddr_t)ndopts->nd_opts_last) { 19515bd77d2Sitojun bzero(ndopts, sizeof(*ndopts)); 19615bd77d2Sitojun return NULL; 19715bd77d2Sitojun } 19815bd77d2Sitojun 199287546eaSitojun olen = nd_opt->nd_opt_len << 3; 200287546eaSitojun if (olen == 0) { 201287546eaSitojun /* 202287546eaSitojun * Message validation requires that all included 203287546eaSitojun * options have a length that is greater than zero. 204287546eaSitojun */ 205287546eaSitojun bzero(ndopts, sizeof(*ndopts)); 206287546eaSitojun return NULL; 207287546eaSitojun } 208287546eaSitojun 209287546eaSitojun ndopts->nd_opts_search = (struct nd_opt_hdr *)((caddr_t)nd_opt + olen); 21015bd77d2Sitojun if (ndopts->nd_opts_search > ndopts->nd_opts_last) { 21115bd77d2Sitojun /* option overruns the end of buffer, invalid */ 21215bd77d2Sitojun bzero(ndopts, sizeof(*ndopts)); 21315bd77d2Sitojun return NULL; 21415bd77d2Sitojun } else if (ndopts->nd_opts_search == ndopts->nd_opts_last) { 21515bd77d2Sitojun /* reached the end of options chain */ 216287546eaSitojun ndopts->nd_opts_done = 1; 217287546eaSitojun ndopts->nd_opts_search = NULL; 218287546eaSitojun } 219287546eaSitojun return nd_opt; 220287546eaSitojun } 221287546eaSitojun 222287546eaSitojun /* 223287546eaSitojun * Parse multiple ND options. 224287546eaSitojun * This function is much easier to use, for ND routines that do not need 225287546eaSitojun * multiple options of the same type. 226287546eaSitojun */ 227287546eaSitojun int 228ee37ea65Smcbride nd6_options(union nd_opts *ndopts) 229287546eaSitojun { 230287546eaSitojun struct nd_opt_hdr *nd_opt; 231287546eaSitojun int i = 0; 232287546eaSitojun 233287546eaSitojun if (!ndopts) 234bc84bce2Skrw panic("ndopts == NULL in nd6_options"); 235287546eaSitojun if (!ndopts->nd_opts_last) 236bc84bce2Skrw panic("uninitialized ndopts in nd6_options"); 237287546eaSitojun if (!ndopts->nd_opts_search) 238287546eaSitojun return 0; 239287546eaSitojun 240287546eaSitojun while (1) { 241287546eaSitojun nd_opt = nd6_option(ndopts); 242287546eaSitojun if (!nd_opt && !ndopts->nd_opts_last) { 243287546eaSitojun /* 244287546eaSitojun * Message validation requires that all included 245287546eaSitojun * options have a length that is greater than zero. 246287546eaSitojun */ 247b79da24aSitojun icmp6stat.icp6s_nd_badopt++; 248287546eaSitojun bzero(ndopts, sizeof(*ndopts)); 249287546eaSitojun return -1; 250287546eaSitojun } 251287546eaSitojun 252287546eaSitojun if (!nd_opt) 253287546eaSitojun goto skip1; 254287546eaSitojun 255287546eaSitojun switch (nd_opt->nd_opt_type) { 256287546eaSitojun case ND_OPT_SOURCE_LINKADDR: 257287546eaSitojun case ND_OPT_TARGET_LINKADDR: 258287546eaSitojun case ND_OPT_MTU: 259287546eaSitojun case ND_OPT_REDIRECTED_HEADER: 260287546eaSitojun if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) { 26115bd77d2Sitojun nd6log((LOG_INFO, 26215bd77d2Sitojun "duplicated ND6 option found (type=%d)\n", 26315bd77d2Sitojun nd_opt->nd_opt_type)); 264287546eaSitojun /* XXX bark? */ 265287546eaSitojun } else { 266287546eaSitojun ndopts->nd_opt_array[nd_opt->nd_opt_type] 267287546eaSitojun = nd_opt; 268287546eaSitojun } 269287546eaSitojun break; 270287546eaSitojun case ND_OPT_PREFIX_INFORMATION: 271287546eaSitojun if (ndopts->nd_opt_array[nd_opt->nd_opt_type] == 0) { 272287546eaSitojun ndopts->nd_opt_array[nd_opt->nd_opt_type] 273287546eaSitojun = nd_opt; 274287546eaSitojun } 275287546eaSitojun ndopts->nd_opts_pi_end = 276287546eaSitojun (struct nd_opt_prefix_info *)nd_opt; 277287546eaSitojun break; 278287546eaSitojun default: 279287546eaSitojun /* 280287546eaSitojun * Unknown options must be silently ignored, 281e4d25771Stodd * to accommodate future extension to the protocol. 282287546eaSitojun */ 283b79da24aSitojun nd6log((LOG_DEBUG, 284287546eaSitojun "nd6_options: unsupported option %d - " 285b79da24aSitojun "option ignored\n", nd_opt->nd_opt_type)); 286287546eaSitojun } 287287546eaSitojun 288287546eaSitojun skip1: 289287546eaSitojun i++; 290287546eaSitojun if (i > nd6_maxndopt) { 291287546eaSitojun icmp6stat.icp6s_nd_toomanyopt++; 292b79da24aSitojun nd6log((LOG_INFO, "too many loop in nd opt\n")); 293287546eaSitojun break; 294287546eaSitojun } 295287546eaSitojun 296287546eaSitojun if (ndopts->nd_opts_done) 297287546eaSitojun break; 298287546eaSitojun } 299287546eaSitojun 300287546eaSitojun return 0; 301287546eaSitojun } 302287546eaSitojun 303287546eaSitojun /* 3049631a17bSitojun * ND6 timer routine to handle ND6 entries 305287546eaSitojun */ 306287546eaSitojun void 307134b71fcSdlg nd6_llinfo_settimer(struct llinfo_nd6 *ln, int secs) 3089631a17bSitojun { 3099631a17bSitojun int s; 3109631a17bSitojun 3119631a17bSitojun s = splsoftnet(); 3129631a17bSitojun 313134b71fcSdlg if (secs < 0) { 3149631a17bSitojun ln->ln_expire = 0; 3159631a17bSitojun timeout_del(&ln->ln_timer_ch); 3169631a17bSitojun } else { 317f303499dSdlg ln->ln_expire = time_uptime + secs; 318134b71fcSdlg timeout_add_sec(&ln->ln_timer_ch, secs); 3199631a17bSitojun } 3209631a17bSitojun 3219631a17bSitojun splx(s); 3229631a17bSitojun } 3239631a17bSitojun 324a0aa363cSjsing void 3259631a17bSitojun nd6_llinfo_timer(void *arg) 326287546eaSitojun { 327287546eaSitojun int s; 328b3c1e4c1Sitojun struct llinfo_nd6 *ln; 329287546eaSitojun struct rtentry *rt; 330287546eaSitojun struct sockaddr_in6 *dst; 3319631a17bSitojun struct ifnet *ifp; 332d374aaacSitojun struct nd_ifinfo *ndi = NULL; 333287546eaSitojun 3349631a17bSitojun s = splsoftnet(); 3359631a17bSitojun 3369631a17bSitojun ln = (struct llinfo_nd6 *)arg; 3379631a17bSitojun 3389631a17bSitojun if ((rt = ln->ln_rt) == NULL) 3399631a17bSitojun panic("ln->ln_rt == NULL"); 34047773228Sjsg if ((ifp = if_get(rt->rt_ifidx)) == NULL) { 34147773228Sjsg splx(s); 342f5e0f62bSmpi return; 34347773228Sjsg } 344d6b9e9b9Sitojun ndi = ND_IFINFO(ifp); 345c3c56496Sbluhm dst = satosin6(rt_key(rt)); 346287546eaSitojun 347287546eaSitojun /* sanity check */ 3489c0643cdSbluhm if (rt->rt_llinfo != NULL && (struct llinfo_nd6 *)rt->rt_llinfo != ln) 349bc84bce2Skrw panic("rt_llinfo(%p) is not equal to ln(%p)", 350d374aaacSitojun rt->rt_llinfo, ln); 351287546eaSitojun if (!dst) 352bc84bce2Skrw panic("dst=0 in nd6_timer(ln=%p)", ln); 353287546eaSitojun 354287546eaSitojun switch (ln->ln_state) { 355287546eaSitojun case ND6_LLINFO_INCOMPLETE: 356287546eaSitojun if (ln->ln_asked < nd6_mmaxtries) { 357287546eaSitojun ln->ln_asked++; 358134b71fcSdlg nd6_llinfo_settimer(ln, ndi->retrans / 1000); 3599631a17bSitojun nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0); 360287546eaSitojun } else { 361287546eaSitojun struct mbuf *m = ln->ln_hold; 362287546eaSitojun if (m) { 3638294a4dfSitojun ln->ln_hold = NULL; 364287546eaSitojun /* 365d8a7e3a7Sitojun * Fake rcvif to make the ICMP error 366d8a7e3a7Sitojun * more helpful in diagnosing for the 367d8a7e3a7Sitojun * receiver. 368287546eaSitojun * XXX: should we consider 369287546eaSitojun * older rcvif? 370287546eaSitojun */ 37128112c65Smpi m->m_pkthdr.ph_ifidx = rt->rt_ifidx; 372d8a7e3a7Sitojun 373287546eaSitojun icmp6_error(m, ICMP6_DST_UNREACH, 374287546eaSitojun ICMP6_DST_UNREACH_ADDR, 0); 375e212adedSkrw if (ln->ln_hold == m) { 376e212adedSkrw /* m is back in ln_hold. Discard. */ 377e212adedSkrw m_freem(ln->ln_hold); 378e212adedSkrw ln->ln_hold = NULL; 379e212adedSkrw } 380287546eaSitojun } 3819631a17bSitojun (void)nd6_free(rt, 0); 3829631a17bSitojun ln = NULL; 383287546eaSitojun } 384287546eaSitojun break; 385287546eaSitojun case ND6_LLINFO_REACHABLE: 3869631a17bSitojun if (!ND6_LLINFO_PERMANENT(ln)) { 387287546eaSitojun ln->ln_state = ND6_LLINFO_STALE; 388134b71fcSdlg nd6_llinfo_settimer(ln, nd6_gctimer); 389be4e9e12Sitojun } 390287546eaSitojun break; 391be4e9e12Sitojun 392be4e9e12Sitojun case ND6_LLINFO_STALE: 393f3fcf2f3Smcbride case ND6_LLINFO_PURGE: 394be4e9e12Sitojun /* Garbage Collection(RFC 2461 5.3) */ 3959631a17bSitojun if (!ND6_LLINFO_PERMANENT(ln)) { 3969631a17bSitojun (void)nd6_free(rt, 1); 3979631a17bSitojun ln = NULL; 3989631a17bSitojun } 399be4e9e12Sitojun break; 400be4e9e12Sitojun 401287546eaSitojun case ND6_LLINFO_DELAY: 402d374aaacSitojun if (ndi && (ndi->flags & ND6_IFF_PERFORMNUD) != 0) { 403d374aaacSitojun /* We need NUD */ 404287546eaSitojun ln->ln_asked = 1; 405287546eaSitojun ln->ln_state = ND6_LLINFO_PROBE; 406134b71fcSdlg nd6_llinfo_settimer(ln, ndi->retrans / 1000); 407d374aaacSitojun nd6_ns_output(ifp, &dst->sin6_addr, 408d8a7e3a7Sitojun &dst->sin6_addr, ln, 0); 409be4e9e12Sitojun } else { 410d374aaacSitojun ln->ln_state = ND6_LLINFO_STALE; /* XXX */ 411134b71fcSdlg nd6_llinfo_settimer(ln, nd6_gctimer); 412be4e9e12Sitojun } 413287546eaSitojun break; 414287546eaSitojun case ND6_LLINFO_PROBE: 415287546eaSitojun if (ln->ln_asked < nd6_umaxtries) { 416287546eaSitojun ln->ln_asked++; 417134b71fcSdlg nd6_llinfo_settimer(ln, ndi->retrans / 1000); 418287546eaSitojun nd6_ns_output(ifp, &dst->sin6_addr, 419287546eaSitojun &dst->sin6_addr, ln, 0); 420d8a7e3a7Sitojun } else { 4219631a17bSitojun (void)nd6_free(rt, 0); 4229631a17bSitojun ln = NULL; 423d8a7e3a7Sitojun } 424287546eaSitojun break; 425287546eaSitojun } 4269631a17bSitojun 427f5e0f62bSmpi if_put(ifp); 4289631a17bSitojun splx(s); 429287546eaSitojun } 430287546eaSitojun 4319631a17bSitojun /* 4329631a17bSitojun * ND6 timer routine to expire default route list and prefix list 4339631a17bSitojun */ 4349631a17bSitojun void 435e4195480Sdlg nd6_timer_work(void *null) 4369631a17bSitojun { 4379631a17bSitojun int s; 438568012e6Sbluhm struct nd_defrouter *dr, *ndr; 439119a433cSbluhm struct nd_prefix *pr, *npr; 4409631a17bSitojun struct in6_ifaddr *ia6, *nia6; 4419631a17bSitojun 4429631a17bSitojun s = splsoftnet(); 4439631a17bSitojun timeout_set(&nd6_timer_ch, nd6_timer, NULL); 44429e86e5eSblambert timeout_add_sec(&nd6_timer_ch, nd6_prune); 4459631a17bSitojun 4460a2c5741Sitojun /* expire default router list */ 447568012e6Sbluhm TAILQ_FOREACH_SAFE(dr, &nd_defrouter, dr_entry, ndr) 448568012e6Sbluhm if (dr->expire && dr->expire < time_second) 449287546eaSitojun defrtrlist_del(dr); 450287546eaSitojun 451287546eaSitojun /* 452d8a7e3a7Sitojun * expire interface addresses. 453d8a7e3a7Sitojun * in the past the loop was inside prefix expiry processing. 4548b542bbeSpascoe * However, from a stricter spec-conformance standpoint, we should 455d8a7e3a7Sitojun * rather separate address lifetimes and prefix lifetimes. 456d8a7e3a7Sitojun */ 457a0fa8079Smpi TAILQ_FOREACH_SAFE(ia6, &in6_ifaddr, ia_list, nia6) { 458d8a7e3a7Sitojun /* check address lifetime */ 459d8a7e3a7Sitojun if (IFA6_IS_INVALID(ia6)) { 460d8a7e3a7Sitojun in6_purgeaddr(&ia6->ia_ifa); 4610a8b9475Smarkus } else if (IFA6_IS_DEPRECATED(ia6)) { 462d8a7e3a7Sitojun ia6->ia6_flags |= IN6_IFF_DEPRECATED; 463d8a7e3a7Sitojun } else { 464d8a7e3a7Sitojun /* 465d8a7e3a7Sitojun * A new RA might have made a deprecated address 466d8a7e3a7Sitojun * preferred. 467d8a7e3a7Sitojun */ 468d8a7e3a7Sitojun ia6->ia6_flags &= ~IN6_IFF_DEPRECATED; 469d8a7e3a7Sitojun } 470d8a7e3a7Sitojun } 471d8a7e3a7Sitojun 472d8a7e3a7Sitojun /* expire prefix list */ 473119a433cSbluhm LIST_FOREACH_SAFE(pr, &nd_prefix, ndpr_entry, npr) { 474d8a7e3a7Sitojun /* 475287546eaSitojun * check prefix lifetime. 476287546eaSitojun * since pltime is just for autoconf, pltime processing for 477287546eaSitojun * prefix is not necessary. 478287546eaSitojun */ 479d8a7e3a7Sitojun if (pr->ndpr_vltime != ND6_INFINITE_LIFETIME && 4803212dc31Stholo time_second - pr->ndpr_lastupdate > pr->ndpr_vltime) { 481287546eaSitojun /* 482287546eaSitojun * address expiration and prefix expiration are 483d8a7e3a7Sitojun * separate. NEVER perform in6_purgeaddr here. 484287546eaSitojun */ 485287546eaSitojun 486287546eaSitojun prelist_remove(pr); 487119a433cSbluhm } 488287546eaSitojun } 489287546eaSitojun splx(s); 490287546eaSitojun } 491287546eaSitojun 492a09574ebSkettenis void 493a09574ebSkettenis nd6_timer(void *ignored_arg) 494a09574ebSkettenis { 495a09574ebSkettenis task_add(systq, &nd6_timer_task); 496a09574ebSkettenis } 497a09574ebSkettenis 49822770369Sitojun /* 49922770369Sitojun * Nuke neighbor cache/prefix/default router management table, right before 50022770369Sitojun * ifp goes away. 50122770369Sitojun */ 50222770369Sitojun void 503ee37ea65Smcbride nd6_purge(struct ifnet *ifp) 50422770369Sitojun { 50522770369Sitojun struct llinfo_nd6 *ln, *nln; 506d8a7e3a7Sitojun struct nd_defrouter *dr, *ndr; 50722770369Sitojun struct nd_prefix *pr, *npr; 50822770369Sitojun 50922770369Sitojun /* 510d8a7e3a7Sitojun * Nuke default router list entries toward ifp. 511d8a7e3a7Sitojun * We defer removal of default router list entries that is installed 512d8a7e3a7Sitojun * in the routing table, in order to keep additional side effects as 513d8a7e3a7Sitojun * small as possible. 51422770369Sitojun */ 515568012e6Sbluhm TAILQ_FOREACH_SAFE(dr, &nd_defrouter, dr_entry, ndr) { 516d8a7e3a7Sitojun if (dr->installed) 517d8a7e3a7Sitojun continue; 518d8a7e3a7Sitojun 51922770369Sitojun if (dr->ifp == ifp) 52022770369Sitojun defrtrlist_del(dr); 52122770369Sitojun } 522568012e6Sbluhm TAILQ_FOREACH_SAFE(dr, &nd_defrouter, dr_entry, ndr) { 523d8a7e3a7Sitojun if (!dr->installed) 524d8a7e3a7Sitojun continue; 525d8a7e3a7Sitojun 52622770369Sitojun if (dr->ifp == ifp) 52722770369Sitojun defrtrlist_del(dr); 52822770369Sitojun } 52922770369Sitojun 53022770369Sitojun /* Nuke prefix list entries toward ifp */ 531119a433cSbluhm LIST_FOREACH_SAFE(pr, &nd_prefix, ndpr_entry, npr) { 532e520a0ecSmpi if (pr->ndpr_ifp == ifp) 53322770369Sitojun prelist_remove(pr); 53422770369Sitojun } 53522770369Sitojun 536d3b1c214Sflorian if (ifp->if_xflags & IFXF_AUTOCONF6) { 53722770369Sitojun /* refresh default router list */ 538f4f4d166Sitojun defrouter_select(); 539e6e438a4Sitojun } 54022770369Sitojun 54122770369Sitojun /* 54222770369Sitojun * Nuke neighbor cache entries for the ifp. 54322770369Sitojun */ 54492c821ddSbluhm TAILQ_FOREACH_SAFE(ln, &nd6_list, ln_list, nln) { 54522770369Sitojun struct rtentry *rt; 54622770369Sitojun struct sockaddr_dl *sdl; 54722770369Sitojun 54822770369Sitojun rt = ln->ln_rt; 54992c821ddSbluhm if (rt != NULL && rt->rt_gateway != NULL && 55022770369Sitojun rt->rt_gateway->sa_family == AF_LINK) { 551c7b7b779Sbluhm sdl = satosdl(rt->rt_gateway); 55222770369Sitojun if (sdl->sdl_index == ifp->if_index) 553d8a7e3a7Sitojun nln = nd6_free(rt, 0); 55422770369Sitojun } 55522770369Sitojun } 55622770369Sitojun } 55722770369Sitojun 558287546eaSitojun struct rtentry * 559f4d1af37Smikeb nd6_lookup(struct in6_addr *addr6, int create, struct ifnet *ifp, 560f4d1af37Smikeb u_int rtableid) 561287546eaSitojun { 562287546eaSitojun struct rtentry *rt; 563287546eaSitojun struct sockaddr_in6 sin6; 5645148b194Smpi int flags; 565287546eaSitojun 566287546eaSitojun bzero(&sin6, sizeof(sin6)); 567287546eaSitojun sin6.sin6_len = sizeof(struct sockaddr_in6); 568287546eaSitojun sin6.sin6_family = AF_INET6; 569287546eaSitojun sin6.sin6_addr = *addr6; 57012d3c25cSclaudio flags = (create) ? RT_RESOLVE : 0; 571d8a7e3a7Sitojun 5725148b194Smpi rt = rtalloc(sin6tosa(&sin6), flags, rtableid); 57316119bbfSmpi if (rt != NULL && (rt->rt_flags & RTF_LLINFO) == 0) { 574287546eaSitojun /* 575287546eaSitojun * This is the case for the default route. 576287546eaSitojun * If we want to create a neighbor cache for the address, we 577287546eaSitojun * should free the route for the destination and allocate an 578287546eaSitojun * interface route. 579287546eaSitojun */ 580287546eaSitojun if (create) { 58127ae666cSmpi rtfree(rt); 58227ae666cSmpi rt = NULL; 583287546eaSitojun } 584287546eaSitojun } 58516119bbfSmpi if (rt == NULL) { 586287546eaSitojun if (create && ifp) { 587cb24f5e5Sclaudio struct rt_addrinfo info; 5880ffd01d4Sbluhm int error; 589d374aaacSitojun 590287546eaSitojun /* 591287546eaSitojun * If no route is available and create is set, 592287546eaSitojun * we allocate a host route for the destination 593287546eaSitojun * and treat it like an interface route. 594287546eaSitojun * This hack is necessary for a neighbor which can't 595287546eaSitojun * be covered by our own prefix. 596287546eaSitojun */ 597287546eaSitojun struct ifaddr *ifa = 598c3c56496Sbluhm ifaof_ifpforaddr(sin6tosa(&sin6), ifp); 599287546eaSitojun if (ifa == NULL) 600287546eaSitojun return (NULL); 601287546eaSitojun 602287546eaSitojun /* 603287546eaSitojun * Create a new route. RTF_LLINFO is necessary 604287546eaSitojun * to create a Neighbor Cache entry for the 605287546eaSitojun * destination in nd6_rtrequest which will be 6067ffb277fSbluhm * called in rtrequest. 607287546eaSitojun */ 608cb24f5e5Sclaudio bzero(&info, sizeof(info)); 609c29fc46aSmpi info.rti_flags = RTF_HOST | RTF_LLINFO; 610c3c56496Sbluhm info.rti_info[RTAX_DST] = sin6tosa(&sin6); 611c7b7b779Sbluhm info.rti_info[RTAX_GATEWAY] = sdltosa(ifp->if_sadl); 6127ffb277fSbluhm error = rtrequest(RTM_ADD, &info, RTP_CONNECTED, &rt, 6130ffd01d4Sbluhm rtableid); 6140ffd01d4Sbluhm if (error) 615287546eaSitojun return (NULL); 6169c0643cdSbluhm if (rt->rt_llinfo != NULL) { 617287546eaSitojun struct llinfo_nd6 *ln = 618287546eaSitojun (struct llinfo_nd6 *)rt->rt_llinfo; 619287546eaSitojun ln->ln_state = ND6_LLINFO_NOSTATE; 620287546eaSitojun } 621f6e55599Sitojun } else 622287546eaSitojun return (NULL); 623287546eaSitojun } 624287546eaSitojun /* 625287546eaSitojun * Validation for the entry. 626d8a7e3a7Sitojun * Note that the check for rt_llinfo is necessary because a cloned 627d8a7e3a7Sitojun * route from a parent route that has the L flag (e.g. the default 628d8a7e3a7Sitojun * route to a p2p interface) may have the flag, too, while the 629d8a7e3a7Sitojun * destination is not actually a neighbor. 630287546eaSitojun */ 631287546eaSitojun if ((rt->rt_flags & RTF_GATEWAY) || (rt->rt_flags & RTF_LLINFO) == 0 || 632d8a7e3a7Sitojun rt->rt_gateway->sa_family != AF_LINK || rt->rt_llinfo == NULL || 633248f6870Smpi (ifp != NULL && rt->rt_ifidx != ifp->if_index)) { 634287546eaSitojun if (create) { 635bbcf0337Smpi char addr[INET6_ADDRSTRLEN]; 63635075f95Smpi nd6log((LOG_DEBUG, "%s: failed to lookup %s (if=%s)\n", 63735075f95Smpi __func__, 638bbcf0337Smpi inet_ntop(AF_INET6, addr6, addr, sizeof(addr)), 639da592434Sitojun ifp ? ifp->if_xname : "unspec")); 640287546eaSitojun } 64116119bbfSmpi rtfree(rt); 642d8a7e3a7Sitojun return (NULL); 643287546eaSitojun } 644287546eaSitojun return (rt); 645287546eaSitojun } 646287546eaSitojun 647287546eaSitojun /* 648287546eaSitojun * Detect if a given IPv6 address identifies a neighbor on a given link. 649287546eaSitojun * XXX: should take care of the destination of a p2p link? 650287546eaSitojun */ 651287546eaSitojun int 652ee37ea65Smcbride nd6_is_addr_neighbor(struct sockaddr_in6 *addr, struct ifnet *ifp) 653287546eaSitojun { 654d8a7e3a7Sitojun struct nd_prefix *pr; 655ef6620bcSmpi struct in6_ifaddr *ia6; 656ef6620bcSmpi struct ifaddr *ifa; 657d8a7e3a7Sitojun struct rtentry *rt; 658287546eaSitojun 659cfb6b8dfSitojun /* 660cfb6b8dfSitojun * A link-local address is always a neighbor. 661cfb6b8dfSitojun * XXX: we should use the sin6_scope_id field rather than the embedded 662cfb6b8dfSitojun * interface index. 663d8a7e3a7Sitojun * XXX: a link does not necessarily specify a single interface. 664cfb6b8dfSitojun */ 665cfb6b8dfSitojun if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr) && 666cfb6b8dfSitojun ntohs(*(u_int16_t *)&addr->sin6_addr.s6_addr[2]) == ifp->if_index) 667287546eaSitojun return (1); 668287546eaSitojun 669ef6620bcSmpi TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { 670ef6620bcSmpi if (ifa->ifa_addr->sa_family != AF_INET6) 671ef6620bcSmpi continue; 672ef6620bcSmpi 673ef6620bcSmpi ia6 = ifatoia6(ifa); 674ef6620bcSmpi 675ef6620bcSmpi /* Prefix check down below. */ 676ef6620bcSmpi if (ia6->ia6_flags & IN6_IFF_AUTOCONF) 677ef6620bcSmpi continue; 678ef6620bcSmpi 679ef6620bcSmpi if (IN6_ARE_MASKED_ADDR_EQUAL(&addr->sin6_addr, 680ef6620bcSmpi &ia6->ia_addr.sin6_addr, 681ef6620bcSmpi &ia6->ia_prefixmask.sin6_addr)) 682ef6620bcSmpi return (1); 683ef6620bcSmpi } 684ef6620bcSmpi 685287546eaSitojun /* 686d8a7e3a7Sitojun * If the address matches one of our on-link prefixes, it should be a 687d8a7e3a7Sitojun * neighbor. 688287546eaSitojun */ 689545205eaSpyr LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { 690d8a7e3a7Sitojun if (pr->ndpr_ifp != ifp) 691d8a7e3a7Sitojun continue; 692287546eaSitojun 693d8a7e3a7Sitojun if (!(pr->ndpr_stateflags & NDPRF_ONLINK)) 694d8a7e3a7Sitojun continue; 695d8a7e3a7Sitojun 696d8a7e3a7Sitojun if (IN6_ARE_MASKED_ADDR_EQUAL(&pr->ndpr_prefix.sin6_addr, 697d8a7e3a7Sitojun &addr->sin6_addr, &pr->ndpr_mask)) 698d8a7e3a7Sitojun return (1); 699287546eaSitojun } 700d8a7e3a7Sitojun 701d8a7e3a7Sitojun /* 702287546eaSitojun * Even if the address matches none of our addresses, it might be 703287546eaSitojun * in the neighbor cache. 704287546eaSitojun */ 70516119bbfSmpi rt = nd6_lookup(&addr->sin6_addr, 0, ifp, ifp->if_rdomain); 70616119bbfSmpi if (rt != NULL) { 70716119bbfSmpi rtfree(rt); 708287546eaSitojun return (1); 70916119bbfSmpi } 710287546eaSitojun 711287546eaSitojun return (0); 712287546eaSitojun } 713287546eaSitojun 714287546eaSitojun /* 715287546eaSitojun * Free an nd6 llinfo entry. 716d8a7e3a7Sitojun * Since the function would cause significant changes in the kernel, DO NOT 717d8a7e3a7Sitojun * make it global, unless you have a strong reason for the change, and are sure 718d8a7e3a7Sitojun * that the change is safe. 719287546eaSitojun */ 720a0aa363cSjsing struct llinfo_nd6 * 721ee37ea65Smcbride nd6_free(struct rtentry *rt, int gc) 722287546eaSitojun { 72329760ae1Sitojun struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo, *next; 724c3c56496Sbluhm struct in6_addr in6 = satosin6(rt_key(rt))->sin6_addr; 725287546eaSitojun struct nd_defrouter *dr; 726248f6870Smpi struct ifnet *ifp; 72781d38878Smpi int s; 728287546eaSitojun 729287546eaSitojun /* 730d8a7e3a7Sitojun * we used to have pfctlinput(PRC_HOSTDEAD) here. 731d8a7e3a7Sitojun * even though it is not harmful, it was not really necessary. 732287546eaSitojun */ 733248f6870Smpi ifp = if_get(rt->rt_ifidx); 734f4f4d166Sitojun 7356e92dee6Sitojun s = splsoftnet(); 73681d38878Smpi if (!ip6_forwarding) { 737c3c56496Sbluhm dr = defrouter_lookup(&satosin6(rt_key(rt))->sin6_addr, 738248f6870Smpi rt->rt_ifidx); 739d8a7e3a7Sitojun 740d8a7e3a7Sitojun if (dr != NULL && dr->expire && 741d8a7e3a7Sitojun ln->ln_state == ND6_LLINFO_STALE && gc) { 742d8a7e3a7Sitojun /* 743d8a7e3a7Sitojun * If the reason for the deletion is just garbage 744d8a7e3a7Sitojun * collection, and the neighbor is an active default 745d8a7e3a7Sitojun * router, do not delete it. Instead, reset the GC 746d8a7e3a7Sitojun * timer using the router's lifetime. 747d8a7e3a7Sitojun * Simply deleting the entry would affect default 748d8a7e3a7Sitojun * router selection, which is not necessarily a good 749d8a7e3a7Sitojun * thing, especially when we're using router preference 750d8a7e3a7Sitojun * values. 751d8a7e3a7Sitojun * XXX: the check for ln_state would be redundant, 752d8a7e3a7Sitojun * but we intentionally keep it just in case. 753d8a7e3a7Sitojun */ 754134b71fcSdlg if (dr->expire > time_second) { 7559631a17bSitojun nd6_llinfo_settimer(ln, 756134b71fcSdlg dr->expire - time_second); 7579631a17bSitojun } else 758134b71fcSdlg nd6_llinfo_settimer(ln, nd6_gctimer); 759d8a7e3a7Sitojun splx(s); 760248f6870Smpi if_put(ifp); 76192c821ddSbluhm return (TAILQ_NEXT(ln, ln_list)); 762d8a7e3a7Sitojun } 763d8a7e3a7Sitojun 764f4f4d166Sitojun if (ln->ln_router || dr) { 765f4f4d166Sitojun /* 766f4f4d166Sitojun * rt6_flush must be called whether or not the neighbor 767f4f4d166Sitojun * is in the Default Router List. 768f4f4d166Sitojun * See a corresponding comment in nd6_na_input(). 769f4f4d166Sitojun */ 770248f6870Smpi rt6_flush(&in6, ifp); 771f4f4d166Sitojun } 772f4f4d166Sitojun 773f4f4d166Sitojun if (dr) { 774f4f4d166Sitojun /* 7758b542bbeSpascoe * Unreachability of a router might affect the default 776f4f4d166Sitojun * router selection and on-link detection of advertised 777f4f4d166Sitojun * prefixes. 778f4f4d166Sitojun */ 779f4f4d166Sitojun 780f4f4d166Sitojun /* 781f4f4d166Sitojun * Temporarily fake the state to choose a new default 782f4f4d166Sitojun * router and to perform on-link determination of 78334deef1eSitojun * prefixes correctly. 784f4f4d166Sitojun * Below the state will be set correctly, 785f4f4d166Sitojun * or the entry itself will be deleted. 786f4f4d166Sitojun */ 787f4f4d166Sitojun ln->ln_state = ND6_LLINFO_INCOMPLETE; 788f4f4d166Sitojun 789f4f4d166Sitojun /* 790d8a7e3a7Sitojun * Since defrouter_select() does not affect the 791d8a7e3a7Sitojun * on-link determination and MIP6 needs the check 792d8a7e3a7Sitojun * before the default router selection, we perform 793d8a7e3a7Sitojun * the check now. 794f4f4d166Sitojun */ 795f4f4d166Sitojun pfxlist_onlink_check(); 796d8a7e3a7Sitojun 797d8a7e3a7Sitojun /* 798d8a7e3a7Sitojun * refresh default router list 799d8a7e3a7Sitojun */ 800d8a7e3a7Sitojun defrouter_select(); 801287546eaSitojun } 802287546eaSitojun } 803287546eaSitojun 80429760ae1Sitojun /* 80529760ae1Sitojun * Before deleting the entry, remember the next entry as the 80629760ae1Sitojun * return value. We need this because pfxlist_onlink_check() above 80729760ae1Sitojun * might have freed other entries (particularly the old next entry) as 80829760ae1Sitojun * a side effect (XXX). 80929760ae1Sitojun */ 81092c821ddSbluhm next = TAILQ_NEXT(ln, ln_list); 81129760ae1Sitojun 81229760ae1Sitojun /* 81329760ae1Sitojun * Detach the route from the routing tree and the list of neighbor 81429760ae1Sitojun * caches, and disable the route entry not to be used in already 81529760ae1Sitojun * cached routes. 81629760ae1Sitojun */ 817e3e87ac7Smpi if (!ISSET(rt->rt_flags, RTF_STATIC)) 818c4cbcae8Sbluhm rtdeletemsg(rt, ifp, ifp->if_rdomain); 81981d38878Smpi splx(s); 82029760ae1Sitojun 821248f6870Smpi if_put(ifp); 822248f6870Smpi 8230a2c5741Sitojun return (next); 824287546eaSitojun } 825287546eaSitojun 826287546eaSitojun /* 827287546eaSitojun * Upper-layer reachability hint for Neighbor Unreachability Detection. 828287546eaSitojun * 8298b542bbeSpascoe * XXX cost-effective methods? 830287546eaSitojun */ 831287546eaSitojun void 832a7fa3e84Smpi nd6_nud_hint(struct rtentry *rt) 833287546eaSitojun { 834287546eaSitojun struct llinfo_nd6 *ln; 835a7fa3e84Smpi struct ifnet *ifp; 836287546eaSitojun 837a7fa3e84Smpi ifp = if_get(rt->rt_ifidx); 838a7fa3e84Smpi if (ifp == NULL) 839287546eaSitojun return; 840287546eaSitojun 841f6e55599Sitojun if ((rt->rt_flags & RTF_GATEWAY) != 0 || 842f6e55599Sitojun (rt->rt_flags & RTF_LLINFO) == 0 || 8439c0643cdSbluhm rt->rt_llinfo == NULL || rt->rt_gateway == NULL || 844f6e55599Sitojun rt->rt_gateway->sa_family != AF_LINK) { 845287546eaSitojun /* This is not a host route. */ 846a7fa3e84Smpi goto out; 847287546eaSitojun } 848287546eaSitojun 849287546eaSitojun ln = (struct llinfo_nd6 *)rt->rt_llinfo; 850804d8827Sitojun if (ln->ln_state < ND6_LLINFO_REACHABLE) 851a7fa3e84Smpi goto out; 852287546eaSitojun 853f6e55599Sitojun /* 854f6e55599Sitojun * if we get upper-layer reachability confirmation many times, 855f6e55599Sitojun * it is possible we have false information. 856f6e55599Sitojun */ 857f6e55599Sitojun ln->ln_byhint++; 858f6e55599Sitojun if (ln->ln_byhint > nd6_maxnudhint) 859a7fa3e84Smpi goto out; 860f6e55599Sitojun 861287546eaSitojun ln->ln_state = ND6_LLINFO_REACHABLE; 862a7fa3e84Smpi if (!ND6_LLINFO_PERMANENT(ln)) 863134b71fcSdlg nd6_llinfo_settimer(ln, ND_IFINFO(ifp)->reachable); 864a7fa3e84Smpi out: 865a7fa3e84Smpi if_put(ifp); 866287546eaSitojun } 867287546eaSitojun 868287546eaSitojun void 869dcb17c31Smpi nd6_rtrequest(struct ifnet *ifp, int req, struct rtentry *rt) 870287546eaSitojun { 871287546eaSitojun struct sockaddr *gate = rt->rt_gateway; 872287546eaSitojun struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo; 873287546eaSitojun struct ifaddr *ifa; 87470fc31beSrainer struct nd_defrouter *dr; 87570fc31beSrainer 87670fc31beSrainer if (req == RTM_DELETE && (rt->rt_flags & RTF_GATEWAY) && 87770fc31beSrainer (IN6_ARE_ADDR_EQUAL(&(satosin6(rt_key(rt)))->sin6_addr, 878ad38292eSmpi &in6addr_any) && rt_plen(rt) == 0)) { 879248f6870Smpi dr = defrouter_lookup(&satosin6(gate)->sin6_addr, 880248f6870Smpi ifp->if_index); 88170fc31beSrainer if (dr) 88270fc31beSrainer dr->installed = 0; 88370fc31beSrainer } 884287546eaSitojun 885d8a7e3a7Sitojun if ((rt->rt_flags & RTF_GATEWAY) != 0) 886287546eaSitojun return; 887287546eaSitojun 888d8a7e3a7Sitojun if (nd6_need_cache(ifp) == 0 && (rt->rt_flags & RTF_HOST) == 0) { 889d8a7e3a7Sitojun /* 890d8a7e3a7Sitojun * This is probably an interface direct route for a link 891d8a7e3a7Sitojun * which does not need neighbor caches (e.g. fe80::%lo0/64). 892d8a7e3a7Sitojun * We do not need special treatment below for such a route. 893d8a7e3a7Sitojun * Moreover, the RTF_LLINFO flag which would be set below 894d8a7e3a7Sitojun * would annoy the ndp(8) command. 895d8a7e3a7Sitojun */ 896d8a7e3a7Sitojun return; 897d8a7e3a7Sitojun } 898d8a7e3a7Sitojun 899af1344beSbluhm if (req == RTM_RESOLVE && nd6_need_cache(ifp) == 0) { 900d8a7e3a7Sitojun /* 901af1344beSbluhm * For routing daemons like ospf6d we allow neighbor discovery 902af1344beSbluhm * based on the cloning route only. This allows us to sent 903af1344beSbluhm * packets directly into a network without having an address 904af1344beSbluhm * with matching prefix on the interface. If the cloning 905af1344beSbluhm * route is used for an stf interface, we would mistakenly 906af1344beSbluhm * make a neighbor cache for the host route, and would see 907af1344beSbluhm * strange neighbor solicitation for the corresponding 908af1344beSbluhm * destination. In order to avoid confusion, we check if the 909af1344beSbluhm * interface is suitable for neighbor discovery, and stop the 910d8a7e3a7Sitojun * process if not. Additionally, we remove the LLINFO flag 911d8a7e3a7Sitojun * so that ndp(8) will not try to get the neighbor information 912d8a7e3a7Sitojun * of the destination. 913d8a7e3a7Sitojun */ 914d8a7e3a7Sitojun rt->rt_flags &= ~RTF_LLINFO; 915d8a7e3a7Sitojun return; 916d8a7e3a7Sitojun } 917d8a7e3a7Sitojun 918287546eaSitojun switch (req) { 919287546eaSitojun case RTM_ADD: 92048ebf8e1Sitojun if ((rt->rt_flags & RTF_CLONING) || 9219c0643cdSbluhm ((rt->rt_flags & (RTF_LLINFO | RTF_LOCAL)) && ln == NULL)) { 9229c0643cdSbluhm if (ln != NULL) 9239631a17bSitojun nd6_llinfo_settimer(ln, 0); 924d8a7e3a7Sitojun if ((rt->rt_flags & RTF_CLONING) != 0) 925287546eaSitojun break; 926287546eaSitojun } 927f4f4d166Sitojun /* 9288b542bbeSpascoe * In IPv4 code, we try to announce new RTF_ANNOUNCE entry here. 929f4f4d166Sitojun * We don't do that here since llinfo is not ready yet. 930f4f4d166Sitojun * 931f4f4d166Sitojun * There are also couple of other things to be discussed: 932f4f4d166Sitojun * - unsolicited NA code needs improvement beforehand 933f4f4d166Sitojun * - RFC2461 says we MAY send multicast unsolicited NA 934f4f4d166Sitojun * (7.2.6 paragraph 4), however, it also says that we 935f4f4d166Sitojun * SHOULD provide a mechanism to prevent multicast NA storm. 936f4f4d166Sitojun * we don't have anything like it right now. 937841d7adbSitojun * note that the mechanism needs a mutual agreement 938f4f4d166Sitojun * between proxies, which means that we need to implement 939841d7adbSitojun * a new protocol, or a new kludge. 940841d7adbSitojun * - from RFC2461 6.2.4, host MUST NOT send an unsolicited NA. 941f4f4d166Sitojun * we need to check ip6forwarding before sending it. 942f4f4d166Sitojun * (or should we allow proxy ND configuration only for 943f4f4d166Sitojun * routers? there's no mention about proxy ND from hosts) 944f4f4d166Sitojun */ 945f4f4d166Sitojun #if 0 946f4f4d166Sitojun /* XXX it does not work */ 947287546eaSitojun if (rt->rt_flags & RTF_ANNOUNCE) 948287546eaSitojun nd6_na_output(ifp, 949c3c56496Sbluhm &satosin6(rt_key(rt))->sin6_addr, 950c3c56496Sbluhm &satosin6(rt_key(rt))->sin6_addr, 951287546eaSitojun ip6_forwarding ? ND_NA_FLAG_ROUTER : 0, 952f4f4d166Sitojun 1, NULL); 953f4f4d166Sitojun #endif 954287546eaSitojun /* FALLTHROUGH */ 955287546eaSitojun case RTM_RESOLVE: 956287546eaSitojun if (gate->sa_family != AF_LINK || 957cfc71c8bSclaudio gate->sa_len < sizeof(struct sockaddr_dl)) { 95886b61919Smpi log(LOG_DEBUG, "%s: bad gateway value: %s\n", 95986b61919Smpi __func__, ifp->if_xname); 960287546eaSitojun break; 961287546eaSitojun } 962c7b7b779Sbluhm satosdl(gate)->sdl_type = ifp->if_type; 963c7b7b779Sbluhm satosdl(gate)->sdl_index = ifp->if_index; 964d374aaacSitojun if (ln != NULL) 965287546eaSitojun break; /* This happens on a route change */ 966287546eaSitojun /* 967287546eaSitojun * Case 2: This route may come from cloning, or a manual route 968287546eaSitojun * add with a LL address. 969287546eaSitojun */ 97093fbd125Sbluhm ln = pool_get(&nd6_pool, PR_NOWAIT | PR_ZERO); 971287546eaSitojun rt->rt_llinfo = (caddr_t)ln; 9729c0643cdSbluhm if (ln == NULL) { 97393fbd125Sbluhm log(LOG_DEBUG, "%s: pool get failed\n", __func__); 974287546eaSitojun break; 975287546eaSitojun } 976287546eaSitojun nd6_inuse++; 977287546eaSitojun nd6_allocated++; 978287546eaSitojun ln->ln_rt = rt; 9799631a17bSitojun timeout_set(&ln->ln_timer_ch, nd6_llinfo_timer, ln); 980287546eaSitojun /* this is required for "ndp" command. - shin */ 981287546eaSitojun if (req == RTM_ADD) { 982287546eaSitojun /* 983287546eaSitojun * gate should have some valid AF_LINK entry, 984287546eaSitojun * and ln->ln_expire should have some lifetime 985287546eaSitojun * which is specified by ndp command. 986287546eaSitojun */ 987287546eaSitojun ln->ln_state = ND6_LLINFO_REACHABLE; 988f6e55599Sitojun ln->ln_byhint = 0; 989287546eaSitojun } else { 990287546eaSitojun /* 991287546eaSitojun * When req == RTM_RESOLVE, rt is created and 992287546eaSitojun * initialized in rtrequest(), so rt_expire is 0. 993287546eaSitojun */ 994287546eaSitojun ln->ln_state = ND6_LLINFO_NOSTATE; 9959631a17bSitojun nd6_llinfo_settimer(ln, 0); 996287546eaSitojun } 997287546eaSitojun rt->rt_flags |= RTF_LLINFO; 99892c821ddSbluhm TAILQ_INSERT_HEAD(&nd6_list, ln, ln_list); 999287546eaSitojun 1000287546eaSitojun /* 1001f3fcf2f3Smcbride * If we have too many cache entries, initiate immediate 1002f3fcf2f3Smcbride * purging for some "less recently used" entries. Note that 1003f3fcf2f3Smcbride * we cannot directly call nd6_free() here because it would 1004f3fcf2f3Smcbride * cause re-entering rtable related routines triggering an LOR 1005f3fcf2f3Smcbride * problem for FreeBSD. 1006f3fcf2f3Smcbride */ 1007f3fcf2f3Smcbride if (ip6_neighborgcthresh >= 0 && 1008f3fcf2f3Smcbride nd6_inuse >= ip6_neighborgcthresh) { 1009f3fcf2f3Smcbride int i; 1010f3fcf2f3Smcbride 101192c821ddSbluhm for (i = 0; i < 10; i++) { 101292c821ddSbluhm struct llinfo_nd6 *ln_end; 101392c821ddSbluhm 101492c821ddSbluhm ln_end = TAILQ_LAST(&nd6_list, llinfo_nd6_head); 101592c821ddSbluhm if (ln_end == ln) 101692c821ddSbluhm break; 1017f3fcf2f3Smcbride 1018f3fcf2f3Smcbride /* Move this entry to the head */ 101992c821ddSbluhm TAILQ_REMOVE(&nd6_list, ln_end, ln_list); 102092c821ddSbluhm TAILQ_INSERT_HEAD(&nd6_list, ln_end, ln_list); 1021f3fcf2f3Smcbride 1022f3fcf2f3Smcbride if (ND6_LLINFO_PERMANENT(ln_end)) 1023f3fcf2f3Smcbride continue; 1024f3fcf2f3Smcbride 1025f3fcf2f3Smcbride if (ln_end->ln_state > ND6_LLINFO_INCOMPLETE) 1026f3fcf2f3Smcbride ln_end->ln_state = ND6_LLINFO_STALE; 1027f3fcf2f3Smcbride else 1028f3fcf2f3Smcbride ln_end->ln_state = ND6_LLINFO_PURGE; 1029f3fcf2f3Smcbride nd6_llinfo_settimer(ln_end, 0); 1030f3fcf2f3Smcbride } 1031f3fcf2f3Smcbride } 1032f3fcf2f3Smcbride 1033f3fcf2f3Smcbride /* 1034287546eaSitojun * check if rt_key(rt) is one of my address assigned 1035287546eaSitojun * to the interface. 1036287546eaSitojun */ 103786b61919Smpi ifa = &in6ifa_ifpwithaddr(ifp, 1038c3c56496Sbluhm &satosin6(rt_key(rt))->sin6_addr)->ia_ifa; 1039287546eaSitojun if (ifa) { 10409631a17bSitojun nd6_llinfo_settimer(ln, -1); 1041287546eaSitojun ln->ln_state = ND6_LLINFO_REACHABLE; 1042f6e55599Sitojun ln->ln_byhint = 0; 1043fac399ceSmpi KASSERT(ifa == rt->rt_ifa); 1044f4f4d166Sitojun } else if (rt->rt_flags & RTF_ANNOUNCE) { 10459631a17bSitojun nd6_llinfo_settimer(ln, -1); 1046f4f4d166Sitojun ln->ln_state = ND6_LLINFO_REACHABLE; 1047f6e55599Sitojun ln->ln_byhint = 0; 1048f4f4d166Sitojun 1049f4f4d166Sitojun /* join solicited node multicast for proxy ND */ 1050f4f4d166Sitojun if (ifp->if_flags & IFF_MULTICAST) { 1051f4f4d166Sitojun struct in6_addr llsol; 1052f4f4d166Sitojun int error; 1053f4f4d166Sitojun 1054c3c56496Sbluhm llsol = satosin6(rt_key(rt))->sin6_addr; 1055f4f4d166Sitojun llsol.s6_addr16[0] = htons(0xff02); 1056f4f4d166Sitojun llsol.s6_addr16[1] = htons(ifp->if_index); 1057f4f4d166Sitojun llsol.s6_addr32[1] = 0; 1058f4f4d166Sitojun llsol.s6_addr32[2] = htonl(1); 1059f4f4d166Sitojun llsol.s6_addr8[12] = 0xff; 1060f4f4d166Sitojun 1061d8a7e3a7Sitojun if (in6_addmulti(&llsol, ifp, &error)) { 1062bbcf0337Smpi char addr[INET6_ADDRSTRLEN]; 1063d8a7e3a7Sitojun nd6log((LOG_ERR, "%s: failed to join " 1064d8a7e3a7Sitojun "%s (errno=%d)\n", ifp->if_xname, 1065bbcf0337Smpi inet_ntop(AF_INET6, &llsol, 1066bbcf0337Smpi addr, sizeof(addr)), 1067bbcf0337Smpi error)); 1068d8a7e3a7Sitojun } 1069f4f4d166Sitojun } 1070287546eaSitojun } 1071287546eaSitojun break; 1072287546eaSitojun 1073287546eaSitojun case RTM_DELETE: 10749c0643cdSbluhm if (ln == NULL) 1075287546eaSitojun break; 1076f4f4d166Sitojun /* leave from solicited node multicast for proxy ND */ 1077f4f4d166Sitojun if ((rt->rt_flags & RTF_ANNOUNCE) != 0 && 1078f4f4d166Sitojun (ifp->if_flags & IFF_MULTICAST) != 0) { 1079f4f4d166Sitojun struct in6_addr llsol; 1080f4f4d166Sitojun struct in6_multi *in6m; 1081f4f4d166Sitojun 1082c3c56496Sbluhm llsol = satosin6(rt_key(rt))->sin6_addr; 1083f4f4d166Sitojun llsol.s6_addr16[0] = htons(0xff02); 1084f4f4d166Sitojun llsol.s6_addr16[1] = htons(ifp->if_index); 1085f4f4d166Sitojun llsol.s6_addr32[1] = 0; 1086f4f4d166Sitojun llsol.s6_addr32[2] = htonl(1); 1087f4f4d166Sitojun llsol.s6_addr8[12] = 0xff; 1088f4f4d166Sitojun 1089f4f4d166Sitojun IN6_LOOKUP_MULTI(llsol, ifp, in6m); 1090f4f4d166Sitojun if (in6m) 1091f4f4d166Sitojun in6_delmulti(in6m); 1092f4f4d166Sitojun } 1093287546eaSitojun nd6_inuse--; 109492c821ddSbluhm TAILQ_REMOVE(&nd6_list, ln, ln_list); 10959631a17bSitojun nd6_llinfo_settimer(ln, -1); 10969c0643cdSbluhm rt->rt_llinfo = NULL; 1097287546eaSitojun rt->rt_flags &= ~RTF_LLINFO; 1098287546eaSitojun m_freem(ln->ln_hold); 109993fbd125Sbluhm pool_put(&nd6_pool, ln); 1100287546eaSitojun } 1101287546eaSitojun } 1102287546eaSitojun 1103287546eaSitojun int 1104ee37ea65Smcbride nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp) 1105287546eaSitojun { 1106287546eaSitojun struct in6_ndireq *ndi = (struct in6_ndireq *)data; 1107287546eaSitojun struct in6_nbrinfo *nbi = (struct in6_nbrinfo *)data; 1108287546eaSitojun struct rtentry *rt; 1109ee3c93eeSbluhm int error = 0; 1110287546eaSitojun int s; 1111287546eaSitojun 1112287546eaSitojun switch (cmd) { 1113d6b9e9b9Sitojun case SIOCGIFINFO_IN6: 1114d6b9e9b9Sitojun ndi->ndi = *ND_IFINFO(ifp); 111502dac871Sderaadt memset(&ndi->ndi.randomseed0, 0, sizeof ndi->ndi.randomseed0); 111602dac871Sderaadt memset(&ndi->ndi.randomseed1, 0, sizeof ndi->ndi.randomseed1); 111702dac871Sderaadt memset(&ndi->ndi.randomid, 0, sizeof ndi->ndi.randomid); 1118287546eaSitojun break; 1119d374aaacSitojun case SIOCSIFINFO_FLAGS: 1120d6b9e9b9Sitojun ND_IFINFO(ifp)->flags = ndi->ndi.flags; 1121d374aaacSitojun break; 1122f4f4d166Sitojun case SIOCSNDFLUSH_IN6: /* XXX: the ioctl name is confusing... */ 1123d8a7e3a7Sitojun /* sync kernel routing table with the default router list */ 1124d8a7e3a7Sitojun defrouter_reset(); 1125f4f4d166Sitojun defrouter_select(); 1126287546eaSitojun break; 1127287546eaSitojun case SIOCSPFXFLUSH_IN6: 1128287546eaSitojun { 1129287546eaSitojun /* flush all the prefix advertised by routers */ 1130119a433cSbluhm struct nd_prefix *pr, *npr; 1131287546eaSitojun 11326e92dee6Sitojun s = splsoftnet(); 1133db657d52Sbluhm /* First purge the addresses referenced by a prefix. */ 1134119a433cSbluhm LIST_FOREACH_SAFE(pr, &nd_prefix, ndpr_entry, npr) { 1135b9e83c60Sbluhm struct in6_ifaddr *ia6, *ia6_next; 1136d8a7e3a7Sitojun 1137d8a7e3a7Sitojun if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) 1138d8a7e3a7Sitojun continue; /* XXX */ 1139d8a7e3a7Sitojun 1140d8a7e3a7Sitojun /* do we really have to remove addresses as well? */ 1141b9e83c60Sbluhm TAILQ_FOREACH_SAFE(ia6, &in6_ifaddr, ia_list, ia6_next) { 1142b9e83c60Sbluhm if ((ia6->ia6_flags & IN6_IFF_AUTOCONF) == 0) 1143d8a7e3a7Sitojun continue; 1144d8a7e3a7Sitojun 1145b9e83c60Sbluhm if (ia6->ia6_ndpr == pr) 1146b9e83c60Sbluhm in6_purgeaddr(&ia6->ia_ifa); 1147d8a7e3a7Sitojun } 1148db657d52Sbluhm } 1149db657d52Sbluhm /* 1150db657d52Sbluhm * Purging the addresses might remove the prefix as well. 1151db657d52Sbluhm * So run the loop again to access only prefixes that have 1152db657d52Sbluhm * not been freed already. 1153db657d52Sbluhm */ 1154119a433cSbluhm LIST_FOREACH_SAFE(pr, &nd_prefix, ndpr_entry, npr) { 1155db657d52Sbluhm if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) 1156db657d52Sbluhm continue; /* XXX */ 1157db657d52Sbluhm 1158287546eaSitojun prelist_remove(pr); 1159287546eaSitojun } 1160287546eaSitojun splx(s); 1161287546eaSitojun break; 1162287546eaSitojun } 1163287546eaSitojun case SIOCSRTRFLUSH_IN6: 1164287546eaSitojun { 1165287546eaSitojun /* flush all the default routers */ 1166568012e6Sbluhm struct nd_defrouter *dr, *ndr; 1167287546eaSitojun 11686e92dee6Sitojun s = splsoftnet(); 1169d8a7e3a7Sitojun defrouter_reset(); 1170568012e6Sbluhm TAILQ_FOREACH_SAFE(dr, &nd_defrouter, dr_entry, ndr) 1171287546eaSitojun defrtrlist_del(dr); 1172d8a7e3a7Sitojun defrouter_select(); 1173287546eaSitojun splx(s); 1174287546eaSitojun break; 1175287546eaSitojun } 1176287546eaSitojun case SIOCGNBRINFO_IN6: 1177287546eaSitojun { 1178287546eaSitojun struct llinfo_nd6 *ln; 1179287546eaSitojun struct in6_addr nb_addr = nbi->addr; /* make local for safety */ 1180f303499dSdlg time_t expire; 1181287546eaSitojun 1182287546eaSitojun /* 1183287546eaSitojun * XXX: KAME specific hack for scoped addresses 1184287546eaSitojun * XXXX: for other scopes than link-local? 1185287546eaSitojun */ 1186287546eaSitojun if (IN6_IS_ADDR_LINKLOCAL(&nbi->addr) || 1187287546eaSitojun IN6_IS_ADDR_MC_LINKLOCAL(&nbi->addr)) { 1188287546eaSitojun u_int16_t *idp = (u_int16_t *)&nb_addr.s6_addr[2]; 1189287546eaSitojun 1190287546eaSitojun if (*idp == 0) 1191287546eaSitojun *idp = htons(ifp->if_index); 1192287546eaSitojun } 1193287546eaSitojun 11946e92dee6Sitojun s = splsoftnet(); 119516119bbfSmpi rt = nd6_lookup(&nb_addr, 0, ifp, ifp->if_rdomain); 119616119bbfSmpi if (rt == NULL || 1197d8a7e3a7Sitojun (ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) { 1198287546eaSitojun error = EINVAL; 119916119bbfSmpi rtfree(rt); 120091e69634Sitojun splx(s); 1201287546eaSitojun break; 1202287546eaSitojun } 1203f303499dSdlg expire = ln->ln_expire; 1204f303499dSdlg if (expire != 0) { 1205f303499dSdlg expire -= time_uptime; 1206f303499dSdlg expire += time_second; 1207f303499dSdlg } 1208f303499dSdlg 1209287546eaSitojun nbi->state = ln->ln_state; 1210287546eaSitojun nbi->asked = ln->ln_asked; 1211287546eaSitojun nbi->isrouter = ln->ln_router; 1212f303499dSdlg nbi->expire = expire; 121316119bbfSmpi rtfree(rt); 1214287546eaSitojun splx(s); 1215287546eaSitojun 1216287546eaSitojun break; 1217287546eaSitojun } 1218287546eaSitojun } 1219287546eaSitojun return (error); 1220287546eaSitojun } 1221287546eaSitojun 1222287546eaSitojun /* 1223287546eaSitojun * Create neighbor cache entry and cache link-layer address, 1224287546eaSitojun * on reception of inbound ND6 packets. (RS/RA/NS/redirect) 1225ee37ea65Smcbride * 1226ee37ea65Smcbride * type - ICMP6 type 1227ee37ea65Smcbride * code - type dependent information 1228287546eaSitojun */ 1229db435b2aSmpi void 1230ee37ea65Smcbride nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr, 1231ee37ea65Smcbride int lladdrlen, int type, int code) 1232287546eaSitojun { 1233287546eaSitojun struct rtentry *rt = NULL; 1234287546eaSitojun struct llinfo_nd6 *ln = NULL; 1235287546eaSitojun int is_newentry; 1236287546eaSitojun struct sockaddr_dl *sdl = NULL; 1237287546eaSitojun int do_update; 1238287546eaSitojun int olladdr; 1239287546eaSitojun int llchange; 1240287546eaSitojun int newstate = 0; 1241287546eaSitojun 1242287546eaSitojun if (!ifp) 1243287546eaSitojun panic("ifp == NULL in nd6_cache_lladdr"); 1244287546eaSitojun if (!from) 1245287546eaSitojun panic("from == NULL in nd6_cache_lladdr"); 1246287546eaSitojun 1247287546eaSitojun /* nothing must be updated for unspecified address */ 1248287546eaSitojun if (IN6_IS_ADDR_UNSPECIFIED(from)) 1249db435b2aSmpi return; 1250287546eaSitojun 1251287546eaSitojun /* 1252287546eaSitojun * Validation about ifp->if_addrlen and lladdrlen must be done in 1253287546eaSitojun * the caller. 1254287546eaSitojun * 12558b542bbeSpascoe * XXX If the link does not have link-layer address, what should 1256287546eaSitojun * we do? (ifp->if_addrlen == 0) 1257287546eaSitojun * Spec says nothing in sections for RA, RS and NA. There's small 1258287546eaSitojun * description on it in NS section (RFC 2461 7.2.3). 1259287546eaSitojun */ 1260287546eaSitojun 1261f4d1af37Smikeb rt = nd6_lookup(from, 0, ifp, ifp->if_rdomain); 126216119bbfSmpi if (rt == NULL) { 1263287546eaSitojun #if 0 1264287546eaSitojun /* nothing must be done if there's no lladdr */ 1265287546eaSitojun if (!lladdr || !lladdrlen) 1266287546eaSitojun return NULL; 1267287546eaSitojun #endif 1268287546eaSitojun 12695148b194Smpi rt = nd6_lookup(from, 1, ifp, ifp->if_rdomain); 1270287546eaSitojun is_newentry = 1; 12710a2c5741Sitojun } else { 12720a2c5741Sitojun /* do nothing if static ndp is set */ 127316119bbfSmpi if (rt->rt_flags & RTF_STATIC) { 127416119bbfSmpi rtfree(rt); 1275db435b2aSmpi return; 127616119bbfSmpi } 1277287546eaSitojun is_newentry = 0; 12780a2c5741Sitojun } 1279287546eaSitojun 1280287546eaSitojun if (!rt) 1281db435b2aSmpi return; 1282287546eaSitojun if ((rt->rt_flags & (RTF_GATEWAY | RTF_LLINFO)) != RTF_LLINFO) { 1283287546eaSitojun fail: 1284d8a7e3a7Sitojun (void)nd6_free(rt, 0); 128516119bbfSmpi rtfree(rt); 1286db435b2aSmpi return; 1287287546eaSitojun } 1288287546eaSitojun ln = (struct llinfo_nd6 *)rt->rt_llinfo; 12899c0643cdSbluhm if (ln == NULL) 1290287546eaSitojun goto fail; 12919c0643cdSbluhm if (rt->rt_gateway == NULL) 1292287546eaSitojun goto fail; 1293287546eaSitojun if (rt->rt_gateway->sa_family != AF_LINK) 1294287546eaSitojun goto fail; 1295c7b7b779Sbluhm sdl = satosdl(rt->rt_gateway); 1296287546eaSitojun 1297287546eaSitojun olladdr = (sdl->sdl_alen) ? 1 : 0; 1298287546eaSitojun if (olladdr && lladdr) { 1299287546eaSitojun if (bcmp(lladdr, LLADDR(sdl), ifp->if_addrlen)) 1300287546eaSitojun llchange = 1; 1301287546eaSitojun else 1302287546eaSitojun llchange = 0; 1303287546eaSitojun } else 1304287546eaSitojun llchange = 0; 1305287546eaSitojun 1306287546eaSitojun /* 1307287546eaSitojun * newentry olladdr lladdr llchange (*=record) 1308287546eaSitojun * 0 n n -- (1) 1309287546eaSitojun * 0 y n -- (2) 1310287546eaSitojun * 0 n y -- (3) * STALE 1311287546eaSitojun * 0 y y n (4) * 1312287546eaSitojun * 0 y y y (5) * STALE 1313287546eaSitojun * 1 -- n -- (6) NOSTATE(= PASSIVE) 1314287546eaSitojun * 1 -- y -- (7) * STALE 1315287546eaSitojun */ 1316287546eaSitojun 1317c8a7c9e3Sbluhm if (llchange) { 1318bbcf0337Smpi char addr[INET6_ADDRSTRLEN]; 1319c8a7c9e3Sbluhm log(LOG_INFO, "ndp info overwritten for %s by %s on %s\n", 1320bbcf0337Smpi inet_ntop(AF_INET6, from, addr, sizeof(addr)), 1321bbcf0337Smpi ether_sprintf(lladdr), ifp->if_xname); 1322c8a7c9e3Sbluhm } 1323287546eaSitojun if (lladdr) { /* (3-5) and (7) */ 1324287546eaSitojun /* 1325287546eaSitojun * Record source link-layer address 1326287546eaSitojun * XXX is it dependent to ifp->if_type? 1327287546eaSitojun */ 1328287546eaSitojun sdl->sdl_alen = ifp->if_addrlen; 1329287546eaSitojun bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen); 1330287546eaSitojun } 1331287546eaSitojun 1332287546eaSitojun if (!is_newentry) { 1333d8a7e3a7Sitojun if ((!olladdr && lladdr) || /* (3) */ 1334d8a7e3a7Sitojun (olladdr && lladdr && llchange)) { /* (5) */ 1335287546eaSitojun do_update = 1; 1336287546eaSitojun newstate = ND6_LLINFO_STALE; 1337287546eaSitojun } else /* (1-2,4) */ 1338287546eaSitojun do_update = 0; 1339287546eaSitojun } else { 1340287546eaSitojun do_update = 1; 1341287546eaSitojun if (!lladdr) /* (6) */ 1342287546eaSitojun newstate = ND6_LLINFO_NOSTATE; 1343287546eaSitojun else /* (7) */ 1344287546eaSitojun newstate = ND6_LLINFO_STALE; 1345287546eaSitojun } 1346287546eaSitojun 1347287546eaSitojun if (do_update) { 1348287546eaSitojun /* 1349287546eaSitojun * Update the state of the neighbor cache. 1350287546eaSitojun */ 1351287546eaSitojun ln->ln_state = newstate; 1352287546eaSitojun 1353287546eaSitojun if (ln->ln_state == ND6_LLINFO_STALE) { 13548a7bb304Sitojun /* 1355*53b39a0bSmpi * Since nd6_resolve() in ifp->if_output() will cause 13568b542bbeSpascoe * state transition to DELAY and reset the timer, 13578a7bb304Sitojun * we must set the timer now, although it is actually 13588a7bb304Sitojun * meaningless. 13598a7bb304Sitojun */ 1360134b71fcSdlg nd6_llinfo_settimer(ln, nd6_gctimer); 13618a7bb304Sitojun 1362287546eaSitojun if (ln->ln_hold) { 1363e212adedSkrw struct mbuf *n = ln->ln_hold; 1364e212adedSkrw ln->ln_hold = NULL; 13656afad192Sitojun /* 13666afad192Sitojun * we assume ifp is not a p2p here, so just 13676afad192Sitojun * set the 2nd argument as the 1st one. 13686afad192Sitojun */ 1369*53b39a0bSmpi ifp->if_output(ifp, n, rt_key(rt), rt); 1370e212adedSkrw if (ln->ln_hold == n) { 1371e212adedSkrw /* n is back in ln_hold. Discard. */ 1372e212adedSkrw m_freem(ln->ln_hold); 13738a7bb304Sitojun ln->ln_hold = NULL; 1374287546eaSitojun } 1375e212adedSkrw } 1376287546eaSitojun } else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { 1377287546eaSitojun /* probe right away */ 13789631a17bSitojun nd6_llinfo_settimer((void *)ln, 0); 1379287546eaSitojun } 1380287546eaSitojun } 1381287546eaSitojun 1382287546eaSitojun /* 1383287546eaSitojun * ICMP6 type dependent behavior. 1384287546eaSitojun * 1385287546eaSitojun * NS: clear IsRouter if new entry 1386287546eaSitojun * RS: clear IsRouter 1387287546eaSitojun * RA: set IsRouter if there's lladdr 1388287546eaSitojun * redir: clear IsRouter if new entry 1389287546eaSitojun * 1390287546eaSitojun * RA case, (1): 1391287546eaSitojun * The spec says that we must set IsRouter in the following cases: 1392287546eaSitojun * - If lladdr exist, set IsRouter. This means (1-5). 1393287546eaSitojun * - If it is old entry (!newentry), set IsRouter. This means (7). 1394287546eaSitojun * So, based on the spec, in (1-5) and (7) cases we must set IsRouter. 13958b542bbeSpascoe * A question arises for (1) case. (1) case has no lladdr in the 1396287546eaSitojun * neighbor cache, this is similar to (6). 1397287546eaSitojun * This case is rare but we figured that we MUST NOT set IsRouter. 1398287546eaSitojun * 1399287546eaSitojun * newentry olladdr lladdr llchange NS RS RA redir 1400287546eaSitojun * D R 1401287546eaSitojun * 0 n n -- (1) c ? s 1402287546eaSitojun * 0 y n -- (2) c s s 1403287546eaSitojun * 0 n y -- (3) c s s 1404287546eaSitojun * 0 y y n (4) c s s 1405287546eaSitojun * 0 y y y (5) c s s 1406287546eaSitojun * 1 -- n -- (6) c c c s 1407287546eaSitojun * 1 -- y -- (7) c c s c s 1408287546eaSitojun * 1409287546eaSitojun * (c=clear s=set) 1410287546eaSitojun */ 1411287546eaSitojun switch (type & 0xff) { 1412287546eaSitojun case ND_NEIGHBOR_SOLICIT: 1413287546eaSitojun /* 1414287546eaSitojun * New entry must have is_router flag cleared. 1415287546eaSitojun */ 1416287546eaSitojun if (is_newentry) /* (6-7) */ 1417287546eaSitojun ln->ln_router = 0; 1418287546eaSitojun break; 1419287546eaSitojun case ND_REDIRECT: 1420287546eaSitojun /* 1421287546eaSitojun * If the icmp is a redirect to a better router, always set the 1422287546eaSitojun * is_router flag. Otherwise, if the entry is newly created, 1423287546eaSitojun * clear the flag. [RFC 2461, sec 8.3] 1424287546eaSitojun */ 1425287546eaSitojun if (code == ND_REDIRECT_ROUTER) 1426287546eaSitojun ln->ln_router = 1; 1427287546eaSitojun else if (is_newentry) /* (6-7) */ 1428287546eaSitojun ln->ln_router = 0; 1429287546eaSitojun break; 1430287546eaSitojun case ND_ROUTER_SOLICIT: 1431287546eaSitojun /* 1432287546eaSitojun * is_router flag must always be cleared. 1433287546eaSitojun */ 1434287546eaSitojun ln->ln_router = 0; 1435287546eaSitojun break; 1436287546eaSitojun case ND_ROUTER_ADVERT: 1437287546eaSitojun /* 1438287546eaSitojun * Mark an entry with lladdr as a router. 1439287546eaSitojun */ 1440d8a7e3a7Sitojun if ((!is_newentry && (olladdr || lladdr)) || /* (2-5) */ 1441d8a7e3a7Sitojun (is_newentry && lladdr)) { /* (7) */ 1442287546eaSitojun ln->ln_router = 1; 1443287546eaSitojun } 1444287546eaSitojun break; 1445287546eaSitojun } 1446287546eaSitojun 144750ccc024Sitojun /* 144850ccc024Sitojun * When the link-layer address of a router changes, select the 144950ccc024Sitojun * best router again. In particular, when the neighbor entry is newly 145050ccc024Sitojun * created, it might affect the selection policy. 145150ccc024Sitojun * Question: can we restrict the first condition to the "is_newentry" 145250ccc024Sitojun * case? 145350ccc024Sitojun * XXX: when we hear an RA from a new router with the link-layer 145450ccc024Sitojun * address option, defrouter_select() is called twice, since 145550ccc024Sitojun * defrtrlist_update called the function as well. However, I believe 145650ccc024Sitojun * we can compromise the overhead, since it only happens the first 145750ccc024Sitojun * time. 145850ccc024Sitojun */ 1459d3b1c214Sflorian if (do_update && ln->ln_router && (ifp->if_xflags & IFXF_AUTOCONF6)) 146050ccc024Sitojun defrouter_select(); 146116119bbfSmpi 146216119bbfSmpi rtfree(rt); 1463287546eaSitojun } 1464287546eaSitojun 1465a0aa363cSjsing void 1466ee37ea65Smcbride nd6_slowtimo(void *ignored_arg) 1467287546eaSitojun { 14686e92dee6Sitojun int s = splsoftnet(); 1469b3c1e4c1Sitojun struct nd_ifinfo *nd6if; 1470d6b9e9b9Sitojun struct ifnet *ifp; 1471287546eaSitojun 1472b3c1e4c1Sitojun timeout_set(&nd6_slowtimo_ch, nd6_slowtimo, NULL); 147329e86e5eSblambert timeout_add_sec(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL); 1474d814b14cSbluhm TAILQ_FOREACH(ifp, &ifnet, if_list) { 1475d6b9e9b9Sitojun nd6if = ND_IFINFO(ifp); 1476287546eaSitojun if (nd6if->basereachable && /* already initialized */ 1477287546eaSitojun (nd6if->recalctm -= ND6_SLOWTIMER_INTERVAL) <= 0) { 1478287546eaSitojun /* 1479287546eaSitojun * Since reachable time rarely changes by router 1480287546eaSitojun * advertisements, we SHOULD insure that a new random 1481287546eaSitojun * value gets recomputed at least once every few hours. 1482287546eaSitojun * (RFC 2461, 6.3.4) 1483287546eaSitojun */ 1484287546eaSitojun nd6if->recalctm = nd6_recalc_reachtm_interval; 1485287546eaSitojun nd6if->reachable = ND_COMPUTE_RTIME(nd6if->basereachable); 1486287546eaSitojun } 1487287546eaSitojun } 1488287546eaSitojun splx(s); 1489287546eaSitojun } 1490287546eaSitojun 1491287546eaSitojun int 14925d32b3a0Smpi nd6_resolve(struct ifnet *ifp, struct rtentry *rt0, struct mbuf *m, 14935d32b3a0Smpi struct sockaddr *dst, u_char *desten) 14945d32b3a0Smpi { 14955d32b3a0Smpi struct sockaddr_dl *sdl; 14965d32b3a0Smpi struct rtentry *rt; 1497287546eaSitojun struct llinfo_nd6 *ln = NULL; 14985d32b3a0Smpi int error; 1499287546eaSitojun 15005d32b3a0Smpi if (m->m_flags & M_MCAST) { 15015d32b3a0Smpi ETHER_MAP_IPV6_MULTICAST(&satosin6(dst)->sin6_addr, desten); 15025d32b3a0Smpi return (0); 15035d32b3a0Smpi } 15045b3a2895Ssthen 1505a9ab5221Smpi error = rt_checkgate(rt0, &rt); 15068c023157Smpi if (error) { 15078c023157Smpi m_freem(m); 15088c023157Smpi return (error); 1509287546eaSitojun } 1510cfb6b8dfSitojun 1511287546eaSitojun /* 1512287546eaSitojun * Address resolution or Neighbor Unreachability Detection 1513287546eaSitojun * for the next hop. 1514287546eaSitojun * At this point, the destination of the packet must be a unicast 1515287546eaSitojun * or an anycast address(i.e. not a multicast). 1516287546eaSitojun */ 15175da3fb44Smpi if (!ISSET(rt->rt_flags, RTF_LLINFO)) { 1518bbcf0337Smpi char addr[INET6_ADDRSTRLEN]; 15195da3fb44Smpi log(LOG_DEBUG, "%s: %s: route contains no ND information\n", 15205da3fb44Smpi __func__, inet_ntop(AF_INET6, 15215da3fb44Smpi &satosin6(rt_key(rt))->sin6_addr, addr, sizeof(addr))); 152216119bbfSmpi m_freem(m); 15235da3fb44Smpi return (EINVAL); 1524287546eaSitojun } 1525287546eaSitojun 15265d32b3a0Smpi if (rt->rt_gateway->sa_family != AF_LINK) { 15275d32b3a0Smpi printf("%s: something odd happens\n", __func__); 15285d32b3a0Smpi m_freem(m); 15295d32b3a0Smpi return (EINVAL); 15305d32b3a0Smpi } 15315d32b3a0Smpi 15325da3fb44Smpi ln = (struct llinfo_nd6 *)rt->rt_llinfo; 15335da3fb44Smpi KASSERT(ln != NULL); 1534cfb6b8dfSitojun 1535f3fcf2f3Smcbride /* 1536f3fcf2f3Smcbride * Move this entry to the head of the queue so that it is less likely 1537f3fcf2f3Smcbride * for this entry to be a target of forced garbage collection (see 1538f3fcf2f3Smcbride * nd6_rtrequest()). 1539f3fcf2f3Smcbride */ 154092c821ddSbluhm TAILQ_REMOVE(&nd6_list, ln, ln_list); 154192c821ddSbluhm TAILQ_INSERT_HEAD(&nd6_list, ln, ln_list); 1542f3fcf2f3Smcbride 1543287546eaSitojun /* 1544287546eaSitojun * The first time we send a packet to a neighbor whose entry is 1545287546eaSitojun * STALE, we have to change the state to DELAY and a sets a timer to 1546287546eaSitojun * expire in DELAY_FIRST_PROBE_TIME seconds to ensure do 1547287546eaSitojun * neighbor unreachability detection on expiration. 1548287546eaSitojun * (RFC 2461 7.3.3) 1549287546eaSitojun */ 1550287546eaSitojun if (ln->ln_state == ND6_LLINFO_STALE) { 1551287546eaSitojun ln->ln_asked = 0; 1552287546eaSitojun ln->ln_state = ND6_LLINFO_DELAY; 1553134b71fcSdlg nd6_llinfo_settimer(ln, nd6_delay); 1554287546eaSitojun } 1555287546eaSitojun 1556287546eaSitojun /* 1557287546eaSitojun * If the neighbor cache entry has a state other than INCOMPLETE 155834deef1eSitojun * (i.e. its link-layer address is already resolved), just 1559287546eaSitojun * send the packet. 1560287546eaSitojun */ 15615d32b3a0Smpi if (ln->ln_state > ND6_LLINFO_INCOMPLETE) { 15625d32b3a0Smpi sdl = satosdl(rt->rt_gateway); 15635d32b3a0Smpi if (sdl->sdl_alen != ETHER_ADDR_LEN) { 15645d32b3a0Smpi char addr[INET6_ADDRSTRLEN]; 15655d32b3a0Smpi log(LOG_DEBUG, "%s: %s: incorrect nd6 information\n", 15665d32b3a0Smpi __func__, 15675d32b3a0Smpi inet_ntop(AF_INET6, &satosin6(dst)->sin6_addr, 15685d32b3a0Smpi addr, sizeof(addr))); 15695d32b3a0Smpi m_freem(m); 15705d32b3a0Smpi return (EINVAL); 15715d32b3a0Smpi } 15725d32b3a0Smpi 15735d32b3a0Smpi bcopy(LLADDR(sdl), desten, sdl->sdl_alen); 15745d32b3a0Smpi return (0); 15755d32b3a0Smpi } 1576287546eaSitojun 1577287546eaSitojun /* 1578287546eaSitojun * There is a neighbor cache entry, but no ethernet address 1579287546eaSitojun * response yet. Replace the held mbuf (if any) with this 1580287546eaSitojun * latest one. 1581287546eaSitojun */ 1582efcf292bSitojun if (ln->ln_state == ND6_LLINFO_NOSTATE) 1583287546eaSitojun ln->ln_state = ND6_LLINFO_INCOMPLETE; 1584287546eaSitojun m_freem(ln->ln_hold); 1585287546eaSitojun ln->ln_hold = m; 15865d32b3a0Smpi 158776843262Sitojun /* 158876843262Sitojun * If there has been no NS for the neighbor after entering the 158976843262Sitojun * INCOMPLETE state, send the first solicitation. 159076843262Sitojun */ 15919631a17bSitojun if (!ND6_LLINFO_PERMANENT(ln) && ln->ln_asked == 0) { 1592287546eaSitojun ln->ln_asked++; 1593134b71fcSdlg nd6_llinfo_settimer(ln, ND_IFINFO(ifp)->retrans / 1000); 15945d32b3a0Smpi nd6_ns_output(ifp, NULL, &satosin6(dst)->sin6_addr, ln, 0); 1595287546eaSitojun } 15965d32b3a0Smpi return (EAGAIN); 1597287546eaSitojun } 1598287546eaSitojun 1599287546eaSitojun int 1600ee37ea65Smcbride nd6_need_cache(struct ifnet *ifp) 1601d8a7e3a7Sitojun { 1602d8a7e3a7Sitojun /* 1603d8a7e3a7Sitojun * RFC2893 says: 1604d8a7e3a7Sitojun * - unidirectional tunnels needs no ND 1605d8a7e3a7Sitojun */ 1606d8a7e3a7Sitojun switch (ifp->if_type) { 1607d8a7e3a7Sitojun case IFT_ETHER: 1608d8a7e3a7Sitojun case IFT_IEEE80211: 1609f4433d56Shenning case IFT_CARP: 1610d8a7e3a7Sitojun return (1); 1611d8a7e3a7Sitojun default: 1612d8a7e3a7Sitojun return (0); 1613d8a7e3a7Sitojun } 1614d8a7e3a7Sitojun } 1615d8a7e3a7Sitojun 1616ee37ea65Smcbride /* 1617ee37ea65Smcbride * oldp - syscall arg, need copyout 1618ee37ea65Smcbride * newp - syscall arg, need copyin 1619ee37ea65Smcbride */ 1620ee37ea65Smcbride 1621d8a7e3a7Sitojun int 1622a0aa363cSjsing nd6_sysctl(int name, void *oldp, size_t *oldlenp, void *newp, size_t newlen) 1623d8a7e3a7Sitojun { 1624db3db419Sitojun void *p; 162534223ca4Schl size_t ol; 1626d8a7e3a7Sitojun int error; 1627d8a7e3a7Sitojun 1628d8a7e3a7Sitojun error = 0; 1629d8a7e3a7Sitojun 1630d8a7e3a7Sitojun if (newp) 1631d8a7e3a7Sitojun return EPERM; 1632d8a7e3a7Sitojun if (oldp && !oldlenp) 1633d8a7e3a7Sitojun return EINVAL; 1634d8a7e3a7Sitojun ol = oldlenp ? *oldlenp : 0; 1635d8a7e3a7Sitojun 1636db3db419Sitojun if (oldp) { 1637e6ca3d0dSmk p = malloc(*oldlenp, M_TEMP, M_WAITOK | M_CANFAIL); 1638db3db419Sitojun if (!p) 1639db3db419Sitojun return ENOMEM; 1640db3db419Sitojun } else 1641db3db419Sitojun p = NULL; 1642d8a7e3a7Sitojun switch (name) { 1643d8a7e3a7Sitojun case ICMPV6CTL_ND6_DRLIST: 1644db3db419Sitojun error = fill_drlist(p, oldlenp, ol); 1645db3db419Sitojun if (!error && p && oldp) 1646732568ffSitojun error = copyout(p, oldp, *oldlenp); 1647d8a7e3a7Sitojun break; 1648d8a7e3a7Sitojun 1649d8a7e3a7Sitojun case ICMPV6CTL_ND6_PRLIST: 1650db3db419Sitojun error = fill_prlist(p, oldlenp, ol); 1651db3db419Sitojun if (!error && p && oldp) 1652732568ffSitojun error = copyout(p, oldp, *oldlenp); 1653d8a7e3a7Sitojun break; 1654d8a7e3a7Sitojun 1655d8a7e3a7Sitojun default: 1656d8a7e3a7Sitojun error = ENOPROTOOPT; 1657d8a7e3a7Sitojun break; 1658d8a7e3a7Sitojun } 1659db3db419Sitojun if (p) 1660dd168dc2Stedu free(p, M_TEMP, 0); 1661d8a7e3a7Sitojun 1662d8a7e3a7Sitojun return (error); 1663d8a7e3a7Sitojun } 1664d8a7e3a7Sitojun 1665a0aa363cSjsing int 1666ee37ea65Smcbride fill_drlist(void *oldp, size_t *oldlenp, size_t ol) 1667d8a7e3a7Sitojun { 1668d8a7e3a7Sitojun int error = 0, s; 1669d8a7e3a7Sitojun struct in6_defrouter *d = NULL, *de = NULL; 1670d8a7e3a7Sitojun struct nd_defrouter *dr; 1671d8a7e3a7Sitojun size_t l; 1672d8a7e3a7Sitojun 16736e92dee6Sitojun s = splsoftnet(); 1674d8a7e3a7Sitojun 1675d8a7e3a7Sitojun if (oldp) { 1676d8a7e3a7Sitojun d = (struct in6_defrouter *)oldp; 1677d8a7e3a7Sitojun de = (struct in6_defrouter *)((caddr_t)oldp + *oldlenp); 1678d8a7e3a7Sitojun } 1679d8a7e3a7Sitojun l = 0; 1680d8a7e3a7Sitojun 1681568012e6Sbluhm TAILQ_FOREACH(dr, &nd_defrouter, dr_entry) { 1682d8a7e3a7Sitojun if (oldp && d + 1 <= de) { 1683d8a7e3a7Sitojun bzero(d, sizeof(*d)); 1684d8a7e3a7Sitojun d->rtaddr.sin6_family = AF_INET6; 1685d8a7e3a7Sitojun d->rtaddr.sin6_len = sizeof(struct sockaddr_in6); 16866c8dd0e9Sclaudio in6_recoverscope(&d->rtaddr, &dr->rtaddr); 1687d8a7e3a7Sitojun d->flags = dr->flags; 1688d8a7e3a7Sitojun d->rtlifetime = dr->rtlifetime; 1689d8a7e3a7Sitojun d->expire = dr->expire; 1690d8a7e3a7Sitojun d->if_index = dr->ifp->if_index; 1691d8a7e3a7Sitojun } 1692d8a7e3a7Sitojun 1693d8a7e3a7Sitojun l += sizeof(*d); 1694d8a7e3a7Sitojun if (d) 1695d8a7e3a7Sitojun d++; 1696d8a7e3a7Sitojun } 1697d8a7e3a7Sitojun 1698d8a7e3a7Sitojun if (oldp) { 1699d8a7e3a7Sitojun *oldlenp = l; /* (caddr_t)d - (caddr_t)oldp */ 1700d8a7e3a7Sitojun if (l > ol) 1701d8a7e3a7Sitojun error = ENOMEM; 1702d8a7e3a7Sitojun } else 1703d8a7e3a7Sitojun *oldlenp = l; 1704d8a7e3a7Sitojun 1705d8a7e3a7Sitojun splx(s); 1706d8a7e3a7Sitojun 1707d8a7e3a7Sitojun return (error); 1708d8a7e3a7Sitojun } 1709d8a7e3a7Sitojun 1710a0aa363cSjsing int 1711ee37ea65Smcbride fill_prlist(void *oldp, size_t *oldlenp, size_t ol) 1712d8a7e3a7Sitojun { 1713d8a7e3a7Sitojun int error = 0, s; 1714d8a7e3a7Sitojun struct nd_prefix *pr; 171508247436Snaddy char *p = NULL, *ps = NULL; 171608247436Snaddy char *pe = NULL; 1717d8a7e3a7Sitojun size_t l; 1718d8a7e3a7Sitojun 17196e92dee6Sitojun s = splsoftnet(); 1720d8a7e3a7Sitojun 1721d8a7e3a7Sitojun if (oldp) { 172208247436Snaddy ps = p = (char *)oldp; 172308247436Snaddy pe = (char *)oldp + *oldlenp; 1724d8a7e3a7Sitojun } 1725d8a7e3a7Sitojun l = 0; 1726d8a7e3a7Sitojun 1727545205eaSpyr LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { 1728d8a7e3a7Sitojun u_short advrtrs; 172908247436Snaddy struct sockaddr_in6 sin6; 1730d8a7e3a7Sitojun struct nd_pfxrouter *pfr; 173108247436Snaddy struct in6_prefix pfx; 1732d8a7e3a7Sitojun 173308247436Snaddy if (oldp && p + sizeof(struct in6_prefix) <= pe) { 173408247436Snaddy memset(&pfx, 0, sizeof(pfx)); 173508247436Snaddy ps = p; 1736d8a7e3a7Sitojun 173708247436Snaddy pfx.prefix = pr->ndpr_prefix; 17386c8dd0e9Sclaudio in6_recoverscope(&pfx.prefix, 17396c8dd0e9Sclaudio &pfx.prefix.sin6_addr); 174008247436Snaddy pfx.raflags = pr->ndpr_raf; 174108247436Snaddy pfx.prefixlen = pr->ndpr_plen; 174208247436Snaddy pfx.vltime = pr->ndpr_vltime; 174308247436Snaddy pfx.pltime = pr->ndpr_pltime; 174408247436Snaddy pfx.if_index = pr->ndpr_ifp->if_index; 1745d8a7e3a7Sitojun if (pr->ndpr_vltime == ND6_INFINITE_LIFETIME) 174608247436Snaddy pfx.expire = 0; 1747d8a7e3a7Sitojun else { 1748d8a7e3a7Sitojun time_t maxexpire; 1749d8a7e3a7Sitojun 1750d8a7e3a7Sitojun /* XXX: we assume time_t is signed. */ 175191a535ffSguenther maxexpire = (time_t)~(1ULL << 175291a535ffSguenther ((sizeof(maxexpire) * 8) - 1)); 1753d8a7e3a7Sitojun if (pr->ndpr_vltime < 1754d8a7e3a7Sitojun maxexpire - pr->ndpr_lastupdate) { 175508247436Snaddy pfx.expire = pr->ndpr_lastupdate + 1756d8a7e3a7Sitojun pr->ndpr_vltime; 1757d8a7e3a7Sitojun } else 175808247436Snaddy pfx.expire = maxexpire; 1759d8a7e3a7Sitojun } 176008247436Snaddy pfx.refcnt = pr->ndpr_refcnt; 176108247436Snaddy pfx.flags = pr->ndpr_stateflags; 176208247436Snaddy pfx.origin = PR_ORIG_RA; 176308247436Snaddy 176408247436Snaddy p += sizeof(pfx); l += sizeof(pfx); 176508247436Snaddy 1766d8a7e3a7Sitojun advrtrs = 0; 1767545205eaSpyr LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry) { 176808247436Snaddy if (p + sizeof(sin6) > pe) { 1769d8a7e3a7Sitojun advrtrs++; 1770d8a7e3a7Sitojun continue; 1771d8a7e3a7Sitojun } 17729c794535Sbluhm bzero(&sin6, sizeof(sin6)); 177308247436Snaddy sin6.sin6_family = AF_INET6; 177408247436Snaddy sin6.sin6_len = sizeof(struct sockaddr_in6); 17756c8dd0e9Sclaudio in6_recoverscope(&sin6, &pfr->router->rtaddr); 1776d8a7e3a7Sitojun advrtrs++; 177708247436Snaddy memcpy(p, &sin6, sizeof(sin6)); 177808247436Snaddy p += sizeof(sin6); 177908247436Snaddy l += sizeof(sin6); 1780d8a7e3a7Sitojun } 178108247436Snaddy pfx.advrtrs = advrtrs; 178208247436Snaddy memcpy(ps, &pfx, sizeof(pfx)); 1783d8a7e3a7Sitojun } 1784d8a7e3a7Sitojun else { 178508247436Snaddy l += sizeof(pfx); 1786d8a7e3a7Sitojun advrtrs = 0; 178708247436Snaddy LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry) { 1788d8a7e3a7Sitojun advrtrs++; 178908247436Snaddy l += sizeof(sin6); 1790d8a7e3a7Sitojun } 179108247436Snaddy } 1792d8a7e3a7Sitojun } 1793d8a7e3a7Sitojun 1794d8a7e3a7Sitojun if (oldp) { 1795d8a7e3a7Sitojun *oldlenp = l; /* (caddr_t)d - (caddr_t)oldp */ 1796d8a7e3a7Sitojun if (l > ol) 1797d8a7e3a7Sitojun error = ENOMEM; 1798d8a7e3a7Sitojun } else 1799d8a7e3a7Sitojun *oldlenp = l; 1800d8a7e3a7Sitojun 1801d8a7e3a7Sitojun splx(s); 1802d8a7e3a7Sitojun 1803d8a7e3a7Sitojun return (error); 1804d8a7e3a7Sitojun } 1805