Commit 31be3085 authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

[IPV4]: Add default config support after inetdev_init

Previously once inetdev_init has been called on a device any changes
made to ipv4_devconf_dflt would have no effect on that device's
configuration.

This creates a problem since we have moved the point where
inetdev_init is called from when an address is added to where the
device is registered.

This patch is the first half of a set that tries to mimic the old
behaviour while still calling inetdev_init.

It propagates any changes to ipv4_devconf_dflt to those devices that
have not had the corresponding attribute set.

The next patch will forcibly set all values at the point where
inetdev_init was previously called.
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 42f811b8
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/bitmap.h>
#include <linux/if.h> #include <linux/if.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
...@@ -12,6 +13,7 @@ struct ipv4_devconf ...@@ -12,6 +13,7 @@ struct ipv4_devconf
{ {
void *sysctl; void *sysctl;
int data[__NET_IPV4_CONF_MAX - 1]; int data[__NET_IPV4_CONF_MAX - 1];
DECLARE_BITMAP(state, __NET_IPV4_CONF_MAX - 1);
}; };
extern struct ipv4_devconf ipv4_devconf; extern struct ipv4_devconf ipv4_devconf;
...@@ -53,6 +55,7 @@ static inline void ipv4_devconf_set(struct in_device *in_dev, int index, ...@@ -53,6 +55,7 @@ static inline void ipv4_devconf_set(struct in_device *in_dev, int index,
int val) int val)
{ {
index--; index--;
set_bit(index, in_dev->cnf.state);
in_dev->cnf.data[index] = val; in_dev->cnf.data[index] = val;
} }
......
...@@ -1244,66 +1244,49 @@ static void rtmsg_ifa(int event, struct in_ifaddr* ifa, struct nlmsghdr *nlh, ...@@ -1244,66 +1244,49 @@ static void rtmsg_ifa(int event, struct in_ifaddr* ifa, struct nlmsghdr *nlh,
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
void inet_forward_change(void) static void devinet_copy_dflt_conf(int i)
{ {
struct net_device *dev; struct net_device *dev;
int on = IPV4_DEVCONF_ALL(FORWARDING);
IPV4_DEVCONF_ALL(ACCEPT_REDIRECTS) = !on;
IPV4_DEVCONF_DFLT(FORWARDING) = on;
read_lock(&dev_base_lock); read_lock(&dev_base_lock);
for_each_netdev(dev) { for_each_netdev(dev) {
struct in_device *in_dev; struct in_device *in_dev;
rcu_read_lock(); rcu_read_lock();
in_dev = __in_dev_get_rcu(dev); in_dev = __in_dev_get_rcu(dev);
if (in_dev) if (in_dev && !test_bit(i, in_dev->cnf.state))
IN_DEV_CONF_SET(in_dev, FORWARDING, on); in_dev->cnf.data[i] = ipv4_devconf_dflt.data[i];
rcu_read_unlock(); rcu_read_unlock();
} }
read_unlock(&dev_base_lock); read_unlock(&dev_base_lock);
rt_cache_flush(0);
} }
static int devinet_sysctl_forward(ctl_table *ctl, int write, static int devinet_conf_proc(ctl_table *ctl, int write,
struct file* filp, void __user *buffer, struct file* filp, void __user *buffer,
size_t *lenp, loff_t *ppos) size_t *lenp, loff_t *ppos)
{ {
int *valp = ctl->data;
int val = *valp;
int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos); int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
if (write && *valp != val) { if (write) {
if (valp == &IPV4_DEVCONF_ALL(FORWARDING)) struct ipv4_devconf *cnf = ctl->extra1;
inet_forward_change(); int i = (int *)ctl->data - cnf->data;
else if (valp != &IPV4_DEVCONF_DFLT(FORWARDING))
rt_cache_flush(0);
}
return ret; set_bit(i, cnf->state);
}
int ipv4_doint_and_flush(ctl_table *ctl, int write, if (cnf == &ipv4_devconf_dflt)
struct file* filp, void __user *buffer, devinet_copy_dflt_conf(i);
size_t *lenp, loff_t *ppos) }
{
int *valp = ctl->data;
int val = *valp;
int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
if (write && *valp != val)
rt_cache_flush(0);
return ret; return ret;
} }
int ipv4_doint_and_flush_strategy(ctl_table *table, int __user *name, int nlen, static int devinet_conf_sysctl(ctl_table *table, int __user *name, int nlen,
void __user *oldval, size_t __user *oldlenp, void __user *oldval, size_t __user *oldlenp,
void __user *newval, size_t newlen) void __user *newval, size_t newlen)
{ {
struct ipv4_devconf *cnf;
int *valp = table->data; int *valp = table->data;
int new; int new;
int i;
if (!newval || !newlen) if (!newval || !newlen)
return 0; return 0;
...@@ -1334,10 +1317,85 @@ int ipv4_doint_and_flush_strategy(ctl_table *table, int __user *name, int nlen, ...@@ -1334,10 +1317,85 @@ int ipv4_doint_and_flush_strategy(ctl_table *table, int __user *name, int nlen,
} }
*valp = new; *valp = new;
rt_cache_flush(0);
cnf = table->extra1;
i = (int *)table->data - cnf->data;
set_bit(i, cnf->state);
if (cnf == &ipv4_devconf_dflt)
devinet_copy_dflt_conf(i);
return 1; return 1;
} }
void inet_forward_change(void)
{
struct net_device *dev;
int on = IPV4_DEVCONF_ALL(FORWARDING);
IPV4_DEVCONF_ALL(ACCEPT_REDIRECTS) = !on;
IPV4_DEVCONF_DFLT(FORWARDING) = on;
read_lock(&dev_base_lock);
for_each_netdev(dev) {
struct in_device *in_dev;
rcu_read_lock();
in_dev = __in_dev_get_rcu(dev);
if (in_dev)
IN_DEV_CONF_SET(in_dev, FORWARDING, on);
rcu_read_unlock();
}
read_unlock(&dev_base_lock);
rt_cache_flush(0);
}
static int devinet_sysctl_forward(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 = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
if (write && *valp != val) {
if (valp == &IPV4_DEVCONF_ALL(FORWARDING))
inet_forward_change();
else if (valp != &IPV4_DEVCONF_DFLT(FORWARDING))
rt_cache_flush(0);
}
return ret;
}
int ipv4_doint_and_flush(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 = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
if (write && *valp != val)
rt_cache_flush(0);
return ret;
}
int ipv4_doint_and_flush_strategy(ctl_table *table, int __user *name, int nlen,
void __user *oldval, size_t __user *oldlenp,
void __user *newval, size_t newlen)
{
int ret = devinet_conf_sysctl(table, name, nlen, oldval, oldlenp,
newval, newlen);
if (ret == 1)
rt_cache_flush(0);
return ret;
}
#define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc, sysctl) \ #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc, sysctl) \
{ \ { \
...@@ -1349,13 +1407,16 @@ int ipv4_doint_and_flush_strategy(ctl_table *table, int __user *name, int nlen, ...@@ -1349,13 +1407,16 @@ int ipv4_doint_and_flush_strategy(ctl_table *table, int __user *name, int nlen,
.mode = mval, \ .mode = mval, \
.proc_handler = proc, \ .proc_handler = proc, \
.strategy = sysctl, \ .strategy = sysctl, \
.extra1 = &ipv4_devconf, \
} }
#define DEVINET_SYSCTL_RW_ENTRY(attr, name) \ #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
DEVINET_SYSCTL_ENTRY(attr, name, 0644, &proc_dointvec, NULL) DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc, \
devinet_conf_sysctl)
#define DEVINET_SYSCTL_RO_ENTRY(attr, name) \ #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
DEVINET_SYSCTL_ENTRY(attr, name, 0444, &proc_dointvec, NULL) DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc, \
devinet_conf_sysctl)
#define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc, sysctl) \ #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc, sysctl) \
DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc, sysctl) DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc, sysctl)
...@@ -1374,7 +1435,8 @@ static struct devinet_sysctl_table { ...@@ -1374,7 +1435,8 @@ static struct devinet_sysctl_table {
} devinet_sysctl = { } devinet_sysctl = {
.devinet_vars = { .devinet_vars = {
DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding", DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
devinet_sysctl_forward, NULL), devinet_sysctl_forward,
devinet_conf_sysctl),
DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"), DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"), DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
...@@ -1448,6 +1510,7 @@ static void devinet_sysctl_register(struct in_device *in_dev, ...@@ -1448,6 +1510,7 @@ static void devinet_sysctl_register(struct in_device *in_dev,
return; return;
for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) { for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf; t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
t->devinet_vars[i].extra1 = p;
} }
if (dev) { if (dev) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment