Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e5d64023 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'noprefixroute'



Thomas Haller says:

====================
ipv6 addrconf: add IFA_F_NOPREFIXROUTE flag to suppress creation of IP6 routes

v1 -> v2: add a second commit, handling NOPREFIXROUTE in ip6_del_addr.
v2 -> v3: reword commit messages, code comments and some refactoring.
v3 -> v4: refactor, rename variables, add enum
v4 -> v5: rebase, so that patch applies cleanly to current net-next/master
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 6631c5ce 5b84efec
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ enum {
#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;
+122 −81
Original line number Diff line number Diff line
@@ -900,15 +900,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;
@@ -922,7 +1002,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);
@@ -933,45 +1013,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) {
	if (ifp->flags & IFA_F_PERMANENT && !(ifp->flags & IFA_F_NOPREFIXROUTE))
		action = check_cleanup_prefix_route(ifp, &expires);

	list_del_init(&ifp->if_list);
	__in6_ifa_put(ifp);

			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);
	write_unlock_bh(&ifp->idev->lock);

	addrconf_del_dad_timer(ifp);

@@ -979,38 +1027,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 are no addresses, delete prefix.
	 * 3) if there are 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 rt6_info *rt;

		rt = addrconf_get_prefix_route(&ifp->addr,
					       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 */
@@ -2434,8 +2453,11 @@ static int inet6_addr_add(struct net *net, int ifindex,
			    valid_lft, prefered_lft);

	if (!IS_ERR(ifp)) {
		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
@@ -3633,6 +3655,7 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags,
	clock_t expires;
	unsigned long timeout;
	bool was_managetempaddr;
	bool had_prefixroute;

	if (!valid_lft || (prefered_lft > valid_lft))
		return -EINVAL;
@@ -3661,8 +3684,11 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags,

	spin_lock_bh(&ifp->lock);
	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_HOMEADDRESS | IFA_F_MANAGETEMPADDR |
			IFA_F_NOPREFIXROUTE);
	ifp->flags |= ifa_flags;
	ifp->tstamp = jiffies;
	ifp->valid_lft = valid_lft;
@@ -3672,8 +3698,22 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags,
	if (!(ifp->flags&IFA_F_TENTATIVE))
		ipv6_ifa_notify(0, ifp);

	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))
@@ -3727,7 +3767,8 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
	ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifm->ifa_flags;

	/* We ignore other flags so far. */
	ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR;
	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) {