Skip to content

Commit d35a00b

Browse files
felix-jiadavem330
authored andcommitted
net/ipv6: allow sysctl to change link-local address generation mode
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>
1 parent 6a4bc2b commit d35a00b

File tree

4 files changed

+86
-21
lines changed

4 files changed

+86
-21
lines changed

include/linux/ipv6.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ struct ipv6_devconf {
6969
__s32 seg6_require_hmac;
7070
#endif
7171
__u32 enhanced_dad;
72+
__u32 addr_gen_mode;
7273

7374
struct ctl_table_header *sysctl_header;
7475
};

include/net/if_inet6.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,6 @@ struct inet6_dev {
205205
__s32 rs_interval; /* in jiffies */
206206
__u8 rs_probes;
207207

208-
__u8 addr_gen_mode;
209208
unsigned long tstamp; /* ipv6InterfaceTable update timestamp */
210209
struct rcu_head rcu;
211210
};

include/uapi/linux/ipv6.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ enum {
182182
DEVCONF_SEG6_ENABLED,
183183
DEVCONF_SEG6_REQUIRE_HMAC,
184184
DEVCONF_ENHANCED_DAD,
185+
DEVCONF_ADDR_GEN_MODE,
185186
DEVCONF_MAX
186187
};
187188

net/ipv6/addrconf.c

Lines changed: 84 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
243243
.seg6_require_hmac = 0,
244244
#endif
245245
.enhanced_dad = 1,
246+
.addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64,
246247
};
247248

248249
static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -294,6 +295,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
294295
.seg6_require_hmac = 0,
295296
#endif
296297
.enhanced_dad = 1,
298+
.addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64,
297299
};
298300

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

388390
if (ndev->cnf.stable_secret.initialized)
389-
ndev->addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
391+
ndev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
390392
else
391-
ndev->addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64;
393+
ndev->cnf.addr_gen_mode = ipv6_devconf_dflt.addr_gen_mode;
392394

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

23882390
static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
23892391
{
2390-
return idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY ||
2391-
idev->addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
2392+
return idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY ||
2393+
idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
23922394
}
23932395

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

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

3155-
switch (idev->addr_gen_mode) {
3157+
switch (idev->cnf.addr_gen_mode) {
31563158
case IN6_ADDR_GEN_MODE_RANDOM:
31573159
ipv6_gen_mode_random_init(idev);
31583160
/* fallthrough */
@@ -3204,8 +3206,8 @@ static void addrconf_dev_config(struct net_device *dev)
32043206

32053207
/* this device type has no EUI support */
32063208
if (dev->type == ARPHRD_NONE &&
3207-
idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64)
3208-
idev->addr_gen_mode = IN6_ADDR_GEN_MODE_RANDOM;
3209+
idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64)
3210+
idev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_RANDOM;
32093211

32103212
addrconf_addr_gen(idev, false);
32113213
}
@@ -4982,6 +4984,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
49824984
array[DEVCONF_SEG6_REQUIRE_HMAC] = cnf->seg6_require_hmac;
49834985
#endif
49844986
array[DEVCONF_ENHANCED_DAD] = cnf->enhanced_dad;
4987+
array[DEVCONF_ADDR_GEN_MODE] = cnf->addr_gen_mode;
49854988
}
49864989

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

5096-
if (nla_put_u8(skb, IFLA_INET6_ADDR_GEN_MODE, idev->addr_gen_mode))
5099+
if (nla_put_u8(skb, IFLA_INET6_ADDR_GEN_MODE, idev->cnf.addr_gen_mode))
50975100
goto nla_put_failure;
50985101

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

5217+
static int check_addr_gen_mode(int mode)
5218+
{
5219+
if (mode != IN6_ADDR_GEN_MODE_EUI64 &&
5220+
mode != IN6_ADDR_GEN_MODE_NONE &&
5221+
mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
5222+
mode != IN6_ADDR_GEN_MODE_RANDOM)
5223+
return -EINVAL;
5224+
return 1;
5225+
}
5226+
5227+
static int check_stable_privacy(struct inet6_dev *idev, struct net *net,
5228+
int mode)
5229+
{
5230+
if (mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
5231+
!idev->cnf.stable_secret.initialized &&
5232+
!net->ipv6.devconf_dflt->stable_secret.initialized)
5233+
return -EINVAL;
5234+
return 1;
5235+
}
5236+
52145237
static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla)
52155238
{
52165239
int err = -EINVAL;
@@ -5232,18 +5255,11 @@ static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla)
52325255
if (tb[IFLA_INET6_ADDR_GEN_MODE]) {
52335256
u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);
52345257

