net: new checksum functions

Introduce new functions to calculate checksums. These new functions
are derivated from the ones provided csumonly.c but slightly reworked.
There is still some room for future optimization of these functions
(maybe SSE/AVX, ...).

This API will be modified in tbe next commits by the introduction of
TSO that requires a different pseudo header checksum to be set in the
packet.

Signed-off-by: Olivier Matz <olivier.matz@6wind.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
This commit is contained in:
Olivier Matz 2014-11-26 16:04:51 +01:00 committed by Thomas Monjalon
parent 51f694dd40
commit 6006818cfb
3 changed files with 193 additions and 126 deletions

View File

@ -87,137 +87,22 @@
#define _htons(x) (x) #define _htons(x) (x)
#endif #endif
static inline uint16_t
get_16b_sum(uint16_t *ptr16, uint32_t nr)
{
uint32_t sum = 0;
while (nr > 1)
{
sum +=*ptr16;
nr -= sizeof(uint16_t);
ptr16++;
if (sum > UINT16_MAX)
sum -= UINT16_MAX;
}
/* If length is in odd bytes */
if (nr)
sum += *((uint8_t*)ptr16);
sum = ((sum & 0xffff0000) >> 16) + (sum & 0xffff);
sum &= 0x0ffff;
return (uint16_t)sum;
}
static inline uint16_t
get_ipv4_cksum(struct ipv4_hdr *ipv4_hdr)
{
uint16_t cksum;
cksum = get_16b_sum((uint16_t*)ipv4_hdr, sizeof(struct ipv4_hdr));
return (uint16_t)((cksum == 0xffff)?cksum:~cksum);
}
static inline uint16_t
get_ipv4_psd_sum(struct ipv4_hdr *ip_hdr)
{
/* Pseudo Header for IPv4/UDP/TCP checksum */
union ipv4_psd_header {
struct {
uint32_t src_addr; /* IP address of source host. */
uint32_t dst_addr; /* IP address of destination host(s). */
uint8_t zero; /* zero. */
uint8_t proto; /* L4 protocol type. */
uint16_t len; /* L4 length. */
} __attribute__((__packed__));
uint16_t u16_arr[0];
} psd_hdr;
psd_hdr.src_addr = ip_hdr->src_addr;
psd_hdr.dst_addr = ip_hdr->dst_addr;
psd_hdr.zero = 0;
psd_hdr.proto = ip_hdr->next_proto_id;
psd_hdr.len = rte_cpu_to_be_16((uint16_t)(rte_be_to_cpu_16(ip_hdr->total_length)
- sizeof(struct ipv4_hdr)));
return get_16b_sum(psd_hdr.u16_arr, sizeof(psd_hdr));
}
static inline uint16_t
get_ipv6_psd_sum(struct ipv6_hdr *ip_hdr)
{
/* Pseudo Header for IPv6/UDP/TCP checksum */
union ipv6_psd_header {
struct {
uint8_t src_addr[16]; /* IP address of source host. */
uint8_t dst_addr[16]; /* IP address of destination host(s). */
uint32_t len; /* L4 length. */
uint32_t proto; /* L4 protocol - top 3 bytes must be zero */
} __attribute__((__packed__));
uint16_t u16_arr[0]; /* allow use as 16-bit values with safe aliasing */
} psd_hdr;
rte_memcpy(&psd_hdr.src_addr, ip_hdr->src_addr,
sizeof(ip_hdr->src_addr) + sizeof(ip_hdr->dst_addr));
psd_hdr.len = ip_hdr->payload_len;
psd_hdr.proto = (ip_hdr->proto << 24);
return get_16b_sum(psd_hdr.u16_arr, sizeof(psd_hdr));
}
static uint16_t static uint16_t
get_psd_sum(void *l3_hdr, uint16_t ethertype) get_psd_sum(void *l3_hdr, uint16_t ethertype)
{ {
if (ethertype == _htons(ETHER_TYPE_IPv4)) if (ethertype == _htons(ETHER_TYPE_IPv4))
return get_ipv4_psd_sum(l3_hdr); return rte_ipv4_phdr_cksum(l3_hdr);
else /* assume ethertype == ETHER_TYPE_IPv6 */ else /* assume ethertype == ETHER_TYPE_IPv6 */
return get_ipv6_psd_sum(l3_hdr); return rte_ipv6_phdr_cksum(l3_hdr);
}
static inline uint16_t
get_ipv4_udptcp_checksum(struct ipv4_hdr *ipv4_hdr, uint16_t *l4_hdr)
{
uint32_t cksum;
uint32_t l4_len;
l4_len = rte_be_to_cpu_16(ipv4_hdr->total_length) - sizeof(struct ipv4_hdr);
cksum = get_16b_sum(l4_hdr, l4_len);
cksum += get_ipv4_psd_sum(ipv4_hdr);
cksum = ((cksum & 0xffff0000) >> 16) + (cksum & 0xffff);
cksum = (~cksum) & 0xffff;
if (cksum == 0)
cksum = 0xffff;
return (uint16_t)cksum;
}
static inline uint16_t
get_ipv6_udptcp_checksum(struct ipv6_hdr *ipv6_hdr, uint16_t *l4_hdr)
{
uint32_t cksum;
uint32_t l4_len;
l4_len = rte_be_to_cpu_16(ipv6_hdr->payload_len);
cksum = get_16b_sum(l4_hdr, l4_len);
cksum += get_ipv6_psd_sum(ipv6_hdr);
cksum = ((cksum & 0xffff0000) >> 16) + (cksum & 0xffff);
cksum = (~cksum) & 0xffff;
if (cksum == 0)
cksum = 0xffff;
return (uint16_t)cksum;
} }
static uint16_t static uint16_t
get_udptcp_checksum(void *l3_hdr, void *l4_hdr, uint16_t ethertype) get_udptcp_checksum(void *l3_hdr, void *l4_hdr, uint16_t ethertype)
{ {
if (ethertype == _htons(ETHER_TYPE_IPv4)) if (ethertype == _htons(ETHER_TYPE_IPv4))
return get_ipv4_udptcp_checksum(l3_hdr, l4_hdr); return rte_ipv4_udptcp_cksum(l3_hdr, l4_hdr);
else /* assume ethertype == ETHER_TYPE_IPv6 */ else /* assume ethertype == ETHER_TYPE_IPv6 */
return get_ipv6_udptcp_checksum(l3_hdr, l4_hdr); return rte_ipv6_udptcp_cksum(l3_hdr, l4_hdr);
} }
/* /*
@ -294,7 +179,7 @@ process_inner_cksums(void *l3_hdr, uint16_t ethertype, uint16_t l3_len,
if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_IP_CKSUM) if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_IP_CKSUM)
ol_flags |= PKT_TX_IP_CKSUM; ol_flags |= PKT_TX_IP_CKSUM;
else else
ipv4_hdr->hdr_checksum = get_ipv4_cksum(ipv4_hdr); ipv4_hdr->hdr_checksum = rte_ipv4_cksum(ipv4_hdr);
ol_flags |= PKT_TX_IPV4; ol_flags |= PKT_TX_IPV4;
} else if (ethertype == _htons(ETHER_TYPE_IPv6)) } else if (ethertype == _htons(ETHER_TYPE_IPv6))
@ -363,7 +248,7 @@ process_outer_cksums(void *outer_l3_hdr, uint16_t outer_ethertype,
ipv4_hdr->hdr_checksum = 0; ipv4_hdr->hdr_checksum = 0;
if ((testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM) == 0) if ((testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM) == 0)
ipv4_hdr->hdr_checksum = get_ipv4_cksum(ipv4_hdr); ipv4_hdr->hdr_checksum = rte_ipv4_cksum(ipv4_hdr);
} }
udp_hdr = (struct udp_hdr *)((char *)outer_l3_hdr + outer_l3_len); udp_hdr = (struct udp_hdr *)((char *)outer_l3_hdr + outer_l3_len);
@ -373,12 +258,10 @@ process_outer_cksums(void *outer_l3_hdr, uint16_t outer_ethertype,
if ((testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM) == 0) { if ((testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM) == 0) {
if (outer_ethertype == _htons(ETHER_TYPE_IPv4)) if (outer_ethertype == _htons(ETHER_TYPE_IPv4))
udp_hdr->dgram_cksum = udp_hdr->dgram_cksum =
get_ipv4_udptcp_checksum(ipv4_hdr, rte_ipv4_udptcp_cksum(ipv4_hdr, udp_hdr);
(uint16_t *)udp_hdr);
else else
udp_hdr->dgram_cksum = udp_hdr->dgram_cksum =
get_ipv6_udptcp_checksum(ipv6_hdr, rte_ipv6_udptcp_cksum(ipv6_hdr, udp_hdr);
(uint16_t *)udp_hdr);
} }
} }

View File

@ -112,7 +112,8 @@ extern "C" {
* - set the flags PKT_TX_TCP_CKSUM, PKT_TX_SCTP_CKSUM or PKT_TX_UDP_CKSUM * - set the flags PKT_TX_TCP_CKSUM, PKT_TX_SCTP_CKSUM or PKT_TX_UDP_CKSUM
* - set the flag PKT_TX_IPV4 or PKT_TX_IPV6 * - set the flag PKT_TX_IPV4 or PKT_TX_IPV6
* - calculate the pseudo header checksum and set it in the L4 header (only * - calculate the pseudo header checksum and set it in the L4 header (only
* for TCP or UDP). For SCTP, set the crc field to 0. * for TCP or UDP). See rte_ipv4_phdr_cksum() and rte_ipv6_phdr_cksum().
* For SCTP, set the crc field to 0.
*/ */
#define PKT_TX_L4_NO_CKSUM (0ULL << 52) /**< Disable L4 cksum of TX pkt. */ #define PKT_TX_L4_NO_CKSUM (0ULL << 52) /**< Disable L4 cksum of TX pkt. */
#define PKT_TX_TCP_CKSUM (1ULL << 52) /**< TCP cksum of TX pkt. computed by NIC. */ #define PKT_TX_TCP_CKSUM (1ULL << 52) /**< TCP cksum of TX pkt. computed by NIC. */

