[NET]: Add net_device change_rx_mode callback
Currently the set_multicast_list (and set_rx_mode) callbacks are responsible for configuring the device according to the IFF_PROMISC, IFF_MULTICAST and IFF_ALLMULTI flags and the mc_list (and uc_list in case of set_rx_mode). These callbacks can be invoked from BH context without the rtnl_mutex by dev_mc_add/dev_mc_delete, which makes reading the device flags and promiscous/allmulti count racy. For real hardware drivers that just commit all changes to the hardware this is not a real problem since the stack guarantees to call them for every change, so at least the final call will not race and commit the correct configuration to the hardware. For software devices that want to synchronize promiscous and multicast state to an underlying device however this can cause corruption of the underlying device's flags or promisc/allmulti counts. When the software device is concurrently put in promiscous or allmulti mode while set_multicast_list is invoked from bottem half context, the device might synchronize the change to the underlying device without holding the rtnl_mutex, which races with concurrent changes to the underlying device. Add a dev->change_rx_flags hook that is invoked when any of the flags that affect rx filtering change (under the rtnl_mutex), which allows drivers to perform synchronization immediately and only synchronize the address lists in set_multicast_list/set_rx_mode. Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
e6c9116d1d
commit
24023451c8
|
@ -516,6 +516,9 @@ struct net_device
|
||||||
void *saddr,
|
void *saddr,
|
||||||
unsigned len);
|
unsigned len);
|
||||||
int (*rebuild_header)(struct sk_buff *skb);
|
int (*rebuild_header)(struct sk_buff *skb);
|
||||||
|
#define HAVE_CHANGE_RX_FLAGS
|
||||||
|
void (*change_rx_flags)(struct net_device *dev,
|
||||||
|
int flags);
|
||||||
#define HAVE_SET_RX_MODE
|
#define HAVE_SET_RX_MODE
|
||||||
void (*set_rx_mode)(struct net_device *dev);
|
void (*set_rx_mode)(struct net_device *dev);
|
||||||
#define HAVE_MULTICAST
|
#define HAVE_MULTICAST
|
||||||
|
|
|
@ -2521,6 +2521,8 @@ static void __dev_set_promiscuity(struct net_device *dev, int inc)
|
||||||
{
|
{
|
||||||
unsigned short old_flags = dev->flags;
|
unsigned short old_flags = dev->flags;
|
||||||
|
|
||||||
|
ASSERT_RTNL();
|
||||||
|
|
||||||
if ((dev->promiscuity += inc) == 0)
|
if ((dev->promiscuity += inc) == 0)
|
||||||
dev->flags &= ~IFF_PROMISC;
|
dev->flags &= ~IFF_PROMISC;
|
||||||
else
|
else
|
||||||
|
@ -2535,6 +2537,9 @@ static void __dev_set_promiscuity(struct net_device *dev, int inc)
|
||||||
dev->name, (dev->flags & IFF_PROMISC),
|
dev->name, (dev->flags & IFF_PROMISC),
|
||||||
(old_flags & IFF_PROMISC),
|
(old_flags & IFF_PROMISC),
|
||||||
audit_get_loginuid(current->audit_context));
|
audit_get_loginuid(current->audit_context));
|
||||||
|
|
||||||
|
if (dev->change_rx_flags)
|
||||||
|
dev->change_rx_flags(dev, IFF_PROMISC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2573,11 +2578,16 @@ void dev_set_allmulti(struct net_device *dev, int inc)
|
||||||
{
|
{
|
||||||
unsigned short old_flags = dev->flags;
|
unsigned short old_flags = dev->flags;
|
||||||
|
|
||||||
|
ASSERT_RTNL();
|
||||||
|
|
||||||
dev->flags |= IFF_ALLMULTI;
|
dev->flags |= IFF_ALLMULTI;
|
||||||
if ((dev->allmulti += inc) == 0)
|
if ((dev->allmulti += inc) == 0)
|
||||||
dev->flags &= ~IFF_ALLMULTI;
|
dev->flags &= ~IFF_ALLMULTI;
|
||||||
if (dev->flags ^ old_flags)
|
if (dev->flags ^ old_flags) {
|
||||||
|
if (dev->change_rx_flags)
|
||||||
|
dev->change_rx_flags(dev, IFF_ALLMULTI);
|
||||||
dev_set_rx_mode(dev);
|
dev_set_rx_mode(dev);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2778,6 +2788,8 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
|
||||||
int ret, changes;
|
int ret, changes;
|
||||||
int old_flags = dev->flags;
|
int old_flags = dev->flags;
|
||||||
|
|
||||||
|
ASSERT_RTNL();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the flags on our device.
|
* Set the flags on our device.
|
||||||
*/
|
*/
|
||||||
|
@ -2792,6 +2804,9 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
|
||||||
* Load in the correct multicast list now the flags have changed.
|
* Load in the correct multicast list now the flags have changed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
if (dev->change_rx_flags && (dev->flags ^ flags) & IFF_MULTICAST)
|
||||||
|
dev->change_rx_flags(dev, IFF_MULTICAST);
|
||||||
|
|
||||||
dev_set_rx_mode(dev);
|
dev_set_rx_mode(dev);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue