1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
|
#include "checksum.h"
#include "net.h"
#include "uint.h"
#include "util.h"
/*
* This is a version of ip_compute_csum() optimized for IP headers,
* which always checksum on 4 octet boundaries.
*
* By Jorge Cwik <jorge@laser.satlink.net>, adapted for linux by
* Arnt Gulbrandsen.
*/
/**
* ip_fast_csum - Compute the IPv4 header checksum efficiently.
* iph: ipv4 header
* ihl: length of header / 4
*/
#if defined(__i386__) || defined(__amd64__)
__inline u16 checksum_ip(const void *iph, unsigned ihl)
{
unsigned sum;
asm( " movl (%1), %0\n"
" subl $4, %2\n"
" jbe 2f\n"
" addl 4(%1), %0\n"
" adcl 8(%1), %0\n"
" adcl 12(%1), %0\n"
"1: adcl 16(%1), %0\n"
" lea 4(%1), %1\n"
" decl %2\n"
" jne 1b\n"
" adcl $0, %0\n"
" movl %0, %2\n"
" shrl $16, %0\n"
" addw %w2, %w0\n"
" adcl $0, %0\n"
" notl %0\n"
"2:"
/* Since the input registers which are loaded with iph and ihl
are modified, we must also specify them as outputs, or gcc
will assume they contain their original values. */
: "=r" (sum), "=r" (iph), "=r" (ihl)
: "1" (iph), "2" (ihl)
: "memory");
return (u16) sum;
}
#else
__inline u16 checksum_ip(const void *iph, unsigned ihl)
{
return checksum_net(iph, 4 * ihl);
}
#endif
__inline u16 checksum_net(const void *p, unsigned len)
{
unsigned sum = 0;
u16 *ip = (u16 *) p;
while(len > 1){
sum += *ip++;
len -= 2;
}
while(sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
#if __BYTE_ORDER == __LITTLE_ENDIAN
return (u16) (~sum);
#else
return bswap16((u16) (~sum));
#endif
}
|