www.pudn.com > src.rar > checksum.c


#ifdef WIN32 
	#include "nids.h" 
#endif 
#include  
#include  
#include  
#include  
 
#if ( __i386__ || __i386 ) 
// all asm procedures are copied from Linux 2.0.36 and 2.2.10 kernels 
 
/* 
  computes the checksum of a memory block at buff, length len, and 
  adds in "sum" (32-bit) 
  
  returns a 32-bit number suitable for feeding into itself or 
  csum_tcpudp_magic 
  
  this function must be called with even lengths, except for the last 
  fragment, which may be odd 
  
  it's best to have buff aligned on a 32-bit boundary 
*/ 
u_int 
csum_partial(const u_char * buff, int len, u_int sum) 
{ 
  __asm__ ( 
/*		"pushl %esi 
	pushl %ebx 
	movl 20(%esp),%eax	# Function arg: u_int sum 
	movl 16(%esp),%ecx	# Function arg: int len 
        movl 12(%esp),%esi	# Function arg: u_char *buff*/ 
 
       "testl $2, %%esi		 
	jz 2f			 
	subl $2, %%ecx		 
	jae 1f			 
	addl $2, %%ecx		 
	jmp 4f 
1:	movw (%%esi), %%bx 
	addl $2, %%esi 
	addw %%bx, %%ax 
	adcl $0, %%eax 
2: 
	movl %%ecx, %%edx 
	shrl $5, %%ecx 
	jz 2f 
	testl %%esi, %%esi 
1:	movl (%%esi), %%ebx 
	adcl %%ebx, %%eax 
	movl 4(%%esi), %%ebx 
	adcl %%ebx, %%eax 
	movl 8(%%esi), %%ebx 
	adcl %%ebx, %%eax 
	movl 12(%%esi), %%ebx 
	adcl %%ebx, %%eax 
	movl 16(%%esi), %%ebx 
	adcl %%ebx, %%eax 
	movl 20(%%esi), %%ebx 
	adcl %%ebx, %%eax 
	movl 24(%%esi), %%ebx 
	adcl %%ebx, %%eax 
	movl 28(%%esi), %%ebx 
	adcl %%ebx, %%eax 
	lea 32(%%esi), %%esi 
	dec %%ecx 
	jne 1b 
	adcl $0, %%eax 
2:	movl %%edx, %%ecx 
	andl $0x1c, %%edx 
	je 4f 
	shrl $2, %%edx		 
3:	adcl (%%esi), %%eax 
	lea 4(%%esi), %%esi 
	dec %%edx 
	jne 3b 
	adcl $0, %%eax 
4:	andl $3, %%ecx 
	jz 7f 
	cmpl $2, %%ecx 
	jb 5f 
	movw (%%esi),%%cx 
	leal 2(%%esi),%%esi 
	je 6f 
	shll $16,%%ecx 
5:	movb (%%esi),%%cl 
6:	addl %%ecx,%%eax 
	adcl $0, %%eax 
7: " 
       : "=a"(sum) 
       : "0"(sum), "c"(len), "S"(buff) 
       : "bx", "dx"); 
 
  return (sum); 
} 
 
