From ffb132889f4f81a78da9216c051f1078ac81f2a0 Mon Sep 17 00:00:00 2001
From: Simon Kuenzer <simon.kuenzer@neclab.eu>
Date: Mon, 16 Aug 2021 17:37:45 +0200
Subject: [PATCH v2 03/10] pbuf: Introduce PBUF_FLAG_CSUM_PARTIAL

`PBUF_FLAG_CSUM_PARTIAL` indicates that a checksum within a
pbuf is only partially computed. For such pbufs, netif drivers
can offload completing the calculation to hardware. On
receiving, drivers are able to indicate the stack that a
checksum is only partially computed. Typically, this can happen
in virtual environments (e.g., virtio-net, netfront) where mixed
types of traffic can be received (partially and fully computed
checksums).

The additional pbuf field `csum_start` points to the pbuf payload
byte where checksum computation must continue until the end of the
payload of a pbuf chain. `csum_offset` points to the checksum
field. For implementation simplicity `PBUF_FLAG_CSUM_PARTIAL`
can only be set once for a pbuf chain with the heading pbuf.

Signed-off-by: Simon Kuenzer <simon.kuenzer@neclab.eu>
---
 src/core/pbuf.c         | 31 ++++++++++++++++++++++++++++++-
 src/include/lwip/opt.h  |  8 ++++++++
 src/include/lwip/pbuf.h | 23 +++++++++++++++++++++++
 3 files changed, 61 insertions(+), 1 deletion(-)

diff --git a/src/core/pbuf.c b/src/core/pbuf.c
index e6027261..41c90fd3 100644
--- a/src/core/pbuf.c
+++ b/src/core/pbuf.c
@@ -526,6 +526,11 @@ pbuf_add_header_impl(struct pbuf *p, size_t header_size_increment, u8_t force)
   p->len = (u16_t)(p->len + increment_magnitude);
   p->tot_len = (u16_t)(p->tot_len + increment_magnitude);
 
+#if LWIP_CHECKSUM_PARTIAL
+  if ((p->flags & PBUF_FLAG_CSUM_PARTIAL)
+      && ((p->csum_start != 0x0) || (p->csum_offset != 0x0)))
+    p->csum_start = (s32_t)(p->csum_start + increment_magnitude);
+#endif /* LWIP_CHECKSUM_PARTIAL */
 
   return 0;
 }
@@ -609,6 +614,12 @@ pbuf_remove_header(struct pbuf *p, size_t header_size_decrement)
   p->len = (u16_t)(p->len - increment_magnitude);
   p->tot_len = (u16_t)(p->tot_len - increment_magnitude);
 
+#if LWIP_CHECKSUM_PARTIAL
+  if ((p->flags & PBUF_FLAG_CSUM_PARTIAL)
+      && ((p->csum_start != 0x0) || (p->csum_offset != 0x0)))
+    p->csum_start = (s32_t)(p->csum_start - increment_magnitude);
+#endif /* LWIP_CHECKSUM_PARTIAL */
+
   LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_remove_header: old %p new %p (%"U16_F")\n",
               (void *)payload, (void *)p->payload, increment_magnitude));
 
@@ -859,6 +870,9 @@ pbuf_cat(struct pbuf *h, struct pbuf *t)
   LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)",
              ((h != NULL) && (t != NULL)), return;);
 
+  /* header flags can only be set on heading pbuf */
+  LWIP_ASSERT("header flags set in tailing pbuf of a chain", !(t->flags & PBUF_HDR_FLAGS_MASK));
+
   /* proceed to last pbuf of chain */
   for (p = h; p->next != NULL; p = p->next) {
     /* add total length of second chain to all totals of first chain */
@@ -958,11 +972,26 @@ pbuf_dechain(struct pbuf *p)
 err_t
 pbuf_copy(struct pbuf *p_to, const struct pbuf *p_from)
 {
+  err_t status;
   LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n",
               (const void *)p_to, (const void *)p_from));
 
   LWIP_ERROR("pbuf_copy: invalid source", p_from != NULL, return ERR_ARG;);
-  return pbuf_copy_partial_pbuf(p_to, p_from, p_from->tot_len, 0);
+
+  status = pbuf_copy_partial_pbuf(p_to, p_from, p_from->tot_len, 0);
+  if (status != ERR_OK)
+    goto out;
+
+  /* copy over DATA_VALID and CSUM_PARTIAL flag */
+  p_to->flags |= (p_from->flags & PBUF_HDR_FLAGS_MASK);
+#if LWIP_CHECKSUM_PARTIAL
+  if (p_from->flags & PBUF_FLAG_CSUM_PARTIAL) {
+    p_to->csum_start = p_from->csum_start;
+    p_to->csum_offset = p_from->csum_offset;
+  }
+#endif /* LWIP_CHECKSUM_PARTIAL */
+out:
+  return status;
 }
 
 /**
diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h
index 9fe0bf04..394f92e3 100644
--- a/src/include/lwip/opt.h
+++ b/src/include/lwip/opt.h
@@ -2400,6 +2400,14 @@
 #if !defined LWIP_CHECKSUM_ON_COPY || defined __DOXYGEN__
 #define LWIP_CHECKSUM_ON_COPY           0
 #endif
+
+/**
+ * LWIP_CHECKSUM_PARTIAL==1: Extend pbufs to support handling packets with
+ * partial computed checksums.
+ */
+#if !defined LWIP_CHECKSUM_PARTIAL || defined __DOXYGEN__
+#define LWIP_CHECKSUM_PARTIAL           0
+#endif
 /**
  * @}
  */
diff --git a/src/include/lwip/pbuf.h b/src/include/lwip/pbuf.h
index 5a4fc88b..9f3d59d3 100644
--- a/src/include/lwip/pbuf.h
+++ b/src/include/lwip/pbuf.h
@@ -181,6 +181,13 @@ typedef enum {
 #define PBUF_FLAG_LLMCAST   0x10U
 /** indicates this pbuf includes a TCP FIN flag */
 #define PBUF_FLAG_TCP_FIN   0x20U
+#if LWIP_CHECKSUM_PARTIAL
+/** indicates this pbuf contains a partially computed checksum */
+#define PBUF_FLAG_CSUM_PARTIAL 0x40U
+#endif /* LWIP_CHECKSUM_PARTIAL */
+/** mask for header flags that can only be set on pbuf heads and
+    that are copied on pbuf_copy() and pbuf_clone() */
+#define PBUF_HDR_FLAGS_MASK 0x40U
 
 /** Main packet buffer struct */
 struct pbuf {
@@ -210,6 +217,22 @@ struct pbuf {
   /** misc flags */
   u8_t flags;
 
+#if LWIP_CHECKSUM_PARTIAL
+  /** when PBUF_FLAG_CSUM_PARTIAL is set, `csum_start` points to where
+      checksum computation needs to continue (e.g., offloaded by hardware).
+      `csum_offset` points to the checksum field within the pbuf. It is the
+      offset from `csum_start`. Please note that for a pbuf chain, the flag can
+      only be set once and with the first pbuf.
+      Please note that during protocol parsing `csum_start` can become negative:
+      This means that currently hidden bytes of an encapsulated header have to
+      included to compute the partial checksum (e.g., source and destination IP
+      addresses of an IPv4 or IPv6 header). An input function of an netif driver
+      should never set `csum_start` to a negative value.
+   */
+  s32_t csum_start;
+  u16_t csum_offset;
+#endif /* LWIP_CHECKSUM_PARTIAL */
+
   /**
    * the reference count always equals the number of pointers
    * that refer to this pbuf. This can be pointers from an application,
-- 
2.30.2

