From 110585f259f4974284e531f0a1e121b001a580dc Mon Sep 17 00:00:00 2001 From: florian Date: Mon, 25 Aug 2014 14:00:34 +0000 Subject: [PATCH] Move sending of router solicitations to the kernel; receiving and processing of router advertisements was already in the kernel. With this rtsol{,d}(8) is no longer necessary. The kernel starts sending solicitations with # ifconfig $IF inet6 autoconf or inet6 autoconf in /etc/hostname.$IF. input stsp@ much help & OK mpi@ tweaks & OK bluhm@ --- sys/net/if.c | 22 +++++++- sys/netinet6/in6.c | 5 +- sys/netinet6/in6_ifattach.c | 11 +++- sys/netinet6/in6_var.h | 3 +- sys/netinet6/nd6.c | 42 +++++++++++++- sys/netinet6/nd6.h | 14 ++++- sys/netinet6/nd6_rtr.c | 109 +++++++++++++++++++++++++++++++++++- 7 files changed, 199 insertions(+), 7 deletions(-) diff --git a/sys/net/if.c b/sys/net/if.c index 92c2d167c65..3b54d7abaee 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if.c,v 1.299 2014/08/14 11:38:14 mikeb Exp $ */ +/* $OpenBSD: if.c,v 1.300 2014/08/25 14:00:34 florian Exp $ */ /* $NetBSD: if.c,v 1.35 1996/05/07 05:26:04 thorpej Exp $ */ /* @@ -1339,6 +1339,26 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, struct proc *p) splx(s); } } + + if (ifr->ifr_flags & IFXF_AUTOCONF6) + nd6_rs_output_set_timo(ND6_RS_OUTPUT_QUICK_INTERVAL); + + if ((ifr->ifr_flags & IFXF_AUTOCONF6) && + !(ifp->if_xflags & IFXF_AUTOCONF6)) { + nd6_rs_timeout_count++; + RS_LHCOOKIE(ifp) = hook_establish( + ifp->if_linkstatehooks, 1, nd6_rs_dev_state, ifp); + if (!timeout_pending(&nd6_rs_output_timer)) + nd6_rs_output_set_timo(nd6_rs_output_timeout); + } + if ((ifp->if_xflags & IFXF_AUTOCONF6) && + !(ifr->ifr_flags & IFXF_AUTOCONF6)) { + hook_disestablish(ifp->if_linkstatehooks, + RS_LHCOOKIE(ifp)); + nd6_rs_timeout_count--; + if (nd6_rs_timeout_count == 0) + timeout_del(&nd6_rs_output_timer); + } #endif #ifdef MPLS diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index e9670897dda..1a9dc206158 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in6.c,v 1.138 2014/07/12 18:44:23 tedu Exp $ */ +/* $OpenBSD: in6.c,v 1.139 2014/08/25 14:00:34 florian Exp $ */ /* $KAME: in6.c,v 1.372 2004/06/14 08:14:21 itojun Exp $ */ /* @@ -2087,6 +2087,9 @@ in6_if_up(struct ifnet *ifp) if (ia6->ia6_flags & IN6_IFF_TENTATIVE) nd6_dad_start(ifa, &dad_delay); } + + if (ifp->if_xflags & IFXF_AUTOCONF6) + nd6_rs_output_set_timo(ND6_RS_OUTPUT_QUICK_INTERVAL); } int diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c index 93620c48377..7f812902a6b 100644 --- a/sys/netinet6/in6_ifattach.c +++ b/sys/netinet6/in6_ifattach.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in6_ifattach.c,v 1.72 2014/07/01 19:37:07 benno Exp $ */ +/* $OpenBSD: in6_ifattach.c,v 1.73 2014/08/25 14:00:34 florian Exp $ */ /* $KAME: in6_ifattach.c,v 1.124 2001/07/18 08:32:51 jinmei Exp $ */ /* @@ -689,4 +689,13 @@ in6_ifdetach(struct ifnet *ifp) ifp->if_rdomain); rtfree(rt); } + + if (ifp->if_xflags & IFXF_AUTOCONF6) { + nd6_rs_timeout_count--; + if (nd6_rs_timeout_count == 0) + timeout_del(&nd6_rs_output_timer); + if (RS_LHCOOKIE(ifp) != NULL) + hook_disestablish(ifp->if_linkstatehooks, + RS_LHCOOKIE(ifp)); + } } diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h index ed1786b466c..d2c444617d9 100644 --- a/sys/netinet6/in6_var.h +++ b/sys/netinet6/in6_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in6_var.h,v 1.49 2014/04/03 08:22:10 mpi Exp $ */ +/* $OpenBSD: in6_var.h,v 1.50 2014/08/25 14:00:34 florian Exp $ */ /* $KAME: in6_var.h,v 1.55 2001/02/16 12:49:45 itojun Exp $ */ /* @@ -92,6 +92,7 @@ struct in6_ifextra { struct in6_ifstat *in6_ifstat; struct icmp6_ifstat *icmp6_ifstat; struct nd_ifinfo *nd_ifinfo; + void *rs_lhcookie; int nprefixes; int ndefrouters; }; diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index d5c48f669b8..e2d018fc4c9 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -1,4 +1,4 @@ -/* $OpenBSD: nd6.c,v 1.121 2014/08/11 13:51:07 mpi Exp $ */ +/* $OpenBSD: nd6.c,v 1.122 2014/08/25 14:00:34 florian Exp $ */ /* $KAME: nd6.c,v 1.280 2002/06/08 19:52:07 itojun Exp $ */ /* @@ -103,6 +103,11 @@ struct timeout nd6_timer_ch; struct task nd6_timer_task; void nd6_timer_work(void *, void *); +struct timeout nd6_rs_output_timer; +int nd6_rs_output_timeout = ND6_RS_OUTPUT_INTERVAL; +int nd6_rs_timeout_count = 0; +void nd6_rs_output_timo(void *); + int fill_drlist(void *, size_t *, size_t); int fill_prlist(void *, size_t *, size_t); @@ -137,6 +142,8 @@ nd6_init(void) /* start timer */ timeout_set(&nd6_slowtimo_ch, nd6_slowtimo, NULL); timeout_add_sec(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL); + + timeout_set(&nd6_rs_output_timer, nd6_rs_output_timo, NULL); } struct nd_ifinfo * @@ -1607,6 +1614,39 @@ nd6_slowtimo(void *ignored_arg) splx(s); } +void +nd6_rs_output_set_timo(int timeout) +{ + nd6_rs_output_timeout = timeout; + timeout_add_sec(&nd6_rs_output_timer, nd6_rs_output_timeout); +} + +void +nd6_rs_output_timo(void *ignored_arg) +{ + struct ifnet *ifp; + struct in6_ifaddr *ia6; + + if (nd6_rs_timeout_count == 0) + return; + + if (nd6_rs_output_timeout < ND6_RS_OUTPUT_INTERVAL) + /* exponential backoff if running quick timeouts */ + nd6_rs_output_timeout *= 2; + if (nd6_rs_output_timeout > ND6_RS_OUTPUT_INTERVAL) + nd6_rs_output_timeout = ND6_RS_OUTPUT_INTERVAL; + + TAILQ_FOREACH(ifp, &ifnet, if_list) { + if (ISSET(ifp->if_flags, IFF_RUNNING) && + ISSET(ifp->if_xflags, IFXF_AUTOCONF6)) { + ia6 = in6ifa_ifpforlinklocal(ifp, IN6_IFF_TENTATIVE); + if (ia6 != NULL) + nd6_rs_output(ifp, ia6); + } + } + timeout_add_sec(&nd6_rs_output_timer, nd6_rs_output_timeout); +} + #define senderr(e) { error = (e); goto bad;} int nd6_output(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m0, diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h index f17e69535f5..e5ca468ed28 100644 --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -1,4 +1,4 @@ -/* $OpenBSD: nd6.h,v 1.38 2014/07/11 15:03:17 blambert Exp $ */ +/* $OpenBSD: nd6.h,v 1.39 2014/08/25 14:00:34 florian Exp $ */ /* $KAME: nd6.h,v 1.95 2002/06/08 11:31:06 itojun Exp $ */ /* @@ -134,6 +134,9 @@ struct in6_ndifreq { #define ND_IFINFO(ifp) \ (((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->nd_ifinfo) +#define RS_LHCOOKIE(ifp) \ + ((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->rs_lhcookie + #define IN6_LINKMTU(ifp) \ ((ND_IFINFO(ifp)->linkmtu && ND_IFINFO(ifp)->linkmtu < (ifp)->if_mtu) \ ? ND_IFINFO(ifp)->linkmtu \ @@ -230,6 +233,12 @@ extern int nd6_debug; extern struct timeout nd6_timer_ch; +#define ND6_RS_OUTPUT_INTERVAL 60 +#define ND6_RS_OUTPUT_QUICK_INTERVAL 1 +extern struct timeout nd6_rs_output_timer; +extern int nd6_rs_output_timeout; +extern int nd6_rs_timeout_count; + union nd_opts { struct nd_opt_hdr *nd_opt_array[9]; struct { @@ -294,6 +303,9 @@ void nd6_dad_duplicated(struct ifaddr *); void nd6_rs_input(struct mbuf *, int, int); void nd6_ra_input(struct mbuf *, int, int); +void nd6_rs_output_set_timo(int); +void nd6_rs_output(struct ifnet *, struct in6_ifaddr *); +void nd6_rs_dev_state(void *); void prelist_del(struct nd_prefix *); void defrouter_addreq(struct nd_defrouter *); void defrouter_reset(void); diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index e8a7b10d487..a50dbf7c080 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: nd6_rtr.c,v 1.83 2014/07/12 18:44:23 tedu Exp $ */ +/* $OpenBSD: nd6_rtr.c,v 1.84 2014/08/25 14:00:34 florian Exp $ */ /* $KAME: nd6_rtr.c,v 1.97 2001/02/07 11:09:13 itojun Exp $ */ /* @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -170,6 +171,108 @@ nd6_rs_input(struct mbuf *m, int off, int icmp6len) m_freem(m); } +void +nd6_rs_output(struct ifnet* ifp, struct in6_ifaddr *ia6) +{ + struct mbuf *m; + struct ip6_hdr *ip6; + struct nd_router_solicit *rs; + struct ip6_moptions im6o; + caddr_t mac; + int icmp6len, maxlen, s; + + KASSERT(ia6 != NULL); + KASSERT(ifp->if_flags & IFF_RUNNING); + KASSERT(ifp->if_xflags & IFXF_AUTOCONF6); + KASSERT(!(ia6->ia6_flags & IN6_IFF_TENTATIVE)); + + maxlen = sizeof(*ip6) + sizeof(*rs); + maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m && max_linkhdr + maxlen >= MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_free(m); + m = NULL; + } + } + if (m == NULL) + return; + + m->m_pkthdr.rcvif = NULL; + m->m_pkthdr.ph_rtableid = ifp->if_rdomain; + m->m_flags |= M_MCAST; + m->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT; + + im6o.im6o_multicast_ifp = ifp; + im6o.im6o_multicast_hlim = 255; + im6o.im6o_multicast_loop = 0; + + icmp6len = sizeof(*rs); + m->m_pkthdr.len = m->m_len = sizeof(*ip6) + icmp6len; + m->m_data += max_linkhdr; /* or MH_ALIGN() equivalent? */ + + /* fill neighbor solicitation packet */ + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_flow = 0; + ip6->ip6_vfc &= ~IPV6_VERSION_MASK; + ip6->ip6_vfc |= IPV6_VERSION; + /* ip6->ip6_plen will be set later */ + ip6->ip6_nxt = IPPROTO_ICMPV6; + ip6->ip6_hlim = 255; + bzero(&ip6->ip6_dst, sizeof(struct in6_addr)); + + ip6->ip6_dst.s6_addr16[0] = htons(0xff02); + ip6->ip6_dst.s6_addr8[15] = 0x02; + + ip6->ip6_src = ia6->ia_addr.sin6_addr; + + rs = (struct nd_router_solicit *)(ip6 + 1); + rs->nd_rs_type = ND_ROUTER_SOLICIT; + rs->nd_rs_code = 0; + rs->nd_rs_cksum = 0; + rs->nd_rs_reserved = 0; + + if ((mac = nd6_ifptomac(ifp))) { + int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; + struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(rs + 1); + /* 8 byte alignments... */ + optlen = (optlen + 7) & ~7; + + m->m_pkthdr.len += optlen; + m->m_len += optlen; + icmp6len += optlen; + bzero((caddr_t)nd_opt, optlen); + nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; + nd_opt->nd_opt_len = optlen >> 3; + bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen); + } + + ip6->ip6_plen = htons((u_short)icmp6len); + + s = splsoftnet(); + ip6_output(m, NULL, NULL, 0, &im6o, NULL, NULL); + splx(s); + + icmp6_ifstat_inc(ifp, ifs6_out_msg); + icmp6_ifstat_inc(ifp, ifs6_out_routersolicit); + icmp6stat.icp6s_outhist[ND_ROUTER_SOLICIT]++; +} + +void +nd6_rs_dev_state(void *arg) +{ + struct ifnet *ifp; + + ifp = (struct ifnet *) arg; + + if (LINK_STATE_IS_UP(ifp->if_link_state) && + ifp->if_flags & IFF_RUNNING) + /* start quick timer, will exponentially back off */ + nd6_rs_output_set_timo(ND6_RS_OUTPUT_QUICK_INTERVAL); +} + /* * Receive Router Advertisement Message. * @@ -201,6 +304,10 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len) if (!(ndi->flags & ND6_IFF_ACCEPT_RTADV)) goto freeit; + if (nd6_rs_output_timeout != ND6_RS_OUTPUT_INTERVAL) + /* we saw a RA, stop quick timer */ + nd6_rs_output_set_timo(ND6_RS_OUTPUT_INTERVAL); + if (ip6->ip6_hlim != 255) { nd6log((LOG_ERR, "nd6_ra_input: invalid hlim (%d) from %s to %s on %s\n", -- 2.20.1