summaryrefslogtreecommitdiffstats
path: root/VNFs/DPPD-PROX/prox_cksum.c
blob: 9a05097ecbe82ee2791c39a65ef4727c6d3e7f7c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*
// Copyright (c) 2010-2017 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/

#include "prox_cksum.h"
#include "prox_port_cfg.h"
#include <rte_byteorder.h>
#include "log.h"

/* compute IP 16 bit checksum */
void prox_ip_cksum_sw(struct ipv4_hdr *buf)
{
	const uint16_t size = sizeof(struct ipv4_hdr);
	uint32_t cksum = 0;
	uint32_t nb_dwords;
	uint32_t tail, mask;
	uint32_t *pdwd = (uint32_t *)buf;

	/* compute 16 bit checksum using hi and low parts of 32 bit integers */
	for (nb_dwords = (size >> 2); nb_dwords > 0; --nb_dwords) {
		cksum += (*pdwd >> 16);
		cksum += (*pdwd & 0xFFFF);
		++pdwd;
	}

	/* deal with the odd byte length */
	if (size & 0x03) {
		tail = *pdwd;
		/* calculate mask for valid parts */
		mask = 0xFFFFFFFF << ((size & 0x03) << 3);
		/* clear unused bits */
		tail &= ~mask;

		cksum += (tail >> 16) + (tail & 0xFFFF);
	}

	cksum = (cksum >> 16) + (cksum & 0xFFFF);
	cksum = (cksum >> 16) + (cksum & 0xFFFF);

	buf->hdr_checksum = ~((uint16_t)cksum);
}

static uint16_t calc_pseudo_checksum(uint8_t ipproto, uint16_t len, uint32_t src_ip_addr, uint32_t dst_ip_addr)
{
	uint32_t csum = 0;

	csum += (src_ip_addr >> 16) + (src_ip_addr & 0xFFFF);
	csum += (dst_ip_addr >> 16) + (dst_ip_addr & 0xFFFF);
	csum += rte_bswap16(ipproto) + rte_bswap16(len);
	csum = (csum >> 16) + (csum & 0xFFFF);
	return csum;
}

static void prox_write_udp_pseudo_hdr(struct udp_hdr *udp, uint16_t len, uint32_t src_ip_addr, uint32_t dst_ip_addr)
{
	/* Note that the csum is not complemented, while the pseaudo
	   header checksum is calculated as "... the 16-bit one's
	   complement of the one's complement sum of a pseudo header
	   of information ...", the psuedoheader forms as a basis for
	   the actual checksum calculated later either in software or
	   hardware. */
	udp->dgram_cksum = calc_pseudo_checksum(IPPROTO_UDP, len, src_ip_addr, dst_ip_addr);
}

static void prox_write_tcp_pseudo_hdr(struct tcp_hdr *tcp, uint16_t len, uint32_t src_ip_addr, uint32_t dst_ip_addr)
{
	tcp->cksum = calc_pseudo_checksum(IPPROTO_TCP, len, src_ip_addr, dst_ip_addr);
}

void prox_ip_udp_cksum(struct rte_mbuf *mbuf, struct ipv4_hdr *pip, uint16_t l2_len, uint16_t l3_len, int cksum_offload)
{
	prox_ip_cksum(mbuf, pip, l2_len, l3_len, cksum_offload & IPV4_CKSUM);

	uint32_t l4_len = rte_bswap16(pip->total_length) - l3_len;
	if (pip->next_proto_id == IPPROTO_UDP) {
		struct udp_hdr *udp = (struct udp_hdr *)(((uint8_t*)pip) + l3_len);
#ifndef SOFT_CRC
		if (cksum_offload & UDP_CKSUM) {
			mbuf->ol_flags |= PKT_TX_UDP_CKSUM;
			prox_write_udp_pseudo_hdr(udp, l4_len, pip->src_addr, pip->dst_addr);
		} else
#endif
		prox_udp_cksum_sw(udp, l4_len, pip->src_addr, pip->dst_addr);
	} else if (pip->next_proto_id == IPPROTO_TCP) {
		struct tcp_hdr *tcp = (struct tcp_hdr *)(((uint8_t*)pip) + l3_len);
#ifndef SOFT_CRC
		if (cksum_offload & UDP_CKSUM) {
			prox_write_tcp_pseudo_hdr(tcp, l4_len, pip->src_addr, pip->dst_addr);
			mbuf->ol_flags |= PKT_TX_UDP_CKSUM;
		} else
#endif
		prox_tcp_cksum_sw(tcp, l4_len, pip->src_addr, pip->dst_addr);
	}
}

static uint16_t checksum_byte_seq(uint16_t *buf, uint16_t len)
{
	uint32_t csum = 0;

	while (len > 1) {
		csum += *buf;
		while (csum >> 16) {
			csum &= 0xffff;
			csum +=1;
		}
		buf++;
		len -= 2;
	}

	if (len) {
		csum += *(uint8_t*)buf;
		while (csum >> 16) {
			csum &= 0xffff;
			csum +=1;
		}
	}
	return ~csum;
}

void prox_udp_cksum_sw(struct udp_hdr *udp, uint16_t len, uint32_t src_ip_addr, uint32_t dst_ip_addr)
{
	prox_write_udp_pseudo_hdr(udp, len, src_ip_addr, dst_ip_addr);
	uint16_t csum = checksum_byte_seq((uint16_t *)udp, len);
	udp->dgram_cksum = csum;
}

void prox_tcp_cksum_sw(struct tcp_hdr *tcp, uint16_t len, uint32_t src_ip_addr, uint32_t dst_ip_addr)
{
	prox_write_tcp_pseudo_hdr(tcp, len, src_ip_addr, dst_ip_addr);

	uint16_t csum = checksum_byte_seq((uint16_t *)tcp, len);
	tcp->cksum = csum;
}