This moves sending of router solicitation packages to the kernel. With
it rtsol{,d}(8) is no longer needed.
Add
inet6 autoconf
to /etc/hostname.IF
or run ifconfig IF inet6 autoconf and the kernel will start sending
rtsol packages.
An the following events a timer will be (re) started with a timeout of
1 second if the autoconf flag is set on the interface. It will
exponentially back off and until a router advertisement is received and
then settle on a 60 second timeout:
* ifconfig IF inet6 autoconf is called
* link is comming up
* the interface is set to up

Much help from mpi@ who patiently reviewed various diffs and explained
the intricacies of the network stack.

Now, mpi@ raised a very valid question: "Does this code [handling of
router advertisements and sending of solicitations] belong in the
kernel?"

I have no good answer for that. I guess not. However historically the
handling of router advertisements is part of the kernel. This is not so
much more code. And it's very convenient. No need to run another
daemon, the kernel knows very well when a router solicitation should
be send etc. And we can get rid of rtsol{,d}(8).

So in my opinion it's worth to move it to the kernel.

Once the magical "network-management-daemon" materializes we can rip
the router advertisement and solicitation handling out of the kernel
if we want to.

(Btw. Once the torches are lit and the pitchforks raised, how far do
we go? If parsing of router advertisements can go to userland, is
duplicate address detection (DAD) fair game?)

I've been running with this (or variations of the diff) since g2k14.

OK?

diff --git net/if.c net/if.c
index c325265..2081cf6 100644
--- net/if.c
+++ net/if.c
@@ -1344,6 +1344,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 netinet6/in6.c netinet6/in6.c
index e967089..2bf27f4 100644
--- netinet6/in6.c
+++ netinet6/in6.c
@@ -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 netinet6/in6_ifattach.c netinet6/in6_ifattach.c
index 93620c4..6703c1e 100644
--- netinet6/in6_ifattach.c
+++ netinet6/in6_ifattach.c
@@ -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 netinet6/in6_var.h netinet6/in6_var.h
index ed1786b..11a42b7 100644
--- netinet6/in6_var.h
+++ netinet6/in6_var.h
@@ -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 netinet6/nd6.c netinet6/nd6.c
index 044a6ea..a653ca6 100644
--- netinet6/nd6.c
+++ netinet6/nd6.c
@@ -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; 
+       else 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 netinet6/nd6.h netinet6/nd6.h
index f17e695..49c79f9 100644
--- netinet6/nd6.h
+++ netinet6/nd6.h
@@ -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 netinet6/nd6_rtr.c netinet6/nd6_rtr.c
index e8a7b10..8511493 100644
--- netinet6/nd6_rtr.c
+++ netinet6/nd6_rtr.c
@@ -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,109 @@ 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 && (ifp->if_flags & IFF_RUNNING) &&
+           (ifp->if_xflags & IFXF_AUTOCONF6) &&
+           !(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_routeradvert);
+       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 +305,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",

-- 
I'm not entirely sure you are real.

Reply via email to