Skip to content

Commit

Permalink
net/ipv6: allow sysctl to change link-local address generation mode
Browse files Browse the repository at this point in the history
The address generation mode for IPv6 link-local can only be configured
by netlink messages. This patch adds the ability to change the address
generation mode via sysctl.

v1 -> v2
Removed the rtnl lock and switch to use RCU lock to iterate through
the netdev list.

v2 -> v3
Removed the addrgenmode variable from the idev structure and use the
systcl storage for the flag.

Simplifed the logic for sysctl handling by removing the supported
for all operation.

Added support for more types of tunnel interfaces for link-local
address generation.

Based the patches from net-next.

v3 -> v4
Removed unnecessary whitespace changes.

Signed-off-by: Felix Jia <felix.jia@alliedtelesis.co.nz>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
felix-jia authored and davem330 committed Jan 27, 2017
1 parent 6a4bc2b commit d35a00b
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 21 deletions.
1 change: 1 addition & 0 deletions include/linux/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ struct ipv6_devconf {
__s32 seg6_require_hmac;
#endif
__u32 enhanced_dad;
__u32 addr_gen_mode;

struct ctl_table_header *sysctl_header;
};
Expand Down
1 change: 0 additions & 1 deletion include/net/if_inet6.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ struct inet6_dev {
__s32 rs_interval; /* in jiffies */
__u8 rs_probes;

__u8 addr_gen_mode;
unsigned long tstamp; /* ipv6InterfaceTable update timestamp */
struct rcu_head rcu;
};
Expand Down
1 change: 1 addition & 0 deletions include/uapi/linux/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ enum {
DEVCONF_SEG6_ENABLED,
DEVCONF_SEG6_REQUIRE_HMAC,
DEVCONF_ENHANCED_DAD,
DEVCONF_ADDR_GEN_MODE,
DEVCONF_MAX
};

Expand Down
104 changes: 84 additions & 20 deletions net/ipv6/addrconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
.seg6_require_hmac = 0,
#endif
.enhanced_dad = 1,
.addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64,
};

static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
Expand Down Expand Up @@ -294,6 +295,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
.seg6_require_hmac = 0,
#endif
.enhanced_dad = 1,
.addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64,
};

/* Check if a valid qdisc is available */
Expand Down Expand Up @@ -386,9 +388,9 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
memcpy(&ndev->cnf, dev_net(dev)->ipv6.devconf_dflt, sizeof(ndev->cnf));

if (ndev->cnf.stable_secret.initialized)
ndev->addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
ndev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
else
ndev->addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64;
ndev->cnf.addr_gen_mode = ipv6_devconf_dflt.addr_gen_mode;

ndev->cnf.mtu6 = dev->mtu;
ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);
Expand Down Expand Up @@ -2387,8 +2389,8 @@ static void manage_tempaddrs(struct inet6_dev *idev,

static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
{
return idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY ||
idev->addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
return idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY ||
idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
}

int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
Expand Down Expand Up @@ -3152,7 +3154,7 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)

ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0);

switch (idev->addr_gen_mode) {
switch (idev->cnf.addr_gen_mode) {
case IN6_ADDR_GEN_MODE_RANDOM:
ipv6_gen_mode_random_init(idev);
/* fallthrough */
Expand Down Expand Up @@ -3204,8 +3206,8 @@ static void addrconf_dev_config(struct net_device *dev)

/* this device type has no EUI support */
if (dev->type == ARPHRD_NONE &&
idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64)
idev->addr_gen_mode = IN6_ADDR_GEN_MODE_RANDOM;
idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64)
idev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_RANDOM;

addrconf_addr_gen(idev, false);
}
Expand Down Expand Up @@ -4982,6 +4984,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
array[DEVCONF_SEG6_REQUIRE_HMAC] = cnf->seg6_require_hmac;
#endif
array[DEVCONF_ENHANCED_DAD] = cnf->enhanced_dad;
array[DEVCONF_ADDR_GEN_MODE] = cnf->addr_gen_mode;
}

