@@ -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 */
@@ -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 *)&ipv4->src_addr,
+ 2 * _ODP_IPV4ADDR_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;
}