tech-net archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

IPv4 Address Flags



Hi List

As discussed here [1], a few people voiced their opinion that they
didn't like address removal when the carrier drops and would rather
re-negotiate at carrier up. The first step of doing this is to add IPv6
address flag semantics to IPv4 addresses.

This patch adds the following flags to IPv4 and mimics the IPv6
behaviour of the same flags:
  IN_IFF_TENTATIVE
  IN_IFF_DUPLICATED
  IN_IFF_DETACHED
  IN_IFF_NOTREADY (IN_IFF_TENTATIVE | IN_IFF_DUPLICATED)

ioctl SIOCGIFAFLAG_IN has been added to retrieve the flags using a ifreq
struct (ifaliasreq is probably better but then we run into compatibility
issues, also why IN_IFF_NODAD is not implemented).

ifconfig(8) has been modified to report the new flags and wait for
tentative to vanish via -w alongside the IPv6 addresses.

sysctl(8) now has these new values:
  net.inet.ip.dad_count=3
  net.inet.arp.debug=0

DAD is implemented according to RFC 5227.
A future patch could be made to implement address defence from the RFC,
but this is optional (although required for IPv4LL, but dhcpcd will
handle that).

It's easy to see this patch working, simply run ntpd, reboot and watch
it complain that it cannot bind to IPv4 tentative addresses.

Commentary welcome, especially on ideas of how to make IN_IFF_NODAD   or
ifaliasreq work with the above.

Roy

[1] http://mail-index.netbsd.org/tech-net/2015/04/07/msg005053.html
Index: sbin/ifconfig/af_inet.c
===================================================================
RCS file: /cvsroot/src/sbin/ifconfig/af_inet.c,v
retrieving revision 1.15
diff -u -r1.15 af_inet.c
--- sbin/ifconfig/af_inet.c	13 Dec 2010 17:35:08 -0000	1.15
+++ sbin/ifconfig/af_inet.c	21 Apr 2015 08:30:16 -0000
@@ -137,6 +137,21 @@
 			strlcpy(hbuf, "", sizeof(hbuf)); /* some message? */
 		printf(" broadcast %s", hbuf);
 	}
+
+#ifdef IN_IFF_TENTATIVE
+	memcpy(&ifr.ifr_addr, &creq->ifra_addr, creq->ifra_addr.sin_len);
+	if (prog_ioctl(s, SIOCGIFAFLAG_IN, &ifr) == -1) {
+		if (errno != EADDRNOTAVAIL)
+			warn("SIOCGIFAFLAG_IN");
+	} else {
+		if (ifr.ifr_flags4 & IN_IFF_TENTATIVE)
+			printf(" tentative");
+		if (ifr.ifr_flags4 & IN_IFF_DUPLICATED)
+			printf(" duplicated");
+		if (ifr.ifr_flags4 & IN_IFF_DETACHED)
+			printf(" detached");
+	}
+#endif
 }
 
 static void
Index: sbin/ifconfig/ifconfig.c
===================================================================
RCS file: /cvsroot/src/sbin/ifconfig/ifconfig.c,v
retrieving revision 1.233
diff -u -r1.233 ifconfig.c
--- sbin/ifconfig/ifconfig.c	12 Sep 2014 08:54:26 -0000	1.233
+++ sbin/ifconfig/ifconfig.c	21 Apr 2015 08:30:16 -0000
@@ -515,10 +515,14 @@
 static int
 wait_dad_exec(prop_dictionary_t env, prop_dictionary_t oenv)
 {
-#ifdef INET6
 	bool waiting;
 	struct ifaddrs *ifaddrs, *ifa;
+#ifdef IN_IFF_TENTATIVE
+	struct ifreq ifr;
+#endif
+#ifdef INET6
 	struct in6_ifreq ifr6;
+#endif
 	int s;
 	const struct timespec ts = { .tv_sec = 0, .tv_nsec = WAIT_DAD };
 	const struct timespec add = { .tv_sec = wflag_secs, .tv_nsec = 0};
@@ -539,6 +543,22 @@
 			if (ifa->ifa_addr == NULL)
 				continue;
 			switch (ifa->ifa_addr->sa_family) {
+#ifdef IN_IFF_TENTATIVE
+			case AF_INET:
+				memset(&ifr, 0, sizeof(ifr));
+				strncpy(ifr.ifr_name,
+				    ifa->ifa_name, sizeof(ifr.ifr_name));
+				ifr.ifr_addr = *ifa->ifa_addr;
+				if ((s = getsock(AF_INET)) == -1)
+					err(EXIT_FAILURE,
+					    "%s: getsock", __func__);
+				if (ioctl(s, SIOCGIFAFLAG_IN, &ifr) == -1)
+					err(EXIT_FAILURE, "SIOCGIFAFLAG_IN");
+				if (ifr.ifr_flags4 & IN_IFF_TENTATIVE)
+					waiting = true;
+				break;
+#endif
+#ifdef INET6
 			case AF_INET6:
 				memset(&ifr6, 0, sizeof(ifr6));
 				strncpy(ifr6.ifr_name,
@@ -552,13 +572,13 @@
 					err(EXIT_FAILURE, "SIOCGIFAFLAG_IN6");
 				if (ifr6.ifr_ifru.ifru_flags6 &
 				    IN6_IFF_TENTATIVE)
-				{
 					waiting = true;
-					break;
-				}
+				break;
+#endif
 			}
 			if (waiting)
 				break;
+
 		}
 		if (!waiting)
 			break;
@@ -572,7 +592,6 @@
 	}
 
 	freeifaddrs(ifaddrs);
-#endif
 	exit(EXIT_SUCCESS);
 }
 
Index: sys/net/if.c
===================================================================
RCS file: /cvsroot/src/sys/net/if.c,v
retrieving revision 1.310
diff -u -r1.310 if.c
--- sys/net/if.c	20 Apr 2015 10:19:54 -0000	1.310
+++ sys/net/if.c	21 Apr 2015 08:30:45 -0000
@@ -1446,20 +1446,26 @@
 		"UNKNOWN");
 #endif
 
-#ifdef INET6
 	/*
 	 * When going from UNKNOWN to UP, we need to mark existing
-	 * IPv6 addresses as tentative and restart DAD as we may have
+	 * addresses as tentative and restart DAD as we may have
 	 * erroneously not found a duplicate.
 	 *
 	 * This needs to happen before rt_ifmsg to avoid a race where
 	 * listeners would have an address and expect it to work right
 	 * away.
 	 */
