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

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
......@@ -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;
......
......@@ -900,15 +900,95 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
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) {
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);
......@@ -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)) {
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
......@@ -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);
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))
......@@ -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) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment