tech-net archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
PATCH to only announce RTM_NEWADDR once IPv6 DAD completes
Hi List
Old discussion references:
http://mail-index4.netbsd.org/tech-net/2012/10/19/msg003676.html
http://mail-index.netbsd.org/current-users/2010/05/12/msg013334.html
http://mail-index.netbsd.org/current-users/2010/05/25/msg013529.html
http://mail-index.netbsd.org/tech-net/2010/05/25/msg002094.html
I agree with the latest proposal and attach a patch for review to
address it.
I've been testing this with a new dhcpcd build which listens for
RTM_NEWADDR or a duplicate NA message and reacts accordingly.
This is important as not only daemons fail to bind with a tentative
address from a RTM_NEWADDR message, but one shot things normally run
after successful address configuration, such as ntpdate also fail.
Although dhcpcd does have it's own DAD engine, it's not RFC
conformation (no kernel allows it to be), so getting kernels to
correctly announce RTM_NEWADDR when the address is ready does at least
allow userland applications such as dhcpcd to take advantage of the
kernel DAD.
Comments are welcome, probably comitting it over the weekend with
hopefully a new dhcpcd shortly afterwards.
Thanks
Roy
Index: sys/netinet6/in6.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/in6.c,v
retrieving revision 1.161
diff -u -p -r1.161 in6.c
--- sys/netinet6/in6.c 23 Jun 2012 03:14:03 -0000 1.161
+++ sys/netinet6/in6.c 15 May 2013 08:14:30 -0000
@@ -157,11 +157,13 @@ in6_ifloop_request(int cmd, struct ifadd
struct sockaddr_in6 lo_sa;
struct sockaddr_in6 all1_sa;
struct rtentry *nrt = NULL;
+ struct in6_ifaddr *ia;
int e;
sockaddr_in6_init(&all1_sa, &in6mask128, 0, 0, 0);
sockaddr_in6_init(&lo_sa, &in6addr_loopback, 0, 0, 0);
+ ia = (struct in6_ifaddr *)ifa;
/*
* We specify the address itself as the gateway, and set the
* RTF_LLINFO flag, so that the corresponding host route would have
@@ -176,7 +178,7 @@ in6_ifloop_request(int cmd, struct ifadd
log(LOG_ERR, "in6_ifloop_request: "
"%s operation failed for %s (errno=%d)\n",
cmd == RTM_ADD ? "ADD" : "DELETE",
- ip6_sprintf(&((struct in6_ifaddr *)ifa)->ia_addr.sin6_addr),
+ ip6_sprintf(&ia->ia_addr.sin6_addr),
e);
}
@@ -191,13 +193,26 @@ in6_ifloop_request(int cmd, struct ifadd
rt_replace_ifa(nrt, ifa);
/*
- * Report the addition/removal of the address to the routing socket.
+ * Report the addition/removal of the address to the routing socket
+ * unless the address is marked tentative, where it will be reported
+ * once DAD completes.
* XXX: since we called rtinit for a p2p interface with a destination,
* we end up reporting twice in such a case. Should we rather
* omit the second report?
*/
if (nrt) {
- rt_newaddrmsg(cmd, ifa, e, nrt);
+ if (cmd != RTM_ADD ||
+ !(ia->ia6_flags & IN6_IFF_TENTATIVE))
+ {
+#if 0
+ log(LOG_DEBUG,
+ "in6_ifloop_request: announced %s (%s %d)\n",
+ ip6_sprintf(&ia->ia_addr.sin6_addr),
+ cmd == RTM_ADD ? "RTM_ADD" : "RTM_DELETE",
+ ia->ia6_flags);
+#endif
+ rt_newaddrmsg(cmd, ifa, e, nrt);
+ }
if (cmd == RTM_DELETE) {
if (nrt->rt_refcnt <= 0) {
/* XXX: we should free the entry ourselves. */
@@ -1058,10 +1073,6 @@ in6_update_ifa1(struct ifnet *ifp, struc
} else
ia->ia6_lifetime.ia6t_preferred = 0;
- /* reset the interface and routing table appropriately. */
- if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0)
- goto unlink;
-
/*
* configure address flags.
*/
@@ -1084,6 +1095,10 @@ in6_update_ifa1(struct ifnet *ifp, struc
if (hostIsNew && in6if_do_dad(ifp))
ia->ia6_flags |= IN6_IFF_TENTATIVE;
+ /* reset the interface and routing table appropriately. */
+ if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0)
+ goto unlink;
+
/*
* We are done if we have simply modified an existing address.
*/
Index: sys/netinet6/nd6_nbr.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/nd6_nbr.c,v
retrieving revision 1.96
diff -u -p -r1.96 nd6_nbr.c
--- sys/netinet6/nd6_nbr.c 22 Mar 2012 20:34:41 -0000 1.96
+++ sys/netinet6/nd6_nbr.c 15 May 2013 08:14:30 -0000
@@ -1013,6 +1013,31 @@ nd6_ifptomac(const struct ifnet *ifp)
}
}
+static void
+nd6_dad_announce(struct ifaddr *ifa)
+{
+ struct sockaddr_in6 all1_sa;
+ struct rtentry *nrt = NULL;
+ int e;
+ const char *ias;
+
+ sockaddr_in6_init(&all1_sa, &in6mask128, 0, 0, 0);
+ ias = ip6_sprintf(&((struct in6_ifaddr *)ifa)->ia_addr.sin6_addr);
+
+ e = rtrequest(RTM_GET, ifa->ifa_addr, ifa->ifa_addr,
+ (struct sockaddr *)&all1_sa, RTF_UP|RTF_HOST|RTF_LLINFO, &nrt);
+ if (e != 0) {
+ log(LOG_ERR, "nd6_dad_announce:"
+ "RTM_GET operation failed for %s (errno=%d)\n", ias, e);
+ }
+
+ if (nrt) {
+ rt_newaddrmsg(RTM_ADD, ifa, e, nrt);
+ //log(LOG_DEBUG, "nd6_dad_announce: announced %s\n", ias);
+ nrt->rt_refcnt--;
+ }
+}
+
TAILQ_HEAD(dadq_head, dadq);
struct dadq {
TAILQ_ENTRY(dadq) dad_list;
@@ -1085,12 +1110,9 @@ nd6_dad_start(struct ifaddr *ifa, int xt
ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
return;
}
- if (ia->ia6_flags & IN6_IFF_ANYCAST) {
- ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
- return;
- }
- if (!ip6_dad_count) {
+ if (ia->ia6_flags & IN6_IFF_ANYCAST || !ip6_dad_count) {
ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
+ nd6_dad_announce(ifa);
return;
}
if (ifa->ifa_ifp == NULL)
@@ -1246,6 +1268,7 @@ nd6_dad_timer(struct ifaddr *ifa)
* No duplicate address found.
*/
ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
+ nd6_dad_announce(ifa);
nd6log((LOG_DEBUG,
"%s: DAD complete for %s - no duplicates found\n",
Home |
Main Index |
Thread Index |
Old Index