1*4dfec2cfSmpi /* $OpenBSD: nd6.c,v 1.207 2017/03/26 08:49:22 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 *); 96d5be4bb8Sbluhm void nd6_timer_work(void *); 97d5be4bb8Sbluhm void nd6_timer(void *); 98c370e97fSmpi void nd6_invalidate(struct rtentry *); 99a0aa363cSjsing struct llinfo_nd6 *nd6_free(struct rtentry *, int); 100a0aa363cSjsing void nd6_llinfo_timer(void *); 101287546eaSitojun 102b3c1e4c1Sitojun struct timeout nd6_slowtimo_ch; 103b3c1e4c1Sitojun struct timeout nd6_timer_ch; 104a09574ebSkettenis struct task nd6_timer_task; 105b3c1e4c1Sitojun 106a0aa363cSjsing int fill_drlist(void *, size_t *, size_t); 107a0aa363cSjsing int fill_prlist(void *, size_t *, size_t); 108d8a7e3a7Sitojun 109287546eaSitojun void 110a0aa363cSjsing nd6_init(void) 111287546eaSitojun { 112287546eaSitojun static int nd6_init_done = 0; 113287546eaSitojun 114287546eaSitojun if (nd6_init_done) { 11535075f95Smpi log(LOG_NOTICE, "%s called more than once\n", __func__); 116287546eaSitojun return; 117287546eaSitojun } 118287546eaSitojun 11992c821ddSbluhm TAILQ_INIT(&nd6_list); 1201378bae2Sdlg pool_init(&nd6_pool, sizeof(struct llinfo_nd6), 0, 1211378bae2Sdlg IPL_SOFTNET, 0, "nd6", NULL); 12293fbd125Sbluhm 123f4f4d166Sitojun /* initialization of the default router list */ 124f4f4d166Sitojun TAILQ_INIT(&nd_defrouter); 125f4f4d166Sitojun 126e4195480Sdlg task_set(&nd6_timer_task, nd6_timer_work, NULL); 127a09574ebSkettenis 128287546eaSitojun nd6_init_done = 1; 129287546eaSitojun 130287546eaSitojun /* start timer */ 13122e88dadSmpi timeout_set_proc(&nd6_slowtimo_ch, nd6_slowtimo, NULL); 13229e86e5eSblambert timeout_add_sec(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL); 133d5be4bb8Sbluhm timeout_set(&nd6_timer_ch, nd6_timer, NULL); 134d5be4bb8Sbluhm timeout_add_sec(&nd6_timer_ch, nd6_prune); 135110585f2Sflorian 13660968cbeSmpi nd6_rs_init(); 137287546eaSitojun } 138287546eaSitojun 139d6b9e9b9Sitojun struct nd_ifinfo * 140ee37ea65Smcbride nd6_ifattach(struct ifnet *ifp) 141287546eaSitojun { 142d6b9e9b9Sitojun struct nd_ifinfo *nd; 143287546eaSitojun 144393af863Skrw nd = malloc(sizeof(*nd), M_IP6NDP, M_WAITOK | M_ZERO); 145287546eaSitojun 146d6b9e9b9Sitojun nd->initialized = 1; 147287546eaSitojun 148d6b9e9b9Sitojun nd->basereachable = REACHABLE_TIME; 149d6b9e9b9Sitojun nd->reachable = ND_COMPUTE_RTIME(nd->basereachable); 150d6b9e9b9Sitojun nd->retrans = RETRANS_TIMER; 1512a4a63f1Shenning /* per-interface IFXF_AUTOCONF6 needs to be set too to accept RAs */ 152d8a7e3a7Sitojun nd->flags = (ND6_IFF_PERFORMNUD | ND6_IFF_ACCEPT_RTADV); 1531b5f410aSitojun 154d6b9e9b9Sitojun return nd; 155287546eaSitojun } 156287546eaSitojun 157d6b9e9b9Sitojun void 158ee37ea65Smcbride nd6_ifdetach(struct nd_ifinfo *nd) 159d6b9e9b9Sitojun { 160d374aaacSitojun 16162cf959dSmpi free(nd, M_IP6NDP, sizeof(*nd)); 162287546eaSitojun } 163287546eaSitojun 164287546eaSitojun void 165ee37ea65Smcbride nd6_option_init(void *opt, int icmp6len, union nd_opts *ndopts) 166287546eaSitojun { 167287546eaSitojun bzero(ndopts, sizeof(*ndopts)); 168287546eaSitojun ndopts->nd_opts_search = (struct nd_opt_hdr *)opt; 169287546eaSitojun ndopts->nd_opts_last 170287546eaSitojun = (struct nd_opt_hdr *)(((u_char *)opt) + icmp6len); 171287546eaSitojun 172287546eaSitojun if (icmp6len == 0) { 173287546eaSitojun ndopts->nd_opts_done = 1; 174287546eaSitojun ndopts->nd_opts_search = NULL; 175287546eaSitojun } 176287546eaSitojun } 177287546eaSitojun 178287546eaSitojun /* 179287546eaSitojun * Take one ND option. 180287546eaSitojun */ 181287546eaSitojun struct nd_opt_hdr * 182ee37ea65Smcbride nd6_option(union nd_opts *ndopts) 183287546eaSitojun { 184287546eaSitojun struct nd_opt_hdr *nd_opt; 185287546eaSitojun int olen; 186287546eaSitojun 187287546eaSitojun if (!ndopts) 188bc84bce2Skrw panic("ndopts == NULL in nd6_option"); 189287546eaSitojun if (!ndopts->nd_opts_last) 190bc84bce2Skrw panic("uninitialized ndopts in nd6_option"); 191287546eaSitojun if (!ndopts->nd_opts_search) 192287546eaSitojun return NULL; 193287546eaSitojun if (ndopts->nd_opts_done) 194287546eaSitojun return NULL; 195287546eaSitojun 196287546eaSitojun nd_opt = ndopts->nd_opts_search; 197287546eaSitojun 19815bd77d2Sitojun /* make sure nd_opt_len is inside the buffer */ 19915bd77d2Sitojun if ((caddr_t)&nd_opt->nd_opt_len >= (caddr_t)ndopts->nd_opts_last) { 20015bd77d2Sitojun bzero(ndopts, sizeof(*ndopts)); 20115bd77d2Sitojun return NULL; 20215bd77d2Sitojun } 20315bd77d2Sitojun 204287546eaSitojun olen = nd_opt->nd_opt_len << 3; 205287546eaSitojun if (olen == 0) { 206287546eaSitojun /* 207287546eaSitojun * Message validation requires that all included 208287546eaSitojun * options have a length that is greater than zero. 209287546eaSitojun */ 210287546eaSitojun bzero(ndopts, sizeof(*ndopts)); 211287546eaSitojun return NULL; 212287546eaSitojun } 213287546eaSitojun 214287546eaSitojun ndopts->nd_opts_search = (struct nd_opt_hdr *)((caddr_t)nd_opt + olen); 21515bd77d2Sitojun if (ndopts->nd_opts_search > ndopts->nd_opts_last) { 21615bd77d2Sitojun /* option overruns the end of buffer, invalid */ 21715bd77d2Sitojun bzero(ndopts, sizeof(*ndopts)); 21815bd77d2Sitojun return NULL; 21915bd77d2Sitojun } else if (ndopts->nd_opts_search == ndopts->nd_opts_last) { 22015bd77d2Sitojun /* reached the end of options chain */ 221287546eaSitojun ndopts->nd_opts_done = 1; 222287546eaSitojun ndopts->nd_opts_search = NULL; 223287546eaSitojun } 224287546eaSitojun return nd_opt; 225287546eaSitojun } 226287546eaSitojun 227287546eaSitojun /* 228287546eaSitojun * Parse multiple ND options. 229287546eaSitojun * This function is much easier to use, for ND routines that do not need 230287546eaSitojun * multiple options of the same type. 231287546eaSitojun */ 232287546eaSitojun int 233ee37ea65Smcbride nd6_options(union nd_opts *ndopts) 234287546eaSitojun { 235287546eaSitojun struct nd_opt_hdr *nd_opt; 236287546eaSitojun int i = 0; 237287546eaSitojun 238287546eaSitojun if (!ndopts) 239bc84bce2Skrw panic("ndopts == NULL in nd6_options"); 240287546eaSitojun if (!ndopts->nd_opts_last) 241bc84bce2Skrw panic("uninitialized ndopts in nd6_options"); 242287546eaSitojun if (!ndopts->nd_opts_search) 243287546eaSitojun return 0; 244287546eaSitojun 245287546eaSitojun while (1) { 246287546eaSitojun nd_opt = nd6_option(ndopts); 247287546eaSitojun if (!nd_opt && !ndopts->nd_opts_last) { 248287546eaSitojun /* 249287546eaSitojun * Message validation requires that all included 250287546eaSitojun * options have a length that is greater than zero. 251287546eaSitojun */ 25207ba5f7cSjca icmp6stat_inc(icp6s_nd_badopt); 253287546eaSitojun bzero(ndopts, sizeof(*ndopts)); 254287546eaSitojun return -1; 255287546eaSitojun } 256287546eaSitojun 257287546eaSitojun if (!nd_opt) 258287546eaSitojun goto skip1; 259287546eaSitojun 260287546eaSitojun switch (nd_opt->nd_opt_type) { 261287546eaSitojun case ND_OPT_SOURCE_LINKADDR: 262287546eaSitojun case ND_OPT_TARGET_LINKADDR: 263287546eaSitojun case ND_OPT_MTU: 264287546eaSitojun case ND_OPT_REDIRECTED_HEADER: 265287546eaSitojun if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) { 26615bd77d2Sitojun nd6log((LOG_INFO, 26715bd77d2Sitojun "duplicated ND6 option found (type=%d)\n", 26815bd77d2Sitojun nd_opt->nd_opt_type)); 269287546eaSitojun /* XXX bark? */ 270287546eaSitojun } else { 271287546eaSitojun ndopts->nd_opt_array[nd_opt->nd_opt_type] 272287546eaSitojun = nd_opt; 273287546eaSitojun } 274287546eaSitojun break; 275287546eaSitojun case ND_OPT_PREFIX_INFORMATION: 276287546eaSitojun if (ndopts->nd_opt_array[nd_opt->nd_opt_type] == 0) { 277287546eaSitojun ndopts->nd_opt_array[nd_opt->nd_opt_type] 278287546eaSitojun = nd_opt; 279287546eaSitojun } 280287546eaSitojun ndopts->nd_opts_pi_end = 281287546eaSitojun (struct nd_opt_prefix_info *)nd_opt; 282287546eaSitojun break; 283287546eaSitojun default: 284287546eaSitojun /* 285287546eaSitojun * Unknown options must be silently ignored, 286e4d25771Stodd * to accommodate future extension to the protocol. 287287546eaSitojun */ 288b79da24aSitojun nd6log((LOG_DEBUG, 289287546eaSitojun "nd6_options: unsupported option %d - " 290b79da24aSitojun "option ignored\n", nd_opt->nd_opt_type)); 291287546eaSitojun } 292287546eaSitojun 293287546eaSitojun skip1: 294287546eaSitojun i++; 295287546eaSitojun if (i > nd6_maxndopt) { 29607ba5f7cSjca icmp6stat_inc(icp6s_nd_toomanyopt); 297b79da24aSitojun nd6log((LOG_INFO, "too many loop in nd opt\n")); 298287546eaSitojun break; 299287546eaSitojun } 300287546eaSitojun 301287546eaSitojun if (ndopts->nd_opts_done) 302287546eaSitojun break; 303287546eaSitojun } 304287546eaSitojun 305287546eaSitojun return 0; 306287546eaSitojun } 307287546eaSitojun 308287546eaSitojun /* 3099631a17bSitojun * ND6 timer routine to handle ND6 entries 310287546eaSitojun */ 311287546eaSitojun void 312134b71fcSdlg nd6_llinfo_settimer(struct llinfo_nd6 *ln, int secs) 3139631a17bSitojun { 314134b71fcSdlg if (secs < 0) { 3153bc414e3Sdlg ln->ln_rt->rt_expire = 0; 3169631a17bSitojun timeout_del(&ln->ln_timer_ch); 3179631a17bSitojun } else { 3183bc414e3Sdlg ln->ln_rt->rt_expire = time_uptime + secs; 319134b71fcSdlg timeout_add_sec(&ln->ln_timer_ch, secs); 3209631a17bSitojun } 3219631a17bSitojun } 3229631a17bSitojun 323a0aa363cSjsing void 3249631a17bSitojun nd6_llinfo_timer(void *arg) 325287546eaSitojun { 326287546eaSitojun int s; 327b3c1e4c1Sitojun struct llinfo_nd6 *ln; 328287546eaSitojun struct rtentry *rt; 329287546eaSitojun struct sockaddr_in6 *dst; 3309631a17bSitojun struct ifnet *ifp; 331d374aaacSitojun struct nd_ifinfo *ndi = NULL; 332287546eaSitojun 3332b4720fcSmpi NET_LOCK(s); 3349631a17bSitojun 3359631a17bSitojun ln = (struct llinfo_nd6 *)arg; 3369631a17bSitojun 3379631a17bSitojun if ((rt = ln->ln_rt) == NULL) 3389631a17bSitojun panic("ln->ln_rt == NULL"); 33947773228Sjsg if ((ifp = if_get(rt->rt_ifidx)) == NULL) { 3402b4720fcSmpi NET_UNLOCK(s); 341f5e0f62bSmpi return; 34247773228Sjsg } 343d6b9e9b9Sitojun ndi = ND_IFINFO(ifp); 344c3c56496Sbluhm dst = satosin6(rt_key(rt)); 345287546eaSitojun 346287546eaSitojun /* sanity check */ 3479c0643cdSbluhm if (rt->rt_llinfo != NULL && (struct llinfo_nd6 *)rt->rt_llinfo != ln) 348bc84bce2Skrw panic("rt_llinfo(%p) is not equal to ln(%p)", 349d374aaacSitojun rt->rt_llinfo, ln); 350287546eaSitojun if (!dst) 351bc84bce2Skrw panic("dst=0 in nd6_timer(ln=%p)", ln); 352287546eaSitojun 353287546eaSitojun switch (ln->ln_state) { 354287546eaSitojun case ND6_LLINFO_INCOMPLETE: 355287546eaSitojun if (ln->ln_asked < nd6_mmaxtries) { 356287546eaSitojun ln->ln_asked++; 357134b71fcSdlg nd6_llinfo_settimer(ln, ndi->retrans / 1000); 3589631a17bSitojun nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0); 359287546eaSitojun } else { 360287546eaSitojun struct mbuf *m = ln->ln_hold; 361287546eaSitojun if (m) { 3628294a4dfSitojun ln->ln_hold = NULL; 363287546eaSitojun /* 364d8a7e3a7Sitojun * Fake rcvif to make the ICMP error 365d8a7e3a7Sitojun * more helpful in diagnosing for the 366d8a7e3a7Sitojun * receiver. 367287546eaSitojun * XXX: should we consider 368287546eaSitojun * older rcvif? 369287546eaSitojun */ 37028112c65Smpi m->m_pkthdr.ph_ifidx = rt->rt_ifidx; 371d8a7e3a7Sitojun 372287546eaSitojun icmp6_error(m, ICMP6_DST_UNREACH, 373287546eaSitojun ICMP6_DST_UNREACH_ADDR, 0); 374e212adedSkrw if (ln->ln_hold == m) { 375e212adedSkrw /* m is back in ln_hold. Discard. */ 376e212adedSkrw m_freem(ln->ln_hold); 377e212adedSkrw ln->ln_hold = NULL; 378e212adedSkrw } 379287546eaSitojun } 3809631a17bSitojun (void)nd6_free(rt, 0); 3819631a17bSitojun ln = NULL; 382287546eaSitojun } 383287546eaSitojun break; 384287546eaSitojun case ND6_LLINFO_REACHABLE: 3859631a17bSitojun if (!ND6_LLINFO_PERMANENT(ln)) { 386287546eaSitojun ln->ln_state = ND6_LLINFO_STALE; 387134b71fcSdlg nd6_llinfo_settimer(ln, nd6_gctimer); 388be4e9e12Sitojun } 389287546eaSitojun break; 390be4e9e12Sitojun 391be4e9e12Sitojun case ND6_LLINFO_STALE: 392f3fcf2f3Smcbride case ND6_LLINFO_PURGE: 393be4e9e12Sitojun /* Garbage Collection(RFC 2461 5.3) */ 3949631a17bSitojun if (!ND6_LLINFO_PERMANENT(ln)) { 3959631a17bSitojun (void)nd6_free(rt, 1); 3969631a17bSitojun ln = NULL; 3979631a17bSitojun } 398be4e9e12Sitojun break; 399be4e9e12Sitojun 400287546eaSitojun case ND6_LLINFO_DELAY: 401d374aaacSitojun if (ndi && (ndi->flags & ND6_IFF_PERFORMNUD) != 0) { 402d374aaacSitojun /* We need NUD */ 403287546eaSitojun ln->ln_asked = 1; 404287546eaSitojun ln->ln_state = ND6_LLINFO_PROBE; 405134b71fcSdlg nd6_llinfo_settimer(ln, ndi->retrans / 1000); 406d374aaacSitojun nd6_ns_output(ifp, &dst->sin6_addr, 407d8a7e3a7Sitojun &dst->sin6_addr, ln, 0); 408be4e9e12Sitojun } else { 409d374aaacSitojun ln->ln_state = ND6_LLINFO_STALE; /* XXX */ 410134b71fcSdlg nd6_llinfo_settimer(ln, nd6_gctimer); 411be4e9e12Sitojun } 412287546eaSitojun break; 413287546eaSitojun case ND6_LLINFO_PROBE: 414287546eaSitojun if (ln->ln_asked < nd6_umaxtries) { 415287546eaSitojun ln->ln_asked++; 416134b71fcSdlg nd6_llinfo_settimer(ln, ndi->retrans / 1000); 417287546eaSitojun nd6_ns_output(ifp, &dst->sin6_addr, 418287546eaSitojun &dst->sin6_addr, ln, 0); 419d8a7e3a7Sitojun } else { 4209631a17bSitojun (void)nd6_free(rt, 0); 4219631a17bSitojun ln = NULL; 422d8a7e3a7Sitojun } 423287546eaSitojun break; 424287546eaSitojun } 4259631a17bSitojun 426f5e0f62bSmpi if_put(ifp); 4272b4720fcSmpi NET_UNLOCK(s); 428287546eaSitojun } 429287546eaSitojun 4309631a17bSitojun /* 4319631a17bSitojun * ND6 timer routine to expire default route list and prefix list 4329631a17bSitojun */ 4339631a17bSitojun void 434e4195480Sdlg nd6_timer_work(void *null) 4359631a17bSitojun { 436568012e6Sbluhm struct nd_defrouter *dr, *ndr; 437119a433cSbluhm struct nd_prefix *pr, *npr; 438a44d5cf0Smpi struct ifnet *ifp; 43922e88dadSmpi int s; 4409631a17bSitojun 44122e88dadSmpi NET_LOCK(s); 44222e88dadSmpi 44329e86e5eSblambert timeout_add_sec(&nd6_timer_ch, nd6_prune); 4449631a17bSitojun 4450a2c5741Sitojun /* expire default router list */ 446568012e6Sbluhm TAILQ_FOREACH_SAFE(dr, &nd_defrouter, dr_entry, ndr) 44756b4879eSmpi if (dr->expire && dr->expire < time_uptime) 448287546eaSitojun defrtrlist_del(dr); 449287546eaSitojun 450287546eaSitojun /* 451d8a7e3a7Sitojun * expire interface addresses. 452d8a7e3a7Sitojun * in the past the loop was inside prefix expiry processing. 4538b542bbeSpascoe * However, from a stricter spec-conformance standpoint, we should 454d8a7e3a7Sitojun * rather separate address lifetimes and prefix lifetimes. 455d8a7e3a7Sitojun */ 456a44d5cf0Smpi TAILQ_FOREACH(ifp, &ifnet, if_list) { 457a44d5cf0Smpi struct ifaddr *ifa, *nifa; 458a44d5cf0Smpi struct in6_ifaddr *ia6; 459a44d5cf0Smpi 460a44d5cf0Smpi TAILQ_FOREACH_SAFE(ifa, &ifp->if_addrlist, ifa_list, nifa) { 461a44d5cf0Smpi if (ifa->ifa_addr->sa_family != AF_INET6) 462a44d5cf0Smpi continue; 463a44d5cf0Smpi ia6 = ifatoia6(ifa); 464d8a7e3a7Sitojun /* check address lifetime */ 465d8a7e3a7Sitojun if (IFA6_IS_INVALID(ia6)) { 466d8a7e3a7Sitojun in6_purgeaddr(&ia6->ia_ifa); 4670a8b9475Smarkus } else if (IFA6_IS_DEPRECATED(ia6)) { 468d8a7e3a7Sitojun ia6->ia6_flags |= IN6_IFF_DEPRECATED; 469d8a7e3a7Sitojun } else { 470d8a7e3a7Sitojun /* 471d8a7e3a7Sitojun * A new RA might have made a deprecated address 472d8a7e3a7Sitojun * preferred. 473d8a7e3a7Sitojun */ 474d8a7e3a7Sitojun ia6->ia6_flags &= ~IN6_IFF_DEPRECATED; 475d8a7e3a7Sitojun } 476d8a7e3a7Sitojun } 477a44d5cf0Smpi } 478d8a7e3a7Sitojun 479d8a7e3a7Sitojun /* expire prefix list */ 480119a433cSbluhm LIST_FOREACH_SAFE(pr, &nd_prefix, ndpr_entry, npr) { 481d8a7e3a7Sitojun /* 482287546eaSitojun * check prefix lifetime. 483287546eaSitojun * since pltime is just for autoconf, pltime processing for 484287546eaSitojun * prefix is not necessary. 485287546eaSitojun */ 486d8a7e3a7Sitojun if (pr->ndpr_vltime != ND6_INFINITE_LIFETIME && 48756b4879eSmpi time_uptime - pr->ndpr_lastupdate > pr->ndpr_vltime) { 488287546eaSitojun /* 489287546eaSitojun * address expiration and prefix expiration are 490d8a7e3a7Sitojun * separate. NEVER perform in6_purgeaddr here. 491287546eaSitojun */ 492287546eaSitojun 493287546eaSitojun prelist_remove(pr); 494119a433cSbluhm } 495287546eaSitojun } 49622e88dadSmpi NET_UNLOCK(s); 497287546eaSitojun } 498287546eaSitojun 499a09574ebSkettenis void 500a09574ebSkettenis nd6_timer(void *ignored_arg) 501a09574ebSkettenis { 502a09574ebSkettenis task_add(systq, &nd6_timer_task); 503a09574ebSkettenis } 504a09574ebSkettenis 50522770369Sitojun /* 50622770369Sitojun * Nuke neighbor cache/prefix/default router management table, right before 50722770369Sitojun * ifp goes away. 50822770369Sitojun */ 50922770369Sitojun void 510ee37ea65Smcbride nd6_purge(struct ifnet *ifp) 51122770369Sitojun { 51222770369Sitojun struct llinfo_nd6 *ln, *nln; 513d8a7e3a7Sitojun struct nd_defrouter *dr, *ndr; 51422770369Sitojun struct nd_prefix *pr, *npr; 51522770369Sitojun 51645ffa37bSmpi splsoftassert(IPL_SOFTNET); 51745ffa37bSmpi 51822770369Sitojun /* 519d8a7e3a7Sitojun * Nuke default router list entries toward ifp. 520d8a7e3a7Sitojun * We defer removal of default router list entries that is installed 521d8a7e3a7Sitojun * in the routing table, in order to keep additional side effects as 522d8a7e3a7Sitojun * small as possible. 52322770369Sitojun */ 524568012e6Sbluhm TAILQ_FOREACH_SAFE(dr, &nd_defrouter, dr_entry, ndr) { 525d8a7e3a7Sitojun if (dr->installed) 526d8a7e3a7Sitojun continue; 527d8a7e3a7Sitojun 52822770369Sitojun if (dr->ifp == ifp) 52922770369Sitojun defrtrlist_del(dr); 53022770369Sitojun } 531568012e6Sbluhm TAILQ_FOREACH_SAFE(dr, &nd_defrouter, dr_entry, ndr) { 532d8a7e3a7Sitojun if (!dr->installed) 533d8a7e3a7Sitojun continue; 534d8a7e3a7Sitojun 53522770369Sitojun if (dr->ifp == ifp) 53622770369Sitojun defrtrlist_del(dr); 53722770369Sitojun } 53822770369Sitojun 53922770369Sitojun /* Nuke prefix list entries toward ifp */ 540119a433cSbluhm LIST_FOREACH_SAFE(pr, &nd_prefix, ndpr_entry, npr) { 541e520a0ecSmpi if (pr->ndpr_ifp == ifp) 54222770369Sitojun prelist_remove(pr); 54322770369Sitojun } 54422770369Sitojun 545d3b1c214Sflorian if (ifp->if_xflags & IFXF_AUTOCONF6) { 54622770369Sitojun /* refresh default router list */ 547f4f4d166Sitojun defrouter_select(); 548e6e438a4Sitojun } 54922770369Sitojun 55022770369Sitojun /* 55122770369Sitojun * Nuke neighbor cache entries for the ifp. 55222770369Sitojun */ 55392c821ddSbluhm TAILQ_FOREACH_SAFE(ln, &nd6_list, ln_list, nln) { 55422770369Sitojun struct rtentry *rt; 55522770369Sitojun struct sockaddr_dl *sdl; 55622770369Sitojun 55722770369Sitojun rt = ln->ln_rt; 55892c821ddSbluhm if (rt != NULL && rt->rt_gateway != NULL && 55922770369Sitojun rt->rt_gateway->sa_family == AF_LINK) { 560c7b7b779Sbluhm sdl = satosdl(rt->rt_gateway); 56122770369Sitojun if (sdl->sdl_index == ifp->if_index) 562d8a7e3a7Sitojun nln = nd6_free(rt, 0); 56322770369Sitojun } 56422770369Sitojun } 56522770369Sitojun } 56622770369Sitojun 567287546eaSitojun struct rtentry * 568f4d1af37Smikeb nd6_lookup(struct in6_addr *addr6, int create, struct ifnet *ifp, 569f4d1af37Smikeb u_int rtableid) 570287546eaSitojun { 571287546eaSitojun struct rtentry *rt; 572287546eaSitojun struct sockaddr_in6 sin6; 5735148b194Smpi int flags; 574287546eaSitojun 575287546eaSitojun bzero(&sin6, sizeof(sin6)); 576287546eaSitojun sin6.sin6_len = sizeof(struct sockaddr_in6); 577287546eaSitojun sin6.sin6_family = AF_INET6; 578287546eaSitojun sin6.sin6_addr = *addr6; 57912d3c25cSclaudio flags = (create) ? RT_RESOLVE : 0; 580d8a7e3a7Sitojun 5815148b194Smpi rt = rtalloc(sin6tosa(&sin6), flags, rtableid); 58216119bbfSmpi if (rt != NULL && (rt->rt_flags & RTF_LLINFO) == 0) { 583287546eaSitojun /* 584287546eaSitojun * This is the case for the default route. 585287546eaSitojun * If we want to create a neighbor cache for the address, we 586287546eaSitojun * should free the route for the destination and allocate an 587287546eaSitojun * interface route. 588287546eaSitojun */ 589287546eaSitojun if (create) { 59027ae666cSmpi rtfree(rt); 59127ae666cSmpi rt = NULL; 592287546eaSitojun } 593287546eaSitojun } 59416119bbfSmpi if (rt == NULL) { 595287546eaSitojun if (create && ifp) { 596cb24f5e5Sclaudio struct rt_addrinfo info; 59766f69fe1Smpi struct ifaddr *ifa; 5980ffd01d4Sbluhm int error; 599d374aaacSitojun 600287546eaSitojun /* 601287546eaSitojun * If no route is available and create is set, 602287546eaSitojun * we allocate a host route for the destination 603287546eaSitojun * and treat it like an interface route. 604287546eaSitojun * This hack is necessary for a neighbor which can't 605287546eaSitojun * be covered by our own prefix. 606287546eaSitojun */ 60766f69fe1Smpi ifa = ifaof_ifpforaddr(sin6tosa(&sin6), ifp); 608287546eaSitojun if (ifa == NULL) 609287546eaSitojun return (NULL); 610287546eaSitojun 611287546eaSitojun /* 612287546eaSitojun * Create a new route. RTF_LLINFO is necessary 613287546eaSitojun * to create a Neighbor Cache entry for the 614287546eaSitojun * destination in nd6_rtrequest which will be 6157ffb277fSbluhm * called in rtrequest. 616287546eaSitojun */ 617cb24f5e5Sclaudio bzero(&info, sizeof(info)); 61866f69fe1Smpi info.rti_ifa = ifa; 619c29fc46aSmpi info.rti_flags = RTF_HOST | RTF_LLINFO; 620c3c56496Sbluhm info.rti_info[RTAX_DST] = sin6tosa(&sin6); 621c7b7b779Sbluhm info.rti_info[RTAX_GATEWAY] = sdltosa(ifp->if_sadl); 6227ffb277fSbluhm error = rtrequest(RTM_ADD, &info, RTP_CONNECTED, &rt, 6230ffd01d4Sbluhm rtableid); 6240ffd01d4Sbluhm if (error) 625287546eaSitojun return (NULL); 6269c0643cdSbluhm if (rt->rt_llinfo != NULL) { 627287546eaSitojun struct llinfo_nd6 *ln = 628287546eaSitojun (struct llinfo_nd6 *)rt->rt_llinfo; 629287546eaSitojun ln->ln_state = ND6_LLINFO_NOSTATE; 630287546eaSitojun } 631f6e55599Sitojun } else 632287546eaSitojun return (NULL); 633287546eaSitojun } 634287546eaSitojun /* 635287546eaSitojun * Validation for the entry. 636d8a7e3a7Sitojun * Note that the check for rt_llinfo is necessary because a cloned 637d8a7e3a7Sitojun * route from a parent route that has the L flag (e.g. the default 638d8a7e3a7Sitojun * route to a p2p interface) may have the flag, too, while the 639d8a7e3a7Sitojun * destination is not actually a neighbor. 640287546eaSitojun */ 641287546eaSitojun if ((rt->rt_flags & RTF_GATEWAY) || (rt->rt_flags & RTF_LLINFO) == 0 || 642d8a7e3a7Sitojun rt->rt_gateway->sa_family != AF_LINK || rt->rt_llinfo == NULL || 643248f6870Smpi (ifp != NULL && rt->rt_ifidx != ifp->if_index)) { 644287546eaSitojun if (create) { 645bbcf0337Smpi char addr[INET6_ADDRSTRLEN]; 64635075f95Smpi nd6log((LOG_DEBUG, "%s: failed to lookup %s (if=%s)\n", 64735075f95Smpi __func__, 648bbcf0337Smpi inet_ntop(AF_INET6, addr6, addr, sizeof(addr)), 649da592434Sitojun ifp ? ifp->if_xname : "unspec")); 650287546eaSitojun } 65116119bbfSmpi rtfree(rt); 652d8a7e3a7Sitojun return (NULL); 653287546eaSitojun } 654287546eaSitojun return (rt); 655287546eaSitojun } 656287546eaSitojun 657287546eaSitojun /* 658287546eaSitojun * Detect if a given IPv6 address identifies a neighbor on a given link. 659287546eaSitojun * XXX: should take care of the destination of a p2p link? 660287546eaSitojun */ 661287546eaSitojun int 662ee37ea65Smcbride nd6_is_addr_neighbor(struct sockaddr_in6 *addr, struct ifnet *ifp) 663287546eaSitojun { 664d8a7e3a7Sitojun struct nd_prefix *pr; 665ef6620bcSmpi struct in6_ifaddr *ia6; 666ef6620bcSmpi struct ifaddr *ifa; 667d8a7e3a7Sitojun struct rtentry *rt; 668287546eaSitojun 669cfb6b8dfSitojun /* 670cfb6b8dfSitojun * A link-local address is always a neighbor. 671cfb6b8dfSitojun * XXX: we should use the sin6_scope_id field rather than the embedded 672cfb6b8dfSitojun * interface index. 673d8a7e3a7Sitojun * XXX: a link does not necessarily specify a single interface. 674cfb6b8dfSitojun */ 675cfb6b8dfSitojun if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr) && 676cfb6b8dfSitojun ntohs(*(u_int16_t *)&addr->sin6_addr.s6_addr[2]) == ifp->if_index) 677287546eaSitojun return (1); 678287546eaSitojun 679ef6620bcSmpi TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { 680ef6620bcSmpi if (ifa->ifa_addr->sa_family != AF_INET6) 681ef6620bcSmpi continue; 682ef6620bcSmpi 683ef6620bcSmpi ia6 = ifatoia6(ifa); 684ef6620bcSmpi 685ef6620bcSmpi /* Prefix check down below. */ 686ef6620bcSmpi if (ia6->ia6_flags & IN6_IFF_AUTOCONF) 687ef6620bcSmpi continue; 688ef6620bcSmpi 689ef6620bcSmpi if (IN6_ARE_MASKED_ADDR_EQUAL(&addr->sin6_addr, 690ef6620bcSmpi &ia6->ia_addr.sin6_addr, 691ef6620bcSmpi &ia6->ia_prefixmask.sin6_addr)) 692ef6620bcSmpi return (1); 693ef6620bcSmpi } 694ef6620bcSmpi 695287546eaSitojun /* 696d8a7e3a7Sitojun * If the address matches one of our on-link prefixes, it should be a 697d8a7e3a7Sitojun * neighbor. 698287546eaSitojun */ 699545205eaSpyr LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { 700d8a7e3a7Sitojun if (pr->ndpr_ifp != ifp) 701d8a7e3a7Sitojun continue; 702287546eaSitojun 703d8a7e3a7Sitojun if (!(pr->ndpr_stateflags & NDPRF_ONLINK)) 704d8a7e3a7Sitojun continue; 705d8a7e3a7Sitojun 706d8a7e3a7Sitojun if (IN6_ARE_MASKED_ADDR_EQUAL(&pr->ndpr_prefix.sin6_addr, 707d8a7e3a7Sitojun &addr->sin6_addr, &pr->ndpr_mask)) 708d8a7e3a7Sitojun return (1); 709287546eaSitojun } 710d8a7e3a7Sitojun 711d8a7e3a7Sitojun /* 712287546eaSitojun * Even if the address matches none of our addresses, it might be 713287546eaSitojun * in the neighbor cache. 714287546eaSitojun */ 71516119bbfSmpi rt = nd6_lookup(&addr->sin6_addr, 0, ifp, ifp->if_rdomain); 71616119bbfSmpi if (rt != NULL) { 71716119bbfSmpi rtfree(rt); 718287546eaSitojun return (1); 71916119bbfSmpi } 720287546eaSitojun 721287546eaSitojun return (0); 722287546eaSitojun } 723287546eaSitojun 724c370e97fSmpi void 725c370e97fSmpi nd6_invalidate(struct rtentry *rt) 726c370e97fSmpi { 727c370e97fSmpi struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo; 728c370e97fSmpi 729c370e97fSmpi m_freem(ln->ln_hold); 730c370e97fSmpi ln->ln_hold = NULL; 731c370e97fSmpi ln->ln_state = ND6_LLINFO_INCOMPLETE; 732c370e97fSmpi ln->ln_asked = 0; 733c370e97fSmpi } 734c370e97fSmpi 735287546eaSitojun /* 736287546eaSitojun * Free an nd6 llinfo entry. 737d8a7e3a7Sitojun * Since the function would cause significant changes in the kernel, DO NOT 738d8a7e3a7Sitojun * make it global, unless you have a strong reason for the change, and are sure 739d8a7e3a7Sitojun * that the change is safe. 740287546eaSitojun */ 741a0aa363cSjsing struct llinfo_nd6 * 742ee37ea65Smcbride nd6_free(struct rtentry *rt, int gc) 743287546eaSitojun { 74429760ae1Sitojun struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo, *next; 745c3c56496Sbluhm struct in6_addr in6 = satosin6(rt_key(rt))->sin6_addr; 746287546eaSitojun struct nd_defrouter *dr; 747248f6870Smpi struct ifnet *ifp; 74845ffa37bSmpi 74945ffa37bSmpi splsoftassert(IPL_SOFTNET); 750287546eaSitojun 751287546eaSitojun /* 752d8a7e3a7Sitojun * we used to have pfctlinput(PRC_HOSTDEAD) here. 753d8a7e3a7Sitojun * even though it is not harmful, it was not really necessary. 754287546eaSitojun */ 755248f6870Smpi ifp = if_get(rt->rt_ifidx); 756f4f4d166Sitojun 75781d38878Smpi if (!ip6_forwarding) { 758c3c56496Sbluhm dr = defrouter_lookup(&satosin6(rt_key(rt))->sin6_addr, 759248f6870Smpi rt->rt_ifidx); 760d8a7e3a7Sitojun 761d8a7e3a7Sitojun if (dr != NULL && dr->expire && 762d8a7e3a7Sitojun ln->ln_state == ND6_LLINFO_STALE && gc) { 763d8a7e3a7Sitojun /* 764d8a7e3a7Sitojun * If the reason for the deletion is just garbage 765d8a7e3a7Sitojun * collection, and the neighbor is an active default 766d8a7e3a7Sitojun * router, do not delete it. Instead, reset the GC 767d8a7e3a7Sitojun * timer using the router's lifetime. 768d8a7e3a7Sitojun * Simply deleting the entry would affect default 769d8a7e3a7Sitojun * router selection, which is not necessarily a good 770d8a7e3a7Sitojun * thing, especially when we're using router preference 771d8a7e3a7Sitojun * values. 772d8a7e3a7Sitojun * XXX: the check for ln_state would be redundant, 773d8a7e3a7Sitojun * but we intentionally keep it just in case. 774d8a7e3a7Sitojun */ 77556b4879eSmpi if (dr->expire > time_uptime) { 7769631a17bSitojun nd6_llinfo_settimer(ln, 77756b4879eSmpi dr->expire - time_uptime); 7789631a17bSitojun } else 779134b71fcSdlg nd6_llinfo_settimer(ln, nd6_gctimer); 780248f6870Smpi if_put(ifp); 78192c821ddSbluhm return (TAILQ_NEXT(ln, ln_list)); 782d8a7e3a7Sitojun } 783d8a7e3a7Sitojun 784f4f4d166Sitojun if (ln->ln_router || dr) { 785f4f4d166Sitojun /* 786f4f4d166Sitojun * rt6_flush must be called whether or not the neighbor 787f4f4d166Sitojun * is in the Default Router List. 788f4f4d166Sitojun * See a corresponding comment in nd6_na_input(). 789f4f4d166Sitojun */ 790248f6870Smpi rt6_flush(&in6, ifp); 791f4f4d166Sitojun } 792f4f4d166Sitojun 793f4f4d166Sitojun if (dr) { 794f4f4d166Sitojun /* 7958b542bbeSpascoe * Unreachability of a router might affect the default 796f4f4d166Sitojun * router selection and on-link detection of advertised 797f4f4d166Sitojun * prefixes. 798f4f4d166Sitojun */ 799f4f4d166Sitojun 800f4f4d166Sitojun /* 801f4f4d166Sitojun * Temporarily fake the state to choose a new default 802f4f4d166Sitojun * router and to perform on-link determination of 80334deef1eSitojun * prefixes correctly. 804f4f4d166Sitojun * Below the state will be set correctly, 805f4f4d166Sitojun * or the entry itself will be deleted. 806f4f4d166Sitojun */ 807f4f4d166Sitojun ln->ln_state = ND6_LLINFO_INCOMPLETE; 808f4f4d166Sitojun 809f4f4d166Sitojun /* 810d8a7e3a7Sitojun * Since defrouter_select() does not affect the 811d8a7e3a7Sitojun * on-link determination and MIP6 needs the check 812d8a7e3a7Sitojun * before the default router selection, we perform 813d8a7e3a7Sitojun * the check now. 814f4f4d166Sitojun */ 815f4f4d166Sitojun pfxlist_onlink_check(); 816d8a7e3a7Sitojun 817d8a7e3a7Sitojun /* 818d8a7e3a7Sitojun * refresh default router list 819d8a7e3a7Sitojun */ 820d8a7e3a7Sitojun defrouter_select(); 821287546eaSitojun } 822287546eaSitojun } 823287546eaSitojun 82429760ae1Sitojun /* 82529760ae1Sitojun * Before deleting the entry, remember the next entry as the 82629760ae1Sitojun * return value. We need this because pfxlist_onlink_check() above 82729760ae1Sitojun * might have freed other entries (particularly the old next entry) as 82829760ae1Sitojun * a side effect (XXX). 82929760ae1Sitojun */ 83092c821ddSbluhm next = TAILQ_NEXT(ln, ln_list); 83129760ae1Sitojun 832*4dfec2cfSmpi nd6_invalidate(rt); 833*4dfec2cfSmpi 83429760ae1Sitojun /* 83529760ae1Sitojun * Detach the route from the routing tree and the list of neighbor 83629760ae1Sitojun * caches, and disable the route entry not to be used in already 83729760ae1Sitojun * cached routes. 83829760ae1Sitojun */ 839c370e97fSmpi if (!ISSET(rt->rt_flags, RTF_STATIC|RTF_CACHED)) 840c4cbcae8Sbluhm rtdeletemsg(rt, ifp, ifp->if_rdomain); 84129760ae1Sitojun 842248f6870Smpi if_put(ifp); 843248f6870Smpi 8440a2c5741Sitojun return (next); 845287546eaSitojun } 846287546eaSitojun 847287546eaSitojun /* 848287546eaSitojun * Upper-layer reachability hint for Neighbor Unreachability Detection. 849287546eaSitojun * 8508b542bbeSpascoe * XXX cost-effective methods? 851287546eaSitojun */ 852287546eaSitojun void 853a7fa3e84Smpi nd6_nud_hint(struct rtentry *rt) 854287546eaSitojun { 855287546eaSitojun struct llinfo_nd6 *ln; 856a7fa3e84Smpi struct ifnet *ifp; 857287546eaSitojun 858a7fa3e84Smpi ifp = if_get(rt->rt_ifidx); 859a7fa3e84Smpi if (ifp == NULL) 860287546eaSitojun return; 861287546eaSitojun 862f6e55599Sitojun if ((rt->rt_flags & RTF_GATEWAY) != 0 || 863f6e55599Sitojun (rt->rt_flags & RTF_LLINFO) == 0 || 8649c0643cdSbluhm rt->rt_llinfo == NULL || rt->rt_gateway == NULL || 865f6e55599Sitojun rt->rt_gateway->sa_family != AF_LINK) { 866287546eaSitojun /* This is not a host route. */ 867a7fa3e84Smpi goto out; 868287546eaSitojun } 869287546eaSitojun 870287546eaSitojun ln = (struct llinfo_nd6 *)rt->rt_llinfo; 871804d8827Sitojun if (ln->ln_state < ND6_LLINFO_REACHABLE) 872a7fa3e84Smpi goto out; 873287546eaSitojun 874f6e55599Sitojun /* 875f6e55599Sitojun * if we get upper-layer reachability confirmation many times, 876f6e55599Sitojun * it is possible we have false information. 877f6e55599Sitojun */ 878f6e55599Sitojun ln->ln_byhint++; 879f6e55599Sitojun if (ln->ln_byhint > nd6_maxnudhint) 880a7fa3e84Smpi goto out; 881f6e55599Sitojun 882287546eaSitojun ln->ln_state = ND6_LLINFO_REACHABLE; 883a7fa3e84Smpi if (!ND6_LLINFO_PERMANENT(ln)) 884134b71fcSdlg nd6_llinfo_settimer(ln, ND_IFINFO(ifp)->reachable); 885a7fa3e84Smpi out: 886a7fa3e84Smpi if_put(ifp); 887287546eaSitojun } 888287546eaSitojun 889287546eaSitojun void 890dcb17c31Smpi nd6_rtrequest(struct ifnet *ifp, int req, struct rtentry *rt) 891287546eaSitojun { 892287546eaSitojun struct sockaddr *gate = rt->rt_gateway; 893287546eaSitojun struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo; 894287546eaSitojun struct ifaddr *ifa; 89570fc31beSrainer struct nd_defrouter *dr; 89670fc31beSrainer 89770fc31beSrainer if (req == RTM_DELETE && (rt->rt_flags & RTF_GATEWAY) && 89870fc31beSrainer (IN6_ARE_ADDR_EQUAL(&(satosin6(rt_key(rt)))->sin6_addr, 899ad38292eSmpi &in6addr_any) && rt_plen(rt) == 0)) { 900248f6870Smpi dr = defrouter_lookup(&satosin6(gate)->sin6_addr, 901248f6870Smpi ifp->if_index); 90270fc31beSrainer if (dr) 90370fc31beSrainer dr->installed = 0; 90470fc31beSrainer } 905287546eaSitojun 906b71b7f0aSmpi if (ISSET(rt->rt_flags, RTF_GATEWAY|RTF_MULTICAST)) 907287546eaSitojun return; 908287546eaSitojun 909d8a7e3a7Sitojun if (nd6_need_cache(ifp) == 0 && (rt->rt_flags & RTF_HOST) == 0) { 910d8a7e3a7Sitojun /* 911d8a7e3a7Sitojun * This is probably an interface direct route for a link 912d8a7e3a7Sitojun * which does not need neighbor caches (e.g. fe80::%lo0/64). 913d8a7e3a7Sitojun * We do not need special treatment below for such a route. 914d8a7e3a7Sitojun * Moreover, the RTF_LLINFO flag which would be set below 915d8a7e3a7Sitojun * would annoy the ndp(8) command. 916d8a7e3a7Sitojun */ 917d8a7e3a7Sitojun return; 918d8a7e3a7Sitojun } 919d8a7e3a7Sitojun 920af1344beSbluhm if (req == RTM_RESOLVE && nd6_need_cache(ifp) == 0) { 921d8a7e3a7Sitojun /* 922af1344beSbluhm * For routing daemons like ospf6d we allow neighbor discovery 923af1344beSbluhm * based on the cloning route only. This allows us to sent 924af1344beSbluhm * packets directly into a network without having an address 925af1344beSbluhm * with matching prefix on the interface. If the cloning 926af1344beSbluhm * route is used for an stf interface, we would mistakenly 927af1344beSbluhm * make a neighbor cache for the host route, and would see 928af1344beSbluhm * strange neighbor solicitation for the corresponding 929af1344beSbluhm * destination. In order to avoid confusion, we check if the 930af1344beSbluhm * interface is suitable for neighbor discovery, and stop the 931d8a7e3a7Sitojun * process if not. Additionally, we remove the LLINFO flag 932d8a7e3a7Sitojun * so that ndp(8) will not try to get the neighbor information 933d8a7e3a7Sitojun * of the destination. 934d8a7e3a7Sitojun */ 935d8a7e3a7Sitojun rt->rt_flags &= ~RTF_LLINFO; 936d8a7e3a7Sitojun return; 937d8a7e3a7Sitojun } 938d8a7e3a7Sitojun 939287546eaSitojun switch (req) { 940287546eaSitojun case RTM_ADD: 94148ebf8e1Sitojun if ((rt->rt_flags & RTF_CLONING) || 9429c0643cdSbluhm ((rt->rt_flags & (RTF_LLINFO | RTF_LOCAL)) && ln == NULL)) { 9439c0643cdSbluhm if (ln != NULL) 9449631a17bSitojun nd6_llinfo_settimer(ln, 0); 945d8a7e3a7Sitojun if ((rt->rt_flags & RTF_CLONING) != 0) 946287546eaSitojun break; 947287546eaSitojun } 948f4f4d166Sitojun /* 9498b542bbeSpascoe * In IPv4 code, we try to announce new RTF_ANNOUNCE entry here. 950f4f4d166Sitojun * We don't do that here since llinfo is not ready yet. 951f4f4d166Sitojun * 952f4f4d166Sitojun * There are also couple of other things to be discussed: 953f4f4d166Sitojun * - unsolicited NA code needs improvement beforehand 954f4f4d166Sitojun * - RFC2461 says we MAY send multicast unsolicited NA 955f4f4d166Sitojun * (7.2.6 paragraph 4), however, it also says that we 956f4f4d166Sitojun * SHOULD provide a mechanism to prevent multicast NA storm. 957f4f4d166Sitojun * we don't have anything like it right now. 958841d7adbSitojun * note that the mechanism needs a mutual agreement 959f4f4d166Sitojun * between proxies, which means that we need to implement 960841d7adbSitojun * a new protocol, or a new kludge. 961841d7adbSitojun * - from RFC2461 6.2.4, host MUST NOT send an unsolicited NA. 962f4f4d166Sitojun * we need to check ip6forwarding before sending it. 963f4f4d166Sitojun * (or should we allow proxy ND configuration only for 964f4f4d166Sitojun * routers? there's no mention about proxy ND from hosts) 965f4f4d166Sitojun */ 966f4f4d166Sitojun #if 0 967f4f4d166Sitojun /* XXX it does not work */ 968287546eaSitojun if (rt->rt_flags & RTF_ANNOUNCE) 969287546eaSitojun nd6_na_output(ifp, 970c3c56496Sbluhm &satosin6(rt_key(rt))->sin6_addr, 971c3c56496Sbluhm &satosin6(rt_key(rt))->sin6_addr, 972287546eaSitojun ip6_forwarding ? ND_NA_FLAG_ROUTER : 0, 973f4f4d166Sitojun 1, NULL); 974f4f4d166Sitojun #endif 975287546eaSitojun /* FALLTHROUGH */ 976287546eaSitojun case RTM_RESOLVE: 977287546eaSitojun if (gate->sa_family != AF_LINK || 978cfc71c8bSclaudio gate->sa_len < sizeof(struct sockaddr_dl)) { 97986b61919Smpi log(LOG_DEBUG, "%s: bad gateway value: %s\n", 98086b61919Smpi __func__, ifp->if_xname); 981287546eaSitojun break; 982287546eaSitojun } 983c7b7b779Sbluhm satosdl(gate)->sdl_type = ifp->if_type; 984c7b7b779Sbluhm satosdl(gate)->sdl_index = ifp->if_index; 985d374aaacSitojun if (ln != NULL) 986287546eaSitojun break; /* This happens on a route change */ 987287546eaSitojun /* 988287546eaSitojun * Case 2: This route may come from cloning, or a manual route 989287546eaSitojun * add with a LL address. 990287546eaSitojun */ 99193fbd125Sbluhm ln = pool_get(&nd6_pool, PR_NOWAIT | PR_ZERO); 992287546eaSitojun rt->rt_llinfo = (caddr_t)ln; 9939c0643cdSbluhm if (ln == NULL) { 99493fbd125Sbluhm log(LOG_DEBUG, "%s: pool get failed\n", __func__); 995287546eaSitojun break; 996287546eaSitojun } 997287546eaSitojun nd6_inuse++; 998287546eaSitojun nd6_allocated++; 999287546eaSitojun ln->ln_rt = rt; 1000593f7790Smpi timeout_set_proc(&ln->ln_timer_ch, nd6_llinfo_timer, ln); 1001287546eaSitojun /* this is required for "ndp" command. - shin */ 1002287546eaSitojun if (req == RTM_ADD) { 1003287546eaSitojun /* 1004287546eaSitojun * gate should have some valid AF_LINK entry, 10053bc414e3Sdlg * and ln expire should have some lifetime 1006287546eaSitojun * which is specified by ndp command. 1007287546eaSitojun */ 1008287546eaSitojun ln->ln_state = ND6_LLINFO_REACHABLE; 1009f6e55599Sitojun ln->ln_byhint = 0; 1010287546eaSitojun } else { 1011287546eaSitojun /* 1012287546eaSitojun * When req == RTM_RESOLVE, rt is created and 1013287546eaSitojun * initialized in rtrequest(), so rt_expire is 0. 1014287546eaSitojun */ 1015287546eaSitojun ln->ln_state = ND6_LLINFO_NOSTATE; 10169631a17bSitojun nd6_llinfo_settimer(ln, 0); 1017287546eaSitojun } 1018287546eaSitojun rt->rt_flags |= RTF_LLINFO; 101992c821ddSbluhm TAILQ_INSERT_HEAD(&nd6_list, ln, ln_list); 1020287546eaSitojun 1021287546eaSitojun /* 1022f3fcf2f3Smcbride * If we have too many cache entries, initiate immediate 1023f3fcf2f3Smcbride * purging for some "less recently used" entries. Note that 1024f3fcf2f3Smcbride * we cannot directly call nd6_free() here because it would 1025f3fcf2f3Smcbride * cause re-entering rtable related routines triggering an LOR 1026f3fcf2f3Smcbride * problem for FreeBSD. 1027f3fcf2f3Smcbride */ 1028f3fcf2f3Smcbride if (ip6_neighborgcthresh >= 0 && 1029f3fcf2f3Smcbride nd6_inuse >= ip6_neighborgcthresh) { 1030f3fcf2f3Smcbride int i; 1031f3fcf2f3Smcbride 103292c821ddSbluhm for (i = 0; i < 10; i++) { 103392c821ddSbluhm struct llinfo_nd6 *ln_end; 103492c821ddSbluhm 103592c821ddSbluhm ln_end = TAILQ_LAST(&nd6_list, llinfo_nd6_head); 103692c821ddSbluhm if (ln_end == ln) 103792c821ddSbluhm break; 1038f3fcf2f3Smcbride 1039f3fcf2f3Smcbride /* Move this entry to the head */ 104092c821ddSbluhm TAILQ_REMOVE(&nd6_list, ln_end, ln_list); 104192c821ddSbluhm TAILQ_INSERT_HEAD(&nd6_list, ln_end, ln_list); 1042f3fcf2f3Smcbride 1043f3fcf2f3Smcbride if (ND6_LLINFO_PERMANENT(ln_end)) 1044f3fcf2f3Smcbride continue; 1045f3fcf2f3Smcbride 1046f3fcf2f3Smcbride if (ln_end->ln_state > ND6_LLINFO_INCOMPLETE) 1047f3fcf2f3Smcbride ln_end->ln_state = ND6_LLINFO_STALE; 1048f3fcf2f3Smcbride else 1049f3fcf2f3Smcbride ln_end->ln_state = ND6_LLINFO_PURGE; 1050f3fcf2f3Smcbride nd6_llinfo_settimer(ln_end, 0); 1051f3fcf2f3Smcbride } 1052f3fcf2f3Smcbride } 1053f3fcf2f3Smcbride 1054f3fcf2f3Smcbride /* 1055287546eaSitojun * check if rt_key(rt) is one of my address assigned 1056287546eaSitojun * to the interface. 1057287546eaSitojun */ 105886b61919Smpi ifa = &in6ifa_ifpwithaddr(ifp, 1059c3c56496Sbluhm &satosin6(rt_key(rt))->sin6_addr)->ia_ifa; 1060287546eaSitojun if (ifa) { 10619631a17bSitojun nd6_llinfo_settimer(ln, -1); 1062287546eaSitojun ln->ln_state = ND6_LLINFO_REACHABLE; 1063f6e55599Sitojun ln->ln_byhint = 0; 1064fac399ceSmpi KASSERT(ifa == rt->rt_ifa); 1065f4f4d166Sitojun } else if (rt->rt_flags & RTF_ANNOUNCE) { 10669631a17bSitojun nd6_llinfo_settimer(ln, -1); 1067f4f4d166Sitojun ln->ln_state = ND6_LLINFO_REACHABLE; 1068f6e55599Sitojun ln->ln_byhint = 0; 1069f4f4d166Sitojun 1070f4f4d166Sitojun /* join solicited node multicast for proxy ND */ 1071f4f4d166Sitojun if (ifp->if_flags & IFF_MULTICAST) { 1072f4f4d166Sitojun struct in6_addr llsol; 1073f4f4d166Sitojun int error; 1074f4f4d166Sitojun 1075c3c56496Sbluhm llsol = satosin6(rt_key(rt))->sin6_addr; 1076f4f4d166Sitojun llsol.s6_addr16[0] = htons(0xff02); 1077f4f4d166Sitojun llsol.s6_addr16[1] = htons(ifp->if_index); 1078f4f4d166Sitojun llsol.s6_addr32[1] = 0; 1079f4f4d166Sitojun llsol.s6_addr32[2] = htonl(1); 1080f4f4d166Sitojun llsol.s6_addr8[12] = 0xff; 1081f4f4d166Sitojun 1082d8a7e3a7Sitojun if (in6_addmulti(&llsol, ifp, &error)) { 1083bbcf0337Smpi char addr[INET6_ADDRSTRLEN]; 1084d8a7e3a7Sitojun nd6log((LOG_ERR, "%s: failed to join " 1085d8a7e3a7Sitojun "%s (errno=%d)\n", ifp->if_xname, 1086bbcf0337Smpi inet_ntop(AF_INET6, &llsol, 1087bbcf0337Smpi addr, sizeof(addr)), 1088bbcf0337Smpi error)); 1089d8a7e3a7Sitojun } 1090f4f4d166Sitojun } 1091287546eaSitojun } 1092287546eaSitojun break; 1093287546eaSitojun 1094287546eaSitojun case RTM_DELETE: 10959c0643cdSbluhm if (ln == NULL) 1096287546eaSitojun break; 1097f4f4d166Sitojun /* leave from solicited node multicast for proxy ND */ 1098f4f4d166Sitojun if ((rt->rt_flags & RTF_ANNOUNCE) != 0 && 1099f4f4d166Sitojun (ifp->if_flags & IFF_MULTICAST) != 0) { 1100f4f4d166Sitojun struct in6_addr llsol; 1101f4f4d166Sitojun struct in6_multi *in6m; 1102f4f4d166Sitojun 1103c3c56496Sbluhm llsol = satosin6(rt_key(rt))->sin6_addr; 1104f4f4d166Sitojun llsol.s6_addr16[0] = htons(0xff02); 1105f4f4d166Sitojun llsol.s6_addr16[1] = htons(ifp->if_index); 1106f4f4d166Sitojun llsol.s6_addr32[1] = 0; 1107f4f4d166Sitojun llsol.s6_addr32[2] = htonl(1); 1108f4f4d166Sitojun llsol.s6_addr8[12] = 0xff; 1109f4f4d166Sitojun 1110f4f4d166Sitojun IN6_LOOKUP_MULTI(llsol, ifp, in6m); 1111f4f4d166Sitojun if (in6m) 1112f4f4d166Sitojun in6_delmulti(in6m); 1113f4f4d166Sitojun } 1114287546eaSitojun nd6_inuse--; 111592c821ddSbluhm TAILQ_REMOVE(&nd6_list, ln, ln_list); 11169631a17bSitojun nd6_llinfo_settimer(ln, -1); 11179c0643cdSbluhm rt->rt_llinfo = NULL; 1118287546eaSitojun rt->rt_flags &= ~RTF_LLINFO; 1119287546eaSitojun m_freem(ln->ln_hold); 112093fbd125Sbluhm pool_put(&nd6_pool, ln); 1121c370e97fSmpi break; 1122c370e97fSmpi 1123c370e97fSmpi case RTM_INVALIDATE: 1124c370e97fSmpi nd6_invalidate(rt); 1125c370e97fSmpi break; 1126287546eaSitojun } 1127287546eaSitojun } 1128287546eaSitojun 1129287546eaSitojun int 1130ee37ea65Smcbride nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp) 1131287546eaSitojun { 1132287546eaSitojun struct in6_ndireq *ndi = (struct in6_ndireq *)data; 1133287546eaSitojun struct in6_nbrinfo *nbi = (struct in6_nbrinfo *)data; 1134287546eaSitojun struct rtentry *rt; 1135ee3c93eeSbluhm int error = 0; 113622e88dadSmpi 113722e88dadSmpi splsoftassert(IPL_SOFTNET); 1138287546eaSitojun 1139287546eaSitojun switch (cmd) { 1140d6b9e9b9Sitojun case SIOCGIFINFO_IN6: 1141d6b9e9b9Sitojun ndi->ndi = *ND_IFINFO(ifp); 114202dac871Sderaadt memset(&ndi->ndi.randomseed0, 0, sizeof ndi->ndi.randomseed0); 114302dac871Sderaadt memset(&ndi->ndi.randomseed1, 0, sizeof ndi->ndi.randomseed1); 114402dac871Sderaadt memset(&ndi->ndi.randomid, 0, sizeof ndi->ndi.randomid); 1145287546eaSitojun break; 1146d374aaacSitojun case SIOCSIFINFO_FLAGS: 1147d6b9e9b9Sitojun ND_IFINFO(ifp)->flags = ndi->ndi.flags; 1148d374aaacSitojun break; 1149f4f4d166Sitojun case SIOCSNDFLUSH_IN6: /* XXX: the ioctl name is confusing... */ 1150d8a7e3a7Sitojun /* sync kernel routing table with the default router list */ 1151d8a7e3a7Sitojun defrouter_reset(); 1152f4f4d166Sitojun defrouter_select(); 1153287546eaSitojun break; 1154287546eaSitojun case SIOCSPFXFLUSH_IN6: 1155287546eaSitojun { 1156287546eaSitojun /* flush all the prefix advertised by routers */ 1157119a433cSbluhm struct nd_prefix *pr, *npr; 1158287546eaSitojun 1159db657d52Sbluhm /* First purge the addresses referenced by a prefix. */ 1160119a433cSbluhm LIST_FOREACH_SAFE(pr, &nd_prefix, ndpr_entry, npr) { 11619cb60e0eSmpi struct ifnet *ifp; 11629cb60e0eSmpi struct ifaddr *ifa, *nifa; 11639cb60e0eSmpi struct in6_ifaddr *ia6; 1164d8a7e3a7Sitojun 1165d8a7e3a7Sitojun if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) 1166d8a7e3a7Sitojun continue; /* XXX */ 1167d8a7e3a7Sitojun 1168d8a7e3a7Sitojun /* do we really have to remove addresses as well? */ 11699cb60e0eSmpi TAILQ_FOREACH(ifp, &ifnet, if_list) { 11709cb60e0eSmpi TAILQ_FOREACH_SAFE(ifa, &ifp->if_addrlist, 11719cb60e0eSmpi ifa_list, nifa) { 11729cb60e0eSmpi if (ifa->ifa_addr->sa_family != 11739cb60e0eSmpi AF_INET6) 11749cb60e0eSmpi continue; 11759cb60e0eSmpi 11769cb60e0eSmpi ia6 = ifatoia6(ifa); 11779cb60e0eSmpi if ((ia6->ia6_flags & IN6_IFF_AUTOCONF) 11789cb60e0eSmpi == 0) 1179d8a7e3a7Sitojun continue; 1180d8a7e3a7Sitojun 1181b9e83c60Sbluhm if (ia6->ia6_ndpr == pr) 1182b9e83c60Sbluhm in6_purgeaddr(&ia6->ia_ifa); 1183d8a7e3a7Sitojun } 1184db657d52Sbluhm } 11859cb60e0eSmpi } 1186db657d52Sbluhm /* 1187db657d52Sbluhm * Purging the addresses might remove the prefix as well. 1188db657d52Sbluhm * So run the loop again to access only prefixes that have 1189db657d52Sbluhm * not been freed already. 1190db657d52Sbluhm */ 1191119a433cSbluhm LIST_FOREACH_SAFE(pr, &nd_prefix, ndpr_entry, npr) { 1192db657d52Sbluhm if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) 1193db657d52Sbluhm continue; /* XXX */ 1194db657d52Sbluhm 1195287546eaSitojun prelist_remove(pr); 1196287546eaSitojun } 1197287546eaSitojun break; 1198287546eaSitojun } 1199287546eaSitojun case SIOCSRTRFLUSH_IN6: 1200287546eaSitojun { 1201287546eaSitojun /* flush all the default routers */ 1202568012e6Sbluhm struct nd_defrouter *dr, *ndr; 1203287546eaSitojun 1204d8a7e3a7Sitojun defrouter_reset(); 1205568012e6Sbluhm TAILQ_FOREACH_SAFE(dr, &nd_defrouter, dr_entry, ndr) 1206287546eaSitojun defrtrlist_del(dr); 1207d8a7e3a7Sitojun defrouter_select(); 1208287546eaSitojun break; 1209287546eaSitojun } 1210287546eaSitojun case SIOCGNBRINFO_IN6: 1211287546eaSitojun { 1212287546eaSitojun struct llinfo_nd6 *ln; 1213287546eaSitojun struct in6_addr nb_addr = nbi->addr; /* make local for safety */ 1214f303499dSdlg time_t expire; 1215287546eaSitojun 1216287546eaSitojun /* 1217287546eaSitojun * XXX: KAME specific hack for scoped addresses 1218287546eaSitojun * XXXX: for other scopes than link-local? 1219287546eaSitojun */ 1220287546eaSitojun if (IN6_IS_ADDR_LINKLOCAL(&nbi->addr) || 1221287546eaSitojun IN6_IS_ADDR_MC_LINKLOCAL(&nbi->addr)) { 1222287546eaSitojun u_int16_t *idp = (u_int16_t *)&nb_addr.s6_addr[2]; 1223287546eaSitojun 1224287546eaSitojun if (*idp == 0) 1225287546eaSitojun *idp = htons(ifp->if_index); 1226287546eaSitojun } 1227287546eaSitojun 122816119bbfSmpi rt = nd6_lookup(&nb_addr, 0, ifp, ifp->if_rdomain); 122916119bbfSmpi if (rt == NULL || 1230d8a7e3a7Sitojun (ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) { 1231287546eaSitojun error = EINVAL; 123216119bbfSmpi rtfree(rt); 1233287546eaSitojun break; 1234287546eaSitojun } 12353bc414e3Sdlg expire = ln->ln_rt->rt_expire; 1236f303499dSdlg if (expire != 0) { 1237f303499dSdlg expire -= time_uptime; 1238f303499dSdlg expire += time_second; 1239f303499dSdlg } 1240f303499dSdlg 1241287546eaSitojun nbi->state = ln->ln_state; 1242287546eaSitojun nbi->asked = ln->ln_asked; 1243287546eaSitojun nbi->isrouter = ln->ln_router; 1244f303499dSdlg nbi->expire = expire; 124516119bbfSmpi rtfree(rt); 1246287546eaSitojun 1247287546eaSitojun break; 1248287546eaSitojun } 1249287546eaSitojun } 1250287546eaSitojun return (error); 1251287546eaSitojun } 1252287546eaSitojun 1253287546eaSitojun /* 1254287546eaSitojun * Create neighbor cache entry and cache link-layer address, 1255287546eaSitojun * on reception of inbound ND6 packets. (RS/RA/NS/redirect) 1256ee37ea65Smcbride * 1257ee37ea65Smcbride * type - ICMP6 type 1258ee37ea65Smcbride * code - type dependent information 1259287546eaSitojun */ 1260db435b2aSmpi void 1261ee37ea65Smcbride nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr, 1262ee37ea65Smcbride int lladdrlen, int type, int code) 1263287546eaSitojun { 1264287546eaSitojun struct rtentry *rt = NULL; 1265287546eaSitojun struct llinfo_nd6 *ln = NULL; 1266287546eaSitojun int is_newentry; 1267287546eaSitojun struct sockaddr_dl *sdl = NULL; 1268287546eaSitojun int do_update; 1269287546eaSitojun int olladdr; 1270287546eaSitojun int llchange; 1271287546eaSitojun int newstate = 0; 1272287546eaSitojun 1273287546eaSitojun if (!ifp) 1274287546eaSitojun panic("ifp == NULL in nd6_cache_lladdr"); 1275287546eaSitojun if (!from) 1276287546eaSitojun panic("from == NULL in nd6_cache_lladdr"); 1277287546eaSitojun 1278287546eaSitojun /* nothing must be updated for unspecified address */ 1279287546eaSitojun if (IN6_IS_ADDR_UNSPECIFIED(from)) 1280db435b2aSmpi return; 1281287546eaSitojun 1282287546eaSitojun /* 1283287546eaSitojun * Validation about ifp->if_addrlen and lladdrlen must be done in 1284287546eaSitojun * the caller. 1285287546eaSitojun * 12868b542bbeSpascoe * XXX If the link does not have link-layer address, what should 1287287546eaSitojun * we do? (ifp->if_addrlen == 0) 1288287546eaSitojun * Spec says nothing in sections for RA, RS and NA. There's small 1289287546eaSitojun * description on it in NS section (RFC 2461 7.2.3). 1290287546eaSitojun */ 1291287546eaSitojun 1292f4d1af37Smikeb rt = nd6_lookup(from, 0, ifp, ifp->if_rdomain); 129316119bbfSmpi if (rt == NULL) { 1294287546eaSitojun #if 0 1295287546eaSitojun /* nothing must be done if there's no lladdr */ 1296287546eaSitojun if (!lladdr || !lladdrlen) 1297287546eaSitojun return NULL; 1298287546eaSitojun #endif 1299287546eaSitojun 13005148b194Smpi rt = nd6_lookup(from, 1, ifp, ifp->if_rdomain); 1301287546eaSitojun is_newentry = 1; 13020a2c5741Sitojun } else { 13030a2c5741Sitojun /* do nothing if static ndp is set */ 130416119bbfSmpi if (rt->rt_flags & RTF_STATIC) { 130516119bbfSmpi rtfree(rt); 1306db435b2aSmpi return; 130716119bbfSmpi } 1308287546eaSitojun is_newentry = 0; 13090a2c5741Sitojun } 1310287546eaSitojun 1311287546eaSitojun if (!rt) 1312db435b2aSmpi return; 1313287546eaSitojun if ((rt->rt_flags & (RTF_GATEWAY | RTF_LLINFO)) != RTF_LLINFO) { 1314287546eaSitojun fail: 1315d8a7e3a7Sitojun (void)nd6_free(rt, 0); 131616119bbfSmpi rtfree(rt); 1317db435b2aSmpi return; 1318287546eaSitojun } 1319287546eaSitojun ln = (struct llinfo_nd6 *)rt->rt_llinfo; 13209c0643cdSbluhm if (ln == NULL) 1321287546eaSitojun goto fail; 13229c0643cdSbluhm if (rt->rt_gateway == NULL) 1323287546eaSitojun goto fail; 1324287546eaSitojun if (rt->rt_gateway->sa_family != AF_LINK) 1325287546eaSitojun goto fail; 1326c7b7b779Sbluhm sdl = satosdl(rt->rt_gateway); 1327287546eaSitojun 1328287546eaSitojun olladdr = (sdl->sdl_alen) ? 1 : 0; 1329287546eaSitojun if (olladdr && lladdr) { 1330287546eaSitojun if (bcmp(lladdr, LLADDR(sdl), ifp->if_addrlen)) 1331287546eaSitojun llchange = 1; 1332287546eaSitojun else 1333287546eaSitojun llchange = 0; 1334287546eaSitojun } else 1335287546eaSitojun llchange = 0; 1336287546eaSitojun 1337287546eaSitojun /* 1338287546eaSitojun * newentry olladdr lladdr llchange (*=record) 1339287546eaSitojun * 0 n n -- (1) 1340287546eaSitojun * 0 y n -- (2) 1341287546eaSitojun * 0 n y -- (3) * STALE 1342287546eaSitojun * 0 y y n (4) * 1343287546eaSitojun * 0 y y y (5) * STALE 1344287546eaSitojun * 1 -- n -- (6) NOSTATE(= PASSIVE) 1345287546eaSitojun * 1 -- y -- (7) * STALE 1346287546eaSitojun */ 1347287546eaSitojun 1348c8a7c9e3Sbluhm if (llchange) { 1349bbcf0337Smpi char addr[INET6_ADDRSTRLEN]; 1350c8a7c9e3Sbluhm log(LOG_INFO, "ndp info overwritten for %s by %s on %s\n", 1351bbcf0337Smpi inet_ntop(AF_INET6, from, addr, sizeof(addr)), 1352bbcf0337Smpi ether_sprintf(lladdr), ifp->if_xname); 1353c8a7c9e3Sbluhm } 1354287546eaSitojun if (lladdr) { /* (3-5) and (7) */ 1355287546eaSitojun /* 1356287546eaSitojun * Record source link-layer address 1357287546eaSitojun * XXX is it dependent to ifp->if_type? 1358287546eaSitojun */ 1359287546eaSitojun sdl->sdl_alen = ifp->if_addrlen; 1360287546eaSitojun bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen); 1361287546eaSitojun } 1362287546eaSitojun 1363287546eaSitojun if (!is_newentry) { 1364d8a7e3a7Sitojun if ((!olladdr && lladdr) || /* (3) */ 1365d8a7e3a7Sitojun (olladdr && lladdr && llchange)) { /* (5) */ 1366287546eaSitojun do_update = 1; 1367287546eaSitojun newstate = ND6_LLINFO_STALE; 1368287546eaSitojun } else /* (1-2,4) */ 1369287546eaSitojun do_update = 0; 1370287546eaSitojun } else { 1371287546eaSitojun do_update = 1; 1372287546eaSitojun if (!lladdr) /* (6) */ 1373287546eaSitojun newstate = ND6_LLINFO_NOSTATE; 1374287546eaSitojun else /* (7) */ 1375287546eaSitojun newstate = ND6_LLINFO_STALE; 1376287546eaSitojun } 1377287546eaSitojun 1378287546eaSitojun if (do_update) { 1379287546eaSitojun /* 1380287546eaSitojun * Update the state of the neighbor cache. 1381287546eaSitojun */ 1382287546eaSitojun ln->ln_state = newstate; 1383287546eaSitojun 1384287546eaSitojun if (ln->ln_state == ND6_LLINFO_STALE) { 13858a7bb304Sitojun /* 138653b39a0bSmpi * Since nd6_resolve() in ifp->if_output() will cause 13878b542bbeSpascoe * state transition to DELAY and reset the timer, 13888a7bb304Sitojun * we must set the timer now, although it is actually 13898a7bb304Sitojun * meaningless. 13908a7bb304Sitojun */ 1391134b71fcSdlg nd6_llinfo_settimer(ln, nd6_gctimer); 13928a7bb304Sitojun 1393287546eaSitojun if (ln->ln_hold) { 1394e212adedSkrw struct mbuf *n = ln->ln_hold; 1395e212adedSkrw ln->ln_hold = NULL; 13966afad192Sitojun /* 13976afad192Sitojun * we assume ifp is not a p2p here, so just 13986afad192Sitojun * set the 2nd argument as the 1st one. 13996afad192Sitojun */ 140053b39a0bSmpi ifp->if_output(ifp, n, rt_key(rt), rt); 1401e212adedSkrw if (ln->ln_hold == n) { 1402e212adedSkrw /* n is back in ln_hold. Discard. */ 1403e212adedSkrw m_freem(ln->ln_hold); 14048a7bb304Sitojun ln->ln_hold = NULL; 1405287546eaSitojun } 1406e212adedSkrw } 1407287546eaSitojun } else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { 1408287546eaSitojun /* probe right away */ 14099631a17bSitojun nd6_llinfo_settimer((void *)ln, 0); 1410287546eaSitojun } 1411287546eaSitojun } 1412287546eaSitojun 1413287546eaSitojun /* 1414287546eaSitojun * ICMP6 type dependent behavior. 1415287546eaSitojun * 1416287546eaSitojun * NS: clear IsRouter if new entry 1417287546eaSitojun * RS: clear IsRouter 1418287546eaSitojun * RA: set IsRouter if there's lladdr 1419287546eaSitojun * redir: clear IsRouter if new entry 1420287546eaSitojun * 1421287546eaSitojun * RA case, (1): 1422287546eaSitojun * The spec says that we must set IsRouter in the following cases: 1423287546eaSitojun * - If lladdr exist, set IsRouter. This means (1-5). 1424287546eaSitojun * - If it is old entry (!newentry), set IsRouter. This means (7). 1425287546eaSitojun * So, based on the spec, in (1-5) and (7) cases we must set IsRouter. 14268b542bbeSpascoe * A question arises for (1) case. (1) case has no lladdr in the 1427287546eaSitojun * neighbor cache, this is similar to (6). 1428287546eaSitojun * This case is rare but we figured that we MUST NOT set IsRouter. 1429287546eaSitojun * 1430287546eaSitojun * newentry olladdr lladdr llchange NS RS RA redir 1431287546eaSitojun * D R 1432287546eaSitojun * 0 n n -- (1) c ? s 1433287546eaSitojun * 0 y n -- (2) c s s 1434287546eaSitojun * 0 n y -- (3) c s s 1435287546eaSitojun * 0 y y n (4) c s s 1436287546eaSitojun * 0 y y y (5) c s s 1437287546eaSitojun * 1 -- n -- (6) c c c s 1438287546eaSitojun * 1 -- y -- (7) c c s c s 1439287546eaSitojun * 1440287546eaSitojun * (c=clear s=set) 1441287546eaSitojun */ 1442287546eaSitojun switch (type & 0xff) { 1443287546eaSitojun case ND_NEIGHBOR_SOLICIT: 1444287546eaSitojun /* 1445287546eaSitojun * New entry must have is_router flag cleared. 1446287546eaSitojun */ 1447287546eaSitojun if (is_newentry) /* (6-7) */ 1448287546eaSitojun ln->ln_router = 0; 1449287546eaSitojun break; 1450287546eaSitojun case ND_REDIRECT: 1451287546eaSitojun /* 1452287546eaSitojun * If the icmp is a redirect to a better router, always set the 1453287546eaSitojun * is_router flag. Otherwise, if the entry is newly created, 1454287546eaSitojun * clear the flag. [RFC 2461, sec 8.3] 1455287546eaSitojun */ 1456287546eaSitojun if (code == ND_REDIRECT_ROUTER) 1457287546eaSitojun ln->ln_router = 1; 1458287546eaSitojun else if (is_newentry) /* (6-7) */ 1459287546eaSitojun ln->ln_router = 0; 1460287546eaSitojun break; 1461287546eaSitojun case ND_ROUTER_SOLICIT: 1462287546eaSitojun /* 1463287546eaSitojun * is_router flag must always be cleared. 1464287546eaSitojun */ 1465287546eaSitojun ln->ln_router = 0; 1466287546eaSitojun break; 1467287546eaSitojun case ND_ROUTER_ADVERT: 1468287546eaSitojun /* 1469287546eaSitojun * Mark an entry with lladdr as a router. 1470287546eaSitojun */ 1471d8a7e3a7Sitojun if ((!is_newentry && (olladdr || lladdr)) || /* (2-5) */ 1472d8a7e3a7Sitojun (is_newentry && lladdr)) { /* (7) */ 1473287546eaSitojun ln->ln_router = 1; 1474287546eaSitojun } 1475287546eaSitojun break; 1476287546eaSitojun } 1477287546eaSitojun 147850ccc024Sitojun /* 147950ccc024Sitojun * When the link-layer address of a router changes, select the 148050ccc024Sitojun * best router again. In particular, when the neighbor entry is newly 148150ccc024Sitojun * created, it might affect the selection policy. 148250ccc024Sitojun * Question: can we restrict the first condition to the "is_newentry" 148350ccc024Sitojun * case? 148450ccc024Sitojun * XXX: when we hear an RA from a new router with the link-layer 148550ccc024Sitojun * address option, defrouter_select() is called twice, since 148650ccc024Sitojun * defrtrlist_update called the function as well. However, I believe 148750ccc024Sitojun * we can compromise the overhead, since it only happens the first 148850ccc024Sitojun * time. 148950ccc024Sitojun */ 1490d3b1c214Sflorian if (do_update && ln->ln_router && (ifp->if_xflags & IFXF_AUTOCONF6)) 149150ccc024Sitojun defrouter_select(); 149216119bbfSmpi 149316119bbfSmpi rtfree(rt); 1494287546eaSitojun } 1495287546eaSitojun 1496a0aa363cSjsing void 1497ee37ea65Smcbride nd6_slowtimo(void *ignored_arg) 1498287546eaSitojun { 1499b3c1e4c1Sitojun struct nd_ifinfo *nd6if; 1500d6b9e9b9Sitojun struct ifnet *ifp; 150122e88dadSmpi int s; 150222e88dadSmpi 150322e88dadSmpi NET_LOCK(s); 1504287546eaSitojun 150529e86e5eSblambert timeout_add_sec(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL); 150622e88dadSmpi 1507d814b14cSbluhm TAILQ_FOREACH(ifp, &ifnet, if_list) { 1508d6b9e9b9Sitojun nd6if = ND_IFINFO(ifp); 1509287546eaSitojun if (nd6if->basereachable && /* already initialized */ 1510287546eaSitojun (nd6if->recalctm -= ND6_SLOWTIMER_INTERVAL) <= 0) { 1511287546eaSitojun /* 1512287546eaSitojun * Since reachable time rarely changes by router 1513287546eaSitojun * advertisements, we SHOULD insure that a new random 1514287546eaSitojun * value gets recomputed at least once every few hours. 1515287546eaSitojun * (RFC 2461, 6.3.4) 1516287546eaSitojun */ 1517287546eaSitojun nd6if->recalctm = nd6_recalc_reachtm_interval; 1518287546eaSitojun nd6if->reachable = ND_COMPUTE_RTIME(nd6if->basereachable); 1519287546eaSitojun } 1520287546eaSitojun } 152122e88dadSmpi NET_UNLOCK(s); 1522287546eaSitojun } 1523287546eaSitojun 1524287546eaSitojun int 15255d32b3a0Smpi nd6_resolve(struct ifnet *ifp, struct rtentry *rt0, struct mbuf *m, 15265d32b3a0Smpi struct sockaddr *dst, u_char *desten) 15275d32b3a0Smpi { 15285d32b3a0Smpi struct sockaddr_dl *sdl; 15295d32b3a0Smpi struct rtentry *rt; 1530287546eaSitojun struct llinfo_nd6 *ln = NULL; 1531287546eaSitojun 15325d32b3a0Smpi if (m->m_flags & M_MCAST) { 15335d32b3a0Smpi ETHER_MAP_IPV6_MULTICAST(&satosin6(dst)->sin6_addr, desten); 15345d32b3a0Smpi return (0); 15355d32b3a0Smpi } 15365b3a2895Ssthen 1537c370e97fSmpi rt = rt_getll(rt0); 1538c370e97fSmpi 1539c370e97fSmpi if (ISSET(rt->rt_flags, RTF_REJECT) && 1540c370e97fSmpi (rt->rt_expire == 0 || time_uptime < rt->rt_expire)) { 15418c023157Smpi m_freem(m); 1542c370e97fSmpi return (rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); 1543287546eaSitojun } 1544cfb6b8dfSitojun 1545287546eaSitojun /* 1546287546eaSitojun * Address resolution or Neighbor Unreachability Detection 1547287546eaSitojun * for the next hop. 1548287546eaSitojun * At this point, the destination of the packet must be a unicast 1549287546eaSitojun * or an anycast address(i.e. not a multicast). 1550287546eaSitojun */ 15515da3fb44Smpi if (!ISSET(rt->rt_flags, RTF_LLINFO)) { 1552bbcf0337Smpi char addr[INET6_ADDRSTRLEN]; 15535da3fb44Smpi log(LOG_DEBUG, "%s: %s: route contains no ND information\n", 15545da3fb44Smpi __func__, inet_ntop(AF_INET6, 15555da3fb44Smpi &satosin6(rt_key(rt))->sin6_addr, addr, sizeof(addr))); 155616119bbfSmpi m_freem(m); 15575da3fb44Smpi return (EINVAL); 1558287546eaSitojun } 1559287546eaSitojun 15605d32b3a0Smpi if (rt->rt_gateway->sa_family != AF_LINK) { 15615d32b3a0Smpi printf("%s: something odd happens\n", __func__); 15625d32b3a0Smpi m_freem(m); 15635d32b3a0Smpi return (EINVAL); 15645d32b3a0Smpi } 15655d32b3a0Smpi 15665da3fb44Smpi ln = (struct llinfo_nd6 *)rt->rt_llinfo; 15675da3fb44Smpi KASSERT(ln != NULL); 1568cfb6b8dfSitojun 1569f3fcf2f3Smcbride /* 1570f3fcf2f3Smcbride * Move this entry to the head of the queue so that it is less likely 1571f3fcf2f3Smcbride * for this entry to be a target of forced garbage collection (see 1572f3fcf2f3Smcbride * nd6_rtrequest()). 1573f3fcf2f3Smcbride */ 157492c821ddSbluhm TAILQ_REMOVE(&nd6_list, ln, ln_list); 157592c821ddSbluhm TAILQ_INSERT_HEAD(&nd6_list, ln, ln_list); 1576f3fcf2f3Smcbride 1577287546eaSitojun /* 1578287546eaSitojun * The first time we send a packet to a neighbor whose entry is 1579287546eaSitojun * STALE, we have to change the state to DELAY and a sets a timer to 1580287546eaSitojun * expire in DELAY_FIRST_PROBE_TIME seconds to ensure do 1581287546eaSitojun * neighbor unreachability detection on expiration. 1582287546eaSitojun * (RFC 2461 7.3.3) 1583287546eaSitojun */ 1584287546eaSitojun if (ln->ln_state == ND6_LLINFO_STALE) { 1585287546eaSitojun ln->ln_asked = 0; 1586287546eaSitojun ln->ln_state = ND6_LLINFO_DELAY; 1587134b71fcSdlg nd6_llinfo_settimer(ln, nd6_delay); 1588287546eaSitojun } 1589287546eaSitojun 1590287546eaSitojun /* 1591287546eaSitojun * If the neighbor cache entry has a state other than INCOMPLETE 159234deef1eSitojun * (i.e. its link-layer address is already resolved), just 1593287546eaSitojun * send the packet. 1594287546eaSitojun */ 15955d32b3a0Smpi if (ln->ln_state > ND6_LLINFO_INCOMPLETE) { 15965d32b3a0Smpi sdl = satosdl(rt->rt_gateway); 15975d32b3a0Smpi if (sdl->sdl_alen != ETHER_ADDR_LEN) { 15985d32b3a0Smpi char addr[INET6_ADDRSTRLEN]; 15995d32b3a0Smpi log(LOG_DEBUG, "%s: %s: incorrect nd6 information\n", 16005d32b3a0Smpi __func__, 16015d32b3a0Smpi inet_ntop(AF_INET6, &satosin6(dst)->sin6_addr, 16025d32b3a0Smpi addr, sizeof(addr))); 16035d32b3a0Smpi m_freem(m); 16045d32b3a0Smpi return (EINVAL); 16055d32b3a0Smpi } 16065d32b3a0Smpi 16075d32b3a0Smpi bcopy(LLADDR(sdl), desten, sdl->sdl_alen); 16085d32b3a0Smpi return (0); 16095d32b3a0Smpi } 1610287546eaSitojun 1611287546eaSitojun /* 1612287546eaSitojun * There is a neighbor cache entry, but no ethernet address 1613287546eaSitojun * response yet. Replace the held mbuf (if any) with this 1614287546eaSitojun * latest one. 1615287546eaSitojun */ 1616efcf292bSitojun if (ln->ln_state == ND6_LLINFO_NOSTATE) 1617287546eaSitojun ln->ln_state = ND6_LLINFO_INCOMPLETE; 1618287546eaSitojun m_freem(ln->ln_hold); 1619287546eaSitojun ln->ln_hold = m; 16205d32b3a0Smpi 162176843262Sitojun /* 162276843262Sitojun * If there has been no NS for the neighbor after entering the 162376843262Sitojun * INCOMPLETE state, send the first solicitation. 162476843262Sitojun */ 16259631a17bSitojun if (!ND6_LLINFO_PERMANENT(ln) && ln->ln_asked == 0) { 1626287546eaSitojun ln->ln_asked++; 1627134b71fcSdlg nd6_llinfo_settimer(ln, ND_IFINFO(ifp)->retrans / 1000); 16285d32b3a0Smpi nd6_ns_output(ifp, NULL, &satosin6(dst)->sin6_addr, ln, 0); 1629287546eaSitojun } 16305d32b3a0Smpi return (EAGAIN); 1631287546eaSitojun } 1632287546eaSitojun 1633287546eaSitojun int 1634ee37ea65Smcbride nd6_need_cache(struct ifnet *ifp) 1635d8a7e3a7Sitojun { 1636d8a7e3a7Sitojun /* 1637d8a7e3a7Sitojun * RFC2893 says: 1638d8a7e3a7Sitojun * - unidirectional tunnels needs no ND 1639d8a7e3a7Sitojun */ 1640d8a7e3a7Sitojun switch (ifp->if_type) { 1641d8a7e3a7Sitojun case IFT_ETHER: 1642d8a7e3a7Sitojun case IFT_IEEE80211: 1643f4433d56Shenning case IFT_CARP: 1644d8a7e3a7Sitojun return (1); 1645d8a7e3a7Sitojun default: 1646d8a7e3a7Sitojun return (0); 1647d8a7e3a7Sitojun } 1648d8a7e3a7Sitojun } 1649d8a7e3a7Sitojun 1650ee37ea65Smcbride /* 1651ee37ea65Smcbride * oldp - syscall arg, need copyout 1652ee37ea65Smcbride * newp - syscall arg, need copyin 1653ee37ea65Smcbride */ 1654ee37ea65Smcbride 1655d8a7e3a7Sitojun int 1656a0aa363cSjsing nd6_sysctl(int name, void *oldp, size_t *oldlenp, void *newp, size_t newlen) 1657d8a7e3a7Sitojun { 1658db3db419Sitojun void *p; 165934223ca4Schl size_t ol; 1660d8a7e3a7Sitojun int error; 1661d8a7e3a7Sitojun 16624eb8db84Sbluhm NET_ASSERT_LOCKED(); 16634eb8db84Sbluhm 1664d8a7e3a7Sitojun error = 0; 1665d8a7e3a7Sitojun 1666d8a7e3a7Sitojun if (newp) 1667d8a7e3a7Sitojun return EPERM; 1668d8a7e3a7Sitojun if (oldp && !oldlenp) 1669d8a7e3a7Sitojun return EINVAL; 1670d8a7e3a7Sitojun ol = oldlenp ? *oldlenp : 0; 1671d8a7e3a7Sitojun 1672db3db419Sitojun if (oldp) { 167362cf959dSmpi p = malloc(ol, M_TEMP, M_WAITOK | M_CANFAIL); 1674db3db419Sitojun if (!p) 1675db3db419Sitojun return ENOMEM; 1676db3db419Sitojun } else 1677db3db419Sitojun p = NULL; 1678d8a7e3a7Sitojun switch (name) { 1679d8a7e3a7Sitojun case ICMPV6CTL_ND6_DRLIST: 1680db3db419Sitojun error = fill_drlist(p, oldlenp, ol); 1681db3db419Sitojun if (!error && p && oldp) 1682732568ffSitojun error = copyout(p, oldp, *oldlenp); 1683d8a7e3a7Sitojun break; 1684d8a7e3a7Sitojun 1685d8a7e3a7Sitojun case ICMPV6CTL_ND6_PRLIST: 1686db3db419Sitojun error = fill_prlist(p, oldlenp, ol); 1687db3db419Sitojun if (!error && p && oldp) 1688732568ffSitojun error = copyout(p, oldp, *oldlenp); 1689d8a7e3a7Sitojun break; 1690d8a7e3a7Sitojun 1691d8a7e3a7Sitojun default: 1692d8a7e3a7Sitojun error = ENOPROTOOPT; 1693d8a7e3a7Sitojun break; 1694d8a7e3a7Sitojun } 169562cf959dSmpi free(p, M_TEMP, ol); 1696d8a7e3a7Sitojun 1697d8a7e3a7Sitojun return (error); 1698d8a7e3a7Sitojun } 1699d8a7e3a7Sitojun 1700a0aa363cSjsing int 1701ee37ea65Smcbride fill_drlist(void *oldp, size_t *oldlenp, size_t ol) 1702d8a7e3a7Sitojun { 17034eb8db84Sbluhm int error = 0; 1704d8a7e3a7Sitojun struct in6_defrouter *d = NULL, *de = NULL; 1705d8a7e3a7Sitojun struct nd_defrouter *dr; 170656b4879eSmpi time_t expire; 1707d8a7e3a7Sitojun size_t l; 1708d8a7e3a7Sitojun 1709d8a7e3a7Sitojun if (oldp) { 1710d8a7e3a7Sitojun d = (struct in6_defrouter *)oldp; 1711d8a7e3a7Sitojun de = (struct in6_defrouter *)((caddr_t)oldp + *oldlenp); 1712d8a7e3a7Sitojun } 1713d8a7e3a7Sitojun l = 0; 1714d8a7e3a7Sitojun 1715568012e6Sbluhm TAILQ_FOREACH(dr, &nd_defrouter, dr_entry) { 1716d8a7e3a7Sitojun if (oldp && d + 1 <= de) { 1717d8a7e3a7Sitojun bzero(d, sizeof(*d)); 1718d8a7e3a7Sitojun d->rtaddr.sin6_family = AF_INET6; 1719d8a7e3a7Sitojun d->rtaddr.sin6_len = sizeof(struct sockaddr_in6); 17206c8dd0e9Sclaudio in6_recoverscope(&d->rtaddr, &dr->rtaddr); 1721d8a7e3a7Sitojun d->flags = dr->flags; 1722d8a7e3a7Sitojun d->rtlifetime = dr->rtlifetime; 172356b4879eSmpi expire = dr->expire; 172456b4879eSmpi if (expire != 0) { 172556b4879eSmpi expire -= time_uptime; 172656b4879eSmpi expire += time_second; 172756b4879eSmpi } 172856b4879eSmpi d->expire = expire; 1729d8a7e3a7Sitojun d->if_index = dr->ifp->if_index; 1730d8a7e3a7Sitojun } 1731d8a7e3a7Sitojun 1732d8a7e3a7Sitojun l += sizeof(*d); 1733d8a7e3a7Sitojun if (d) 1734d8a7e3a7Sitojun d++; 1735d8a7e3a7Sitojun } 1736d8a7e3a7Sitojun 1737d8a7e3a7Sitojun if (oldp) { 1738d8a7e3a7Sitojun *oldlenp = l; /* (caddr_t)d - (caddr_t)oldp */ 1739d8a7e3a7Sitojun if (l > ol) 1740d8a7e3a7Sitojun error = ENOMEM; 1741d8a7e3a7Sitojun } else 1742d8a7e3a7Sitojun *oldlenp = l; 1743d8a7e3a7Sitojun 1744d8a7e3a7Sitojun return (error); 1745d8a7e3a7Sitojun } 1746d8a7e3a7Sitojun 1747a0aa363cSjsing int 1748ee37ea65Smcbride fill_prlist(void *oldp, size_t *oldlenp, size_t ol) 1749d8a7e3a7Sitojun { 17504eb8db84Sbluhm int error = 0; 1751d8a7e3a7Sitojun struct nd_prefix *pr; 175208247436Snaddy char *p = NULL, *ps = NULL; 175308247436Snaddy char *pe = NULL; 1754d8a7e3a7Sitojun size_t l; 1755d8a7e3a7Sitojun 1756d8a7e3a7Sitojun if (oldp) { 175708247436Snaddy ps = p = (char *)oldp; 175808247436Snaddy pe = (char *)oldp + *oldlenp; 1759d8a7e3a7Sitojun } 1760d8a7e3a7Sitojun l = 0; 1761d8a7e3a7Sitojun 1762545205eaSpyr LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { 1763d8a7e3a7Sitojun u_short advrtrs; 176408247436Snaddy struct sockaddr_in6 sin6; 1765d8a7e3a7Sitojun struct nd_pfxrouter *pfr; 176608247436Snaddy struct in6_prefix pfx; 1767d8a7e3a7Sitojun 176808247436Snaddy if (oldp && p + sizeof(struct in6_prefix) <= pe) { 176908247436Snaddy memset(&pfx, 0, sizeof(pfx)); 177008247436Snaddy ps = p; 1771d8a7e3a7Sitojun 177208247436Snaddy pfx.prefix = pr->ndpr_prefix; 17736c8dd0e9Sclaudio in6_recoverscope(&pfx.prefix, 17746c8dd0e9Sclaudio &pfx.prefix.sin6_addr); 177508247436Snaddy pfx.raflags = pr->ndpr_raf; 177608247436Snaddy pfx.prefixlen = pr->ndpr_plen; 177708247436Snaddy pfx.vltime = pr->ndpr_vltime; 177808247436Snaddy pfx.pltime = pr->ndpr_pltime; 177908247436Snaddy pfx.if_index = pr->ndpr_ifp->if_index; 1780d8a7e3a7Sitojun if (pr->ndpr_vltime == ND6_INFINITE_LIFETIME) 178108247436Snaddy pfx.expire = 0; 1782d8a7e3a7Sitojun else { 1783d8a7e3a7Sitojun time_t maxexpire; 1784d8a7e3a7Sitojun 1785d8a7e3a7Sitojun /* XXX: we assume time_t is signed. */ 178691a535ffSguenther maxexpire = (time_t)~(1ULL << 178791a535ffSguenther ((sizeof(maxexpire) * 8) - 1)); 1788d8a7e3a7Sitojun if (pr->ndpr_vltime < 1789d8a7e3a7Sitojun maxexpire - pr->ndpr_lastupdate) { 179008247436Snaddy pfx.expire = pr->ndpr_lastupdate + 1791d8a7e3a7Sitojun pr->ndpr_vltime; 1792d8a7e3a7Sitojun } else 179308247436Snaddy pfx.expire = maxexpire; 1794d8a7e3a7Sitojun } 179508247436Snaddy pfx.refcnt = pr->ndpr_refcnt; 179608247436Snaddy pfx.flags = pr->ndpr_stateflags; 179708247436Snaddy pfx.origin = PR_ORIG_RA; 179808247436Snaddy 179908247436Snaddy p += sizeof(pfx); l += sizeof(pfx); 180008247436Snaddy 1801d8a7e3a7Sitojun advrtrs = 0; 1802545205eaSpyr LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry) { 180308247436Snaddy if (p + sizeof(sin6) > pe) { 1804d8a7e3a7Sitojun advrtrs++; 1805d8a7e3a7Sitojun continue; 1806d8a7e3a7Sitojun } 18079c794535Sbluhm bzero(&sin6, sizeof(sin6)); 180808247436Snaddy sin6.sin6_family = AF_INET6; 180908247436Snaddy sin6.sin6_len = sizeof(struct sockaddr_in6); 18106c8dd0e9Sclaudio in6_recoverscope(&sin6, &pfr->router->rtaddr); 1811d8a7e3a7Sitojun advrtrs++; 181208247436Snaddy memcpy(p, &sin6, sizeof(sin6)); 181308247436Snaddy p += sizeof(sin6); 181408247436Snaddy l += sizeof(sin6); 1815d8a7e3a7Sitojun } 181608247436Snaddy pfx.advrtrs = advrtrs; 181708247436Snaddy memcpy(ps, &pfx, sizeof(pfx)); 1818d8a7e3a7Sitojun } 1819d8a7e3a7Sitojun else { 182008247436Snaddy l += sizeof(pfx); 1821d8a7e3a7Sitojun advrtrs = 0; 182208247436Snaddy LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry) { 1823d8a7e3a7Sitojun advrtrs++; 182408247436Snaddy l += sizeof(sin6); 1825d8a7e3a7Sitojun } 182608247436Snaddy } 1827d8a7e3a7Sitojun } 1828d8a7e3a7Sitojun 1829d8a7e3a7Sitojun if (oldp) { 1830d8a7e3a7Sitojun *oldlenp = l; /* (caddr_t)d - (caddr_t)oldp */ 1831d8a7e3a7Sitojun if (l > ol) 1832d8a7e3a7Sitojun error = ENOMEM; 1833d8a7e3a7Sitojun } else 1834d8a7e3a7Sitojun *oldlenp = l; 1835d8a7e3a7Sitojun 1836d8a7e3a7Sitojun return (error); 1837d8a7e3a7Sitojun } 1838