IPv6: Add 'autoconf' and 'disable_ipv6' module parameters

Add 'autoconf' and 'disable_ipv6' parameters to the IPv6 module.

The first controls if IPv6 addresses are autoconfigured from
prefixes received in Router Advertisements.  The IPv6 loopback
(::1) and link-local addresses are still configured.

The second controls if IPv6 addresses are desired at all.  No
IPv6 addresses will be added to any interfaces.

Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Brian Haley 2009-06-01 03:07:33 -07:00 committed by David S. Miller
parent 0220ff7fc3
commit 56d417b12e
5 changed files with 145 additions and 10 deletions

View File

@ -1057,6 +1057,13 @@ disable_ipv6 - BOOLEAN
address.
Default: FALSE (enable IPv6 operation)
When this value is changed from 1 to 0 (IPv6 is being enabled),
it will dynamically create a link-local address on the given
interface and start Duplicate Address Detection, if necessary.
When this value is changed from 0 to 1 (IPv6 is being disabled),
it will dynamically delete all address on the given interface.
accept_dad - INTEGER
Whether to accept DAD (Duplicate Address Detection).
0: Disable DAD

View File

@ -33,3 +33,40 @@ disable
A reboot is required to enable IPv6.
autoconf
Specifies whether to enable IPv6 address autoconfiguration
on all interfaces. This might be used when one does not wish
for addresses to be automatically generated from prefixes
received in Router Advertisements.
The possible values and their effects are:
0
IPv6 address autoconfiguration is disabled on all interfaces.
Only the IPv6 loopback address (::1) and link-local addresses
will be added to interfaces.
1
IPv6 address autoconfiguration is enabled on all interfaces.
This is the default value.
disable_ipv6
Specifies whether to disable IPv6 on all interfaces.
This might be used when no IPv6 addresses are desired.
The possible values and their effects are:
0
IPv6 is enabled on all interfaces.
This is the default value.
1
IPv6 is disabled on all interfaces.
No IPv6 addresses will be added to interfaces.

View File

@ -169,6 +169,12 @@ struct ipv6_devconf {
__s32 accept_dad;
void *sysctl;
};
struct ipv6_params {
__s32 disable_ipv6;
__s32 autoconf;
};
extern struct ipv6_params ipv6_defaults;
#endif
/* index values for the variables in ipv6_devconf */

View File

