From 1806cd6e51cd8bcae0f30287e454852e768aea7c Mon Sep 17 00:00:00 2001
From: Simon Kuenzer <simon.kuenzer@neclab.eu>
Date: Wed, 18 Aug 2021 15:15:15 +0200
Subject: [UNIKRAFT PATCH 3/3] tcp_out: Partial checksumming

Implements partial checksummed TCP segments and control packets.
Resulting pbufs will have `PBUF_FLAG_CSUM_PARTIAL` set and
`csum_start` and `csum_offset` will point to the corresponding
locations within the pbuf payload.
Partial checksumming for TCP is enabled if `CHECKSUM_PARTIAL_TCP`
and `CHECKSUM_GEN_TCP` are enabled via `lwipopts.h`.
If netif checksum control flags are activated, the control flags
`NETIF_CHECKSUM_GEN_TCP` and `NETIF_CHECKSUM_PARTIAL_TCP` must
also be set.

Signed-off-by: Simon Kuenzer <simon.kuenzer@neclab.eu>
---
 src/core/tcp_in.c                |  3 +++
 src/core/tcp_out.c               | 32 +++++++++++++++++++++++++++-----
 src/include/lwip/netif.h         |  3 ++-
 src/include/lwip/opt.h           | 12 ++++++++++++
 src/include/lwip/priv/tcp_priv.h |  7 +++++--
 src/include/lwip/prot/tcp.h      |  2 ++
 src/include/lwip/tcp.h           |  4 ++++
 7 files changed, 55 insertions(+), 8 deletions(-)

diff --git a/src/core/tcp_in.c b/src/core/tcp_in.c
index c7a1f7b7..acbcc77c 100644
--- a/src/core/tcp_in.c
+++ b/src/core/tcp_in.c
@@ -675,6 +675,9 @@ tcp_listen_input(struct tcp_pcb_listen *pcb)
     /* Set up the new PCB. */
     ip_addr_copy(npcb->local_ip, *ip_current_dest_addr());
     ip_addr_copy(npcb->remote_ip, *ip_current_src_addr());
+#if TCP_CHECKSUM_PARTIAL
+    npcb->chksum_base = ip_chksum_pseudohdr_base(IP_PROTO_TCP, &npcb->local_ip, &npcb->remote_ip);
+#endif /* TCP_CHECKSUM_PARTIAL */
     npcb->local_port = pcb->local_port;
     npcb->remote_port = tcphdr->src;
     npcb->state = SYN_RCVD;
diff --git a/src/core/tcp_out.c b/src/core/tcp_out.c
index c40e5af8..35375871 100644
--- a/src/core/tcp_out.c
+++ b/src/core/tcp_out.c
@@ -1600,8 +1600,18 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb, struct netif *netif
     }
 #endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
 #else /* TCP_CHECKSUM_ON_COPY */
-    seg->tcphdr->chksum = ip_chksum_pseudo(seg->p, IP_PROTO_TCP,
-                                           seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip);
+#if TCP_CHECKSUM_PARTIAL
+    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_PARTIAL_TCP) {
+      seg->tcphdr->chksum = ip_chksum_pseudohdr_complete(pcb->chksum_base, seg->p->tot_len);
+      seg->p->flags |= PBUF_FLAG_CSUM_PARTIAL;
+      seg->p->csum_start = 0;
+      seg->p->csum_offset = TCPH_CHKSUM_OFFSET;
+    } else
+#endif /* TCP_CHECKSUM_PARTIAL */
+    {
+      seg->tcphdr->chksum = ip_chksum_pseudo(seg->p, IP_PROTO_TCP,
+                                             seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip);
+    }
 #endif /* TCP_CHECKSUM_ON_COPY */
   }
 #endif /* CHECKSUM_GEN_TCP */
