/*1*2* INET An implementation of the TCP/IP protocol suite for the LINUX3* operating system. INET is implemented using the BSD Socket4* interface as the means of communication with the user level.5*6* IP/TCP/UDP checksumming routines7*8* Authors: Jorge Cwik, <[email protected]>9* Arnt Gulbrandsen, <[email protected]>10* Tom May, <[email protected]>11* Andreas Schwab, <[email protected]>12* Lots of code moved from tcp.c and ip.c; see those files13* for more names.14*15* 03/02/96 Jes Sorensen, Andreas Schwab, Roman Hodek:16* Fixed some nasty bugs, causing some horrible crashes.17* A: At some points, the sum (%0) was used as18* length-counter instead of the length counter19* (%1). Thanks to Roman Hodek for pointing this out.20* B: GCC seems to mess up if one uses too many21* data-registers to hold input values and one tries to22* specify d0 and d1 as scratch registers. Letting gcc23* choose these registers itself solves the problem.24*25* This program is free software; you can redistribute it and/or26* modify it under the terms of the GNU General Public License27* as published by the Free Software Foundation; either version28* 2 of the License, or (at your option) any later version.29*/3031/* Revised by Kenneth Albanowski for m68knommu. Basic problem: unaligned access32kills, so most of the assembly has to go. */3334#include <linux/module.h>35#include <net/checksum.h>3637#include <asm/byteorder.h>3839#ifndef do_csum40static inline unsigned short from32to16(unsigned int x)41{42/* add up 16-bit and 16-bit for 16+c bit */43x = (x & 0xffff) + (x >> 16);44/* add up carry.. */45x = (x & 0xffff) + (x >> 16);46return x;47}4849static unsigned int do_csum(const unsigned char *buff, int len)50{51int odd, count;52unsigned int result = 0;5354if (len <= 0)55goto out;56odd = 1 & (unsigned long) buff;57if (odd) {58#ifdef __LITTLE_ENDIAN59result += (*buff << 8);60#else61result = *buff;62#endif63len--;64buff++;65}66count = len >> 1; /* nr of 16-bit words.. */67if (count) {68if (2 & (unsigned long) buff) {69result += *(unsigned short *) buff;70count--;71len -= 2;72buff += 2;73}74count >>= 1; /* nr of 32-bit words.. */75if (count) {76unsigned int carry = 0;77do {78unsigned int w = *(unsigned int *) buff;79count--;80buff += 4;81result += carry;82result += w;83carry = (w > result);84} while (count);85result += carry;86result = (result & 0xffff) + (result >> 16);87}88if (len & 2) {89result += *(unsigned short *) buff;90buff += 2;91}92}93if (len & 1)94#ifdef __LITTLE_ENDIAN95result += *buff;96#else97result += (*buff << 8);98#endif99result = from32to16(result);100if (odd)101result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);102out:103return result;104}105#endif106107/*108* This is a version of ip_compute_csum() optimized for IP headers,109* which always checksum on 4 octet boundaries.110*/111__sum16 ip_fast_csum(const void *iph, unsigned int ihl)112{113return (__force __sum16)~do_csum(iph, ihl*4);114}115EXPORT_SYMBOL(ip_fast_csum);116117/*118* computes the checksum of a memory block at buff, length len,119* and adds in "sum" (32-bit)120*121* returns a 32-bit number suitable for feeding into itself122* or csum_tcpudp_magic123*124* this function must be called with even lengths, except125* for the last fragment, which may be odd126*127* it's best to have buff aligned on a 32-bit boundary128*/129__wsum csum_partial(const void *buff, int len, __wsum wsum)130{131unsigned int sum = (__force unsigned int)wsum;132unsigned int result = do_csum(buff, len);133134/* add in old sum, and carry.. */135result += sum;136if (sum > result)137result += 1;138return (__force __wsum)result;139}140EXPORT_SYMBOL(csum_partial);141142/*143* this routine is used for miscellaneous IP-like checksums, mainly144* in icmp.c145*/146__sum16 ip_compute_csum(const void *buff, int len)147{148return (__force __sum16)~do_csum(buff, len);149}150EXPORT_SYMBOL(ip_compute_csum);151152/*153* copy from fs while checksumming, otherwise like csum_partial154*/155__wsum156csum_partial_copy_from_user(const void __user *src, void *dst, int len,157__wsum sum, int *csum_err)158{159int missing;160161missing = __copy_from_user(dst, src, len);162if (missing) {163memset(dst + len - missing, 0, missing);164*csum_err = -EFAULT;165} else166*csum_err = 0;167168return csum_partial(dst, len, sum);169}170EXPORT_SYMBOL(csum_partial_copy_from_user);171172/*173* copy from ds while checksumming, otherwise like csum_partial174*/175__wsum176csum_partial_copy(const void *src, void *dst, int len, __wsum sum)177{178memcpy(dst, src, len);179return csum_partial(dst, len, sum);180}181EXPORT_SYMBOL(csum_partial_copy);182183#ifndef csum_tcpudp_nofold184__wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,185unsigned short len,186unsigned short proto,187__wsum sum)188{189unsigned long long s = (__force u32)sum;190191s += (__force u32)saddr;192s += (__force u32)daddr;193#ifdef __BIG_ENDIAN194s += proto + len;195#else196s += (proto + len) << 8;197#endif198s += (s >> 32);199return (__force __wsum)s;200}201EXPORT_SYMBOL(csum_tcpudp_nofold);202#endif203204205