@ -591,7 +591,6 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
{
struct inet6_ifaddr *ifa = NULL;
struct rt6_info *rt;
struct net *net = dev_net(idev->dev);
int hash;
int err = 0;
int addr_type = ipv6_addr_type(addr);
@ -608,7 +607,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
goto out2;
}
if (idev->cnf.disable_ipv6 || net->ipv6.devconf_all->disable_ipv6) {
if (idev->cnf.disable_ipv6) {
err = -EACCES;
goto out2;
}
@ -1752,6 +1751,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
__u32 prefered_lft;
int addr_type;
struct inet6_dev *in6_dev;
struct net *net = dev_net(dev);
pinfo = (struct prefix_info *) opt;
@ -1809,7 +1809,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
if (addrconf_finite_timeout(rt_expires))
rt_expires *= HZ;
rt = rt6_lookup(dev_net(dev), &pinfo->prefix, NULL,
rt = rt6_lookup(net, &pinfo->prefix, NULL,
dev->ifindex, 1);
if (rt && addrconf_is_prefix_route(rt)) {
@ -1846,7 +1846,6 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
struct inet6_ifaddr * ifp;
struct in6_addr addr;
int create = 0, update_lft = 0;
struct net *net = dev_net(dev);
if (pinfo->prefix_len == 64) {
memcpy(&addr, &pinfo->prefix, 8);
@ -3988,6 +3987,75 @@ static int addrconf_sysctl_forward_strategy(ctl_table *table,
return addrconf_fixup_forwarding(table, valp, val);
}
static void dev_disable_change(struct inet6_dev *idev)
{
if (!idev || !idev->dev)
return;
if (idev->cnf.disable_ipv6)
addrconf_notify(NULL, NETDEV_DOWN, idev->dev);
else
addrconf_notify(NULL, NETDEV_UP, idev->dev);
}
static void addrconf_disable_change(struct net *net, __s32 newf)
{
struct net_device *dev;
struct inet6_dev *idev;
read_lock(&dev_base_lock);
for_each_netdev(net, dev) {
rcu_read_lock();
idev = __in6_dev_get(dev);
if (idev) {
int changed = (!idev->cnf.disable_ipv6) ^ (!newf);
idev->cnf.disable_ipv6 = newf;
if (changed)
dev_disable_change(idev);
}
rcu_read_unlock();
}
read_unlock(&dev_base_lock);
}
static int addrconf_disable_ipv6(struct ctl_table *table, int *p, int old)
{
struct net *net;
net = (struct net *)table->extra2;
if (p == &net->ipv6.devconf_dflt->disable_ipv6)
return 0;
if (!rtnl_trylock())
return restart_syscall();
if (p == &net->ipv6.devconf_all->disable_ipv6) {
__s32 newf = net->ipv6.devconf_all->disable_ipv6;
net->ipv6.devconf_dflt->disable_ipv6 = newf;
addrconf_disable_change(net, newf);
} else if ((!*p) ^ (!old))
dev_disable_change((struct inet6_dev *)table->extra1);
rtnl_unlock();
return 0;
}
static
int addrconf_sysctl_disable(ctl_table *ctl, int write, struct file * filp,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
int *valp = ctl->data;
int val = *valp;
int ret;
ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
if (write)
ret = addrconf_disable_ipv6(ctl, valp, val);
return ret;
}
static struct addrconf_sysctl_table
{
struct ctl_table_header *sysctl_header;
@ -4225,7 +4293,8 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.disable_ipv6,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
.proc_handler = addrconf_sysctl_disable,
.strategy = sysctl_intvec,
},
{
.ctl_name = CTL_UNNUMBERED,
@ -4346,6 +4415,10 @@ static int addrconf_init_net(struct net *net)
dflt = kmemdup(dflt, sizeof(ipv6_devconf_dflt), GFP_KERNEL);
if (dflt == NULL)
goto err_alloc_dflt;
} else {
/* these will be inherited by all namespaces */
dflt->autoconf = ipv6_defaults.autoconf;
dflt->disable_ipv6 = ipv6_defaults.disable_ipv6;
}
net->ipv6.devconf_all = all;

View File

@ -72,9 +72,21 @@ MODULE_LICENSE("GPL");
static struct list_head inetsw6[SOCK_MAX];
static DEFINE_SPINLOCK(inetsw6_lock);
static int disable_ipv6 = 0;
module_param_named(disable, disable_ipv6, int, 0);
MODULE_PARM_DESC(disable, "Disable IPv6 such that it is non-functional");
struct ipv6_params ipv6_defaults = {
.disable_ipv6 = 0,
.autoconf = 1,
};
static int disable_ipv6_mod = 0;
module_param_named(disable, disable_ipv6_mod, int, 0444);
MODULE_PARM_DESC(disable, "Disable IPv6 module such that it is non-functional");
module_param_named(disable_ipv6, ipv6_defaults.disable_ipv6, int, 0444);
MODULE_PARM_DESC(disable_ipv6, "Disable IPv6 on all interfaces");
module_param_named(autoconf, ipv6_defaults.autoconf, int, 0444);
MODULE_PARM_DESC(autoconf, "Enable IPv6 address autoconfiguration on all interfaces");
static __inline__ struct ipv6_pinfo *inet6_sk_generic(struct sock *sk)
{
@ -1038,7 +1050,7 @@ static int __init inet6_init(void)
for(r = &inetsw6[0]; r < &inetsw6[SOCK_MAX]; ++r)
INIT_LIST_HEAD(r);
if (disable_ipv6) {
if (disable_ipv6_mod) {
printk(KERN_INFO
"IPv6: Loaded, but administratively disabled, "
"reboot required to enable\n");
@ -1227,7 +1239,7 @@ module_init(inet6_init);
static void __exit inet6_exit(void)
{
if (disable_ipv6)
if (disable_ipv6_mod)
return;
/* First of all disallow new sockets creation. */