@@ -1954,9 +1964,21 @@ tcp_output_control_segment_netif(const struct tcp_pcb *pcb, struct pbuf *p,
 
 #if CHECKSUM_GEN_TCP
   IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
-    struct tcp_hdr *tcphdr = (struct tcp_hdr *)p->payload;
-    tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
-                                      src, dst);
+#if TCP_CHECKSUM_PARTIAL
+    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_PARTIAL_TCP) {
+      struct tcp_hdr *tcphdr = (struct tcp_hdr *)p->payload;
+      tcphdr->chksum = ip_chksum_pseudohdr(IP_PROTO_TCP, p->tot_len,
+                                           src, dst);
+      p->flags |= PBUF_FLAG_CSUM_PARTIAL;
+      p->csum_start = 0;
+      p->csum_offset = TCPH_CHKSUM_OFFSET;
+    } else
+#endif /* TCP_CHECKSUM_PARTIAL */
+    {
+      struct tcp_hdr *tcphdr = (struct tcp_hdr *)p->payload;
+      tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
+                                        src, dst);
+    }
   }
 #endif
   if (pcb != NULL) {
diff --git a/src/include/lwip/netif.h b/src/include/lwip/netif.h
index 6e04ce84..0011d5ed 100644
--- a/src/include/lwip/netif.h
+++ b/src/include/lwip/netif.h
@@ -148,7 +148,8 @@ enum lwip_internal_netif_client_data_index
 #define NETIF_CHECKSUM_CHECK_TCP    0x0400
 #define NETIF_CHECKSUM_CHECK_ICMP   0x0800
 #define NETIF_CHECKSUM_CHECK_ICMP6  0x1000
-#define NETIF_CHECKSUM_ENABLE_ALL   0xFFFF
+#define NETIF_CHECKSUM_PARTIAL_TCP  0x4000
+#define NETIF_CHECKSUM_ENABLE_ALL   0x1FFF
 #define NETIF_CHECKSUM_DISABLE_ALL  0x0000
 #endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */
 
diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h
index 6fb0e860..e0398ae4 100644
--- a/src/include/lwip/opt.h
+++ b/src/include/lwip/opt.h
@@ -2408,6 +2408,18 @@
 #if !defined LWIP_CHECKSUM_PARTIAL || defined __DOXYGEN__
 #define LWIP_CHECKSUM_PARTIAL           0
 #endif
+
+/**
+ * Turn on partial checksum generation for TCP headers when LWIP_CHECKSUM_PARTIAL is on
+ * and CHECKSUM_GEN_TCP is set. Otherwise, we disable partial checksumming.
+ */
+#if !defined CHECKSUM_PARTIAL_TCP || defined __DOXYGEN__
+#if LWIP_CHECKSUM_PARTIAL && (CHECKSUM_GEN_TCP || LWIP_CHECKSUM_CTRL_PER_NETIF)
+#define CHECKSUM_PARTIAL_TCP            1
+#else
+#define CHECKSUM_PARTIAL_TCP            0
+#endif
+#endif
 /**
  * @}
  */
diff --git a/src/include/lwip/priv/tcp_priv.h b/src/include/lwip/priv/tcp_priv.h
index a8e87e59..0c2b8a36 100644
--- a/src/include/lwip/priv/tcp_priv.h
+++ b/src/include/lwip/priv/tcp_priv.h
@@ -243,8 +243,11 @@ err_t            tcp_process_refused_data(struct tcp_pcb *pcb);
 #define TCP_OVERSIZE_DBGCHECK 0
 #endif
 
-/** Don't generate checksum on copy if CHECKSUM_GEN_TCP is disabled */
-#define TCP_CHECKSUM_ON_COPY  (LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_TCP)
+/** Don't generate checksum on copy if CHECKSUM_GEN_TCP is disabled or partial checksumming is enabled */
+#define TCP_CHECKSUM_ON_COPY  (LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_TCP && !CHECKSUM_PARTIAL_TCP)
+
+/** Enable partial checksumming for TCP when selected by the options */
+#define TCP_CHECKSUM_PARTIAL  (LWIP_CHECKSUM_PARTIAL && CHECKSUM_GEN_TCP && CHECKSUM_PARTIAL_TCP)
 
 /* This structure represents a TCP segment on the unsent, unacked and ooseq queues */
 struct tcp_seg {
diff --git a/src/include/lwip/prot/tcp.h b/src/include/lwip/prot/tcp.h
index c1d7de1f..bbb06ad2 100644
--- a/src/include/lwip/prot/tcp.h
+++ b/src/include/lwip/prot/tcp.h
@@ -93,6 +93,8 @@ PACK_STRUCT_END
 #define TCPH_SET_FLAG(phdr, flags ) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags | lwip_htons(flags))
 #define TCPH_UNSET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags & ~lwip_htons(flags))
 
+#define TCPH_CHKSUM_OFFSET 16
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/include/lwip/tcp.h b/src/include/lwip/tcp.h
index 3991fd6e..7fe6060c 100644
--- a/src/include/lwip/tcp.h
+++ b/src/include/lwip/tcp.h
@@ -372,6 +372,10 @@ struct tcp_pcb {
   u32_t keep_cnt;
 #endif /* LWIP_TCP_KEEPALIVE */
 
+#if CHECKSUM_PARTIAL_TCP
+  u16_t chksum_base;
+#endif
+
   /* Persist timer counter */
   u8_t persist_cnt;
   /* Persist timer back-off */
-- 
2.20.1

