File net/ipv6/addrconf.c has the following code:
static
int addrconf_sysctl_forward(ctl_table *ctl, int write, struct file * filp,
void *buffer, size_t *lenp)
{
...
if (valp != &ipv6_devconf.forwarding) {
struct net_device *dev = dev_get_by_index(ctl->ctl_name)
The problem is, at this point, "ctl" points to the child sysctl entry (procname
"forwarding", ctl_name is 1 (always)). The parent node has ifindex set, but not
the child. On my system, the dev returned (no matter what device the sysctl
was changing) is "lo", which has ifindex 1.
Below is a patch that saves a pointer to in6_dev in ctl->extra1 and
uses that as the argument to addrconf_forward_change(). The existing code
doesn't actually use the idev pointer for anything meaningful, and the case
where it'd matter most ("all") doesn't do the look-up. This would be most
broken on existing systems if an ifindex 1 device didn't exist. Then, it
appears a change on any interface would affect all of them.
The new code passes a pointer, rather than an ifindex, in the unused
"extra1" field. Note that the cnf data is embedded in the in6_dev structure,
so no extra reference count is needed, and that avoids the call and search by
index in dev_get_by_index(). Because no new reference is acquired, no
in6_dev_put() on exit.
Another patch (to follow) actually needs a valid idev pointer here,
which is how I came across it. Patch line numbers are relative to 2.4.9.
+-DLS
--- linux-2.4.9/net/ipv6/addrconf.c Tue Aug 7 08:30:50 2001
+++ linux-2.4.9NEW/net/ipv6/addrconf.c Tue Aug 28 14:49:48 2001
@@ -1836,11 +1836,7 @@
struct inet6_dev *idev = NULL;
if (valp != &ipv6_devconf.forwarding) {
- struct net_device *dev = dev_get_by_index(ctl->ctl_name);
- if (dev) {
- idev = in6_dev_get(dev);
- dev_put(dev);
- }
+ idev = (struct net_device *)ctl->extra1;
if (idev == NULL)
return ret;
} else
@@ -1850,8 +1846,6 @@
if (*valp)
rt6_purge_dflt_routers(0);
- if (idev)
- in6_dev_put(idev);
}
return ret;
@@ -1928,6 +1922,7 @@
for (i=0; i<sizeof(t->addrconf_vars)/sizeof(t->addrconf_vars[0])-1; i++) {
t->addrconf_vars[i].data += (char*)p - (char*)&ipv6_devconf;
t->addrconf_vars[i].de = NULL;
+ t->addrconf_vars[i].extra1 = idev; /* embedded; no ref */
}
if (dev) {
t->addrconf_dev[0].procname = dev->name;
|