static inline size_t inet6_ifla6_size(void)
Expand Down Expand Up @@ -5093,7 +5096,7 @@ static int inet6_fill_ifla6_attrs(struct sk_buff *skb, struct inet6_dev *idev,
if (!nla)
goto nla_put_failure;

if (nla_put_u8(skb, IFLA_INET6_ADDR_GEN_MODE, idev->addr_gen_mode))
if (nla_put_u8(skb, IFLA_INET6_ADDR_GEN_MODE, idev->cnf.addr_gen_mode))
goto nla_put_failure;

read_lock_bh(&idev->lock);
Expand Down Expand Up @@ -5211,6 +5214,26 @@ static int inet6_validate_link_af(const struct net_device *dev,
return nla_parse_nested(tb, IFLA_INET6_MAX, nla, inet6_af_policy);
}

static int check_addr_gen_mode(int mode)
{
if (mode != IN6_ADDR_GEN_MODE_EUI64 &&
mode != IN6_ADDR_GEN_MODE_NONE &&
mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
mode != IN6_ADDR_GEN_MODE_RANDOM)
return -EINVAL;
return 1;
}

static int check_stable_privacy(struct inet6_dev *idev, struct net *net,
int mode)
{
if (mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
!idev->cnf.stable_secret.initialized &&
!net->ipv6.devconf_dflt->stable_secret.initialized)
return -EINVAL;
return 1;
}

static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla)
{
int err = -EINVAL;
Expand All @@ -5232,18 +5255,11 @@ static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla)
if (tb[IFLA_INET6_ADDR_GEN_MODE]) {
u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);

if (mode != IN6_ADDR_GEN_MODE_EUI64 &&
mode != IN6_ADDR_GEN_MODE_NONE &&
mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
mode != IN6_ADDR_GEN_MODE_RANDOM)
return -EINVAL;

if (mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
!idev->cnf.stable_secret.initialized &&
!dev_net(dev)->ipv6.devconf_dflt->stable_secret.initialized)
if (check_addr_gen_mode(mode) < 0 ||
check_stable_privacy(idev, dev_net(dev), mode) < 0)
return -EINVAL;

idev->addr_gen_mode = mode;
idev->cnf.addr_gen_mode = mode;
err = 0;
}

Expand Down Expand Up @@ -5652,6 +5668,47 @@ int addrconf_sysctl_proxy_ndp(struct ctl_table *ctl, int write,
return ret;
}

static int addrconf_sysctl_addr_gen_mode(struct ctl_table *ctl, int write,
void __user *buffer, size_t *lenp,
loff_t *ppos)
{
int ret = 0;
int new_val;
struct inet6_dev *idev = (struct inet6_dev *)ctl->extra1;
struct net *net = (struct net *)ctl->extra2;

ret = proc_dointvec(ctl, write, buffer, lenp, ppos);

if (write) {
new_val = *((int *)ctl->data);

if (check_addr_gen_mode(new_val) < 0)
return -EINVAL;

/* request for default */
if (&net->ipv6.devconf_dflt->addr_gen_mode == ctl->data) {
ipv6_devconf_dflt.addr_gen_mode = new_val;

/* request for individual net device */
} else {
if (!idev)
return ret;

if (check_stable_privacy(idev, net, new_val) < 0)
return -EINVAL;

if (idev->cnf.addr_gen_mode != new_val) {
idev->cnf.addr_gen_mode = new_val;
rtnl_lock();
addrconf_dev_config(idev->dev);
rtnl_unlock();
}
}
}

return ret;
}

static int addrconf_sysctl_stable_secret(struct ctl_table *ctl, int write,
void __user *buffer, size_t *lenp,
loff_t *ppos)
Expand Down Expand Up @@ -5702,14 +5759,14 @@ static int addrconf_sysctl_stable_secret(struct ctl_table *ctl, int write,
struct inet6_dev *idev = __in6_dev_get(dev);

if (idev) {
idev->addr_gen_mode =
idev->cnf.addr_gen_mode =
IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
}
}
} else {
struct inet6_dev *idev = ctl->extra1;

idev->addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
idev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
}

out:
Expand Down Expand Up @@ -6096,6 +6153,13 @@ static const struct ctl_table addrconf_sysctl[] = {
.mode = 0644,
.proc_handler = proc_dointvec,
},
{
.procname = "addr_gen_mode",
.data = &ipv6_devconf.addr_gen_mode,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = addrconf_sysctl_addr_gen_mode,
},
{
/* sentinel */
}
Expand Down

0 comments on commit d35a00b

Please sign in to comment.