The GVNIC DQO hardware restricts each packet (or MTU segment in TSO)
to at most 10 data descriptors (GVE_TX_MAX_DATA_DESCS), beyond which
the hardware drops traffic. In addition to this 10-descriptor limit,
the hardware imposes strict packet sizing and header constraints.

Enforce these hardware packet and TSO validation constraints in DQO
RDA mode within gve_tx_validate_descs() matching the upstream Linux
kernel driver:
- Define GVE_TX_MAX_TSO_MSS_DQO (9728 bytes) to avoid 14-bit MSS
  field overflow in struct gve_tx_tso_context_desc_dqo.
- Define GVE_TX_MIN_PKT_SIZE_DQO (17 bytes) and GVE_TX_MAX_PKT_SIZE_DQO
  (9728 bytes) to reject runts (which cause hardware DMA parse errors)
  and oversized non-TSO packets.
- In gve_tx_validate_descs():
  * Reject zero-length mbufs (nb_descs == 0 or pkt_len == 0).
  * Enforce packet size in [17, 9728] for non-TSO packets alongside
    the 10-descriptor limit (nb_descs <= GVE_TX_MAX_DATA_DESCS).
  * Reject split transport headers across mbuf segments
    (data_len < headlen).
  * Reject TSO packets missing an L4 header (l4_len == 0).
  * Reject TSO packets without payload (pkt_len <= headlen).
  * Enforce MSS limits [88, 9728] and header length limit <= 255.

Fixes: 403c671a46b6 ("net/gve: support TSO in DQO RDA")
Cc: [email protected]

Acked-by: Joshua Washington <[email protected]>
Signed-off-by: Tathagat Priyadarshi <[email protected]>
---
 drivers/net/gve/base/gve_desc_dqo.h |  3 ++
 drivers/net/gve/gve_tx_dqo.c        | 44 ++++++++++++++++++++++++-----
 2 files changed, 40 insertions(+), 7 deletions(-)

diff --git a/drivers/net/gve/base/gve_desc_dqo.h 
b/drivers/net/gve/base/gve_desc_dqo.h
index c1534959c2..1740f69d73 100644
--- a/drivers/net/gve/base/gve_desc_dqo.h
+++ b/drivers/net/gve/base/gve_desc_dqo.h
@@ -12,6 +12,9 @@
 
 #define GVE_TX_MAX_HDR_SIZE_DQO 255
 #define GVE_TX_MIN_TSO_MSS_DQO 88
+#define GVE_TX_MAX_TSO_MSS_DQO 9728
+#define GVE_TX_MIN_PKT_SIZE_DQO 17
+#define GVE_TX_MAX_PKT_SIZE_DQO 9728
 
 /* Basic TX descriptor (DTYPE 0x0C) */
 struct gve_tx_pkt_desc_dqo {
diff --git a/drivers/net/gve/gve_tx_dqo.c b/drivers/net/gve/gve_tx_dqo.c
index 9210b02eb1..03ac3bd5dd 100644
--- a/drivers/net/gve/gve_tx_dqo.c
+++ b/drivers/net/gve/gve_tx_dqo.c
@@ -134,12 +134,43 @@ gve_tx_clean_descs_dqo(struct gve_tx_queue *txq, uint16_t 
nb_descs) {
 static inline bool
 gve_tx_validate_descs(struct rte_mbuf *tx_pkt, uint16_t nb_descs, bool is_tso)
 {
-       if (!is_tso)
+       int tso_segsz, seg_remain, mbuf_remain;
+       int num_descs, seg_offset, mbuf_len;
+       int headlen;
+
+       /* Reject zero-length packet */
+       if (unlikely(nb_descs == 0 || tx_pkt->pkt_len == 0))
+               return false;
+
+       if (!is_tso) {
+               /* Reject packets that violate hardware length constraints */
+               if (unlikely(tx_pkt->pkt_len < GVE_TX_MIN_PKT_SIZE_DQO ||
+                            tx_pkt->pkt_len > GVE_TX_MAX_PKT_SIZE_DQO))
+                       return false;
                return nb_descs <= GVE_TX_MAX_DATA_DESCS;
+       }
 
-       int tso_segsz = tx_pkt->tso_segsz;
-       int num_descs, seg_offset, mbuf_len;
-       int headlen = tx_pkt->l2_len + tx_pkt->l3_len + tx_pkt->l4_len;
+       headlen = tx_pkt->l2_len + tx_pkt->l3_len + tx_pkt->l4_len;
+
+       /* Reject split transport header */
+       if (unlikely(tx_pkt->data_len < headlen))
+               return false;
+
+       /* Reject missing TCP header on TSO */
+       if (unlikely(tx_pkt->l4_len == 0))
+               return false;
+
+       /* Reject TSO packet without payload */
+       if (unlikely(tx_pkt->pkt_len <= (uint32_t)headlen))
+               return false;
+
+       /* Enforce GVE hardware MSS and header limits */
+       if (unlikely(tx_pkt->tso_segsz < GVE_TX_MIN_TSO_MSS_DQO ||
+                    tx_pkt->tso_segsz > GVE_TX_MAX_TSO_MSS_DQO ||
+                    headlen > GVE_TX_MAX_HDR_SIZE_DQO))
+               return false;
+
+       tso_segsz = tx_pkt->tso_segsz;
 
        /* Headers will be split into their own buffer. */
        num_descs = 1;
@@ -150,7 +181,7 @@ gve_tx_validate_descs(struct rte_mbuf *tx_pkt, uint16_t 
nb_descs, bool is_tso)
                if (!mbuf_len)
                        goto next_mbuf;
 
-               int seg_remain = tso_segsz - seg_offset;
+               seg_remain = tso_segsz - seg_offset;
                if (num_descs == GVE_TX_MAX_DATA_DESCS && seg_remain)
                        return false;
 
@@ -162,7 +193,7 @@ gve_tx_validate_descs(struct rte_mbuf *tx_pkt, uint16_t 
nb_descs, bool is_tso)
                         * is not at risk of defying this limitation.
                         * Thus, such segments are ignored.
                         */
-                       int mbuf_remain = tx_pkt->data_len % 
GVE_TX_MAX_BUF_SIZE_DQO;
+                       mbuf_remain = tx_pkt->data_len % 
GVE_TX_MAX_BUF_SIZE_DQO;
 
                        /* For each TSO segment, HW will prepend
                         * headers. The remaining bytes of this mbuf
@@ -184,7 +215,6 @@ gve_tx_validate_descs(struct rte_mbuf *tx_pkt, uint16_t 
nb_descs, bool is_tso)
                        mbuf_len = tx_pkt->data_len;
        }
 
-
        return true;
 }
 
-- 
2.43.0

Reply via email to