From: Dmitry Eremin-Solenikov <dmitry.ereminsoleni...@linaro.org>

Add code to enable checking of UDP checksums.

Signed-off-by: Dmitry Eremin-Solenikov <dmitry.ereminsoleni...@linaro.org>
---
/** Email created from pull request 389 (lumag:parse-checksums)
 ** https://github.com/Linaro/odp/pull/389
 ** Patch: https://github.com/Linaro/odp/pull/389.patch
 ** Base sha: 49ebafae0edebbc750742d8874ad0a7588286dea
 ** Merge commit sha: e6a448356c90f142122e5b5d4796bdf19e04e8c6
 **/
 .../linux-generic/include/odp_packet_internal.h    |   6 +
 platform/linux-generic/odp_packet.c                | 134 +++++++++++++++++++--
 2 files changed, 132 insertions(+), 8 deletions(-)

diff --git a/platform/linux-generic/include/odp_packet_internal.h 
b/platform/linux-generic/include/odp_packet_internal.h
index 86f0d80a5..f8b698335 100644
--- a/platform/linux-generic/include/odp_packet_internal.h
+++ b/platform/linux-generic/include/odp_packet_internal.h
@@ -100,6 +100,12 @@ typedef struct {
 
        /* offset to L4 hdr (TCP, UDP, SCTP, also ICMP) */
        uint16_t l4_offset;
+
+       /* Partial sum for L4 checksumming */
+       uint32_t l4_part_sum;
+
+       /* L4 checksum */
+       uint32_t l4_sum;
 } packet_parser_t;
 
 /* Packet extra data length */
diff --git a/platform/linux-generic/odp_packet.c 
b/platform/linux-generic/odp_packet.c
index d3c608ab0..98ecf714e 100644
--- a/platform/linux-generic/odp_packet.c
+++ b/platform/linux-generic/odp_packet.c
@@ -12,6 +12,7 @@
 #include <odp_debug_internal.h>
 #include <odp/api/hints.h>
 #include <odp/api/byteorder.h>
+#include <odp_chksum_internal.h>
 
 #include <protocols/eth.h>
 #include <protocols/ip.h>
@@ -1948,6 +1949,35 @@ int _odp_packet_copy_md_to_packet(odp_packet_t srcpkt, 
odp_packet_t dstpkt)
        return dst_uarea_size < src_uarea_size;
 }
 
+static uint16_t packet_sum_ones_comp16(odp_packet_hdr_t *pkt_hdr,
+                                      uint32_t offset,
+                                      uint32_t len)
+{
+       uint32_t sum = pkt_hdr->p.l4_part_sum;
+       odp_bool_t odd_offset = false;
+
+       if (offset + len > pkt_hdr->frame_len)
+               return 0;
+
+       while (len > 0) {
+               uint32_t seglen = 0; /* GCC */
+               void *mapaddr = packet_map(pkt_hdr, offset, &seglen, NULL);
+
+               if (seglen < len)
+                       seglen = len;
+
+               len -= seglen;
+               sum += _odp_chksum_ones_comp16_32(mapaddr, seglen, odd_offset);
+               odd_offset ^= (seglen % 2);
+       }
+
+       /* Not more than two additions */
+       sum = (sum & 0xffff) + (sum >> 16);
+       sum = (sum & 0xffff) + (sum >> 16);
+
+       return ~sum;
+}
+
 /** Parser helper function for Ethernet packets */
 static inline uint16_t parse_eth(packet_parser_t *prs, const uint8_t 
**parseptr,
                                 uint32_t *offset, uint32_t frame_len)