/* 
  This is a version of ip_compute_csum() optimized for IP headers, 
  which always checksum on 4 octet boundaries. 
  
  By Jorge Cwik , adapted for linux by Arnt 
  Gulbrandsen. 
*/ 
inline u_short ip_fast_csum(u_char * iph, u_int ihl) 
{ 
  u_int sum; 
   
  __asm__ __volatile__(" 
	    movl (%1), %0 
	    subl $4, %2 
	    jbe 2f 
	    addl 4(%1), %0 
	    adcl 8(%1), %0 
	    adcl 12(%1), %0 
1:	    adcl 16(%1), %0 
	    lea 4(%1), %1 
	    decl %2 
	    jne	1b 
	    adcl $0, %0 
	    movl %0, %2 
	    shrl $16, %0 
	    addw %w2, %w0 
	    adcl $0, %0 
	    notl %0 
2: 
	    " 
	/* 
	  Since the input registers which are loaded with iph and ipl 
	  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)); 
   
  return (sum); 
} 
 
/* Fold a partial checksum. */ 
static inline u_int 
csum_fold(u_int sum) 
{ 
  __asm__(" 
	addl %1, %0 
	adcl $0xffff, %0 
	" 
	: "=r" (sum) 
	: "r" (sum << 16), "0" (sum & 0xffff0000) 
	  ); 
  return ((~sum) >> 16); 
} 
  
/* 
  computes the checksum of the TCP/UDP pseudo-header 
  returns a 16-bit checksum, already complemented 
*/ 
static inline u_short 
csum_tcpudp_magic(u_int saddr, u_int daddr, u_short len, 
		  u_short proto, u_int sum) 
{ 
  __asm__(" 
	addl %1, %0 
	adcl %2, %0 
	adcl %3, %0 
	adcl $0, %0 
	" 
	: "=r" (sum) 
	: "g" (daddr), "g"(saddr), "g"((ntohs(len) << 16) + proto * 256), "0"(sum)); 
  return (csum_fold(sum)); 
} 
 
/* 
  this routine is used for miscellaneous IP-like checksums, mainly in 
  icmp.c 
*/ 
inline u_short 
ip_compute_csum(u_char * buff, int len) 
{ 
  return (csum_fold(csum_partial(buff, len, 0))); 
} 
 
inline u_short 
my_tcp_check(struct tcphdr *th, int len, u_int saddr, u_int daddr) 
{ 
  return csum_tcpudp_magic(saddr, daddr, len, IPPROTO_TCP, 
			   csum_partial((char *)th, len, 0)); 
} 
inline u_short 
my_udp_check(void *u, int len, u_int saddr, u_int daddr) 
{ 
  return csum_tcpudp_magic(saddr, daddr, len, IPPROTO_UDP, 
			   csum_partial((char *)u, len, 0)); 
} 
 
#else /* !i386 */ 
 
struct psuedo_hdr 
{ 
  u_int saddr;       
  u_int daddr;       
  u_char zero;         
  u_char protocol;     
  u_short len;         
}; 
 
u_short 
ip_check_ext(register u_short *addr, register int len, int addon) 
{ 
  register int nleft = len; 
  register u_short *w = addr; 
  register int sum = addon; 
  u_short answer = 0; 
 
  /* 
   *  Our algorithm is simple, using a 32 bit accumulator (sum), 
   *  we add sequential 16 bit words to it, and at the end, fold 
   *  back all the carry bits from the top 16 bits into the lower 
   *  16 bits. 
   */ 
  while (nleft > 1)  { 
    sum += *w++; 
    nleft -= 2; 
  } 
  /* mop up an odd byte, if necessary */ 
  if (nleft == 1) { 
    *(u_char *)(&answer) = *(u_char *)w; 
    sum += answer; 
  }   
  /* add back carry outs from top 16 bits to low 16 bits */ 
  sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */ 
  sum += (sum >> 16);                     /* add carry */ 
  answer = ~sum;                          /* truncate to 16 bits */ 
  return (answer); 
} 
 
u_short 
ip_fast_csum(u_short *addr, int len) 
{ 
  return ip_check_ext(addr, len << 2, 0); 
} 
 
u_short 
ip_compute_csum(u_short *addr, int len) 
{ 
  return ip_check_ext(addr, len, 0); 
} 
 
u_short 
my_tcp_check(struct tcphdr *th, int len, u_int saddr, u_int daddr) 
{ 
  int i, sum = 0; 
  struct psuedo_hdr hdr; 
   
  hdr.saddr = saddr; 
  hdr.daddr = daddr; 
  hdr.zero = 0; 
  hdr.protocol = IPPROTO_TCP; 
  hdr.len = htons((unsigned short)len); 
  for (i = 0; i < sizeof(hdr); i += 2) 
    sum += *(u_short *)((char *)(&hdr) + i); 
   
  return (ip_check_ext((u_short *)th, len, sum)); 
}                      
u_short 
my_udp_check(void *u, int len, u_int saddr, u_int daddr) 
{ 
  int i, sum = 0; 
  struct psuedo_hdr hdr; 
   
  hdr.saddr = saddr; 
  hdr.daddr = daddr; 
  hdr.zero = 0; 
  hdr.protocol = IPPROTO_UDP; 
  hdr.len = htons((unsigned short)len); 
  for (i = 0; i < sizeof(hdr); i += 2) 
    sum += *(u_short *)((char *)(&hdr) + i); 
   
  return (ip_check_ext((u_short *)u, len, sum)); 
}                      
 
#endif /* !i386 */