18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * INET		An implementation of the TCP/IP protocol suite for the LINUX
58c2ecf20Sopenharmony_ci *		operating system.  INET is implemented using the  BSD Socket
68c2ecf20Sopenharmony_ci *		interface as the means of communication with the user level.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *		IP/TCP/UDP checksumming routines
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Authors:	Jorge Cwik, <jorge@laser.satlink.net>
118c2ecf20Sopenharmony_ci *		Arnt Gulbrandsen, <agulbra@nvg.unit.no>
128c2ecf20Sopenharmony_ci *		Tom May, <ftom@netcom.com>
138c2ecf20Sopenharmony_ci *		Andreas Schwab, <schwab@issan.informatik.uni-dortmund.de>
148c2ecf20Sopenharmony_ci *		Lots of code moved from tcp.c and ip.c; see those files
158c2ecf20Sopenharmony_ci *		for more names.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * 03/02/96	Jes Sorensen, Andreas Schwab, Roman Hodek:
188c2ecf20Sopenharmony_ci *		Fixed some nasty bugs, causing some horrible crashes.
198c2ecf20Sopenharmony_ci *		A: At some points, the sum (%0) was used as
208c2ecf20Sopenharmony_ci *		length-counter instead of the length counter
218c2ecf20Sopenharmony_ci *		(%1). Thanks to Roman Hodek for pointing this out.
228c2ecf20Sopenharmony_ci *		B: GCC seems to mess up if one uses too many
238c2ecf20Sopenharmony_ci *		data-registers to hold input values and one tries to
248c2ecf20Sopenharmony_ci *		specify d0 and d1 as scratch registers. Letting gcc
258c2ecf20Sopenharmony_ci *		choose these registers itself solves the problem.
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* Revised by Kenneth Albanowski for m68knommu. Basic problem: unaligned access
298c2ecf20Sopenharmony_ci kills, so most of the assembly has to go. */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <linux/export.h>
328c2ecf20Sopenharmony_ci#include <net/checksum.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include <asm/byteorder.h>
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#ifndef do_csum
378c2ecf20Sopenharmony_cistatic inline unsigned short from32to16(unsigned int x)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	/* add up 16-bit and 16-bit for 16+c bit */
408c2ecf20Sopenharmony_ci	x = (x & 0xffff) + (x >> 16);
418c2ecf20Sopenharmony_ci	/* add up carry.. */
428c2ecf20Sopenharmony_ci	x = (x & 0xffff) + (x >> 16);
438c2ecf20Sopenharmony_ci	return x;
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic unsigned int do_csum(const unsigned char *buff, int len)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	int odd;
498c2ecf20Sopenharmony_ci	unsigned int result = 0;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (len <= 0)
528c2ecf20Sopenharmony_ci		goto out;
538c2ecf20Sopenharmony_ci	odd = 1 & (unsigned long) buff;
548c2ecf20Sopenharmony_ci	if (odd) {
558c2ecf20Sopenharmony_ci#ifdef __LITTLE_ENDIAN
568c2ecf20Sopenharmony_ci		result += (*buff << 8);
578c2ecf20Sopenharmony_ci#else
588c2ecf20Sopenharmony_ci		result = *buff;
598c2ecf20Sopenharmony_ci#endif
608c2ecf20Sopenharmony_ci		len--;
618c2ecf20Sopenharmony_ci		buff++;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci	if (len >= 2) {
648c2ecf20Sopenharmony_ci		if (2 & (unsigned long) buff) {
658c2ecf20Sopenharmony_ci			result += *(unsigned short *) buff;
668c2ecf20Sopenharmony_ci			len -= 2;
678c2ecf20Sopenharmony_ci			buff += 2;
688c2ecf20Sopenharmony_ci		}
698c2ecf20Sopenharmony_ci		if (len >= 4) {
708c2ecf20Sopenharmony_ci			const unsigned char *end = buff + ((unsigned)len & ~3);
718c2ecf20Sopenharmony_ci			unsigned int carry = 0;
728c2ecf20Sopenharmony_ci			do {
738c2ecf20Sopenharmony_ci				unsigned int w = *(unsigned int *) buff;
748c2ecf20Sopenharmony_ci				buff += 4;
758c2ecf20Sopenharmony_ci				result += carry;
768c2ecf20Sopenharmony_ci				result += w;
778c2ecf20Sopenharmony_ci				carry = (w > result);
788c2ecf20Sopenharmony_ci			} while (buff < end);
798c2ecf20Sopenharmony_ci			result += carry;
808c2ecf20Sopenharmony_ci			result = (result & 0xffff) + (result >> 16);
818c2ecf20Sopenharmony_ci		}
828c2ecf20Sopenharmony_ci		if (len & 2) {
838c2ecf20Sopenharmony_ci			result += *(unsigned short *) buff;
848c2ecf20Sopenharmony_ci			buff += 2;
858c2ecf20Sopenharmony_ci		}
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci	if (len & 1)
888c2ecf20Sopenharmony_ci#ifdef __LITTLE_ENDIAN
898c2ecf20Sopenharmony_ci		result += *buff;
908c2ecf20Sopenharmony_ci#else
918c2ecf20Sopenharmony_ci		result += (*buff << 8);
928c2ecf20Sopenharmony_ci#endif
938c2ecf20Sopenharmony_ci	result = from32to16(result);
948c2ecf20Sopenharmony_ci	if (odd)
958c2ecf20Sopenharmony_ci		result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
968c2ecf20Sopenharmony_ciout:
978c2ecf20Sopenharmony_ci	return result;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci#endif
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci#ifndef ip_fast_csum
1028c2ecf20Sopenharmony_ci/*
1038c2ecf20Sopenharmony_ci *	This is a version of ip_compute_csum() optimized for IP headers,
1048c2ecf20Sopenharmony_ci *	which always checksum on 4 octet boundaries.
1058c2ecf20Sopenharmony_ci */
1068c2ecf20Sopenharmony_ci__sum16 ip_fast_csum(const void *iph, unsigned int ihl)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	return (__force __sum16)~do_csum(iph, ihl*4);
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ip_fast_csum);
1118c2ecf20Sopenharmony_ci#endif
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/*
1148c2ecf20Sopenharmony_ci * computes the checksum of a memory block at buff, length len,
1158c2ecf20Sopenharmony_ci * and adds in "sum" (32-bit)
1168c2ecf20Sopenharmony_ci *
1178c2ecf20Sopenharmony_ci * returns a 32-bit number suitable for feeding into itself
1188c2ecf20Sopenharmony_ci * or csum_tcpudp_magic
1198c2ecf20Sopenharmony_ci *
1208c2ecf20Sopenharmony_ci * this function must be called with even lengths, except
1218c2ecf20Sopenharmony_ci * for the last fragment, which may be odd
1228c2ecf20Sopenharmony_ci *
1238c2ecf20Sopenharmony_ci * it's best to have buff aligned on a 32-bit boundary
1248c2ecf20Sopenharmony_ci */
1258c2ecf20Sopenharmony_ci__wsum csum_partial(const void *buff, int len, __wsum wsum)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	unsigned int sum = (__force unsigned int)wsum;
1288c2ecf20Sopenharmony_ci	unsigned int result = do_csum(buff, len);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* add in old sum, and carry.. */
1318c2ecf20Sopenharmony_ci	result += sum;
1328c2ecf20Sopenharmony_ci	if (sum > result)
1338c2ecf20Sopenharmony_ci		result += 1;
1348c2ecf20Sopenharmony_ci	return (__force __wsum)result;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(csum_partial);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci/*
1398c2ecf20Sopenharmony_ci * this routine is used for miscellaneous IP-like checksums, mainly
1408c2ecf20Sopenharmony_ci * in icmp.c
1418c2ecf20Sopenharmony_ci */
1428c2ecf20Sopenharmony_ci__sum16 ip_compute_csum(const void *buff, int len)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	return (__force __sum16)~do_csum(buff, len);
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ip_compute_csum);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci#ifndef csum_tcpudp_nofold
1498c2ecf20Sopenharmony_cistatic inline u32 from64to32(u64 x)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	/* add up 32-bit and 32-bit for 32+c bit */
1528c2ecf20Sopenharmony_ci	x = (x & 0xffffffff) + (x >> 32);
1538c2ecf20Sopenharmony_ci	/* add up carry.. */
1548c2ecf20Sopenharmony_ci	x = (x & 0xffffffff) + (x >> 32);
1558c2ecf20Sopenharmony_ci	return (u32)x;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci__wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
1598c2ecf20Sopenharmony_ci			  __u32 len, __u8 proto, __wsum sum)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	unsigned long long s = (__force u32)sum;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	s += (__force u32)saddr;
1648c2ecf20Sopenharmony_ci	s += (__force u32)daddr;
1658c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN
1668c2ecf20Sopenharmony_ci	s += proto + len;
1678c2ecf20Sopenharmony_ci#else
1688c2ecf20Sopenharmony_ci	s += (proto + len) << 8;
1698c2ecf20Sopenharmony_ci#endif
1708c2ecf20Sopenharmony_ci	return (__force __wsum)from64to32(s);
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(csum_tcpudp_nofold);
1738c2ecf20Sopenharmony_ci#endif
174