skb BPF paths have a number of helpers to fix up the GSO state after packet modifications. XDP has no such support.
"Real" / driver XDP runs before the SW GRO, and drivers generally disable HW-GRO when XDP is attached (with the exception of IDPF, story for another time), so this is not an issue. We also try to disable HW-GRO and elide SW GRO when generic XDP prog is attached. These precautions are not 100% today, and IMHO fixing that is impossible. Let's explicitly drop GSO skbs on input to XDP. First example how things can go sideways today - bonding. We only disable & elide GRO on the device to which program is attached. Nothing reaches the devices below it, and for a stacked device that is where the packets come from: ip link set dev bond0 xdpgeneric obj prog.o sec xdp leaves every slave with GRO on and no xdp_prog of its own, so netif_elide_gro() lets them coalesce as usual. bond_handle_frame() then returns RX_HANDLER_ANOTHER, __netif_receive_skb_core() loops back round to another_round with skb->dev switched to the bond, and the program gets handed a packet which was never on the wire. HW-GRO is the same story - while netif_disable_lro() walks the lower devices, its HW-GRO counterpart never did (this is largely the point of distinguishing between LRO and HW-GRO). Example two - veth. Generic XDP on a veth gets there by a different route. veth_xdp_set() takes NETIF_F_GSO_SOFTWARE away from the peer so that no GSO skb is ever built for a device running XDP, but generic XDP does not go through ndo_bpf, so none of that happens. Signed-off-by: Jakub Kicinski <[email protected]> --- include/net/xdp.h | 16 ++++++++++++++++ drivers/net/veth.c | 3 +++ net/core/dev.c | 4 ++++ 3 files changed, 23 insertions(+) diff --git a/include/net/xdp.h b/include/net/xdp.h index aa742f413c35..5a234aab1e1c 100644 --- a/include/net/xdp.h +++ b/include/net/xdp.h @@ -291,6 +291,22 @@ static inline bool xdp_buff_add_frag(struct xdp_buff *xdp, netmem_ref netmem, return true; } +/** + * xdp_skb_feature_check() - can this skb be handed to an XDP program? + * @skb: skb about to be turned into an xdp_buff + * + * Return: true if the skb must not reach the program and has to be dropped. + */ +static inline bool xdp_skb_feature_check(const struct sk_buff *skb) +{ + if (likely(!skb_is_gso(skb))) + return false; + + net_warn_ratelimited("%s: dropping coalesced packet before XDP, receive offloads are still on\n", + skb->dev->name); + return true; +} + struct xdp_frame { void *data; u32 len; diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 71227d0389aa..5635037e3b60 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -756,6 +756,9 @@ static int veth_convert_skb_to_xdp_buff(struct veth_rq *rq, struct sk_buff *skb = *pskb; u32 frame_sz; + if (unlikely(xdp_skb_feature_check(skb))) + goto drop; + if (skb_shared(skb) || skb_head_is_locked(skb) || skb_is_nonlinear(skb) || skb_headroom(skb) < XDP_PACKET_HEADROOM) { diff --git a/net/core/dev.c b/net/core/dev.c index 096d1dedebfd..fbeb3df4c11f 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -158,6 +158,7 @@ #include <linux/once_lite.h> #include <net/netdev_lock.h> #include <net/netdev_rx_queue.h> +#include <net/xdp.h> #include <net/page_pool/types.h> #include <net/page_pool/helpers.h> #include <net/page_pool/memory_provider.h> @@ -5516,6 +5517,9 @@ u32 bpf_prog_run_generic_xdp(struct sk_buff *skb, struct xdp_buff *xdp, u32 metalen, act; int off; + if (unlikely(xdp_skb_feature_check(skb))) + return XDP_DROP; + /* The XDP program wants to see the packet starting at the MAC * header. */ -- 2.55.0
