bridge: Fix netpoll support
There are multiple problems with the newly added netpoll support: 1) Use-after-free on each netpoll packet. 2) Invoking unsafe code on netpoll/IRQ path. 3) Breaks when netpoll is enabled on the underlying device. This patch fixes all of these problems. In particular, we now allocate proper netpoll structures for each underlying device. We only allow netpoll to be enabled on the bridge when all the devices underneath it support netpoll. Once it is enabled, we do not allow non-netpoll devices to join the bridge (until netpoll is disabled again). This allows us to do away with the npinfo juggling that caused problem number 1. Incidentally this patch fixes number 2 by bypassing unsafe code such as multicast snooping and netfilter. Reported-by: Qianfeng Zhang <frzhang@redhat.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
committed by
David S. Miller
parent
c18370f5b2
commit
91d2c34a4e
@@ -50,14 +50,7 @@ int br_dev_queue_push_xmit(struct sk_buff *skb)
|
||||
kfree_skb(skb);
|
||||
else {
|
||||
skb_push(skb, ETH_HLEN);
|
||||
|
||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||
if (unlikely(skb->dev->priv_flags & IFF_IN_NETPOLL)) {
|
||||
netpoll_send_skb(skb->dev->npinfo->netpoll, skb);
|
||||
skb->dev->priv_flags &= ~IFF_IN_NETPOLL;
|
||||
} else
|
||||
#endif
|
||||
dev_queue_xmit(skb);
|
||||
dev_queue_xmit(skb);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,23 +66,20 @@ int br_forward_finish(struct sk_buff *skb)
|
||||
|
||||
static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
|
||||
{
|
||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||
struct net_bridge *br = to->br;
|
||||
if (unlikely(br->dev->priv_flags & IFF_IN_NETPOLL)) {
|
||||
struct netpoll *np;
|
||||
to->dev->npinfo = skb->dev->npinfo;
|
||||
np = skb->dev->npinfo->netpoll;
|
||||
np->real_dev = np->dev = to->dev;
|
||||
to->dev->priv_flags |= IFF_IN_NETPOLL;
|
||||
}
|
||||
#endif
|
||||
skb->dev = to->dev;
|
||||
|
||||
if (unlikely(netpoll_tx_running(to->dev))) {
|
||||
if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb))
|
||||
kfree_skb(skb);
|
||||
else {
|
||||
skb_push(skb, ETH_HLEN);
|
||||
br_netpoll_send_skb(to, skb);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
|
||||
br_forward_finish);
|
||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||
if (skb->dev->npinfo)
|
||||
skb->dev->npinfo->netpoll->dev = br->dev;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
|
||||
|
Reference in New Issue
Block a user