forked from Minki/linux
vlan: Fix vlan-in-vlan crashes.
As analyzed by Patrick McHardy, vlan needs to reset it's netdev_ops pointer in it's ->init() function but this leaves the compat method pointers stale. Add a netdev_resync_ops() and call it from the vlan code. Any other driver which changes ->netdev_ops after register_netdevice() will need to call this new function after doing so too. With help from Patrick McHardy. Tested-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
54acd0efab
commit
9d40bbda59
@ -1079,6 +1079,7 @@ extern void synchronize_net(void);
|
|||||||
extern int register_netdevice_notifier(struct notifier_block *nb);
|
extern int register_netdevice_notifier(struct notifier_block *nb);
|
||||||
extern int unregister_netdevice_notifier(struct notifier_block *nb);
|
extern int unregister_netdevice_notifier(struct notifier_block *nb);
|
||||||
extern int init_dummy_netdev(struct net_device *dev);
|
extern int init_dummy_netdev(struct net_device *dev);
|
||||||
|
extern void netdev_resync_ops(struct net_device *dev);
|
||||||
|
|
||||||
extern int call_netdevice_notifiers(unsigned long val, struct net_device *dev);
|
extern int call_netdevice_notifiers(unsigned long val, struct net_device *dev);
|
||||||
extern struct net_device *dev_get_by_index(struct net *net, int ifindex);
|
extern struct net_device *dev_get_by_index(struct net *net, int ifindex);
|
||||||
|
@ -553,7 +553,7 @@ static int vlan_dev_neigh_setup(struct net_device *dev, struct neigh_parms *pa)
|
|||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
if (netif_device_present(real_dev) && ops->ndo_neigh_setup)
|
if (netif_device_present(real_dev) && ops->ndo_neigh_setup)
|
||||||
err = ops->ndo_neigh_setup(dev, pa);
|
err = ops->ndo_neigh_setup(real_dev, pa);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -639,6 +639,7 @@ static int vlan_dev_init(struct net_device *dev)
|
|||||||
dev->hard_header_len = real_dev->hard_header_len + VLAN_HLEN;
|
dev->hard_header_len = real_dev->hard_header_len + VLAN_HLEN;
|
||||||
dev->netdev_ops = &vlan_netdev_ops;
|
dev->netdev_ops = &vlan_netdev_ops;
|
||||||
}
|
}
|
||||||
|
netdev_resync_ops(dev);
|
||||||
|
|
||||||
if (is_vlan_dev(real_dev))
|
if (is_vlan_dev(real_dev))
|
||||||
subclass = 1;
|
subclass = 1;
|
||||||
|
@ -4282,6 +4282,39 @@ unsigned long netdev_fix_features(unsigned long features, const char *name)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(netdev_fix_features);
|
EXPORT_SYMBOL(netdev_fix_features);
|
||||||
|
|
||||||
|
/* Some devices need to (re-)set their netdev_ops inside
|
||||||
|
* ->init() or similar. If that happens, we have to setup
|
||||||
|
* the compat pointers again.
|
||||||
|
*/
|
||||||
|
void netdev_resync_ops(struct net_device *dev)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_COMPAT_NET_DEV_OPS
|
||||||
|
const struct net_device_ops *ops = dev->netdev_ops;
|
||||||
|
|
||||||
|
dev->init = ops->ndo_init;
|
||||||
|
dev->uninit = ops->ndo_uninit;
|
||||||
|
dev->open = ops->ndo_open;
|
||||||
|
dev->change_rx_flags = ops->ndo_change_rx_flags;
|
||||||
|
dev->set_rx_mode = ops->ndo_set_rx_mode;
|
||||||
|
dev->set_multicast_list = ops->ndo_set_multicast_list;
|
||||||
|
dev->set_mac_address = ops->ndo_set_mac_address;
|
||||||
|
dev->validate_addr = ops->ndo_validate_addr;
|
||||||
|
dev->do_ioctl = ops->ndo_do_ioctl;
|
||||||
|
dev->set_config = ops->ndo_set_config;
|
||||||
|
dev->change_mtu = ops->ndo_change_mtu;
|
||||||
|
dev->neigh_setup = ops->ndo_neigh_setup;
|
||||||
|
dev->tx_timeout = ops->ndo_tx_timeout;
|
||||||
|
dev->get_stats = ops->ndo_get_stats;
|
||||||
|
dev->vlan_rx_register = ops->ndo_vlan_rx_register;
|
||||||
|
dev->vlan_rx_add_vid = ops->ndo_vlan_rx_add_vid;
|
||||||
|
dev->vlan_rx_kill_vid = ops->ndo_vlan_rx_kill_vid;
|
||||||
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||||
|
dev->poll_controller = ops->ndo_poll_controller;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(netdev_resync_ops);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* register_netdevice - register a network device
|
* register_netdevice - register a network device
|
||||||
* @dev: device to register
|
* @dev: device to register
|
||||||
@ -4326,28 +4359,7 @@ int register_netdevice(struct net_device *dev)
|
|||||||
* This is temporary until all network devices are converted.
|
* This is temporary until all network devices are converted.
|
||||||
*/
|
*/
|
||||||
if (dev->netdev_ops) {
|
if (dev->netdev_ops) {
|
||||||
const struct net_device_ops *ops = dev->netdev_ops;
|
netdev_resync_ops(dev);
|
||||||
|
|
||||||
dev->init = ops->ndo_init;
|
|
||||||
dev->uninit = ops->ndo_uninit;
|
|
||||||
dev->open = ops->ndo_open;
|
|
||||||
dev->change_rx_flags = ops->ndo_change_rx_flags;
|
|
||||||
dev->set_rx_mode = ops->ndo_set_rx_mode;
|
|
||||||
dev->set_multicast_list = ops->ndo_set_multicast_list;
|
|
||||||
dev->set_mac_address = ops->ndo_set_mac_address;
|
|
||||||
dev->validate_addr = ops->ndo_validate_addr;
|
|
||||||
dev->do_ioctl = ops->ndo_do_ioctl;
|
|
||||||
dev->set_config = ops->ndo_set_config;
|
|
||||||
dev->change_mtu = ops->ndo_change_mtu;
|
|
||||||
dev->neigh_setup = ops->ndo_neigh_setup;
|
|
||||||
dev->tx_timeout = ops->ndo_tx_timeout;
|
|
||||||
dev->get_stats = ops->ndo_get_stats;
|
|
||||||
dev->vlan_rx_register = ops->ndo_vlan_rx_register;
|
|
||||||
dev->vlan_rx_add_vid = ops->ndo_vlan_rx_add_vid;
|
|
||||||
dev->vlan_rx_kill_vid = ops->ndo_vlan_rx_kill_vid;
|
|
||||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
||||||
dev->poll_controller = ops->ndo_poll_controller;
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
char drivername[64];
|
char drivername[64];
|
||||||
pr_info("%s (%s): not using net_device_ops yet\n",
|
pr_info("%s (%s): not using net_device_ops yet\n",
|
||||||
|
Loading…
Reference in New Issue
Block a user