View File

@ -2,6 +2,7 @@
* BSD LICENSE * BSD LICENSE
* *
* Copyright(c) 2010-2014 Intel Corporation. All rights reserved. * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
* Copyright 2014 6WIND S.A.
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -78,6 +79,9 @@
#include <stdint.h> #include <stdint.h>
#include <rte_memcpy.h>
#include <rte_byteorder.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -246,6 +250,127 @@ struct ipv4_hdr {
#define IS_IPV4_MCAST(x) \ #define IS_IPV4_MCAST(x) \
((x) >= IPV4_MIN_MCAST && (x) <= IPV4_MAX_MCAST) /**< check if IPv4 address is multicast */ ((x) >= IPV4_MIN_MCAST && (x) <= IPV4_MAX_MCAST) /**< check if IPv4 address is multicast */
/**
* Process the non-complemented checksum of a buffer.
*
* @param buf
* Pointer to the buffer.
* @param len
* Length of the buffer.
* @return
* The non-complemented checksum.
*/
static inline uint16_t
rte_raw_cksum(const char *buf, size_t len)
{
const uint16_t *u16 = (const uint16_t *)buf;
uint32_t sum = 0;
while (len >= (sizeof(*u16) * 4)) {
sum += u16[0];
sum += u16[1];
sum += u16[2];
sum += u16[3];
len -= sizeof(*u16) * 4;
u16 += 4;
}
while (len >= sizeof(*u16)) {
sum += *u16;
len -= sizeof(*u16);
u16 += 1;
}
/* if length is in odd bytes */
if (len == 1)
sum += *((const uint8_t *)u16);
sum = ((sum & 0xffff0000) >> 16) + (sum & 0xffff);
sum = ((sum & 0xffff0000) >> 16) + (sum & 0xffff);
return (uint16_t)sum;
}
/**
* Process the IPv4 checksum of an IPv4 header.
*
* The checksum field must be set to 0 by the caller.
*
* @param ipv4_hdr
* The pointer to the contiguous IPv4 header.
* @return
* The complemented checksum to set in the IP packet.
*/
static inline uint16_t
rte_ipv4_cksum(const struct ipv4_hdr *ipv4_hdr)
{
uint16_t cksum;
cksum = rte_raw_cksum((const char *)ipv4_hdr, sizeof(struct ipv4_hdr));
return ((cksum == 0xffff) ? cksum : ~cksum);
}
/**
* Process the pseudo-header checksum of an IPv4 header.
*
* The checksum field must be set to 0 by the caller.
*
* @param ipv4_hdr
* The pointer to the contiguous IPv4 header.
* @return
* The non-complemented checksum to set in the L4 header.
*/
static inline uint16_t
rte_ipv4_phdr_cksum(const struct ipv4_hdr *ipv4_hdr)
{
struct ipv4_psd_header {
uint32_t src_addr; /* IP address of source host. */
uint32_t dst_addr; /* IP address of destination host. */
uint8_t zero; /* zero. */
uint8_t proto; /* L4 protocol type. */
uint16_t len; /* L4 length. */
} psd_hdr;
psd_hdr.src_addr = ipv4_hdr->src_addr;
psd_hdr.dst_addr = ipv4_hdr->dst_addr;
psd_hdr.zero = 0;
psd_hdr.proto = ipv4_hdr->next_proto_id;
psd_hdr.len = rte_cpu_to_be_16(
(uint16_t)(rte_be_to_cpu_16(ipv4_hdr->total_length)
- sizeof(struct ipv4_hdr)));
return rte_raw_cksum((const char *)&psd_hdr, sizeof(psd_hdr));
}
/**
* Process the IPv4 UDP or TCP checksum.
*
* The IPv4 header should not contains options. The IP and layer 4
* checksum must be set to 0 in the packet by the caller.
*
* @param ipv4_hdr
* The pointer to the contiguous IPv4 header.
* @param l4_hdr
* The pointer to the beginning of the L4 header.
* @return
* The complemented checksum to set in the IP packet.
*/
static inline uint16_t
rte_ipv4_udptcp_cksum(const struct ipv4_hdr *ipv4_hdr, const void *l4_hdr)
{
uint32_t cksum;
uint32_t l4_len;
l4_len = rte_be_to_cpu_16(ipv4_hdr->total_length) -
sizeof(struct ipv4_hdr);
cksum = rte_raw_cksum(l4_hdr, l4_len);
cksum += rte_ipv4_phdr_cksum(ipv4_hdr);
cksum = ((cksum & 0xffff0000) >> 16) + (cksum & 0xffff);
cksum = (~cksum) & 0xffff;
if (cksum == 0)
cksum = 0xffff;
return cksum;
}
/** /**
* IPv6 Header * IPv6 Header
*/ */
@ -258,6 +383,64 @@ struct ipv6_hdr {
uint8_t dst_addr[16]; /**< IP address of destination host(s). */ uint8_t dst_addr[16]; /**< IP address of destination host(s). */
} __attribute__((__packed__)); } __attribute__((__packed__));
/**
* Process the pseudo-header checksum of an IPv6 header.
*
* @param ipv6_hdr
* The pointer to the contiguous IPv6 header.
* @return
* The non-complemented checksum to set in the L4 header.
*/
static inline uint16_t
rte_ipv6_phdr_cksum(const struct ipv6_hdr *ipv6_hdr)
{
struct ipv6_psd_header {
uint8_t src_addr[16]; /* IP address of source host. */
uint8_t dst_addr[16]; /* IP address of destination host. */
uint32_t len; /* L4 length. */
uint32_t proto; /* L4 protocol - top 3 bytes must be zero */
} psd_hdr;
rte_memcpy(&psd_hdr.src_addr, ipv6_hdr->src_addr,
sizeof(ipv6_hdr->src_addr) + sizeof(ipv6_hdr->dst_addr));
psd_hdr.proto = (ipv6_hdr->proto << 24);
psd_hdr.len = ipv6_hdr->payload_len;
return rte_raw_cksum((const char *)&psd_hdr, sizeof(psd_hdr));
}
/**
* Process the IPv6 UDP or TCP checksum.
*
* The IPv4 header should not contains options. The layer 4 checksum
* must be set to 0 in the packet by the caller.
*
* @param ipv6_hdr
* The pointer to the contiguous IPv6 header.
* @param l4_hdr
* The pointer to the beginning of the L4 header.
* @return
* The complemented checksum to set in the IP packet.
*/
static inline uint16_t
rte_ipv6_udptcp_cksum(const struct ipv6_hdr *ipv6_hdr, const void *l4_hdr)
{
uint32_t cksum;
uint32_t l4_len;
l4_len = rte_be_to_cpu_16(ipv6_hdr->payload_len);
cksum = rte_raw_cksum(l4_hdr, l4_len);
cksum += rte_ipv6_phdr_cksum(ipv6_hdr, 0);
cksum = ((cksum & 0xffff0000) >> 16) + (cksum & 0xffff);
cksum = (~cksum) & 0xffff;
if (cksum == 0)
cksum = 0xffff;
return cksum;
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif