// SPDX-License-Identifier: GPL-2.0-or-later1/*2*3* INET An implementation of the TCP/IP protocol suite for the LINUX4* operating system. INET is implemented using the BSD Socket5* interface as the means of communication with the user level.6*7* IP/TCP/UDP checksumming routines8*9* Authors: Jorge Cwik, <[email protected]>10* Arnt Gulbrandsen, <[email protected]>11* Tom May, <[email protected]>12* Andreas Schwab, <[email protected]>13* Lots of code moved from tcp.c and ip.c; see those files14* for more names.15*16* 03/02/96 Jes Sorensen, Andreas Schwab, Roman Hodek:17* Fixed some nasty bugs, causing some horrible crashes.18* A: At some points, the sum (%0) was used as19* length-counter instead of the length counter20* (%1). Thanks to Roman Hodek for pointing this out.21* B: GCC seems to mess up if one uses too many22* data-registers to hold input values and one tries to23* specify d0 and d1 as scratch registers. Letting gcc24* choose these registers itself solves the problem.25*/2627/* Revised by Kenneth Albanowski for m68knommu. Basic problem: unaligned access28kills, so most of the assembly has to go. */2930#include <linux/export.h>31#include <net/checksum.h>3233#include <asm/byteorder.h>3435#ifndef do_csum36static unsigned int do_csum(const unsigned char *buff, int len)37{38int odd;39unsigned int result = 0;4041if (len <= 0)42goto out;43odd = 1 & (unsigned long) buff;44if (odd) {45#ifdef __LITTLE_ENDIAN46result += (*buff << 8);47#else48result = *buff;49#endif50len--;51buff++;52}53if (len >= 2) {54if (2 & (unsigned long) buff) {55result += *(unsigned short *) buff;56len -= 2;57buff += 2;58}59if (len >= 4) {60const unsigned char *end = buff + ((unsigned)len & ~3);61unsigned int carry = 0;62do {63unsigned int w = *(unsigned int *) buff;64buff += 4;65result += carry;66result += w;67carry = (w > result);68} while (buff < end);69result += carry;70result = (result & 0xffff) + (result >> 16);71}72if (len & 2) {73result += *(unsigned short *) buff;74buff += 2;75}76}77if (len & 1)78#ifdef __LITTLE_ENDIAN79result += *buff;80#else81result += (*buff << 8);82#endif83result = csum_from32to16(result);84if (odd)85result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);86out:87return result;88}89#endif9091#ifndef ip_fast_csum92/*93* This is a version of ip_compute_csum() optimized for IP headers,94* which always checksum on 4 octet boundaries.95*/96__sum16 ip_fast_csum(const void *iph, unsigned int ihl)97{98return (__force __sum16)~do_csum(iph, ihl*4);99}100EXPORT_SYMBOL(ip_fast_csum);101#endif102103/*104* computes the checksum of a memory block at buff, length len,105* and adds in "sum" (32-bit)106*107* returns a 32-bit number suitable for feeding into itself108* or csum_tcpudp_magic109*110* this function must be called with even lengths, except111* for the last fragment, which may be odd112*113* it's best to have buff aligned on a 32-bit boundary114*/115__wsum csum_partial(const void *buff, int len, __wsum wsum)116{117unsigned int sum = (__force unsigned int)wsum;118unsigned int result = do_csum(buff, len);119120/* add in old sum, and carry.. */121result += sum;122if (sum > result)123result += 1;124return (__force __wsum)result;125}126EXPORT_SYMBOL(csum_partial);127128/*129* this routine is used for miscellaneous IP-like checksums, mainly130* in icmp.c131*/132__sum16 ip_compute_csum(const void *buff, int len)133{134return (__force __sum16)~do_csum(buff, len);135}136EXPORT_SYMBOL(ip_compute_csum);137138#ifndef csum_tcpudp_nofold139static inline u32 from64to32(u64 x)140{141/* add up 32-bit and 32-bit for 32+c bit */142x = (x & 0xffffffff) + (x >> 32);143/* add up carry.. */144x = (x & 0xffffffff) + (x >> 32);145return (u32)x;146}147148__wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,149__u32 len, __u8 proto, __wsum sum)150{151unsigned long long s = (__force u32)sum;152153s += (__force u32)saddr;154s += (__force u32)daddr;155#ifdef __BIG_ENDIAN156s += proto + len;157#else158s += (proto + len) << 8;159#endif160return (__force __wsum)from64to32(s);161}162EXPORT_SYMBOL(csum_tcpudp_nofold);163#endif164165166