IPv6: Add 'autoconf' and 'disable_ipv6' module parameters
authorBrian Haley <brian.haley@hp.com>
Mon, 1 Jun 2009 10:07:33 +0000 (03:07 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 1 Jun 2009 10:07:33 +0000 (03:07 -0700)
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>
Documentation/networking/ip-sysctl.txt
Documentation/networking/ipv6.txt
include/linux/ipv6.h
net/ipv6/addrconf.c
net/ipv6/af_inet6.c

index 3ffd233c369c97084556159d3851fee2f98348ea..8be76235fe6724c43e0c2b39778f3f741e53b619 100644 (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
index 268e5c103dd8e764c03e2dfba3356c3b4a2c7610..9fd7e21296c8f500938e7bc272f3ef0528fc3917 100644 (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.
+
index 476d9464ac82d27c53a0fc48c435639f77391dcd..c662efa6828985a4025951a1c655cf5bf85300cf 100644 (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 */
index 31938e5fb2206be53a0945cc39b2737d2018b214..c3488372f12d3d74d5da86871998411e03d7ecfd 100644 (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;
index b6215be0963f57689262886f5442eeab95fd9076..85b3d0036afdeff08efc32d1179dbabf7a58f9b3 100644 (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. */