From 30ba55f269c7f22b70c88ba1404b64101c3f8f10 Mon Sep 17 00:00:00 2001
From: Simon Kuenzer <simon.kuenzer@neclab.eu>
Date: Wed, 18 Aug 2021 01:00:14 +0200
Subject: [UNIKRAFT PATCH 2/3] inet_chksum: Partial checksums

Introduces routines to compute a partial checksum that covers
only the pseudo header. For protocols such as TCP, this checksum
is used to initialize the checksum field for hardware offloading.
The computation is divided into two parts:
 - `ip_chksum_pseudohdr_base()`: Computes the checksum of the
   static part of the pseudo header, which should not change for
   an established connection:
     protocol number, source IP, destination IP
   For performance reasons, this static part need to be computed
   only once for each connection.
 - `ip_chksum_pseudohdr_complete()`: Completes the partial
   checksum with the length field that is likely be different for
   each segment.

Signed-off-by: Simon Kuenzer <simon.kuenzer@neclab.eu>
---
 src/core/inet_chksum.c         | 126 +++++++++++++++++++++++++++++++++
 src/include/lwip/inet_chksum.h |  17 +++++
 2 files changed, 143 insertions(+)

diff --git a/src/core/inet_chksum.c b/src/core/inet_chksum.c
index 818c68f4..0cf70ab3 100644
--- a/src/core/inet_chksum.c
+++ b/src/core/inet_chksum.c
@@ -606,3 +606,129 @@ lwip_chksum_copy(void *dst, const void *src, u16_t len)
   return LWIP_CHKSUM(dst, len);
 }
 #endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */
+
+#if LWIP_CHECKSUM_PARTIAL
+#if LWIP_IPV4
+/**
+ * inet_chksum_pseudohdr_base
+ *
+ * Computes the static part of a partial IPv4 internet checksum.
+ *
+ * @param proto ip protocol (pseudo header)
+ * @param src source ip address (pseudo header)
+ * @param dest destination ip address (pseudo header)
+ * @return base of partial checksum
+ *         (to be completed with ip_chksum_partial_calc())
+ */
+u16_t
+inet_chksum_pseudohdr_base(u8_t proto,
+			   const ip4_addr_t *src, const ip4_addr_t *dest)
+{
+  u32_t acc;
+  u32_t addr;
+
+  addr = ip4_addr_get_u32(src);
+  acc = (addr & 0xffffUL);
+  acc += ((addr >> 16) & 0xffffUL);
+  addr = ip4_addr_get_u32(dest);
+  acc += (addr & 0xffffUL);
+  acc += ((addr >> 16) & 0xffffUL);
+  acc += ((u16_t)htons((u16_t)proto & 0x00ff));
+
+  /* Fold 32-bit sum to 16 bits
+     calling this twice is probably faster than if statements... */
+  acc = FOLD_U32T(acc);
+  acc = FOLD_U32T(acc);
+  return (acc & 0xffffUL);
+}
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+/**
+ * ip6_chksum_pseudohdr_base
+ *
+ * Computes the static part of a partial IPv6 internet checksum.
+ *
+ * @param proto ip protocol (pseudo header)
+ * @param src source ip address (pseudo header)
+ * @param dest destination ip address (pseudo header)
+ * @return base of partial checksum
+ *         (to be completed with ip_chksum_partial_calc())
+ */
+u16_t
+ip6_chksum_pseudohdr_base(u8_t proto,
+			  const ip6_addr_t *src, const ip6_addr_t *dest)
+{
+  u32_t acc;
+  u32_t addr;
+
+  for (addr_part = 0; addr_part < 4; addr_part++) {
+    addr = src->addr[addr_part];
+    acc += (addr & 0xffffUL);
+    acc += ((addr >> 16) & 0xffffUL);
+    addr = dest->addr[addr_part];
+    acc += (addr & 0xffffUL);
+    acc += ((addr >> 16) & 0xffffUL);
+  }
+  acc += ((u16_t)htons((u16_t)proto & 0x00ff));
+
+  /* Fold 32-bit sum to 16 bits
+     calling this twice is probably faster than if statements... */
+  acc = FOLD_U32T(acc);
+  acc = FOLD_U32T(acc);
+  return (acc & 0xffffUL);
+}
+#endif /* LWIP_IPV6 */
+
+/**
+ * ip_chksum_pseudohdr_base
+ *
+ * Computes the static part of a partial IPv4/IPv6 internet checksum.
+ *
+ * @param proto ip protocol (pseudo header)
+ * @param src source ip address (pseudo header)
+ * @param dest destination ip address (pseudo header)
+ * @return base of partial checksum
+ *         (to be completed with ip_chksum_partial_calc())
+ */
+u16_t
+ip_chksum_pseudohdr_base(u8_t proto,
+			 const ip_addr_t *src, const ip_addr_t *dest)
+{
+#if LWIP_IPV6
+  if (IP_IS_V6(dest)) {
+    return ip6_chksum_pseudohdr_base(proto, ip_2_ip6(src), ip_2_ip6(dest));
+  }
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4 && LWIP_IPV6
+  else
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_IPV4
+  {
+    return inet_chksum_pseudohdr_base(proto, ip_2_ip4(src), ip_2_ip4(dest));
+  }
+#endif /* LWIP_IPV4 */
+}
+
+/**
+ * ip_chksum_pseudohdr_complete
+ *
+ * Completes the computation of a partial IPv4/IPv6 internet checksum which
+ * covers the pseudo header only. This checksum is used for offloading
+ * the checksum computation of the rest of the payload to hardware.
+ *
+ * @param base Initialized checksum base value (via ip_chksum_pseudohdr_base())
+ * @param proto_len length of the data part
+ * @return partial checksum
+ */
+u16_t
+ip_chksum_pseudohdr_complete(u16_t base, u16_t proto_len)
+{
+  u32_t acc = base;
+
+  acc += ((u16_t)htons(proto_len));
+  acc = FOLD_U32T(acc);
+  acc = FOLD_U32T(acc);
+  return (acc & 0xffffUL);
+}
+#endif /* LWIP_CHECKSUM_PARTIAL */
diff --git a/src/include/lwip/inet_chksum.h b/src/include/lwip/inet_chksum.h
index 76893ef5..ef6676ee 100644
--- a/src/include/lwip/inet_chksum.h
+++ b/src/include/lwip/inet_chksum.h
@@ -97,6 +97,23 @@ u16_t ip_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
 u16_t ip_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
        u16_t chksum_len, const ip_addr_t *src, const ip_addr_t *dest);
 
+#if LWIP_CHECKSUM_PARTIAL
+#if LWIP_IPV4
+u16_t inet_chksum_pseudohdr_base(u8_t proto, const ip4_addr_t *src, const ip4_addr_t *dest);
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+u16_t ip6_chksum_pseudohdr_base(u8_t proto, const ip6_addr_t *src, const ip6_addr_t *dest);
+#endif /* LWIP_IPV6 */
+
+u16_t ip_chksum_pseudohdr_base(u8_t proto, const ip_addr_t *src, const ip_addr_t *dest);
+u16_t ip_chksum_pseudohdr_complete(u16_t base, u16_t proto_len);
+#define ip_chksum_pseudohdr(proto, proto_len, src, dest)                     \
+       ip_chksum_pseudohdr_complete(ip_chksum_pseudohdr_base((proto),        \
+                                                             (src), (dest)), \
+                                                             (proto_len))
+#endif /* LWIP_CHECKSUM_PARTIAL */
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.20.1