@@ -2033,6 +2063,7 @@ static inline uint8_t parse_ipv4(packet_parser_t *prs, 
const uint8_t **parseptr,
        uint16_t frag_offset;
        uint32_t dstaddr = odp_be_to_cpu_32(ipv4->dst_addr);
        uint32_t l3_len = odp_be_to_cpu_16(ipv4->tot_len);
+       uint32_t l4_part_sum = 0;
 
        if (odp_unlikely(ihl < _ODP_IPV4HDR_IHL_MIN) ||
            odp_unlikely(ver != 4) ||
@@ -2052,6 +2083,18 @@ static inline uint8_t parse_ipv4(packet_parser_t *prs, 
const uint8_t **parseptr,
        *offset   += ihl * 4;
        *parseptr += ihl * 4;
 
+       if (chksums.chksum.udp || chksums.chksum.tcp) {
+               l4_part_sum = _odp_chksum_ones_comp16_32(
+                               (const uint16_t *)&ipv4->src_addr,
+                               2 * _ODP_IPV4ADDR_LEN, false);
+#if ODP_BYTE_ORDER == ODP_BIG_ENDIAN
+               l4_part_sum += ipv4->proto;
+#else
+               l4_part_sum += ((uint16_t)ipv4->proto) << 8;
+#endif
+               prs->l4_part_sum = l4_part_sum;
+       }
+
        if (odp_unlikely(ihl > _ODP_IPV4HDR_IHL_MIN))
                prs->input_flags.ipopt = 1;
 
@@ -2076,13 +2119,15 @@ static inline uint8_t parse_ipv4(packet_parser_t *prs, 
const uint8_t **parseptr,
  */
 static inline uint8_t parse_ipv6(packet_parser_t *prs, const uint8_t 
**parseptr,
                                 uint32_t *offset, uint32_t frame_len,
-                                uint32_t seg_len)
+                                uint32_t seg_len,
+                                odp_proto_chksums_t chksums)
 {
        const _odp_ipv6hdr_t *ipv6 = (const _odp_ipv6hdr_t *)*parseptr;
        const _odp_ipv6hdr_ext_t *ipv6ext;
        uint32_t dstaddr0 = odp_be_to_cpu_32(ipv6->dst_addr.u8[0]);
        uint32_t l3_len = odp_be_to_cpu_16(ipv6->payload_len) +
                        _ODP_IPV6HDR_LEN;
+       uint32_t l4_part_sum = 0;
 
        /* Basic sanity checks on IPv6 header */
        if ((odp_be_to_cpu_32(ipv6->ver_tc_flow) >> 28) != 6 ||
@@ -2099,6 +2144,18 @@ static inline uint8_t parse_ipv6(packet_parser_t *prs, 
const uint8_t **parseptr,
        *offset   += sizeof(_odp_ipv6hdr_t);
        *parseptr += sizeof(_odp_ipv6hdr_t);
 
+       if (chksums.chksum.udp || chksums.chksum.tcp) {
+               l4_part_sum = _odp_chksum_ones_comp16_32(
+                               (const uint16_t *)(uintptr_t)&ipv6->src_addr,
+                               2 * _ODP_IPV6ADDR_LEN, false);
+#if ODP_BYTE_ORDER == ODP_BIG_ENDIAN
+               l4_part_sum += ipv6->next_hdr;
+#else
+               l4_part_sum += ((uint16_t)ipv6->next_hdr) << 8;
+#endif
+               prs->l4_part_sum = l4_part_sum;
+       }
+
        /* Skip past any IPv6 extension headers */
        if (ipv6->next_hdr == _ODP_IPPROTO_HOPOPTS ||
            ipv6->next_hdr == _ODP_IPPROTO_ROUTE) {
@@ -2155,14 +2212,35 @@ static inline void parse_tcp(packet_parser_t *prs,
 /**
  * Parser helper function for UDP
  */
-static inline void parse_udp(packet_parser_t *prs,
-                            const uint8_t **parseptr, uint32_t *offset)
+static inline void parse_udp(packet_parser_t *prs, const uint8_t **parseptr,
+                            uint32_t *offset, odp_proto_chksums_t chksums)
 {
        const _odp_udphdr_t *udp = (const _odp_udphdr_t *)*parseptr;
        uint32_t udplen = odp_be_to_cpu_16(udp->length);
 
-       if (odp_unlikely(udplen < sizeof(_odp_udphdr_t)))
+       if (odp_unlikely(udplen < sizeof(_odp_udphdr_t))) {
                prs->error_flags.udp_err = 1;
+               return;
+       }
+
+       if (chksums.chksum.udp &&
+           !prs->input_flags.ipfrag) {
+               if (udp->chksum == 0) {
+                       prs->input_flags.l4_chksum_done =
+                               (prs->input_flags.ipv4 != 1);
+                       prs->error_flags.l4_chksum =
+                               (prs->input_flags.ipv4 != 1);
+               } else {
+                       prs->input_flags.l4_chksum_done = 1;
+                       prs->l4_part_sum += udp->length;
+                       /* Do not include checksum into partial sum */
+                       prs->l4_part_sum += _odp_chksum_ones_comp16_32(
+                                       (const void *)udp,
+                                       _ODP_UDPHDR_LEN - 2,
+                                       false);
+               }
+               prs->l4_sum = udp->chksum;
+       }
 
        if (odp_cpu_to_be_16(_ODP_UDP_IPSEC_PORT) == udp->dst_port &&
            udplen > 4) {
@@ -2208,7 +2286,7 @@ int packet_parse_common_l3_l4(packet_parser_t *prs, const 
uint8_t *parseptr,
        case _ODP_ETHTYPE_IPV6:
                prs->input_flags.ipv6 = 1;
                ip_proto = parse_ipv6(prs, &parseptr, &offset, frame_len,
-                                     seg_len);
+                                     seg_len, chksums);
                prs->l4_offset = offset;
                break;
 
@@ -2252,7 +2330,7 @@ int packet_parse_common_l3_l4(packet_parser_t *prs, const 
uint8_t *parseptr,
                if (odp_unlikely(offset + _ODP_UDPHDR_LEN > seg_len))
                        return -1;
                prs->input_flags.udp = 1;
-               parse_udp(prs, &parseptr, NULL);
+               parse_udp(prs, &parseptr, NULL, chksums);
                break;
 
        case _ODP_IPPROTO_AH:
@@ -2309,6 +2387,35 @@ int packet_parse_common(packet_parser_t *prs, const 
uint8_t *ptr,
                                         seg_len, layer, ethtype, chksums);
 }
 
+static int packet_l4_chksum(odp_packet_hdr_t *pkt_hdr,
+                           odp_proto_chksums_t chksums)
+{
+       /* UDP chksum == 0 case is covered in parse_udp() */
+       if (chksums.chksum.udp &&
+           pkt_hdr->p.input_flags.udp &&
+           !pkt_hdr->p.input_flags.ipfrag &&
+           pkt_hdr->p.l4_sum != 0) {
+               uint16_t sum = packet_sum_ones_comp16(pkt_hdr,
+                                                     pkt_hdr->p.l4_offset +
+                                                     _ODP_UDPHDR_LEN,
+                                                     pkt_hdr->frame_len -
+                                                     pkt_hdr->p.l4_offset -
+                                                     _ODP_UDPHDR_LEN);
+
+               if (sum == 0)
+                       sum = 0xffff;
+
+               if (sum != pkt_hdr->p.l4_sum) {
+                       pkt_hdr->p.error_flags.l4_chksum = 1;
+                       ODP_DBG("UDP chksum fail (%x)!\n", sum);
+               } else {
+                       ODP_DBG("UDP chksum OK!\n");
+               }
+       }
+
+       return pkt_hdr->p.error_flags.all != 0;
+}
+
 /**
  * Simple packet parser
  */
@@ -2318,9 +2425,15 @@ int packet_parse_layer(odp_packet_hdr_t *pkt_hdr,
 {
        uint32_t seg_len = packet_first_seg_len(pkt_hdr);
        void *base = packet_data(pkt_hdr);
+       int rc;
+
+       rc = packet_parse_common(&pkt_hdr->p, base, pkt_hdr->frame_len,
+                                seg_len, layer, chksums);
 
-       return packet_parse_common(&pkt_hdr->p, base, pkt_hdr->frame_len,
-                                  seg_len, layer, chksums);
+       if (rc != 0)
+               return rc;
+
+       return packet_l4_chksum(pkt_hdr, chksums);
 }
 
 int odp_packet_parse(odp_packet_t pkt, uint32_t offset,
@@ -2363,7 +2476,12 @@ int odp_packet_parse(odp_packet_t pkt, uint32_t offset,
                                                layer, ethtype,
                                                param->chksums);
 
+               if (ret)
+                       return -1;
+       }
 
+       if (layer == ODP_PROTO_LAYER_L4) {
+               ret = packet_l4_chksum(pkt_hdr, param->chksums);
                if (ret)
                        return -1;
        }

Reply via email to