Hi,

I've implemented support for IPv6 SLAAC in brlib (and by extension,
rumprun).

Please review the following patch; it all seems to work here but I've not
written NetBSD kernel code that accesses the socket layer before so I may
have missed something subtle.

I'm not posting the change required for rumprun/rumpconfig since that's
trivial.

One thing I'm not sure about, I modeled the sending code on what
nfs_boot_sendrecv() does, am I right in understanding that if so_send()
completes successfully it takes ownership of the "nam" and "m" mbufs passed
to it?

-mato

diff --git a/brlib/libnetconfig/netconfig.c b/brlib/libnetconfig/netconfig.c
index 7a33b0e..43d6aa2 100644
--- a/brlib/libnetconfig/netconfig.c
+++ b/brlib/libnetconfig/netconfig.c
@@ -28,9 +28,13 @@
 #include <sys/socketvar.h>
 
 #include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_ether.h>
+#include <net/if_types.h>
 #include <net/route.h>
 
 #include <netinet/in.h>
+#include <netinet/icmp6.h>
 
 #include <netinet6/in6.h>
 #include <netinet6/in6_var.h>
@@ -317,6 +321,93 @@ rump_netconfig_ipv6_gw(const char *gwaddr)
        return rv;
 }
 
+extern int ip6_accept_rtadv;
+
+/* Perform IPv6 autoconfiguration for the specified interface.
+ * This function sets the kernel to accept IPv6 RAs on all interfaces,
+ * brings the interface up and sends a single IPv6 RS packet to the
+ * all-routers multicast address from the specified interface. No attempt is
+ * made to check whether or not this actually provoked an RA in response.
+ */
+int
+rump_netconfig_auto_ipv6_oneshot(const char *ifname)
+{
+       struct ifnet *ifp = NULL;
+       int ifindex;
+       struct socket *rsso = NULL;
+       int rv = 0;
+       int hoplimit = 255;
+       struct mbuf *m = NULL,
+                   *nam = NULL;
+       struct sockaddr_in6 *sin6;
+       char *buf;
+       struct nd_router_solicit *rs;
+       struct nd_opt_hdr *opt;
+       size_t rslen;
+
+       ifp = ifunit(ifname);
+       if (ifp == NULL)
+               return ENODEV;
+       if (ifp->if_sadl->sdl_type != IFT_ETHER)
+               return EINVAL;
+
+       rv = socreate(PF_INET6, &rsso, SOCK_RAW, IPPROTO_ICMPV6, curlwp, NULL);
+       if (rv != 0)
+               goto out;
+       ifindex = ifp->if_index;
+       rv = so_setsockopt(curlwp, rsso, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+                       &ifindex, sizeof ifindex);
+       if (rv != 0)
+               goto out;
+       rv = so_setsockopt(curlwp, rsso, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+                       &hoplimit, sizeof hoplimit);
+       if (rv != 0)
+               goto out;
+
+       nam = m_get(M_WAIT, MT_SONAME);
+       sin6 = mtod(nam, struct sockaddr_in6 *);
+       sin6->sin6_len = nam->m_len = sizeof (*sin6);
+       sin6->sin6_family = AF_INET6;
+       netconfig_inet_pton6("ff02::2", &sin6->sin6_addr);
+
+       rslen = sizeof (struct nd_router_solicit) +
+               sizeof (struct nd_opt_hdr) + ETHER_ADDR_LEN;
+       m = m_gethdr(M_WAIT, MT_DATA);
+       m_clget(m, M_WAIT);
+       m->m_pkthdr.len = m->m_len = rslen;
+       m->m_pkthdr.rcvif = NULL;
+       buf = mtod(m, char *);
+       memset (buf, 0, rslen);
+       rs = (struct nd_router_solicit *)buf;
+       rs->nd_rs_type = ND_ROUTER_SOLICIT;
+       buf += sizeof (*rs);
+       opt = (struct nd_opt_hdr *)buf;
+       opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR;
+       opt->nd_opt_len = 1; /* units of 8 octets */
+       buf += sizeof (*opt);
+       memcpy(buf, ifp->if_sadl->sdl_data + ifp->if_sadl->sdl_nlen,
+               ETHER_ADDR_LEN);
+
+       ip6_accept_rtadv = 1; /* XXX */
+       rv = rump_netconfig_ifup(ifname);
+       if (rv != 0)
+               goto out;
+       rv = (*rsso->so_send)(rsso, nam, NULL, m, NULL, 0, curlwp);
+       if (rv != 0)
+               goto out;
+       m = nam = NULL;
+
+       rv = 0;
+out:
+       if (m)
+               m_freem(m);
+       if (nam)
+               m_freem(nam);
+       if (rsso)
+               soclose(rsso);
+       return rv;
+}
+
 RUMP_COMPONENT(RUMP_COMPONENT_NET_IFCFG)
 {
        int rv;
diff --git a/brlib/libnetconfig/netconfig.ifspec 
b/brlib/libnetconfig/netconfig.ifspec
index ccd1d01..0626663 100644
--- a/brlib/libnetconfig/netconfig.ifspec
+++ b/brlib/libnetconfig/netconfig.ifspec
@@ -36,3 +36,6 @@ int   |netconfig_ipv6_gw      |const char *
 
 ; dhcp
 int    |netconfig_dhcp_ipv4_oneshot    |const char *
+
+; ipv6 autoconfiguration
+int    |netconfig_auto_ipv6_oneshot    |const char *
diff --git a/brlib/libnetconfig/netconfig_if_priv.h 
b/brlib/libnetconfig/netconfig_if_priv.h
index a95101c..2395244 100644
--- a/brlib/libnetconfig/netconfig_if_priv.h
+++ b/brlib/libnetconfig/netconfig_if_priv.h
@@ -16,3 +16,4 @@ int rump_netconfig_ipv6_ifaddr(const char *, const char *, 
int);
 int rump_netconfig_ipv4_gw(const char *);
 int rump_netconfig_ipv6_gw(const char *);
 int rump_netconfig_dhcp_ipv4_oneshot(const char *);
+int rump_netconfig_auto_ipv6_oneshot(const char *);
diff --git a/brlib/libnetconfig/netconfig_if_wrappers.c 
b/brlib/libnetconfig/netconfig_if_wrappers.c
index 1240373..de09a75 100644
--- a/brlib/libnetconfig/netconfig_if_wrappers.c
+++ b/brlib/libnetconfig/netconfig_if_wrappers.c
@@ -142,3 +142,15 @@ rump_pub_netconfig_dhcp_ipv4_oneshot(const char *arg1)
 
        return rv;
 }
+
+int
+rump_pub_netconfig_auto_ipv6_oneshot(const char *arg1)
+{
+       int rv;
+
+       rump_schedule();
+       rv = rump_netconfig_auto_ipv6_oneshot(arg1);
+       rump_unschedule();
+
+       return rv;
+}
diff --git a/brlib/libnetconfig/rump/netconfig.h 
b/brlib/libnetconfig/rump/netconfig.h
index 392c761..71ced46 100644
--- a/brlib/libnetconfig/rump/netconfig.h
+++ b/brlib/libnetconfig/rump/netconfig.h
@@ -16,3 +16,4 @@ int rump_pub_netconfig_ipv6_ifaddr(const char *, const char 
*, int);
 int rump_pub_netconfig_ipv4_gw(const char *);
 int rump_pub_netconfig_ipv6_gw(const char *);
 int rump_pub_netconfig_dhcp_ipv4_oneshot(const char *);
+int rump_pub_netconfig_auto_ipv6_oneshot(const char *);

Reply via email to