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; }