UBUNTU: SAUCE: ipv6: make the net.ipv6.conf.all.use_tempaddr sysctl propagate to...
authorMathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com>
Tue, 10 Jan 2012 13:54:34 +0000 (14:54 +0100)
committerLeann Ogasawara <leann.ogasawara@canonical.com>
Mon, 2 Apr 2012 20:16:25 +0000 (13:16 -0700)
The description for IPV6_PRIVACY mentions using .../all/use_tempaddr to enable
IPv6 Privacy Extensions, and IP sysctl documentation mentions 'all' as setting
all interface-specific settings. We make sure at least use_tempaddr actually
works as documented.

Signed-off-by: Mathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com>
Signed-off-by: Tim Gardner <tim.gardner@canonical.com>

net/ipv6/addrconf.c

index a5521c5..98bb9ae 100644 (file)
@@ -4345,6 +4345,84 @@ int addrconf_sysctl_disable(ctl_table *ctl, int write,
        return ret;
 }
 
+#ifdef CONFIG_IPV6_PRIVACY
+static void dev_tempaddr_change(struct inet6_dev *idev)
+{
+       if (!idev || !idev->dev)
+               return;
+
+       if (!idev->cnf.disable_ipv6) {
+               /* If ipv6 is enabled, try to bring down and back up the
+                * interface to get new temporary addresses created
+                */
+               addrconf_notify(NULL, NETDEV_DOWN, idev->dev);
+               addrconf_notify(NULL, NETDEV_UP, idev->dev);
+       }
+}
+
+static void addrconf_tempaddr_change(struct net *net, __s32 newf)
+{
+       struct net_device *dev;
+       struct inet6_dev *idev;
+
+       rcu_read_lock();
+       for_each_netdev_rcu(net, dev) {
+               idev = __in6_dev_get(dev);
+               if (idev) {
+                       int changed = (!idev->cnf.use_tempaddr) ^ (!newf);
+                       idev->cnf.use_tempaddr = newf;
+                       if (changed)
+                               dev_tempaddr_change(idev);
+               }
+       }
+       rcu_read_unlock();
+}
+
+static int addrconf_use_tempaddr(struct ctl_table *table, int *p, int old)
+{
+       struct net *net;
+
+       net = (struct net *)table->extra2;
+
+       if (p == &net->ipv6.devconf_dflt->use_tempaddr)
+               return 0;
+
+       if (!rtnl_trylock()) {
+               /* Restore the original values before restarting */
+               *p = old;
+               return restart_syscall();
+       }
+
+       if (p == &net->ipv6.devconf_all->use_tempaddr) {
+               __s32 newf = net->ipv6.devconf_all->use_tempaddr;
+               net->ipv6.devconf_dflt->use_tempaddr = newf;
+               addrconf_tempaddr_change(net, newf);
+       } else if ((!*p) ^ (!old))
+               dev_tempaddr_change((struct inet6_dev *)table->extra1);
+
+       rtnl_unlock();
+       return 0;
+}
+
+static
+int addrconf_sysctl_tempaddr(ctl_table *ctl, int write,
+                            void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+       int *valp = ctl->data;
+       int val = *valp;
+       loff_t pos = *ppos;
+       int ret;
+
+       ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
+
+       if (write)
+               ret = addrconf_use_tempaddr(ctl, valp, val);
+       if (ret)
+               *ppos = pos;
+       return ret;
+}
+#endif
+
 static struct addrconf_sysctl_table
 {
        struct ctl_table_header *sysctl_header;
@@ -4436,7 +4514,7 @@ static struct addrconf_sysctl_table
                        .data           = &ipv6_devconf.use_tempaddr,
                        .maxlen         = sizeof(int),
                        .mode           = 0644,
-                       .proc_handler   = proc_dointvec,
+                       .proc_handler   = addrconf_sysctl_tempaddr,
                },
                {
                        .procname       = "temp_valid_lft",