ipv6 addrconf: introduce IFA_F_MANAGETEMPADDR to tell kernel to manage temporary addresses
Creating an address with this flag set will result in kernel taking care of temporary addresses in the same way as if the address was created by kernel itself (after RA receive). This allows userspace applications implementing the autoconfiguration (NetworkManager for example) to implement ipv6 addresses privacy. Signed-off-by: Jiri Pirko <jiri@resnulli.us> Signed-off-by: Thomas Haller <thaller@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
479840ffdb
commit
53bd674915
|
@ -48,6 +48,7 @@ enum {
|
||||||
#define IFA_F_DEPRECATED 0x20
|
#define IFA_F_DEPRECATED 0x20
|
||||||
#define IFA_F_TENTATIVE 0x40
|
#define IFA_F_TENTATIVE 0x40
|
||||||
#define IFA_F_PERMANENT 0x80
|
#define IFA_F_PERMANENT 0x80
|
||||||
|
#define IFA_F_MANAGETEMPADDR 0x100
|
||||||
|
|
||||||
struct ifa_cacheinfo {
|
struct ifa_cacheinfo {
|
||||||
__u32 ifa_prefered;
|
__u32 ifa_prefered;
|
||||||
|
|
|
@ -1024,7 +1024,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
|
||||||
u32 addr_flags;
|
u32 addr_flags;
|
||||||
unsigned long now = jiffies;
|
unsigned long now = jiffies;
|
||||||
|
|
||||||
write_lock(&idev->lock);
|
write_lock_bh(&idev->lock);
|
||||||
if (ift) {
|
if (ift) {
|
||||||
spin_lock_bh(&ift->lock);
|
spin_lock_bh(&ift->lock);
|
||||||
memcpy(&addr.s6_addr[8], &ift->addr.s6_addr[8], 8);
|
memcpy(&addr.s6_addr[8], &ift->addr.s6_addr[8], 8);
|
||||||
|
@ -1036,7 +1036,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
|
||||||
retry:
|
retry:
|
||||||
in6_dev_hold(idev);
|
in6_dev_hold(idev);
|
||||||
if (idev->cnf.use_tempaddr <= 0) {
|
if (idev->cnf.use_tempaddr <= 0) {
|
||||||
write_unlock(&idev->lock);
|
write_unlock_bh(&idev->lock);
|
||||||
pr_info("%s: use_tempaddr is disabled\n", __func__);
|
pr_info("%s: use_tempaddr is disabled\n", __func__);
|
||||||
in6_dev_put(idev);
|
in6_dev_put(idev);
|
||||||
ret = -1;
|
ret = -1;
|
||||||
|
@ -1046,7 +1046,7 @@ retry:
|
||||||
if (ifp->regen_count++ >= idev->cnf.regen_max_retry) {
|
if (ifp->regen_count++ >= idev->cnf.regen_max_retry) {
|
||||||
idev->cnf.use_tempaddr = -1; /*XXX*/
|
idev->cnf.use_tempaddr = -1; /*XXX*/
|
||||||
spin_unlock_bh(&ifp->lock);
|
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",
|
pr_warn("%s: regeneration time exceeded - disabled temporary address support\n",
|
||||||
__func__);
|
__func__);
|
||||||
in6_dev_put(idev);
|
in6_dev_put(idev);
|
||||||
|
@ -1072,7 +1072,7 @@ retry:
|
||||||
regen_advance = idev->cnf.regen_max_retry *
|
regen_advance = idev->cnf.regen_max_retry *
|
||||||
idev->cnf.dad_transmits *
|
idev->cnf.dad_transmits *
|
||||||
idev->nd_parms->retrans_time / HZ;
|
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
|
/* A temporary address is created only if this calculated Preferred
|
||||||
* Lifetime is greater than REGEN_ADVANCE time units. In particular,
|
* Lifetime is greater than REGEN_ADVANCE time units. In particular,
|
||||||
|
@ -1099,7 +1099,7 @@ retry:
|
||||||
in6_dev_put(idev);
|
in6_dev_put(idev);
|
||||||
pr_info("%s: retry temporary address regeneration\n", __func__);
|
pr_info("%s: retry temporary address regeneration\n", __func__);
|
||||||
tmpaddr = &addr;
|
tmpaddr = &addr;
|
||||||
write_lock(&idev->lock);
|
write_lock_bh(&idev->lock);
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2016,6 +2016,73 @@ static struct inet6_dev *addrconf_add_dev(struct net_device *dev)
|
||||||
return idev;
|
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)
|
void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
|
||||||
{
|
{
|
||||||
struct prefix_info *pinfo;
|
struct prefix_info *pinfo;
|
||||||
|
@ -2170,6 +2237,7 @@ ok:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ifp->flags |= IFA_F_MANAGETEMPADDR;
|
||||||
update_lft = 0;
|
update_lft = 0;
|
||||||
create = 1;
|
create = 1;
|
||||||
ifp->cstamp = jiffies;
|
ifp->cstamp = jiffies;
|
||||||
|
@ -2180,7 +2248,6 @@ ok:
|
||||||
if (ifp) {
|
if (ifp) {
|
||||||
u32 flags;
|
u32 flags;
|
||||||
unsigned long now;
|
unsigned long now;
|
||||||
struct inet6_ifaddr *ift;
|
|
||||||
u32 stored_lft;
|
u32 stored_lft;
|
||||||
|
|
||||||
/* update lifetime (RFC2462 5.5.3 e) */
|
/* update lifetime (RFC2462 5.5.3 e) */
|
||||||
|
@ -2221,70 +2288,8 @@ ok:
|
||||||
} else
|
} else
|
||||||
spin_unlock(&ifp->lock);
|
spin_unlock(&ifp->lock);
|
||||||
|
|
||||||
read_lock_bh(&in6_dev->lock);
|
manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft,
|
||||||
/* update all temporary addresses in the list */
|
create, now);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
in6_ifa_put(ifp);
|
in6_ifa_put(ifp);
|
||||||
addrconf_verify(0);
|
addrconf_verify(0);
|
||||||
|
@ -2386,6 +2391,9 @@ static int inet6_addr_add(struct net *net, int ifindex,
|
||||||
if (!valid_lft || prefered_lft > valid_lft)
|
if (!valid_lft || prefered_lft > valid_lft)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (ifa_flags & IFA_F_MANAGETEMPADDR && plen != 64)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
dev = __dev_get_by_index(net, ifindex);
|
dev = __dev_get_by_index(net, ifindex);
|
||||||
if (!dev)
|
if (!dev)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -2426,6 +2434,9 @@ static int inet6_addr_add(struct net *net, int ifindex,
|
||||||
* manually configured addresses
|
* manually configured addresses
|
||||||
*/
|
*/
|
||||||
addrconf_dad_start(ifp);
|
addrconf_dad_start(ifp);
|
||||||
|
if (ifa_flags & IFA_F_MANAGETEMPADDR)
|
||||||
|
manage_tempaddrs(idev, ifp, valid_lft, prefered_lft,
|
||||||
|
true, jiffies);
|
||||||
in6_ifa_put(ifp);
|
in6_ifa_put(ifp);
|
||||||
addrconf_verify(0);
|
addrconf_verify(0);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -3603,10 +3614,15 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags,
|
||||||
u32 flags;
|
u32 flags;
|
||||||
clock_t expires;
|
clock_t expires;
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
|
bool was_managetempaddr;
|
||||||
|
|
||||||
if (!valid_lft || (prefered_lft > valid_lft))
|
if (!valid_lft || (prefered_lft > valid_lft))
|
||||||
return -EINVAL;
|
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);
|
timeout = addrconf_timeout_fixup(valid_lft, HZ);
|
||||||
if (addrconf_finite_timeout(timeout)) {
|
if (addrconf_finite_timeout(timeout)) {
|
||||||
expires = jiffies_to_clock_t(timeout * HZ);
|
expires = jiffies_to_clock_t(timeout * HZ);
|
||||||
|
@ -3626,7 +3642,10 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags,
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_bh(&ifp->lock);
|
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;
|
||||||
|
ifp->flags &= ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_NODAD |
|
||||||
|
IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR);
|
||||||
|
ifp->flags |= ifa_flags;
|
||||||
ifp->tstamp = jiffies;
|
ifp->tstamp = jiffies;
|
||||||
ifp->valid_lft = valid_lft;
|
ifp->valid_lft = valid_lft;
|
||||||
ifp->prefered_lft = prefered_lft;
|
ifp->prefered_lft = prefered_lft;
|
||||||
|
@ -3637,6 +3656,14 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags,
|
||||||
|
|
||||||
addrconf_prefix_route(&ifp->addr, ifp->prefix_len, ifp->idev->dev,
|
addrconf_prefix_route(&ifp->addr, ifp->prefix_len, ifp->idev->dev,
|
||||||
expires, flags);
|
expires, flags);
|
||||||
|
|
||||||
|
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);
|
addrconf_verify(0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -3682,7 +3709,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||||
ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifm->ifa_flags;
|
ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifm->ifa_flags;
|
||||||
|
|
||||||
/* We ignore other flags so far. */
|
/* We ignore other flags so far. */
|
||||||
ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS;
|
ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR;
|
||||||
|
|
||||||
ifa = ipv6_get_ifaddr(net, pfx, dev, 1);
|
ifa = ipv6_get_ifaddr(net, pfx, dev, 1);
|
||||||
if (ifa == NULL) {
|
if (ifa == NULL) {
|
||||||
|
|
Loading…
Reference in a new issue