[PATCH F20/F21 (3.13)] ipv6: introduce IFA_F_NOPREFIXROUTE and IFA_F_MANAGETEMPADDR flags

Jiri Pirko jpirko at redhat.com
Wed Jan 29 13:46:45 UTC 2014


Josh, should I repost this with the fix or will you apply the fix onto
this?

Thanks, Jiri

Fri, Jan 24, 2014 at 04:44:34PM CET, jpirko at redhat.com wrote:
>This patch is made against 3.13 kernel. So it's suitable for F21 and for F20
>later on when F20 is rebased to 3.13.
>
>https://bugzilla.redhat.com/show_bug.cgi?id=1056711
>
>NetworkManager depends on IFA_F_NOPREFIXROUTE IFA_F_MANAGETEMPADDR
>in order to do SLAAC in userspace correctly.
>
>Split patches available here:
>
>http://people.redhat.com/jpirko/f21_backport_of_IFA_F_NOPREFIXROUTE_and_IFA_F_MANAGETEMPADDR/
>
>Jiri Pirko (2):
>  ipv6 addrconf: extend ifa_flags to u32
>  ipv6 addrconf: introduce IFA_F_MANAGETEMPADDR to tell kernel to manage
>    temporary addresses
>
>Li RongQing (1):
>  ipv6: unneccessary to get address prefix in addrconf_get_prefix_route
>
>Thomas Haller (2):
>  ipv6 addrconf: add IFA_F_NOPREFIXROUTE flag to suppress creation of
>    IP6 routes
>  ipv6 addrconf: don't cleanup prefix route for IFA_F_NOPREFIXROUTE
>
>stephen hemminger (1):
>  ipv6: addrconf spelling fixes
>
> include/net/addrconf.h       |   4 +-
> include/net/if_inet6.h       |   2 +-
> include/uapi/linux/if_addr.h |   6 +
> net/ipv6/addrconf.c          | 409 +++++++++++++++++++++++++------------------
> 4 files changed, 250 insertions(+), 171 deletions(-)
>
>Signed-off-by: Jiri Pirko <jpirko at redhat.com>
>
>diff --git a/include/net/addrconf.h b/include/net/addrconf.h
>index 86505bf..e70278e 100644
>--- a/include/net/addrconf.h
>+++ b/include/net/addrconf.h
>@@ -81,9 +81,9 @@ int ipv6_dev_get_saddr(struct net *net, const struct net_device *dev,
> 		       const struct in6_addr *daddr, unsigned int srcprefs,
> 		       struct in6_addr *saddr);
> int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr,
>-		      unsigned char banned_flags);
>+		      u32 banned_flags);
> int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
>-		    unsigned char banned_flags);
>+		    u32 banned_flags);
> int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2);
> void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr);
> void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr);
>diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h
>index 65bb130..9650a3f 100644
>--- a/include/net/if_inet6.h
>+++ b/include/net/if_inet6.h
>@@ -50,8 +50,8 @@ struct inet6_ifaddr {
> 
> 	int			state;
> 
>+	__u32			flags;
> 	__u8			dad_probes;
>-	__u8			flags;
> 
> 	__u16			scope;
> 
>diff --git a/include/uapi/linux/if_addr.h b/include/uapi/linux/if_addr.h
>index 23357ab..dea10a8 100644
>--- a/include/uapi/linux/if_addr.h
>+++ b/include/uapi/linux/if_addr.h
>@@ -18,6 +18,9 @@ struct ifaddrmsg {
>  * It makes no difference for normally configured broadcast interfaces,
>  * but for point-to-point IFA_ADDRESS is DESTINATION address,
>  * local address is supplied in IFA_LOCAL attribute.
>+ *
>+ * IFA_FLAGS is a u32 attribute that extends the u8 field ifa_flags.
>+ * If present, the value from struct ifaddrmsg will be ignored.
>  */
> enum {
> 	IFA_UNSPEC,
>@@ -28,6 +31,7 @@ enum {
> 	IFA_ANYCAST,
> 	IFA_CACHEINFO,
> 	IFA_MULTICAST,
>+	IFA_FLAGS,
> 	__IFA_MAX,
> };
> 
>@@ -44,6 +48,8 @@ enum {
> #define IFA_F_DEPRECATED	0x20
> #define IFA_F_TENTATIVE		0x40
> #define IFA_F_PERMANENT		0x80
>+#define IFA_F_MANAGETEMPADDR	0x100
>+#define IFA_F_NOPREFIXROUTE	0x200
> 
> struct ifa_cacheinfo {
> 	__u32	ifa_prefered;
>diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
>index 4b6b720..3c4e25d 100644
>--- a/net/ipv6/addrconf.c
>+++ b/net/ipv6/addrconf.c
>@@ -891,15 +891,95 @@ out:
> 	goto out2;
> }
> 
>+enum cleanup_prefix_rt_t {
>+	CLEANUP_PREFIX_RT_NOP,    /* no cleanup action for prefix route */
>+	CLEANUP_PREFIX_RT_DEL,    /* delete the prefix route */
>+	CLEANUP_PREFIX_RT_EXPIRE, /* update the lifetime of the prefix route */
>+};
>+
>+/*
>+ * Check, whether the prefix for ifp would still need a prefix route
>+ * after deleting ifp. The function returns one of the CLEANUP_PREFIX_RT_*
>+ * constants.
>+ *
>+ * 1) we don't purge prefix if address was not permanent.
>+ *    prefix is managed by its own lifetime.
>+ * 2) we also don't purge, if the address was IFA_F_NOPREFIXROUTE.
>+ * 3) if there are no addresses, delete prefix.
>+ * 4) if there are still other permanent address(es),
>+ *    corresponding prefix is still permanent.
>+ * 5) if there are still other addresses with IFA_F_NOPREFIXROUTE,
>+ *    don't purge the prefix, assume user space is managing it.
>+ * 6) otherwise, update prefix lifetime to the
>+ *    longest valid lifetime among the corresponding
>+ *    addresses on the device.
>+ *    Note: subsequent RA will update lifetime.
>+ **/
>+static enum cleanup_prefix_rt_t
>+check_cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long *expires)
>+{
>+	struct inet6_ifaddr *ifa;
>+	struct inet6_dev *idev = ifp->idev;
>+	unsigned long lifetime;
>+	enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_DEL;
>+
>+	*expires = jiffies;
>+
>+	list_for_each_entry(ifa, &idev->addr_list, if_list) {
>+		if (ifa == ifp)
>+			continue;
>+		if (!ipv6_prefix_equal(&ifa->addr, &ifp->addr,
>+				       ifp->prefix_len))
>+			continue;
>+		if (ifa->flags & (IFA_F_PERMANENT | IFA_F_NOPREFIXROUTE))
>+			return CLEANUP_PREFIX_RT_NOP;
>+
>+		action = CLEANUP_PREFIX_RT_EXPIRE;
>+
>+		spin_lock(&ifa->lock);
>+
>+		lifetime = addrconf_timeout_fixup(ifa->valid_lft, HZ);
>+		/*
>+		 * Note: Because this address is
>+		 * not permanent, lifetime <
>+		 * LONG_MAX / HZ here.
>+		 */
>+		if (time_before(*expires, ifa->tstamp + lifetime * HZ))
>+			*expires = ifa->tstamp + lifetime * HZ;
>+		spin_unlock(&ifa->lock);
>+	}
>+
>+	return action;
>+}
>+
>+static void
>+cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long expires, bool del_rt)
>+{
>+	struct rt6_info *rt;
>+
>+	rt = addrconf_get_prefix_route(&ifp->addr,
>+				       ifp->prefix_len,
>+				       ifp->idev->dev,
>+				       0, RTF_GATEWAY | RTF_DEFAULT);
>+	if (rt) {
>+		if (del_rt)
>+			ip6_del_rt(rt);
>+		else {
>+			if (!(rt->rt6i_flags & RTF_EXPIRES))
>+				rt6_set_expires(rt, expires);
>+			ip6_rt_put(rt);
>+		}
>+	}
>+}
>+
>+
> /* This function wants to get referenced ifp and releases it before return */
> 
> static void ipv6_del_addr(struct inet6_ifaddr *ifp)
> {
>-	struct inet6_ifaddr *ifa, *ifn;
>-	struct inet6_dev *idev = ifp->idev;
> 	int state;
>-	int deleted = 0, onlink = 0;
>-	unsigned long expires = jiffies;
>+	enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_NOP;
>+	unsigned long expires;
> 
> 	spin_lock_bh(&ifp->state_lock);
> 	state = ifp->state;
>@@ -913,7 +993,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
> 	hlist_del_init_rcu(&ifp->addr_lst);
> 	spin_unlock_bh(&addrconf_hash_lock);
> 
>-	write_lock_bh(&idev->lock);
>+	write_lock_bh(&ifp->idev->lock);
> 
> 	if (ifp->flags&IFA_F_TEMPORARY) {
> 		list_del(&ifp->tmp_list);
>@@ -924,45 +1004,13 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
> 		__in6_ifa_put(ifp);
> 	}
> 
>-	list_for_each_entry_safe(ifa, ifn, &idev->addr_list, if_list) {
>-		if (ifa == ifp) {
>-			list_del_init(&ifp->if_list);
>-			__in6_ifa_put(ifp);
>+	if (ifp->flags & IFA_F_PERMANENT && !(ifp->flags & IFA_F_NOPREFIXROUTE))
>+		action = check_cleanup_prefix_route(ifp, &expires);
> 
>-			if (!(ifp->flags & IFA_F_PERMANENT) || onlink > 0)
>-				break;
>-			deleted = 1;
>-			continue;
>-		} else if (ifp->flags & IFA_F_PERMANENT) {
>-			if (ipv6_prefix_equal(&ifa->addr, &ifp->addr,
>-					      ifp->prefix_len)) {
>-				if (ifa->flags & IFA_F_PERMANENT) {
>-					onlink = 1;
>-					if (deleted)
>-						break;
>-				} else {
>-					unsigned long lifetime;
>-
>-					if (!onlink)
>-						onlink = -1;
>-
>-					spin_lock(&ifa->lock);
>-
>-					lifetime = addrconf_timeout_fixup(ifa->valid_lft, HZ);
>-					/*
>-					 * Note: Because this address is
>-					 * not permanent, lifetime <
>-					 * LONG_MAX / HZ here.
>-					 */
>-					if (time_before(expires,
>-							ifa->tstamp + lifetime * HZ))
>-						expires = ifa->tstamp + lifetime * HZ;
>-					spin_unlock(&ifa->lock);
>-				}
>-			}
>-		}
>-	}
>-	write_unlock_bh(&idev->lock);
>+	list_del_init(&ifp->if_list);
>+	__in6_ifa_put(ifp);
>+
>+	write_unlock_bh(&ifp->idev->lock);
> 
> 	addrconf_del_dad_timer(ifp);
> 
>@@ -970,41 +1018,9 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
> 
> 	inet6addr_notifier_call_chain(NETDEV_DOWN, ifp);
> 
>-	/*
>-	 * Purge or update corresponding prefix
>-	 *
>-	 * 1) we don't purge prefix here if address was not permanent.
>-	 *    prefix is managed by its own lifetime.
>-	 * 2) if there're no addresses, delete prefix.
>-	 * 3) if there're still other permanent address(es),
>-	 *    corresponding prefix is still permanent.
>-	 * 4) otherwise, update prefix lifetime to the
>-	 *    longest valid lifetime among the corresponding
>-	 *    addresses on the device.
>-	 *    Note: subsequent RA will update lifetime.
>-	 *
>-	 * --yoshfuji
>-	 */
>-	if ((ifp->flags & IFA_F_PERMANENT) && onlink < 1) {
>-		struct in6_addr prefix;
>-		struct rt6_info *rt;
>-
>-		ipv6_addr_prefix(&prefix, &ifp->addr, ifp->prefix_len);
>-
>-		rt = addrconf_get_prefix_route(&prefix,
>-					       ifp->prefix_len,
>-					       ifp->idev->dev,
>-					       0, RTF_GATEWAY | RTF_DEFAULT);
>-
>-		if (rt) {
>-			if (onlink == 0) {
>-				ip6_del_rt(rt);
>-				rt = NULL;
>-			} else if (!(rt->rt6i_flags & RTF_EXPIRES)) {
>-				rt6_set_expires(rt, expires);
>-			}
>-		}
>-		ip6_rt_put(rt);
>+	if (action != CLEANUP_PREFIX_RT_NOP) {
>+		cleanup_prefix_route(ifp, expires,
>+			action == CLEANUP_PREFIX_RT_DEL);
> 	}
> 
> 	/* clean up prefsrc entries */
>@@ -1024,7 +1040,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
> 	u32 addr_flags;
> 	unsigned long now = jiffies;
> 
>-	write_lock(&idev->lock);
>+	write_lock_bh(&idev->lock);
> 	if (ift) {
> 		spin_lock_bh(&ift->lock);
> 		memcpy(&addr.s6_addr[8], &ift->addr.s6_addr[8], 8);
>@@ -1036,7 +1052,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
> retry:
> 	in6_dev_hold(idev);
> 	if (idev->cnf.use_tempaddr <= 0) {
>-		write_unlock(&idev->lock);
>+		write_unlock_bh(&idev->lock);
> 		pr_info("%s: use_tempaddr is disabled\n", __func__);
> 		in6_dev_put(idev);
> 		ret = -1;
>@@ -1046,7 +1062,7 @@ retry:
> 	if (ifp->regen_count++ >= idev->cnf.regen_max_retry) {
> 		idev->cnf.use_tempaddr = -1;	/*XXX*/
> 		spin_unlock_bh(&ifp->lock);
>-		write_unlock(&idev->lock);
>+		write_unlock_bh(&idev->lock);
> 		pr_warn("%s: regeneration time exceeded - disabled temporary address support\n",
> 			__func__);
> 		in6_dev_put(idev);
>@@ -1072,7 +1088,7 @@ retry:
> 	regen_advance = idev->cnf.regen_max_retry *
> 	                idev->cnf.dad_transmits *
> 	                idev->nd_parms->retrans_time / HZ;
>-	write_unlock(&idev->lock);
>+	write_unlock_bh(&idev->lock);
> 
> 	/* A temporary address is created only if this calculated Preferred
> 	 * Lifetime is greater than REGEN_ADVANCE time units.  In particular,
>@@ -1099,7 +1115,7 @@ retry:
> 		in6_dev_put(idev);
> 		pr_info("%s: retry temporary address regeneration\n", __func__);
> 		tmpaddr = &addr;
>-		write_lock(&idev->lock);
>+		write_lock_bh(&idev->lock);
> 		goto retry;
> 	}
> 
>@@ -1200,7 +1216,7 @@ static int ipv6_get_saddr_eval(struct net *net,
> 		 *       |             d is scope of the destination.
> 		 *  B-d  |  \
> 		 *       |   \      <- smaller scope is better if
>-		 *  B-15 |    \        if scope is enough for destinaion.
>+		 *  B-15 |    \        if scope is enough for destination.
> 		 *       |             ret = B - scope (-1 <= scope >= d <= 15).
> 		 * d-C-1 | /
> 		 *       |/         <- greater is better
>@@ -1407,7 +1423,7 @@ try_nextdev:
> EXPORT_SYMBOL(ipv6_dev_get_saddr);
> 
> int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr,
>-		      unsigned char banned_flags)
>+		      u32 banned_flags)
> {
> 	struct inet6_ifaddr *ifp;
> 	int err = -EADDRNOTAVAIL;
>@@ -1424,7 +1440,7 @@ int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr,
> }
> 
> int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
>-		    unsigned char banned_flags)
>+		    u32 banned_flags)
> {
> 	struct inet6_dev *idev;
> 	int err = -EADDRNOTAVAIL;
>@@ -2016,6 +2032,73 @@ static struct inet6_dev *addrconf_add_dev(struct net_device *dev)
> 	return idev;
> }
> 
>+static void manage_tempaddrs(struct inet6_dev *idev,
>+			     struct inet6_ifaddr *ifp,
>+			     __u32 valid_lft, __u32 prefered_lft,
>+			     bool create, unsigned long now)
>+{
>+	u32 flags;
>+	struct inet6_ifaddr *ift;
>+
>+	read_lock_bh(&idev->lock);
>+	/* update all temporary addresses in the list */
>+	list_for_each_entry(ift, &idev->tempaddr_list, tmp_list) {
>+		int age, max_valid, max_prefered;
>+
>+		if (ifp != ift->ifpub)
>+			continue;
>+
>+		/* RFC 4941 section 3.3:
>+		 * If a received option will extend the lifetime of a public
>+		 * address, the lifetimes of temporary addresses should
>+		 * be extended, subject to the overall constraint that no
>+		 * temporary addresses should ever remain "valid" or "preferred"
>+		 * for a time longer than (TEMP_VALID_LIFETIME) or
>+		 * (TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR), respectively.
>+		 */
>+		age = (now - ift->cstamp) / HZ;
>+		max_valid = idev->cnf.temp_valid_lft - age;
>+		if (max_valid < 0)
>+			max_valid = 0;
>+
>+		max_prefered = idev->cnf.temp_prefered_lft -
>+			       idev->cnf.max_desync_factor - age;
>+		if (max_prefered < 0)
>+			max_prefered = 0;
>+
>+		if (valid_lft > max_valid)
>+			valid_lft = max_valid;
>+
>+		if (prefered_lft > max_prefered)
>+			prefered_lft = max_prefered;
>+
>+		spin_lock(&ift->lock);
>+		flags = ift->flags;
>+		ift->valid_lft = valid_lft;
>+		ift->prefered_lft = prefered_lft;
>+		ift->tstamp = now;
>+		if (prefered_lft > 0)
>+			ift->flags &= ~IFA_F_DEPRECATED;
>+
>+		spin_unlock(&ift->lock);
>+		if (!(flags&IFA_F_TENTATIVE))
>+			ipv6_ifa_notify(0, ift);
>+	}
>+
>+	if ((create || list_empty(&idev->tempaddr_list)) &&
>+	    idev->cnf.use_tempaddr > 0) {
>+		/* When a new public address is created as described
>+		 * in [ADDRCONF], also create a new temporary address.
>+		 * Also create a temporary address if it's enabled but
>+		 * no temporary address currently exists.
>+		 */
>+		read_unlock_bh(&idev->lock);
>+		ipv6_create_tempaddr(ifp, NULL);
>+	} else {
>+		read_unlock_bh(&idev->lock);
>+	}
>+}
>+
> void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
> {
> 	struct prefix_info *pinfo;
>@@ -2170,6 +2253,7 @@ ok:
> 				return;
> 			}
> 
>+			ifp->flags |= IFA_F_MANAGETEMPADDR;
> 			update_lft = 0;
> 			create = 1;
> 			ifp->cstamp = jiffies;
>@@ -2178,9 +2262,8 @@ ok:
> 		}
> 
> 		if (ifp) {
>-			int flags;
>+			u32 flags;
> 			unsigned long now;
>-			struct inet6_ifaddr *ift;
> 			u32 stored_lft;
> 
> 			/* update lifetime (RFC2462 5.5.3 e) */
>@@ -2221,70 +2304,8 @@ ok:
> 			} else
> 				spin_unlock(&ifp->lock);
> 
>-			read_lock_bh(&in6_dev->lock);
>-			/* update all temporary addresses in the list */
>-			list_for_each_entry(ift, &in6_dev->tempaddr_list,
>-					    tmp_list) {
>-				int age, max_valid, max_prefered;
>-
>-				if (ifp != ift->ifpub)
>-					continue;
>-
>-				/*
>-				 * RFC 4941 section 3.3:
>-				 * If a received option will extend the lifetime
>-				 * of a public address, the lifetimes of
>-				 * temporary addresses should be extended,
>-				 * subject to the overall constraint that no
>-				 * temporary addresses should ever remain
>-				 * "valid" or "preferred" for a time longer than
>-				 * (TEMP_VALID_LIFETIME) or
>-				 * (TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR),
>-				 * respectively.
>-				 */
>-				age = (now - ift->cstamp) / HZ;
>-				max_valid = in6_dev->cnf.temp_valid_lft - age;
>-				if (max_valid < 0)
>-					max_valid = 0;
>-
>-				max_prefered = in6_dev->cnf.temp_prefered_lft -
>-					       in6_dev->cnf.max_desync_factor -
>-					       age;
>-				if (max_prefered < 0)
>-					max_prefered = 0;
>-
>-				if (valid_lft > max_valid)
>-					valid_lft = max_valid;
>-
>-				if (prefered_lft > max_prefered)
>-					prefered_lft = max_prefered;
>-
>-				spin_lock(&ift->lock);
>-				flags = ift->flags;
>-				ift->valid_lft = valid_lft;
>-				ift->prefered_lft = prefered_lft;
>-				ift->tstamp = now;
>-				if (prefered_lft > 0)
>-					ift->flags &= ~IFA_F_DEPRECATED;
>-
>-				spin_unlock(&ift->lock);
>-				if (!(flags&IFA_F_TENTATIVE))
>-					ipv6_ifa_notify(0, ift);
>-			}
>-
>-			if ((create || list_empty(&in6_dev->tempaddr_list)) && in6_dev->cnf.use_tempaddr > 0) {
>-				/*
>-				 * When a new public address is created as
>-				 * described in [ADDRCONF], also create a new
>-				 * temporary address. Also create a temporary
>-				 * address if it's enabled but no temporary
>-				 * address currently exists.
>-				 */
>-				read_unlock_bh(&in6_dev->lock);
>-				ipv6_create_tempaddr(ifp, NULL);
>-			} else {
>-				read_unlock_bh(&in6_dev->lock);
>-			}
>+			manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft,
>+					 create, now);
> 
> 			in6_ifa_put(ifp);
> 			addrconf_verify(0);
>@@ -2363,10 +2384,11 @@ err_exit:
> /*
>  *	Manual configuration of address on an interface
>  */
>-static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *pfx,
>+static int inet6_addr_add(struct net *net, int ifindex,
>+			  const struct in6_addr *pfx,
> 			  const struct in6_addr *peer_pfx,
>-			  unsigned int plen, __u8 ifa_flags, __u32 prefered_lft,
>-			  __u32 valid_lft)
>+			  unsigned int plen, __u32 ifa_flags,
>+			  __u32 prefered_lft, __u32 valid_lft)
> {
> 	struct inet6_ifaddr *ifp;
> 	struct inet6_dev *idev;
>@@ -2385,6 +2407,9 @@ static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *p
> 	if (!valid_lft || prefered_lft > valid_lft)
> 		return -EINVAL;
> 
>+	if (ifa_flags & IFA_F_MANAGETEMPADDR && plen != 64)
>+		return -EINVAL;
>+
> 	dev = __dev_get_by_index(net, ifindex);
> 	if (!dev)
> 		return -ENODEV;
>@@ -2417,14 +2442,20 @@ static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *p
> 			    valid_lft, prefered_lft);
> 
> 	if (!IS_ERR(ifp)) {
>-		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev,
>-				      expires, flags);
>+		if (!(ifa_flags & IFA_F_NOPREFIXROUTE)) {
>+			addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev,
>+					      expires, flags);
>+		}
>+
> 		/*
> 		 * Note that section 3.1 of RFC 4429 indicates
> 		 * that the Optimistic flag should not be set for
> 		 * manually configured addresses
> 		 */
> 		addrconf_dad_start(ifp);
>+		if (ifa_flags & IFA_F_MANAGETEMPADDR)
>+			manage_tempaddrs(idev, ifp, valid_lft, prefered_lft,
>+					 true, jiffies);
> 		in6_ifa_put(ifp);
> 		addrconf_verify(0);
> 		return 0;
>@@ -2857,7 +2888,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
> 		}
> 
> 		/*
>-		 * MTU falled under IPV6_MIN_MTU.
>+		 * if MTU under IPV6_MIN_MTU.
> 		 * Stop IPv6 on this interface.
> 		 */
> 
>@@ -3366,7 +3397,7 @@ static void if6_seq_stop(struct seq_file *seq, void *v)
> static int if6_seq_show(struct seq_file *seq, void *v)
> {
> 	struct inet6_ifaddr *ifp = (struct inet6_ifaddr *)v;
>-	seq_printf(seq, "%pi6 %02x %02x %02x %02x %8s\n",
>+	seq_printf(seq, "%pi6 %02x %02x %02x %03x %8s\n",
> 		   &ifp->addr,
> 		   ifp->idev->dev->ifindex,
> 		   ifp->prefix_len,
>@@ -3593,6 +3624,7 @@ static const struct nla_policy ifa_ipv6_policy[IFA_MAX+1] = {
> 	[IFA_ADDRESS]		= { .len = sizeof(struct in6_addr) },
> 	[IFA_LOCAL]		= { .len = sizeof(struct in6_addr) },
> 	[IFA_CACHEINFO]		= { .len = sizeof(struct ifa_cacheinfo) },
>+	[IFA_FLAGS]		= { .len = sizeof(u32) },
> };
> 
> static int
>@@ -3616,16 +3648,22 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
> 	return inet6_addr_del(net, ifm->ifa_index, pfx, ifm->ifa_prefixlen);
> }
> 
>-static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
>+static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags,
> 			     u32 prefered_lft, u32 valid_lft)
> {
> 	u32 flags;
> 	clock_t expires;
> 	unsigned long timeout;
>+	bool was_managetempaddr;
>+	bool had_prefixroute;
> 
> 	if (!valid_lft || (prefered_lft > valid_lft))
> 		return -EINVAL;
> 
>+	if (ifa_flags & IFA_F_MANAGETEMPADDR &&
>+	    (ifp->flags & IFA_F_TEMPORARY || ifp->prefix_len != 64))
>+		return -EINVAL;
>+
> 	timeout = addrconf_timeout_fixup(valid_lft, HZ);
> 	if (addrconf_finite_timeout(timeout)) {
> 		expires = jiffies_to_clock_t(timeout * HZ);
>@@ -3645,7 +3683,13 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
> 	}
> 
> 	spin_lock_bh(&ifp->lock);
>-	ifp->flags = (ifp->flags & ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_NODAD | IFA_F_HOMEADDRESS)) | ifa_flags;
>+	was_managetempaddr = ifp->flags & IFA_F_MANAGETEMPADDR;
>+	had_prefixroute = ifp->flags & IFA_F_PERMANENT &&
>+			  !(ifp->flags & IFA_F_NOPREFIXROUTE);
>+	ifp->flags &= ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_NODAD |
>+			IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR |
>+			IFA_F_NOPREFIXROUTE);
>+	ifp->flags |= ifa_flags;
> 	ifp->tstamp = jiffies;
> 	ifp->valid_lft = valid_lft;
> 	ifp->prefered_lft = prefered_lft;
>@@ -3654,8 +3698,30 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
> 	if (!(ifp->flags&IFA_F_TENTATIVE))
> 		ipv6_ifa_notify(0, ifp);
> 
>-	addrconf_prefix_route(&ifp->addr, ifp->prefix_len, ifp->idev->dev,
>-			      expires, flags);
>+	if (!(ifa_flags & IFA_F_NOPREFIXROUTE)) {
>+		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, ifp->idev->dev,
>+				      expires, flags);
>+	} else if (had_prefixroute) {
>+		enum cleanup_prefix_rt_t action;
>+		unsigned long rt_expires;
>+
>+		write_lock_bh(&ifp->idev->lock);
>+		action = check_cleanup_prefix_route(ifp, &rt_expires);
>+		write_unlock_bh(&ifp->idev->lock);
>+
>+		if (action != CLEANUP_PREFIX_RT_NOP) {
>+			cleanup_prefix_route(ifp, rt_expires,
>+				action == CLEANUP_PREFIX_RT_DEL);
>+		}
>+	}
>+
>+	if (was_managetempaddr || ifp->flags & IFA_F_MANAGETEMPADDR) {
>+		if (was_managetempaddr && !(ifp->flags & IFA_F_MANAGETEMPADDR))
>+			valid_lft = prefered_lft = 0;
>+		manage_tempaddrs(ifp->idev, ifp, valid_lft, prefered_lft,
>+				 !was_managetempaddr, jiffies);
>+	}
>+
> 	addrconf_verify(0);
> 
> 	return 0;
>@@ -3671,7 +3737,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
> 	struct inet6_ifaddr *ifa;
> 	struct net_device *dev;
> 	u32 valid_lft = INFINITY_LIFE_TIME, preferred_lft = INFINITY_LIFE_TIME;
>-	u8 ifa_flags;
>+	u32 ifa_flags;
> 	int err;
> 
> 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy);
>@@ -3698,14 +3764,17 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
> 	if (dev == NULL)
> 		return -ENODEV;
> 
>+	ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifm->ifa_flags;
>+
> 	/* We ignore other flags so far. */
>-	ifa_flags = ifm->ifa_flags & (IFA_F_NODAD | IFA_F_HOMEADDRESS);
>+	ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR |
>+		     IFA_F_NOPREFIXROUTE;
> 
> 	ifa = ipv6_get_ifaddr(net, pfx, dev, 1);
> 	if (ifa == NULL) {
> 		/*
> 		 * It would be best to check for !NLM_F_CREATE here but
>-		 * userspace alreay relies on not having to provide this.
>+		 * userspace already relies on not having to provide this.
> 		 */
> 		return inet6_addr_add(net, ifm->ifa_index, pfx, peer_pfx,
> 				      ifm->ifa_prefixlen, ifa_flags,
>@@ -3723,7 +3792,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
> 	return err;
> }
> 
>-static void put_ifaddrmsg(struct nlmsghdr *nlh, u8 prefixlen, u8 flags,
>+static void put_ifaddrmsg(struct nlmsghdr *nlh, u8 prefixlen, u32 flags,
> 			  u8 scope, int ifindex)
> {
> 	struct ifaddrmsg *ifm;
>@@ -3766,7 +3835,8 @@ static inline int inet6_ifaddr_msgsize(void)
> 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
> 	       + nla_total_size(16) /* IFA_LOCAL */
> 	       + nla_total_size(16) /* IFA_ADDRESS */
>-	       + nla_total_size(sizeof(struct ifa_cacheinfo));
>+	       + nla_total_size(sizeof(struct ifa_cacheinfo))
>+	       + nla_total_size(4)  /* IFA_FLAGS */;
> }
> 
> static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
>@@ -3815,6 +3885,9 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
> 	if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0)
> 		goto error;
> 
>+	if (nla_put_u32(skb, IFA_FLAGS, ifa->flags) < 0)
>+		goto error;
>+
> 	return nlmsg_end(skb, nlh);
> 
> error:


More information about the kernel mailing list