diff options
author | Jesper Dangaard Brouer <brouer@redhat.com> | 2018-10-09 06:04:43 -0400 |
---|---|---|
committer | Alexei Starovoitov <ast@kernel.org> | 2018-10-10 00:59:09 -0400 |
commit | 2972495699320229b55b8e5065a310be5c81485b (patch) | |
tree | 817a6d59427a927677f69f6efe97d5f98e6f1267 /net/core/dev.c | |
parent | e0aa5cf5919ee272732fce9576222e58e48622ab (diff) |
net: fix generic XDP to handle if eth header was mangled
XDP can modify (and resize) the Ethernet header in the packet.
There is a bug in generic-XDP, because skb->protocol and skb->pkt_type
are setup before reaching (netif_receive_)generic_xdp.
This bug was hit when XDP were popping VLAN headers (changing
eth->h_proto), as skb->protocol still contains VLAN-indication
(ETH_P_8021Q) causing invocation of skb_vlan_untag(skb), which corrupt
the packet (basically popping the VLAN again).
This patch catch if XDP changed eth header in such a way, that SKB
fields needs to be updated.
V2: on request from Song Liu, use ETH_HLEN instead of mac_len,
in __skb_push() as eth_type_trans() use ETH_HLEN in paired skb_pull_inline().
Fixes: d445516966dc ("net: xdp: support xdp generic on virtual devices")
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'net/core/dev.c')
-rw-r--r-- | net/core/dev.c | 14 |
1 files changed, 14 insertions, 0 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index 0b2d777e5b9e..ec96f50b0782 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -4258,6 +4258,9 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb, | |||
4258 | struct netdev_rx_queue *rxqueue; | 4258 | struct netdev_rx_queue *rxqueue; |
4259 | void *orig_data, *orig_data_end; | 4259 | void *orig_data, *orig_data_end; |
4260 | u32 metalen, act = XDP_DROP; | 4260 | u32 metalen, act = XDP_DROP; |
4261 | __be16 orig_eth_type; | ||
4262 | struct ethhdr *eth; | ||
4263 | bool orig_bcast; | ||
4261 | int hlen, off; | 4264 | int hlen, off; |
4262 | u32 mac_len; | 4265 | u32 mac_len; |
4263 | 4266 | ||
@@ -4298,6 +4301,9 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb, | |||
4298 | xdp->data_hard_start = skb->data - skb_headroom(skb); | 4301 | xdp->data_hard_start = skb->data - skb_headroom(skb); |
4299 | orig_data_end = xdp->data_end; | 4302 | orig_data_end = xdp->data_end; |
4300 | orig_data = xdp->data; | 4303 | orig_data = xdp->data; |
4304 | eth = (struct ethhdr *)xdp->data; | ||
4305 | orig_bcast = is_multicast_ether_addr_64bits(eth->h_dest); | ||
4306 | orig_eth_type = eth->h_proto; | ||
4301 | 4307 | ||
4302 | rxqueue = netif_get_rxqueue(skb); | 4308 | rxqueue = netif_get_rxqueue(skb); |
4303 | xdp->rxq = &rxqueue->xdp_rxq; | 4309 | xdp->rxq = &rxqueue->xdp_rxq; |
@@ -4321,6 +4327,14 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb, | |||
4321 | 4327 | ||
4322 | } | 4328 | } |
4323 | 4329 | ||
4330 | /* check if XDP changed eth hdr such SKB needs update */ | ||
4331 | eth = (struct ethhdr *)xdp->data; | ||
4332 | if ((orig_eth_type != eth->h_proto) || | ||
4333 | (orig_bcast != is_multicast_ether_addr_64bits(eth->h_dest))) { | ||
4334 | __skb_push(skb, ETH_HLEN); | ||
4335 | skb->protocol = eth_type_trans(skb, skb->dev); | ||
4336 | } | ||
4337 | |||
4324 | switch (act) { | 4338 | switch (act) { |
4325 | case XDP_REDIRECT: | 4339 | case XDP_REDIRECT: |
4326 | case XDP_TX: | 4340 | case XDP_TX: |