Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9d40bbda authored by David S. Miller's avatar David S. Miller
Browse files

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: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 54acd0ef
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -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);
+2 −1
Original line number Original line Diff line number Diff line
@@ -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;
+34 −22
Original line number Original line Diff line number Diff line
@@ -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",