Move sending of router solicitations to the kernel; receiving and
authorflorian <florian@openbsd.org>
Mon, 25 Aug 2014 14:00:34 +0000 (14:00 +0000)
committerflorian <florian@openbsd.org>
Mon, 25 Aug 2014 14:00:34 +0000 (14:00 +0000)
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
sys/netinet6/in6.c
sys/netinet6/in6_ifattach.c
sys/netinet6/in6_var.h
sys/netinet6/nd6.c
sys/netinet6/nd6.h
sys/netinet6/nd6_rtr.c

index 92c2d16..3b54d7a 100644 (file)
@@ -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
index e967089..1a9dc20 100644 (file)
@@ -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
index 93620c4..7f81290 100644 (file)
@@ -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));
+       }
 }
index ed1786b..d2c4446 100644 (file)
@@ -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;
 };
index d5c48f6..e2d018f 100644 (file)
@@ -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, 
index f17e695..e5ca468 100644 (file)
@@ -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);
index e8a7b10..a50dbf7 100644 (file)
@@ -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 <sys/param.h>
 #include <sys/systm.h>
+#include <sys/timeout.h>
 #include <sys/malloc.h>
 #include <sys/mbuf.h>
 #include <sys/socket.h>
@@ -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",