-	if (in6_present && link_state == LINK_STATE_UP &&
+	if (link_state == LINK_STATE_UP &&
 	    old_link_state == LINK_STATE_UNKNOWN)
-		in6_if_link_down(ifp);
+	{
+#ifdef INET
+		in_if_link_down(ifp);
+#endif
+#ifdef INET6
+		if (in6_present)
+			in6_if_link_down(ifp);
 #endif
+	}
 
 	/* Notify that the link state has changed. */
 	rt_ifmsg(ifp);
@@ -1469,14 +1475,23 @@
 		carp_carpdev_state(ifp);
 #endif
 
+	if (link_state == LINK_STATE_DOWN) {
+#ifdef INET
+		in_if_link_down(ifp);
+#endif
 #ifdef INET6
-	if (in6_present) {
-		if (link_state == LINK_STATE_DOWN)
+		if (in6_present)
 			in6_if_link_down(ifp);
-		else if (link_state == LINK_STATE_UP)
+#endif
+	} else if (link_state == LINK_STATE_UP) {
+#ifdef INET
+		in_if_link_up(ifp);
+#endif
+#ifdef INET6
+		if (in6_present)
 			in6_if_link_up(ifp);
-	}
 #endif
+	}
 
 	splx(s);
 }
@@ -1553,6 +1568,9 @@
 		carp_carpdev_state(ifp);
 #endif
 	rt_ifmsg(ifp);
+#ifdef INET
+	in_if_down(ifp);
+#endif
 #ifdef INET6
 	if (in6_present)
 		in6_if_down(ifp);
@@ -1583,6 +1601,9 @@
 		carp_carpdev_state(ifp);
 #endif
 	rt_ifmsg(ifp);
+#ifdef INET
+	in_if_up(ifp);
+#endif
 #ifdef INET6
 	if (in6_present)
 		in6_if_up(ifp);
@@ -2066,13 +2087,16 @@
 	}
 
 	if (((oif_flags ^ ifp->if_flags) & IFF_UP) != 0) {
+		int s = splnet();
+#ifdef INET
+		in_if_up(ifp);
+#endif
 #ifdef INET6
-		if (in6_present && (ifp->if_flags & IFF_UP) != 0) {
-			int s = splnet();
+		if (in6_present && (ifp->if_flags & IFF_UP) != 0)
 			in6_if_up(ifp);
-			splx(s);
-		}
 #endif
+
+		splx(s);
 	}
 #ifdef COMPAT_OIFREQ
 	if (cmd != ocmd)
Index: sys/net/if.h
===================================================================
RCS file: /cvsroot/src/sys/net/if.h,v
retrieving revision 1.188
diff -u -r1.188 if.h
--- sys/net/if.h	20 Apr 2015 10:19:54 -0000	1.188
+++ sys/net/if.h	21 Apr 2015 08:30:46 -0000
@@ -594,6 +594,7 @@
 		struct	sockaddr ifru_broadaddr;
 		struct	sockaddr_storage ifru_space;
 		short	ifru_flags;
+		int	ifru_flags4;
 		int	ifru_metric;
 		int	ifru_mtu;
 		int	ifru_dlt;
@@ -609,6 +610,7 @@
 #define	ifr_broadaddr	ifr_ifru.ifru_broadaddr	/* broadcast address */
 #define	ifr_space	ifr_ifru.ifru_space	/* sockaddr_storage */
 #define	ifr_flags	ifr_ifru.ifru_flags	/* flags */
+#define	ifr_flags4	ifr_ifru.ifru_flags4	/* addr flags */
 #define	ifr_metric	ifr_ifru.ifru_metric	/* metric */
 #define	ifr_mtu		ifr_ifru.ifru_mtu	/* mtu */
 #define	ifr_dlt		ifr_ifru.ifru_dlt	/* data link type (DLT_*) */