5235-
if (mode != IN6_ADDR_GEN_MODE_EUI64 &&
5236-
mode != IN6_ADDR_GEN_MODE_NONE &&
5237-
mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
5238-
mode != IN6_ADDR_GEN_MODE_RANDOM)
5239-
return -EINVAL;
5240-
5241-
if (mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
5242-
!idev->cnf.stable_secret.initialized &&
5243-
!dev_net(dev)->ipv6.devconf_dflt->stable_secret.initialized)
5258+
if (check_addr_gen_mode(mode) < 0 ||
5259+
check_stable_privacy(idev, dev_net(dev), mode) < 0)
52445260
return -EINVAL;
52455261

5246-
idev->addr_gen_mode = mode;
5262+
idev->cnf.addr_gen_mode = mode;
52475263
err = 0;
52485264
}
52495265

@@ -5652,6 +5668,47 @@ int addrconf_sysctl_proxy_ndp(struct ctl_table *ctl, int write,
56525668
return ret;
56535669
}
56545670

5671+
static int addrconf_sysctl_addr_gen_mode(struct ctl_table *ctl, int write,
5672+
void __user *buffer, size_t *lenp,
5673+
loff_t *ppos)
5674+
{
5675+
int ret = 0;
5676+
int new_val;
5677+
struct inet6_dev *idev = (struct inet6_dev *)ctl->extra1;
5678+
struct net *net = (struct net *)ctl->extra2;
5679+
5680+
ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
5681+
5682+
if (write) {
5683+
new_val = *((int *)ctl->data);
5684+
5685+
if (check_addr_gen_mode(new_val) < 0)
5686+
return -EINVAL;
5687+
5688+
/* request for default */
5689+
if (&net->ipv6.devconf_dflt->addr_gen_mode == ctl->data) {
5690+
ipv6_devconf_dflt.addr_gen_mode = new_val;
5691+
5692+
/* request for individual net device */
5693+
} else {
5694+
if (!idev)
5695+
return ret;
5696+
5697+
if (check_stable_privacy(idev, net, new_val) < 0)
5698+
return -EINVAL;
5699+
5700+
if (idev->cnf.addr_gen_mode != new_val) {
5701+
idev->cnf.addr_gen_mode = new_val;
5702+
rtnl_lock();
5703+
addrconf_dev_config(idev->dev);
5704+
rtnl_unlock();
5705+
}
5706+
}
5707+
}
5708+
5709+
return ret;
5710+
}
5711+
56555712
static int addrconf_sysctl_stable_secret(struct ctl_table *ctl, int write,
56565713
void __user *buffer, size_t *lenp,
56575714
loff_t *ppos)
@@ -5702,14 +5759,14 @@ static int addrconf_sysctl_stable_secret(struct ctl_table *ctl, int write,
57025759
struct inet6_dev *idev = __in6_dev_get(dev);
57035760

57045761
if (idev) {
5705-
idev->addr_gen_mode =
5762+
idev->cnf.addr_gen_mode =
57065763
IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
57075764
}
57085765
}
57095766
} else {
57105767
struct inet6_dev *idev = ctl->extra1;
57115768

5712-
idev->addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
5769+
idev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
57135770
}
57145771

57155772
out:
@@ -6096,6 +6153,13 @@ static const struct ctl_table addrconf_sysctl[] = {
60966153
.mode = 0644,
60976154
.proc_handler = proc_dointvec,
60986155
},
6156+
{
6157+
.procname = "addr_gen_mode",
6158+
.data = &ipv6_devconf.addr_gen_mode,
6159+
.maxlen = sizeof(int),
6160+
.mode = 0644,
6161+
.proc_handler = addrconf_sysctl_addr_gen_mode,
6162+
},
60996163
{
61006164
/* sentinel */
61016165
}

0 commit comments

Comments
 (0)