[PATCH net 3/3] net: enetc: pad short XDP frames coming from devmap

Vladimir Oltean posted 3 patches 3 hours ago
[PATCH net 3/3] net: enetc: pad short XDP frames coming from devmap
Posted by Vladimir Oltean 3 hours ago
Similar to the skb path issue explained in the previous change, ENETC
could end up transmitting short frames coming from XDP.

The way in which this could happen is a bit contrived, but it involves
XDP_REDIRECT from a veth interface pair.

Introduce a xdp_frame_pad() generic helper and call it from enetc's
ndo_xdp_xmit() implementation. This should be safe, because
ndo_xdp_xmit() is the hand-off function where the XDP frames become the
responsibility of the driver, so modifying them is fine. AFAIU, struct
xdp_frame doesn't have multiple copies.

Fixes: 9d2b68cc108d ("net: enetc: add support for XDP_REDIRECT")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
 drivers/net/ethernet/freescale/enetc/enetc.c |  3 +++
 include/net/xdp.h                            | 17 +++++++++++++++++
 2 files changed, 20 insertions(+)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index 4b87fbfde0d6..3ceb9dfd2316 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -1801,6 +1801,9 @@ int enetc_xdp_xmit(struct net_device *ndev, int num_frames,
 	prefetchw(ENETC_TXBD(*tx_ring, tx_ring->next_to_use));
 
 	for (k = 0; k < num_frames; k++) {
+		if (unlikely(xdp_frame_pad(frames[k])))
+			break;
+
 		xdp_tx_bd_cnt = enetc_xdp_frame_to_xdp_tx_swbd(tx_ring,
 							       xdp_redirect_arr,
 							       frames[k]);
diff --git a/include/net/xdp.h b/include/net/xdp.h
index aa742f413c35..0cdeb23c6bd7 100644
--- a/include/net/xdp.h
+++ b/include/net/xdp.h
@@ -477,6 +477,23 @@ xdp_get_frame_len(const struct xdp_frame *xdpf)
 	return len;
 }
 
+static inline int xdp_frame_pad(struct xdp_frame *xdpf)
+{
+	void *sinfo;
+
+	if (likely(xdpf->len >= ETH_ZLEN))
+		return 0;
+
+	sinfo = xdp_get_shared_info_from_frame(xdpf);
+	if (unlikely(xdpf->data + ETH_ZLEN > sinfo))
+		return -ENOMEM;
+
+	memset(xdpf->data + xdpf->len, 0, ETH_ZLEN - xdpf->len);
+	xdpf->len = ETH_ZLEN;
+
+	return 0;
+}
+
 int __xdp_rxq_info_reg(struct xdp_rxq_info *xdp_rxq,
 		       struct net_device *dev, u32 queue_index,
 		       unsigned int napi_id, u32 frag_size);
-- 
2.43.0