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 *);