Index: sys/net/if_spppsubr.c
===================================================================
RCS file: /cvsroot/src/sys/net/if_spppsubr.c,v
retrieving revision 1.132
diff -u -r1.132 if_spppsubr.c
--- sys/net/if_spppsubr.c	20 Apr 2015 10:19:54 -0000	1.132
+++ sys/net/if_spppsubr.c	21 Apr 2015 08:30:48 -0000
@@ -4875,7 +4875,7 @@
 
 found:
 	{
-		int error;
+		int error, hostIsNew;
 		struct sockaddr_in new_sin = *si;
 		struct sockaddr_in new_dst = *dest;
 
@@ -4886,8 +4886,13 @@
 		 */
 		in_ifscrub(ifp, ifatoia(ifa));
 
-		if (myaddr != 0)
-			new_sin.sin_addr.s_addr = htonl(myaddr);
+		hostIsNew = 0;
+		if (myaddr != 0) {
+			if (new_sin.sin_addr.s_addr != htonl(myaddr)) {
+				new_sin.sin_addr.s_addr = htonl(myaddr);
+				hostIsNew = 1;
+			}
+		}
 		if (hisaddr != 0) {
 			new_dst.sin_addr.s_addr = htonl(hisaddr);
 			if (new_dst.sin_addr.s_addr != dest->sin_addr.s_addr) {
@@ -4895,7 +4900,7 @@
 				*dest = new_dst; /* fix dstaddr in place */
 			}
 		}
-		error = in_ifinit(ifp, ifatoia(ifa), &new_sin, 0);
+		error = in_ifinit(ifp, ifatoia(ifa), &new_sin, 0, hostIsNew);
 		if (debug && error)
 		{
 			log(LOG_DEBUG, "%s: sppp_set_ip_addrs: in_ifinit "
@@ -4948,7 +4953,7 @@
 		if (sp->ipcp.flags & IPCP_HISADDR_DYN)
 			/* replace peer addr in place */
 			dest->sin_addr.s_addr = sp->ipcp.saved_hisaddr;
-		in_ifinit(ifp, ifatoia(ifa), &new_sin, 0);
+		in_ifinit(ifp, ifatoia(ifa), &new_sin, 0, 0);
 		(void)pfil_run_hooks(if_pfil,
 		    (struct mbuf **)SIOCDIFADDR, ifp, PFIL_IFADDR);
 	}
Index: sys/netinet/if_arp.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/if_arp.c,v
retrieving revision 1.162
diff -u -r1.162 if_arp.c
--- sys/netinet/if_arp.c	23 Mar 2015 18:33:17 -0000	1.162
+++ sys/netinet/if_arp.c	21 Apr 2015 08:30:49 -0000
@@ -95,6 +95,7 @@
 #include <sys/sysctl.h>
 #include <sys/socketvar.h>
 #include <sys/percpu.h>
+#include <sys/cprng.h>
 
 #include <net/ethertypes.h>
 #include <net/if.h>
@@ -102,6 +103,7 @@
 #include <net/if_token.h>
 #include <net/if_types.h>
 #include <net/if_ether.h>
+#include <net/net_osdep.h>
 #include <net/route.h>
 #include <net/net_stats.h>
 
@@ -142,6 +144,14 @@
 #define	rt_expire rt_rmx.rmx_expire
 #define	rt_pksent rt_rmx.rmx_pksent
 
+int		ip_dad_count = PROBE_NUM;
+#ifdef ARP_DEBUG
+static int	arp_debug = 1;
+#else
+static int	arp_debug = 0;
+#endif
+#define arplog(x)	do { if (arp_debug) log x; } while (/*CONSTCOND*/ 0)
+
 static	struct sockaddr *arp_setgate(struct rtentry *, struct sockaddr *,
 	    const struct sockaddr *);
 static	void arptfree(struct llinfo_arp *);
@@ -153,6 +163,9 @@
 static	void in_arpinput(struct mbuf *);
 static	void arp_drainstub(void);
 
+static void arp_dad_timer(struct ifaddr *);
+static void arp_dad_duplicated(struct ifaddr *);
+
 LIST_HEAD(llinfo_arpq, llinfo_arp) llinfo_arp;
 struct	ifqueue arpintrq = {
 	.ifq_head = NULL,
@@ -511,6 +524,14 @@
 
 		in = &ifatoia(ifa)->ia_addr.sin_addr;
 
+		if (ifatoia(ifa)->ia4_flags &
+		    (IN_IFF_NOTREADY | IN_IFF_DETACHED))
+		{
+			arplog((LOG_DEBUG, "arp_request: %s not ready\n",
+			   in_fmtaddr(*in)));
+			return;
+		}
+
 		arprequest(ifa->ifa_ifp, in, in,
 		    CLLADDR(ifa->ifa_ifp->if_sadl));
 		return;
@@ -601,10 +622,17 @@
 		}
 		/* Announce a new entry if requested. */
 		if (rt->rt_flags & RTF_ANNOUNCE) {
-			arprequest(ifp,
-			    &satocsin(rt_getkey(rt))->sin_addr,
-			    &satocsin(rt_getkey(rt))->sin_addr,
-			    CLLADDR(satocsdl(gate)));
+			INADDR_TO_IA(satocsin(rt_getkey(rt))->sin_addr, ia);
+			while (ia && ia->ia_ifp != ifp)
+				NEXT_IA_WITH_SAME_ADDR(ia);
+			if (ia == NULL ||
+			    ia->ia4_flags & (IN_IFF_NOTREADY | IN_IFF_DETACHED))
+				;
+			else
+				arprequest(ifp,
+				    &satocsin(rt_getkey(rt))->sin_addr,
+				    &satocsin(rt_getkey(rt))->sin_addr,
+				    CLLADDR(satocsdl(gate)));
 		}
 		/*FALLTHROUGH*/
 	case RTM_RESOLVE:
@@ -999,16 +1027,6 @@
 	if (m->m_flags & (M_BCAST|M_MCAST))
 		ARP_STATINC(ARP_STAT_RCVMCAST);
 
-	/*
-	 * If the target IP address is zero, ignore the packet.
-	 * This prevents the code below from tring to answer
-	 * when we are using IP address zero (booting).
-	 */
-	if (in_nullhost(itaddr)) {
-		ARP_STATINC(ARP_STAT_RCVZEROTPA);
-		goto out;
-	}
-
 
 	/*
 	 * Search for a matching interface address
@@ -1089,13 +1107,38 @@
 	/*
 	 * If the source IP address is zero, this is an RFC 5227 ARP probe
 	 */
-	if (in_nullhost(isaddr)) {
+	if (in_nullhost(isaddr))
 		ARP_STATINC(ARP_STAT_RCVZEROSPA);
-		goto reply;
+	else if (in_hosteq(isaddr, myaddr))
+		ARP_STATINC(ARP_STAT_RCVLOCALSPA);
+
+	if (in_nullhost(itaddr))
+		ARP_STATINC(ARP_STAT_RCVZEROTPA);
+
+	/* DAD check, RFC 5227 2.1.1, Probe Details */
+	if (in_hosteq(isaddr, myaddr) ||
+	    (in_nullhost(isaddr) && in_hosteq(itaddr, myaddr)))
+	{
+		/* If our address is tentative, mark it as duplicated */
+		if (ia->ia4_flags & IN_IFF_TENTATIVE)
+			arp_dad_duplicated((struct ifaddr *)ia);
+		/* If our address is unuseable, don't reply */
+		if (ia->ia4_flags & (IN_IFF_NOTREADY | IN_IFF_DETACHED))
+			goto out;
 	}
 
+	/*
+	 * If the target IP address is zero, ignore the packet.
+	 * This prevents the code below from tring to answer
+	 * when we are using IP address zero (booting).
+	 */
+	if (in_nullhost(itaddr))
+		goto out;
+
+	if (in_nullhost(isaddr))
+		goto reply;
+
 	if (in_hosteq(isaddr, myaddr)) {
-		ARP_STATINC(ARP_STAT_RCVLOCALSPA);
 		log(LOG_ERR,
 		   "duplicate IP address %s sent from link address %s\n",
 		   in_fmtaddr(isaddr), lla_snprintf(ar_sha(ah), ah->ar_hln));
@@ -1211,6 +1254,9 @@
 	}
 	ARP_STATINC(ARP_STAT_RCVREQUEST);
 	if (in_hosteq(itaddr, myaddr)) {
+		/* If our address is unuseable, don't reply */
+		if (ia->ia4_flags & (IN_IFF_NOTREADY | IN_IFF_DETACHED))
+			goto out;
 		/* I am the target */
 		tha = ar_tha(ah);
 		if (tha)
@@ -1358,19 +1404,321 @@
 arp_ifinit(struct ifnet *ifp, struct ifaddr *ifa)
 {
 	struct in_addr *ip;
+	struct in_ifaddr *ia = (struct in_ifaddr *)ifa;
 
 	/*
 	 * Warn the user if another station has this IP address,
 	 * but only if the interface IP address is not zero.
 	 */
 	ip = &IA_SIN(ifa)->sin_addr;
-	if (!in_nullhost(*ip))
+	if (!in_nullhost(*ip) &&
+	    (ia->ia4_flags & (IN_IFF_NOTREADY | IN_IFF_DETACHED)) == 0)
 		arprequest(ifp, ip, ip, CLLADDR(ifp->if_sadl));
 
 	ifa->ifa_rtrequest = arp_rtrequest;
 	ifa->ifa_flags |= RTF_CLONING;
 }
 
+TAILQ_HEAD(dadq_head, dadq);
+struct dadq {
+	TAILQ_ENTRY(dadq) dad_list;
+	struct ifaddr *dad_ifa;
+	int dad_count;		/* max ARP to send */
+	int dad_arp_tcount;	/* # of trials to send ARP */
+	int dad_arp_ocount;	/* ARP sent so far */
+	int dad_arp_announce;	/* max ARP announcements */
+	int dad_arp_acount;	/* # of announcements */
+	struct callout dad_timer_ch;
+};
+MALLOC_JUSTDEFINE(M_IPARP, "ARP DAD", "ARP DAD Structure");
+
+static struct dadq_head dadq;
+static int dad_init = 0;
+static int dad_maxtry = 15;     /* max # of *tries* to transmit DAD packet */
+
+static struct dadq *
+arp_dad_find(struct ifaddr *ifa)
+{
+	struct dadq *dp;
+
+	TAILQ_FOREACH(dp, &dadq, dad_list) {
+		if (dp->dad_ifa == ifa)
+			return dp;
+	}
+	return NULL;
+}
+
+static void
+arp_dad_starttimer(struct dadq *dp, int ticks)
+{
+
+	callout_reset(&dp->dad_timer_ch, ticks,
+	    (void (*)(void *))arp_dad_timer, (void *)dp->dad_ifa);
+}
+
+static void
+arp_dad_stoptimer(struct dadq *dp)
+{
+
+	callout_stop(&dp->dad_timer_ch);
+}
+
+static void
+arp_dad_output(struct dadq *dp, struct ifaddr *ifa)
+{
+	struct in_ifaddr *ia = (struct in_ifaddr *)ifa;
+	struct ifnet *ifp = ifa->ifa_ifp;
+	struct in_addr sip;
+
+	dp->dad_arp_tcount++;
+	if ((ifp->if_flags & IFF_UP) == 0)
+		return;
+	if ((ifp->if_flags & IFF_RUNNING) == 0)
+		return;
+
+	dp->dad_arp_tcount = 0;
+	dp->dad_arp_ocount++;
+
+	memset(&sip, 0, sizeof(sip));
+	arprequest(ifa->ifa_ifp, &sip, &ia->ia_addr.sin_addr,
+	    CLLADDR(ifa->ifa_ifp->if_sadl));
+}
+
+/*
+ * Start Duplicate Address Detection (DAD) for specified interface address.
+ */
+void
+arp_dad_start(struct ifaddr *ifa)
+{
+	struct in_ifaddr *ia = (struct in_ifaddr *)ifa;
+	struct dadq *dp;
+
+	if (!dad_init) {
+		TAILQ_INIT(&dadq);
+		dad_init++;
+	}
+
+	/*
+	 * If we don't need DAD, don't do it.
+	 * - DAD is disabled (ip_dad_count == 0)
+	 */
+	if (!(ia->ia4_flags & IN_IFF_TENTATIVE)) {
+		log(LOG_DEBUG,
+			"arp_dad_start: called with non-tentative address "
+			"%s(%s)\n",
+			in_fmtaddr(ia->ia_addr.sin_addr),
+			ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
+		return;
+	}
+	if (!ip_dad_count) {
+		struct in_addr *ip = &IA_SIN(ifa)->sin_addr;
+
+		ia->ia4_flags &= ~IN_IFF_TENTATIVE;
+		rt_newaddrmsg(RTM_NEWADDR, ifa, 0, NULL);
+		arprequest(ifa->ifa_ifp, ip, ip,
+		    CLLADDR(ifa->ifa_ifp->if_sadl));
+		return;
+	}
+	if (ifa->ifa_ifp == NULL)
+		panic("arp_dad_start: ifa->ifa_ifp == NULL");
+	if (!(ifa->ifa_ifp->if_flags & IFF_UP))
+		return;
+	if (arp_dad_find(ifa) != NULL) {
+		/* DAD already in progress */
+		return;
+	}
+
+	dp = malloc(sizeof(*dp), M_IPARP, M_NOWAIT);
+	if (dp == NULL) {
+		log(LOG_ERR, "arp_dad_start: memory allocation failed for "
+			"%s(%s)\n",
+			in_fmtaddr(ia->ia_addr.sin_addr),
+			ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
+		return;
+	}
+	memset(dp, 0, sizeof(*dp));
+	callout_init(&dp->dad_timer_ch, CALLOUT_MPSAFE);
+	TAILQ_INSERT_TAIL(&dadq, (struct dadq *)dp, dad_list);
+
+	arplog((LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp),
+	    in_fmtaddr(ia->ia_addr.sin_addr)));
+
+	/*
+	 * Send ARP packet for DAD, ip_dad_count times.
+	 * Note that we must delay the first transmission.
+	 */
+	dp->dad_ifa = ifa;
+	ifaref(ifa);	/* just for safety */
+	dp->dad_count = ip_dad_count;
+	dp->dad_arp_announce = 0; /* Will be set when starting to announce */
+	dp->dad_arp_acount = dp->dad_arp_ocount = dp->dad_arp_tcount = 0;
+
+	arp_dad_starttimer(dp, cprng_fast32() % (PROBE_WAIT * hz));
+}
+
+/*
+ * terminate DAD unconditionally.  used for address removals.
+ */
+void
+arp_dad_stop(struct ifaddr *ifa)
+{
+	struct dadq *dp;
+
+	if (!dad_init)
+		return;
+	dp = arp_dad_find(ifa);
+	if (dp == NULL) {
+		/* DAD wasn't started yet */
+		return;
+	}
+
+	arp_dad_stoptimer(dp);
+
+	TAILQ_REMOVE(&dadq, dp, dad_list);
+	free(dp, M_IPARP);
+	dp = NULL;
+	ifafree(ifa);
+}
+
+static void
+arp_dad_timer(struct ifaddr *ifa)
+{
+	struct in_ifaddr *ia = (struct in_ifaddr *)ifa;
+	struct dadq *dp;
+	struct in_addr *ip;
+
+	mutex_enter(softnet_lock);
+	KERNEL_LOCK(1, NULL);
+
+	/* Sanity check */
+	if (ia == NULL) {
+		log(LOG_ERR, "arp_dad_timer: called with null parameter\n");
+		goto done;
+	}
+	dp = arp_dad_find(ifa);
+	if (dp == NULL) {
+		log(LOG_ERR, "arp_dad_timer: DAD structure not found\n");
+		goto done;
+	}
+	if (ia->ia4_flags & IN_IFF_DUPLICATED) {
+		log(LOG_ERR, "nd4_dad_timer: called with duplicate address "
+			"%s(%s)\n",
+			in_fmtaddr(ia->ia_addr.sin_addr),
+			ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
+		goto done;
+	}
+	if ((ia->ia4_flags & IN_IFF_TENTATIVE) == 0 && dp->dad_arp_acount == 0){
+		log(LOG_ERR, "arp_dad_timer: called with non-tentative address "
+			"%s(%s)\n",
+			in_fmtaddr(ia->ia_addr.sin_addr),
+			ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
+		goto done;
+	}
+
+	/* timeouted with IFF_{RUNNING,UP} check */
+	if (dp->dad_arp_tcount > dad_maxtry) {
+		arplog((LOG_INFO, "%s: could not run DAD, driver problem?\n",
+			if_name(ifa->ifa_ifp)));
+
+		TAILQ_REMOVE(&dadq, dp, dad_list);
+		free(dp, M_IPARP);
+		dp = NULL;
+		ifafree(ifa);
+		goto done;
+	}
+
+	/* Need more checks? */
+	if (dp->dad_arp_ocount < dp->dad_count) {
+		int delay;
+
+		/*
+		 * We have more ARP to go.  Send ARP packet for DAD.
+		 */
+		arp_dad_output(dp, ifa);
+		if (dp->dad_arp_ocount < dp->dad_count)
+			delay = (PROBE_MIN * hz) +
+			    (cprng_fast32() %
+			    ((PROBE_MAX * hz) - (PROBE_MIN * hz)));
+		else
+			delay = ANNOUNCE_WAIT * hz;
+		arp_dad_starttimer(dp, delay);
+		goto done;
+	} else if (dp->dad_arp_acount == 0) {
+		/*
+		 * We are done with DAD.
+		 * No duplicate address found.
+		 */
+		ia->ia4_flags &= ~IN_IFF_TENTATIVE;
+		rt_newaddrmsg(RTM_NEWADDR, ifa, 0, NULL);
+		arplog((LOG_DEBUG,
+		    "%s: DAD complete for %s - no duplicates found\n",
+		    if_name(ifa->ifa_ifp),
+		    in_fmtaddr(ia->ia_addr.sin_addr)));
+		dp->dad_arp_announce = ANNOUNCE_NUM;
+		goto announce;
+	} else if (dp->dad_arp_acount < dp->dad_arp_announce) {
+announce:
+		/*
+		 * Announce the address.
+		 */
+		ip = &IA_SIN(ifa)->sin_addr;
+		arprequest(ifa->ifa_ifp, ip, ip,
+		    CLLADDR(ifa->ifa_ifp->if_sadl));
+		dp->dad_arp_acount++;
+		if (dp->dad_arp_acount < dp->dad_arp_announce) {
+			arp_dad_starttimer(dp, ANNOUNCE_INTERVAL * hz);
+			goto done;
+		}
+		arplog((LOG_DEBUG,
+		    "%s: ARP announcement complete for %s\n",
+		    if_name(ifa->ifa_ifp),
+		    in_fmtaddr(ia->ia_addr.sin_addr)));
+	}
+
+	TAILQ_REMOVE(&dadq, dp, dad_list);
+	free(dp, M_IPARP);
+	dp = NULL;
+	ifafree(ifa);
+
+done:
+	KERNEL_UNLOCK_ONE(NULL);
+	mutex_exit(softnet_lock);
+}
+
+static void
+arp_dad_duplicated(struct ifaddr *ifa)
+{
+	struct in_ifaddr *ia = (struct in_ifaddr *)ifa;
+	struct ifnet *ifp;
+	struct dadq *dp;
+
+	dp = arp_dad_find(ifa);
+	if (dp == NULL) {
+		log(LOG_ERR, "arp_dad_duplicated: DAD structure not found\n");
+		return;
+	}
+
+	ifp = ifa->ifa_ifp;
+	log(LOG_ERR, "%s: DAD detected duplicate IPv4 address %s: "
+	    "ARP out=%d\n",
+	    if_name(ifp), in_fmtaddr(ia->ia_addr.sin_addr),
+	    dp->dad_arp_ocount);
+
+	ia->ia4_flags &= ~IN_IFF_TENTATIVE;
+	ia->ia4_flags |= IN_IFF_DUPLICATED;
+
+	/* We are done with DAD, with duplicated address found. (failure) */
+	arp_dad_stoptimer(dp);
+
+	/* Inform the routing socket that DAD has completed */
+	rt_newaddrmsg(RTM_NEWADDR, ifa, 0, NULL);
+
+	TAILQ_REMOVE(&dadq, dp, dad_list);
+	free(dp, M_IPARP);
+	dp = NULL;
+	ifafree(ifa);
+}
+
 /*
  * Called from 10 Mb/s Ethernet interrupt handlers
  * when ether packet type ETHERTYPE_REVARP
@@ -1734,6 +2082,13 @@
 			SYSCTL_DESCR("log ARP packets from non-local network"),
 			NULL, 0, &log_unknown_network, 0,
 			CTL_NET,PF_INET, node->sysctl_num, CTL_CREATE, CTL_EOL);
+
+	sysctl_createv(clog, 0, NULL, NULL,
+		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+		       CTLTYPE_INT, "debug",
+		       SYSCTL_DESCR("Enable ARP DAD debug output"),
+		       NULL, 0, &arp_debug, 0,
+		       CTL_NET, PF_INET, node->sysctl_num, CTL_CREATE, CTL_EOL);
 }
 
 #endif /* INET */
Index: sys/netinet/if_inarp.h
===================================================================
RCS file: /cvsroot/src/sys/netinet/if_inarp.h,v
retrieving revision 1.44
diff -u -r1.44 if_inarp.h
--- sys/netinet/if_inarp.h	30 Sep 2012 05:13:12 -0000	1.44
+++ sys/netinet/if_inarp.h	21 Apr 2015 08:30:49 -0000
@@ -56,6 +56,22 @@
 };
 
 #ifdef _KERNEL
+
+/* ARP timings from RFC5227 */
+#define PROBE_WAIT               1
+#define PROBE_NUM                3
+#define PROBE_MIN                1
+#define PROBE_MAX                2
+#define ANNOUNCE_WAIT            2
+#define ANNOUNCE_NUM             2
+#define ANNOUNCE_INTERVAL        2
+#define MAX_CONFLICTS           10
+#define RATE_LIMIT_INTERVAL     60
+#define DEFEND_INTERVAL         10
+
+#include <sys/malloc.h>
+MALLOC_DECLARE(M_IPARP);
+
 extern struct ifqueue arpintrq;
 void arp_ifinit(struct ifnet *, struct ifaddr *);
 void arp_rtrequest(int, struct rtentry *, const struct rt_addrinfo *);
@@ -69,6 +85,9 @@
 int arpioctl(u_long, void *);
 void arpwhohas(struct ifnet *, struct in_addr *);
 
+void arp_dad_start(struct ifaddr *);
+void arp_dad_stop(struct ifaddr *);
+
 void revarpinput(struct mbuf *);
 void in_revarpinput(struct mbuf *);
 void revarprequest(struct ifnet *);
Index: sys/netinet/in.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/in.c,v
retrieving revision 1.151
diff -u -r1.151 in.c
--- sys/netinet/in.c	26 Feb 2015 12:58:36 -0000	1.151
+++ sys/netinet/in.c	21 Apr 2015 08:30:49 -0000
@@ -100,6 +100,7 @@
 #include <sys/param.h>
 #include <sys/ioctl.h>
 #include <sys/errno.h>
+#include <sys/kernel.h>
 #include <sys/malloc.h>
 #include <sys/socket.h>
 #include <sys/socketvar.h>
@@ -391,6 +392,16 @@
 		}
 		/* FALLTHROUGH */
 	case SIOCSIFADDR:
+		hostIsNew = 1;
+		if (ia == NULL || ia->ia_addr.sin_family != AF_INET)
+			;
+		else if (ifra->ifra_addr.sin_len == 0) {
+			ifra->ifra_addr = ia->ia_addr;
+			hostIsNew = 0;
+		} else if (in_hosteq(ia->ia_addr.sin_addr,
+		           ifra->ifra_addr.sin_addr))
+			hostIsNew = 0;
+		/* FALLTHROUGH */
 	case SIOCSIFDSTADDR:
 		if (ifra->ifra_addr.sin_family != AF_INET)
 			return (EAFNOSUPPORT);
@@ -439,6 +450,24 @@
 		}
 		break;
 
+	case SIOCGIFAFLAG_IN:
+		if (ifr->ifr_addr.sa_family == AF_INET) {
+			struct sockaddr_in *sa;
+
+			sa = (struct sockaddr_in *)&ifr->ifr_addr;
+			LIST_FOREACH(ia,
+			    &IN_IFADDR_HASH(sa->sin_addr.s_addr),
+			    ia_hash) {
+				if (ia->ia_ifp == ifp &&
+				    in_hosteq(ia->ia_addr.sin_addr,
+				    ifra->ifra_addr.sin_addr))
+					break;
+			}
+		}
+		if (ia == NULL)
+			return (EADDRNOTAVAIL);
+		break;
+
 	case SIOCSIFBRDADDR:
 		if (kauth_authorize_network(curlwp->l_cred, KAUTH_NETWORK_INTERFACE,
 		    KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, (void *)cmd,
@@ -509,7 +538,7 @@
 
 	case SIOCSIFADDR:
 		error = in_ifinit(ifp, ia, satocsin(ifreq_getaddr(cmd, ifr)),
-		    1);
+		    1, hostIsNew);
 		if (error == 0) {
 			(void)pfil_run_hooks(if_pfil,
 			    (struct mbuf **)SIOCSIFADDR, ifp, PFIL_IFADDR);
@@ -520,20 +549,11 @@
 		in_ifscrub(ifp, ia);
 		ia->ia_sockmask = *satocsin(ifreq_getaddr(cmd, ifr));
 		ia->ia_subnetmask = ia->ia_sockmask.sin_addr.s_addr;
-		error = in_ifinit(ifp, ia, NULL, 0);
+		error = in_ifinit(ifp, ia, NULL, 0, 0);
 		break;
 
 	case SIOCAIFADDR:
 		maskIsNew = 0;
-		hostIsNew = 1;
-		if (ia->ia_addr.sin_family != AF_INET)
-			;
-		else if (ifra->ifra_addr.sin_len == 0) {
-			ifra->ifra_addr = ia->ia_addr;
-			hostIsNew = 0;
-		} else if (in_hosteq(ia->ia_addr.sin_addr,
-		           ifra->ifra_addr.sin_addr))
-			hostIsNew = 0;
 		if (ifra->ifra_mask.sin_len) {
 			/* Only scrub if we control the prefix route,
 			 * otherwise userland gets a bogus message */
@@ -554,7 +574,8 @@
 		}
 		if (ifra->ifra_addr.sin_family == AF_INET &&
 		    (hostIsNew || maskIsNew)) {
-			error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0);
+			error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0,
+			    hostIsNew);
 		}
 		if ((ifp->if_flags & IFF_BROADCAST) &&
 		    (ifra->ifra_broadaddr.sin_family == AF_INET))
@@ -577,6 +598,10 @@
 			      sizeof(ifra->ifra_broadaddr));
 		break;
 
+	case SIOCGIFAFLAG_IN:
+		ifr->ifr_flags4 = ia->ia4_flags;
+		break;
+
 	case SIOCDIFADDR:
 		in_purgeaddr(&ia->ia_ifa);
 		(void)pfil_run_hooks(if_pfil, (struct mbuf **)SIOCDIFADDR,
@@ -652,6 +677,9 @@
 	struct ifnet *ifp = ifa->ifa_ifp;
 	struct in_ifaddr *ia = (void *) ifa;
 
+        /* stop DAD processing */
+	arp_dad_stop(ifa);
+
 	in_ifscrub(ifp, ia);
 	in_ifremlocal(ifa);
 	LIST_REMOVE(ia, ia_hash);
@@ -870,7 +898,7 @@
  */
 int
 in_ifinit(struct ifnet *ifp, struct in_ifaddr *ia,
-    const struct sockaddr_in *sin, int scrub)
+    const struct sockaddr_in *sin, int scrub, int hostIsNew)
 {
 	u_int32_t i;
 	struct sockaddr_in oldaddr;
@@ -888,6 +916,14 @@
 	ia->ia_addr = *sin;
 	LIST_INSERT_HEAD(&IN_IFADDR_HASH(ia->ia_addr.sin_addr.s_addr), ia, ia_hash);
 
+	/* Set IN_IFF flags early for arp_ifinit() */
+	if (hostIsNew && if_do_dad(ifp) && !in_nullhost(ia->ia_addr.sin_addr)) {
+		if (ifp->if_link_state == LINK_STATE_DOWN)
+			ia->ia4_flags |= IN_IFF_DETACHED;
+		else
+			ia->ia4_flags |= IN_IFF_TENTATIVE;
+	}
+
 	/*
 	 * Give the interface a chance to initialize
 	 * if this is its first address,
@@ -956,6 +992,12 @@
 		addr.s_addr = INADDR_ALLHOSTS_GROUP;
 		ia->ia_allhosts = in_addmulti(&addr, ifp);
 	}
+
+	if (hostIsNew && if_do_dad(ifp) &&
+	    !in_nullhost(ia->ia_addr.sin_addr) &&
+	    ia->ia4_flags & IN_IFF_TENTATIVE)
+		arp_dad_start((struct ifaddr *)ia);
+
 	return (error);
 bad:
 	splx(s);
@@ -1122,6 +1164,87 @@
 }
 
 /*
+ * perform DAD when interface becomes IFF_UP.
+ */
+void
+in_if_link_up(struct ifnet *ifp)
+{
+	struct ifaddr *ifa;
+	struct in_ifaddr *ia;
+
+	/* Ensure it's sane to run DAD */
+	if (ifp->if_link_state == LINK_STATE_DOWN)
+		return;
+	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
+		return;
+
+	IFADDR_FOREACH(ifa, ifp) {
+		if (ifa->ifa_addr->sa_family != AF_INET)
+			continue;
+		ia = (struct in_ifaddr *)ifa;
+
+		/* If detached then mark as tentative */
+		if (ia->ia4_flags & IN_IFF_DETACHED) {
+			ia->ia4_flags &= ~IN_IFF_DETACHED;
+			if (if_do_dad(ifp))
+				ia->ia4_flags |= IN_IFF_TENTATIVE;
+			else if ((ia->ia4_flags & IN_IFF_TENTATIVE) == 0)
+				rt_newaddrmsg(RTM_NEWADDR, ifa, 0, NULL);
+		}
+
+		if (ia->ia4_flags & IN_IFF_TENTATIVE) {
+			/* Clear the duplicated flag as we're starting DAD. */
+			ia->ia4_flags &= ~IN_IFF_DUPLICATED;
+			arp_dad_start(ifa);
+		}
+	}
+}
+
+void
+in_if_up(struct ifnet *ifp)
+{
+
+	/* interface may not support link state, so bring it up also */
+	in_if_link_up(ifp);
+}
+
+/*
+ * Mark all addresses as detached.
+ */
+void
+in_if_link_down(struct ifnet *ifp)
+{
+	struct ifaddr *ifa;
+	struct in_ifaddr *ia;
+
+	IFADDR_FOREACH(ifa, ifp) {
+		if (ifa->ifa_addr->sa_family != AF_INET)
+			continue;
+		ia = (struct in_ifaddr *)ifa;
+
+		/* Stop DAD processing */
+		arp_dad_stop(ifa);
+
+		/*
+		 * Mark the address as detached.
+		 */
+		if (!(ia->ia4_flags & IN_IFF_DETACHED)) {
+			ia->ia4_flags |= IN_IFF_DETACHED;
+			ia->ia4_flags &=
+			    ~(IN_IFF_TENTATIVE | IN_IFF_DUPLICATED);
+			rt_newaddrmsg(RTM_NEWADDR, ifa, 0, NULL);
+		}
+	}
+}
+
+void
+in_if_down(struct ifnet *ifp)
+{
+
+	in_if_link_down(ifp);
+}
+
+/*
  * in_lookup_multi: look up the in_multi record for a given IP
  * multicast address on a given interface.  If no matching record is
  * found, return NULL.
@@ -1375,7 +1498,7 @@
 		if (imo->imo_multicast_ifp != NULL) {
 			ifp = imo->imo_multicast_ifp;
 			IFP_TO_IA(ifp, ia);		/* XXX */
-			if (ia == 0) {
+			if (ia == 0 || ia->ia4_flags & IN_IFF_NOTREADY) {
 				*errorp = EADDRNOTAVAIL;
 				return NULL;
 			}
@@ -1384,6 +1507,10 @@
 	if (ia->ia_ifa.ifa_getifa != NULL) {
 		ia = ifatoia((*ia->ia_ifa.ifa_getifa)(&ia->ia_ifa,
 		                                      sintosa(sin)));
+		if (ia == NULL) {
+			*errorp = EADDRNOTAVAIL;
+			return NULL;
+		}
 	}
 #ifdef GETIFA_DEBUG
 	else
Index: sys/netinet/in.h
===================================================================
RCS file: /cvsroot/src/sys/netinet/in.h,v
retrieving revision 1.96
diff -u -r1.96 in.h
--- sys/netinet/in.h	10 Feb 2015 19:11:52 -0000	1.96
+++ sys/netinet/in.h	21 Apr 2015 08:30:50 -0000
@@ -467,7 +467,8 @@
 #define	IPCTL_RANDOMID	       22	/* use random IP ids (if configured) */
 #define	IPCTL_LOOPBACKCKSUM    23	/* do IP checksum on loopback */
 #define	IPCTL_STATS		24	/* IP statistics */
-#define	IPCTL_MAXID	       25
+#define	IPCTL_DAD_COUNT        25	/* DAD packets to send */
+#define	IPCTL_MAXID	       26
 
 #define	IPCTL_NAMES { \
 	{ 0, 0 }, \
@@ -495,6 +496,7 @@
 	{ "random_id", CTLTYPE_INT }, \
 	{ "do_loopback_cksum", CTLTYPE_INT }, \
 	{ "stats", CTLTYPE_STRUCT }, \
+	{ "dad_count", CTLTYPE_INT }, \
 }
 #endif /* _NETBSD_SOURCE */
 
@@ -564,6 +566,11 @@
 int	in_localaddr(struct in_addr);
 void	in_socktrim(struct sockaddr_in *);
 
+void	in_if_link_up(struct ifnet *);
+void	in_if_link_down(struct ifnet *);
+void	in_if_up(struct ifnet *);
+void	in_if_down(struct ifnet *);
+
 struct route;
 struct ip_moptions;
 
Index: sys/netinet/in_pcb.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/in_pcb.c,v
retrieving revision 1.156
diff -u -r1.156 in_pcb.c
--- sys/netinet/in_pcb.c	3 Apr 2015 20:01:07 -0000	1.156
+++ sys/netinet/in_pcb.c	21 Apr 2015 08:30:50 -0000
@@ -287,6 +287,8 @@
 			ia = ifatoia(ifa_ifwithaddr(sintosa(sin)));
 		if (ia == NULL)
 			return (EADDRNOTAVAIL);
+		if (ia->ia4_flags & (IN_IFF_NOTREADY | IN_IFF_DETACHED))
+			return (EADDRNOTAVAIL);
 	}
 
 	inp->inp_laddr = sin->sin_addr;
Index: sys/netinet/in_selsrc.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/in_selsrc.c,v
retrieving revision 1.11
diff -u -r1.11 in_selsrc.c
--- sys/netinet/in_selsrc.c	25 Feb 2014 18:30:12 -0000	1.11
+++ sys/netinet/in_selsrc.c	21 Apr 2015 08:30:50 -0000
@@ -300,6 +300,7 @@
 	struct in_ifsysctl *isc;
 	struct in_ifselsrc *iss;
 	int best_score[IN_SCORE_SRC_MAX], score[IN_SCORE_SRC_MAX];
+	struct in_ifaddr *ia;
 
 	if (ifa->ifa_addr->sa_family != AF_INET ||
 	    dst0 == NULL || dst0->sa_family != AF_INET) {	/* Possible. */
@@ -346,6 +347,9 @@
 
 		if (alt_ifa == ifa || src->sin_family != AF_INET)
 			continue;
+		ia = (struct in_ifaddr *)alt_ifa;
+		if (ia->ia4_flags & IN_IFF_NOTREADY)
+			continue;
 
 		in_score(score_src, score, NULL, &src->sin_addr,
 		         alt_ifa->ifa_preference, idx, &dst->sin_addr);
@@ -363,6 +367,13 @@
 			best_ifa = alt_ifa;
 		}
 	}
+
+	ia = (struct in_ifaddr *)best_ifa;
+	if (ia->ia4_flags & IN_IFF_NOTREADY) {
+		errno = EADDRNOTAVAIL;
+		return NULL;
+	}
+
 #ifdef GETIFA_DEBUG
 	if (in_selsrc_debug) {
 		printf("%s: choose src %#" PRIx32 " score ", __func__,
Index: sys/netinet/in_var.h
===================================================================
RCS file: /cvsroot/src/sys/netinet/in_var.h,v
retrieving revision 1.70
diff -u -r1.70 in_var.h
--- sys/netinet/in_var.h	1 Jul 2014 05:49:18 -0000	1.70
+++ sys/netinet/in_var.h	21 Apr 2015 08:30:51 -0000
@@ -66,6 +66,13 @@
 
 #include <sys/queue.h>
 
+#define IN_IFF_TENTATIVE	0x01	/* tentative address */
+#define IN_IFF_DUPLICATED	0x02	/* DAD detected duplicate */
+#define IN_IFF_DETACHED		0x04	/* may be detached from the link */
+
+/* do not input/output */
+#define IN_IFF_NOTREADY (IN_IFF_TENTATIVE | IN_IFF_DUPLICATED)
+
 /*
  * Interface address, Internet version.  One of these structures
  * is allocated for each interface with an Internet address.
@@ -92,6 +99,7 @@
 	struct	in_multi *ia_allhosts;	/* multicast address record for
 					   the allhosts multicast group */
 	uint16_t ia_idsalt;		/* ip_id salt for this ia */
+	int ia4_flags;
 };
 
 struct	in_aliasreq {
@@ -217,6 +225,8 @@
 
 extern pktqueue_t *ip_pktq;
 
+extern int ip_dad_count;		/* Duplicate Address Detection probes */
+
 /*
  * Structure used by functions below to remember position when stepping
  * through all of the in_multi records.
@@ -240,7 +250,7 @@
 struct ifaddr;
 
 int	in_ifinit(struct ifnet *,
-	    struct in_ifaddr *, const struct sockaddr_in *, int);
+	    struct in_ifaddr *, const struct sockaddr_in *, int, int);
 void	in_savemkludge(struct in_ifaddr *);
 void	in_restoremkludge(struct in_ifaddr *, struct ifnet *);
 void	in_purgemkludge(struct ifnet *);
Index: sys/netinet/ip_icmp.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/ip_icmp.c,v
retrieving revision 1.135
diff -u -r1.135 ip_icmp.c
--- sys/netinet/ip_icmp.c	2 Dec 2014 20:25:47 -0000	1.135
+++ sys/netinet/ip_icmp.c	21 Apr 2015 08:30:52 -0000
@@ -703,6 +703,8 @@
 
 	/* Look for packet addressed to us */
 	INADDR_TO_IA(t, ia);
+	if (ia->ia4_flags & IN_IFF_NOTREADY)
+		ia = NULL;
 
 	/* look for packet sent to broadcast address */
 	if (ia == NULL && m->m_pkthdr.rcvif &&
@@ -712,7 +714,9 @@
 				continue;
 			if (in_hosteq(t,ifatoia(ifa)->ia_broadaddr.sin_addr)) {
 				ia = ifatoia(ifa);
-				break;
+				if ((ia->ia4_flags & IN_IFF_NOTREADY) == 0)
+					break;
+				ia = NULL;
 			}
 		}
 	}
Index: sys/netinet/ip_input.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/ip_input.c,v
retrieving revision 1.320
diff -u -r1.320 ip_input.c
--- sys/netinet/ip_input.c	26 Mar 2015 04:05:58 -0000	1.320
+++ sys/netinet/ip_input.c	21 Apr 2015 08:30:52 -0000
@@ -593,11 +593,13 @@
 	 *
 	 * Traditional 4.4BSD did not consult IFF_UP at all.
 	 * The behavior here is to treat addresses on !IFF_UP interface
-	 * as not mine.
+	 * or IN_IFF_NOTREADY addresses as not mine.
 	 */
 	downmatch = 0;
 	LIST_FOREACH(ia, &IN_IFADDR_HASH(ip->ip_dst.s_addr), ia_hash) {
 		if (in_hosteq(ia->ia_addr.sin_addr, ip->ip_dst)) {
+			if (ia->ia4_flags & IN_IFF_NOTREADY)
+				continue;
 			if (checkif && ia->ia_ifp != ifp)
 				continue;
 			if ((ia->ia_ifp->if_flags & IFF_UP) != 0)
@@ -613,6 +615,8 @@
 			if (ifa->ifa_addr->sa_family != AF_INET)
 				continue;
 			ia = ifatoia(ifa);
+			if (ia->ia4_flags & IN_IFF_NOTREADY)
+				continue;
 			if (in_hosteq(ip->ip_dst, ia->ia_broadaddr.sin_addr) ||
 			    in_hosteq(ip->ip_dst, ia->ia_netbroadcast) ||
 			    /*
@@ -1641,6 +1645,14 @@
 		       sysctl_net_inet_ip_stats, 0, NULL, 0,
 		       CTL_NET, PF_INET, IPPROTO_IP, IPCTL_STATS,
 		       CTL_EOL);
+	sysctl_createv(clog, 0, NULL, NULL,
+		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+		       CTLTYPE_INT, "dad_count",
+		       SYSCTL_DESCR("Number of Duplicate Address Detection "
+				    "probes to send"),
+		       NULL, 0, &ip_dad_count, 0,
+		       CTL_NET, PF_INET, IPPROTO_IP,
+		       IPCTL_DAD_COUNT, CTL_EOL);
 
 	/* anonportalgo RFC6056 subtree */
 	const struct sysctlnode *portalgo_node;
Index: sys/netinet/raw_ip.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/raw_ip.c,v
retrieving revision 1.147
diff -u -r1.147 raw_ip.c
--- sys/netinet/raw_ip.c	3 Apr 2015 20:01:07 -0000	1.147
+++ sys/netinet/raw_ip.c	21 Apr 2015 08:30:53 -0000
@@ -563,6 +563,7 @@
 	struct sockaddr_in *addr = (struct sockaddr_in *)nam;
 	int error = 0;
 	int s;
+	struct ifaddr *ia;
 
 	KASSERT(solocked(so));
 	KASSERT(inp != NULL);
@@ -580,11 +581,19 @@
 		error = EAFNOSUPPORT;
 		goto release;
 	}
-	if (!in_nullhost(addr->sin_addr) &&
-	    ifa_ifwithaddr(sintosa(addr)) == 0) {
+	if ((ia = ifa_ifwithaddr(sintosa(addr))) == 0 &&
+	    !in_nullhost(addr->sin_addr))
+	{
 		error = EADDRNOTAVAIL;
 		goto release;
 	}
+        if (ia && ((struct in_ifaddr *)ia)->ia4_flags &
+	            (IN6_IFF_NOTREADY | IN_IFF_DETACHED))
+	{
+		error = EADDRNOTAVAIL;
+		goto release;
+	}
+
 	inp->inp_laddr = addr->sin_addr;
 
 release:
Index: sys/sys/sockio.h
===================================================================
RCS file: /cvsroot/src/sys/sys/sockio.h,v
retrieving revision 1.32
diff -u -r1.32 sockio.h
--- sys/sys/sockio.h	5 Oct 2013 23:16:54 -0000	1.32
+++ sys/sys/sockio.h	21 Apr 2015 08:30:54 -0000
@@ -72,6 +72,7 @@
 
 #define	SIOCAIFADDR	 _IOW('i', 26, struct ifaliasreq)/* add/chg IF alias */
 #define	SIOCGIFALIAS	_IOWR('i', 27, struct ifaliasreq)/* get IF alias */
+#define	SIOCGIFAFLAG_IN _IOWR('i', 39, struct ifreq)	/* get addr flags */
 
 #define	SIOCALIFADDR	 _IOW('i', 28, struct if_laddrreq) /* add IF addr */
 #define	SIOCGLIFADDR	_IOWR('i', 29, struct if_laddrreq) /* get IF addr */


Home | Main Index | Thread Index | Old Index