Skip to content

Commit

Permalink
ipv6 addrconf: introduce IFA_F_MANAGETEMPADDR to tell kernel to manag…
Browse files Browse the repository at this point in the history
…e 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>
  • Loading branch information
jpirko authored and davem330 committed Dec 6, 2013
1 parent 479840f commit 53bd674
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 72 deletions.
1 change: 1 addition & 0 deletions include/uapi/linux/if_addr.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ enum {
#define IFA_F_DEPRECATED 0x20
#define IFA_F_TENTATIVE 0x40
#define IFA_F_PERMANENT 0x80
#define IFA_F_MANAGETEMPADDR 0x100

struct ifa_cacheinfo {
__u32 ifa_prefered;
Expand Down
171 changes: 99 additions & 72 deletions net/ipv6/addrconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1024,7 +1024,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);
Expand All @@ -1036,7 +1036,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;
Expand All @@ -1046,7 +1046,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
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);
Expand All @@ -1072,7 +1072,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
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,
Expand All @@ -1099,7 +1099,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
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;
}

Expand Down Expand Up @@ -2016,6 +2016,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;
Expand Down Expand Up @@ -2170,6 +2237,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
return;
}

ifp->flags |= IFA_F_MANAGETEMPADDR;
update_lft = 0;
create = 1;
ifp->cstamp = jiffies;
Expand All @@ -2180,7 +2248,6 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
if (ifp) {
u32 flags;
unsigned long now;
struct inet6_ifaddr *ift;
u32 stored_lft;

/* update lifetime (RFC2462 5.5.3 e) */
Expand Down Expand Up @@ -2221,70 +2288,8 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
} 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);
Expand Down Expand Up @@ -2386,6 +2391,9 @@ static int inet6_addr_add(struct net *net, int ifindex,
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;
Expand Down Expand Up @@ -2426,6 +2434,9 @@ static int inet6_addr_add(struct net *net, int ifindex,
* 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;
Expand Down Expand Up @@ -3603,10 +3614,15 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags,
u32 flags;
clock_t expires;
unsigned long timeout;
bool was_managetempaddr;

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);
Expand All @@ -3626,7 +3642,10 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 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;
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->valid_lft = valid_lft;
ifp->prefered_lft = prefered_lft;
Expand All @@ -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,
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);

return 0;
Expand Down Expand Up @@ -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;

/* 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);
if (ifa == NULL) {
Expand Down

0 comments on commit 53bd674

Please sign in to comment.