Files
m241Neelima/components/ip_net2-6.9/ipcom/port/src/ipcom_checksum.c
T
2026-07-30 11:19:44 +05:30

476 lines
14 KiB
C

/*
******************************************************************************
* INTERPEAK SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipcom_checksum.c,v $ $Revision: 1.28.10.3 $
* $Source: /home/interpeak/CVSRoot/ipcom/port/src/ipcom_checksum.c,v $
* $Author: hxie $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Kenneth Jonsson <kenneth@interpeak.se>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#include "ipcom_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#define IPCOM_USE_CLIB_PROTO
#include "ipcom_clib.h"
#include "ipcom_inet.h"
#include "ipcom_sock.h"
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
/*
*===========================================================================
* IPCOM_CPY_32_BITS_ALIGNED
*===========================================================================
* Description: Copies 32-bits of data where the source and destination
* addresses both are aligned on a 32-bit boundary.
*
*/
#define IPCOM_CPY_32_BITS_ALIGNED \
data = *dst_u32++ = *src_u32++; \
chksum += (Ip_u16) data + (data >> 16); \
/*
*===========================================================================
* IPCOM_CPY_32_BITS_UNALIGNED
*===========================================================================
* Description: Copies 32-bits of data where the source and destination
* addresses cannot be aligned on a 32-bit boundary at the same
* time. Data is read and written in 32-bit chunks at a 32-bit
* boundary.
*
*/
#ifdef IP_BIG_ENDIAN
#define IPCOM_CPY_32_BITS_UNALIGNED(shift1, shift2) \
data = src_data << shift1; \
src_data = *src_u32++; \
data = (src_data >> shift2) | data; \
chksum += (Ip_u16) data; \
*dst_u32++ = data; \
chksum += (data >> 16);
#else /* IP_LITTLE_ENDIAN */
#define IPCOM_CPY_32_BITS_UNALIGNED(shift1, shift2) \
data = src_data >> shift1; \
src_data = *src_u32++; \
data = (src_data << shift2) | data; \
chksum += (Ip_u16) data; \
*dst_u32++ = data; \
chksum += (data >> 16);
#endif
/*
*===========================================================================
* IPCOM_UNALIGNED_CPY_LOOP
*===========================================================================
* Description: Copies data as long as a full 32-bit word can be written.
*
*/
#define IPCOM_UNALIGNED_CPY_LOOP(shift1, shift2) \
i = (int)((len - octet_unalignment) >> 5); \
while (i--) \
{ \
IPCOM_CPY_32_BITS_UNALIGNED(shift1, shift2); \
IPCOM_CPY_32_BITS_UNALIGNED(shift1, shift2); \
IPCOM_CPY_32_BITS_UNALIGNED(shift1, shift2); \
IPCOM_CPY_32_BITS_UNALIGNED(shift1, shift2); \
IPCOM_CPY_32_BITS_UNALIGNED(shift1, shift2); \
IPCOM_CPY_32_BITS_UNALIGNED(shift1, shift2); \
IPCOM_CPY_32_BITS_UNALIGNED(shift1, shift2); \
IPCOM_CPY_32_BITS_UNALIGNED(shift1, shift2); \
} \
i = (int)(((len - octet_unalignment) & 0x1f) >> 2); \
while (i--) \
{ \
IPCOM_CPY_32_BITS_UNALIGNED(shift1, shift2); \
}
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
#ifdef IPCOM_USE_ASM
IP_PUBLIC Ip_u32
ipcom_in_checksum_update2_asm(void *pbuf, Ip_size_t bytes);
IP_PUBLIC Ip_u32
ipcom_in_checksum_memcpy_asm(void *dst, void *src, Ip_size_t bytes);
#endif
#ifdef IP_PORT_LKM
IP_EXTERN Ip_u32
ipcom_in_checksum_update2(void *pbuf, Ip_size_t bytes);
#endif
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
****************************************************************************
* 9 STATIC FUNCTIONS
****************************************************************************
*/
/*
****************************************************************************
* 10 GLOBAL FUNCTIONS
****************************************************************************
*/
/*
****************************************************************************
* 11 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipcom_in_checksum_update
*===========================================================================
* Description: Computes the internet checksum (RFC 1071). NOTE
* it does not compute the 1 complement checksum.
* Parameters: pbuf and bytes.
* Returns: A standard internet checksum..
*
*/
IP_PUBLIC Ip_u32
ipcom_in_checksum_update(void *pbuf, Ip_size_t bytes)
{
Ip_u32 sum;
#if defined(IP_PORT_LKM) && !defined(IP_TARGET_ARM) && !defined(IP_TARGET_MIPS)
sum = ipcom_in_checksum_update2(pbuf, bytes);
/* One folding step MUST be done to avoid overflow in IPNET/IPLITE */
return (sum & 0xffff) + (sum >> 16);
#else
Ip_u32 nwords;
Ip_u16 *buf;
#ifdef IPCOM_USE_ASM
if (IP_LIKELY(((long)pbuf & 0x1) == 0)
&& IP_LIKELY(bytes >= sizeof(Ip_u16)))
{
sum = ipcom_in_checksum_update2_asm(pbuf, bytes);
/* One folding step MUST be done to avoid overflow in IPNET/IPLITE */
return (sum & 0xffff) + (sum >> 16);
}
#endif
buf = (Ip_u16 *) pbuf;
sum = 0;
for (nwords = (Ip_u32)bytes >> 5; nwords > 0; nwords--)
{
sum += *buf++; sum += *buf++; sum += *buf++; sum += *buf++;
sum += *buf++; sum += *buf++; sum += *buf++; sum += *buf++;
sum += *buf++; sum += *buf++; sum += *buf++; sum += *buf++;
sum += *buf++; sum += *buf++; sum += *buf++; sum += *buf++;
}
for (nwords = (Ip_u32)(bytes & 0x1f) >> 1; nwords > 0; nwords--)
sum += *buf++;
/* Add left-over byte, if any. */
if (bytes & 0x01)
sum += *buf & ip_htons(0xff00);
return sum;
#endif /* IP_PORT_LKM && !IP_TARGET_ARM*/
}
/*
*===========================================================================
* ipcom_in_checksum_finish
*===========================================================================
* Description: Computes the one complement internet checksum (RFC 1071).
* Parameters: sum - The accumulated 32 bit internet sum.
* Returns: A one complement standard internet checksum.
*
*/
IP_PUBLIC Ip_u16
ipcom_in_checksum_finish(Ip_u32 sum)
{
/* Fold 32-bit sum to 16 bits. */
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
return (Ip_u16)~(Ip_u16)sum;
}
/*
*===========================================================================
* ipcom_in_checksum
*===========================================================================
* Description: Calculates the checksum on the IPv4 header.
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_u16
ipcom_in_checksum(void *buf, Ip_size_t bytes)
{
Ip_u32 sum;
sum = ipcom_in_checksum_update(buf, bytes);
/* inline ipcom_in_checksum_finish code: */
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
return (Ip_u16)~(Ip_u16)sum;
}
/*
*===========================================================================
* ipcom_in_checksum_update_pkt
*===========================================================================
* Description: Calculates the checksum on the IPv4 header.
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_u32
ipcom_in_checksum_update_pkt(Ipcom_pkt *pkt, Ip_u32 chksum)
{
Ip_u32 sum = chksum;
Ip_u32 pkt_sum = 0;
int len = 0;
while (pkt != IP_NULL)
{
pkt_sum = ipcom_in_checksum_update(pkt->data+pkt->start, pkt->end-pkt->start);
if (len & 0x01)
pkt_sum = (pkt_sum >> 8) + ((pkt_sum & 0xff) << 8);
sum += pkt_sum;
len += (pkt->end - pkt->start);
pkt = pkt->next_part;
}
return sum;
}
/*
*===========================================================================
* ipcom_in_checksum_pkt
*===========================================================================
* Description: Calculates the checksum on the IPv4 header.
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_u16
ipcom_in_checksum_pkt(Ipcom_pkt *pkt, Ip_u32 chksum)
{
Ip_u32 sum = ipcom_in_checksum_update_pkt(pkt, chksum);
return ipcom_in_checksum_finish(sum);
}
/*
*===========================================================================
* ipcom_in_checksum_memcpy
*===========================================================================
* Description: Copies data and calculate the internet checksum at the same
* time.
* Parameters: dst - Destination buffer.
* src - Source buffer.
* len - Number of octets to copy.
* Returns: The intermediate internet checksum, use
* ipcom_in_checksum_finish() to get the actual checksum.
*
*/
#if !defined(IP_PORT_LKM) || (defined(IP_PORT_LKM) && (defined(IP_TARGET_ARM) || defined(IP_TARGET_MIPS)))
IP_PUBLIC Ip_u32
ipcom_in_checksum_memcpy(void *dst, void *src, Ip_size_t len)
{
#ifdef IPCOM_USE_ASM
Ip_u32 sum = ipcom_in_checksum_memcpy_asm(dst, src, len);
/* One folding step MUST be done to avoid overflow in IPNET/IPLITE */
return (sum & 0xffff) + (sum >> 16);
#elif defined(IPBOUNDS) || defined(__BOUNDS_CHECKING_ON) || defined(IP_PORT_LKM)
(void)ipcom_copy_from_user(dst, src, len);
return ipcom_in_checksum_update(dst, len);
#else
Ip_u8 *dst_u8;
Ip_u8 *src_u8;
Ip_u32 *dst_u32;
Ip_u32 *src_u32;
Ip_u32 data;
Ip_u32 src_data;
Ip_u32 chksum = 0;
int i;
int initial_octets;
int octet_unalignment = 0;
Ip_bool dst_addr_is_odd = (Ip_bool)((Ip_ptrdiff_t) dst & 0x1);
initial_octets = (int) IP_MIN(((-(Ip_ptrdiff_t) dst) & 0x3), (Ip_ptrdiff_t) len);
if (initial_octets == 0)
{
dst_u32 = (Ip_u32 *) dst;
src_u32 = (Ip_u32 *) src;
}
else
{
dst_u8 = (Ip_u8 *) dst;
src_u8 = (Ip_u8 *) src;
for (i = 0; i < initial_octets; ++i)
{
/* Copy initial octets (so that dst will be 32-bit aligned) */
data = *dst_u8++ = *src_u8++;
#ifdef IP_BIG_ENDIAN
if ((i ^ dst_addr_is_odd) & 0x1)
chksum += data;
else
chksum += data << 8;
#else /* IP_LITTLE_ENDIAN */
if ((i ^ dst_addr_is_odd) & 0x1)
chksum += data << 8;
else
chksum += data;
#endif
}
dst_u32 = (Ip_u32 *) dst_u8;
src_u32 = (Ip_u32 *) src_u8;
len -= initial_octets;
}
/* dst is now 32-bit aligned */
if (((Ip_ptrdiff_t) dst_u32 & 0x3) == ((Ip_ptrdiff_t) src_u32 & 0x3))
{
/* Both src and dst is now on an even 32-bit boundary */
i = (int)(len >> 5);
while (i--)
{
/* Big main copy loop */
IPCOM_CPY_32_BITS_ALIGNED;
IPCOM_CPY_32_BITS_ALIGNED;
IPCOM_CPY_32_BITS_ALIGNED;
IPCOM_CPY_32_BITS_ALIGNED;
IPCOM_CPY_32_BITS_ALIGNED;
IPCOM_CPY_32_BITS_ALIGNED;
IPCOM_CPY_32_BITS_ALIGNED;
IPCOM_CPY_32_BITS_ALIGNED;
}
i = (int)((len & 0x1f) >> 2);
while (i--)
{
/* Small main copy loop */
IPCOM_CPY_32_BITS_ALIGNED;
}
len -= len & ~0x3;
}
else if (len >= 4)
{
/* dst and src cannot be aligned */
octet_unalignment = (int)((Ip_ptrdiff_t) src_u32 & 0x3);
src_u32 = (Ip_u32 *) ((Ip_u8 *) src_u32 - octet_unalignment);
src_data = *src_u32++;
/* src is now 32-bit aligned */
switch (octet_unalignment)
{
case 1:
IPCOM_UNALIGNED_CPY_LOOP(8, 24);
break;
case 2:
IPCOM_UNALIGNED_CPY_LOOP(16, 16);
break;
case 3:
IPCOM_UNALIGNED_CPY_LOOP(24, 8);
break;
default:
IP_PANIC();
break;
}
--src_u32;
len -= ((len - octet_unalignment) & ~0x3);
}
if (len)
{
/* Copy data that cannot be written in 32-bit chunks */
src_u8 = (Ip_u8 *) src_u32 + octet_unalignment;
dst_u8 = (Ip_u8 *) dst_u32;
for (i = 0; i < (int) len; ++i)
{
data = *dst_u8++ = *src_u8++;
#ifdef IP_BIG_ENDIAN
if (i & 0x1)
chksum += data;
else
chksum += data << 8;
#else /* IP_LITTLE_ENDIAN */
if (i & 0x1)
chksum += data << 8;
else
chksum += data;
#endif
}
}
if (dst_addr_is_odd)
{
/* The checksum has the low and hi bytes swaped */
return (((Ip_u8 *) &chksum)[0] << 8)
+ ((Ip_u8 *) &chksum)[1]
+ (((Ip_u8 *) &chksum)[2] << 8)
+ ((Ip_u8 *) &chksum)[3]; /*lint !e647 */
}
return chksum;
#endif /* IPCOM_USE_ASM */
}
#endif
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/