重点:这篇为转载,作者在这块分析的很好,虽然现在libnids已经更新到了1.24,但函数的大体流程还是未变,正文
1 void process_tcp(u_char * data, int skblen)//传入数据与其长度 2 { 3 struct ip *this_iphdr = (struct ip *)data;//ip与tcp结构体见后面说明 4 struct tcphdr *this_tcphdr = (struct tcphdr *)(data + 4 * this_iphdr->ip_hl); 5 //计算ip部分偏移指到TCP头部 6 int datalen, iplen;//数据部分长度,以及ip长度 7 int from_client = 1; 8 unsigned int tmp_ts;//时间戳 9 struct tcp_stream *a_tcp;//一个TCP流的全部信息 10 struct half_stream *snd, *rcv; 11 //一个方向上的TCP流,TCP分为两个方向上的,一个是客户到服务端,一个是服务端到客户 12 13 ugly_iphdr = this_iphdr; 14 iplen = ntohs(this_iphdr->ip_len); 15 if ((unsigned)iplen < 4 * this_iphdr->ip_hl + sizeof(struct tcphdr)) { 16 nids_params.syslog(NIDS_WARN_TCP, NIDS_WARN_TCP_HDR, this_iphdr, 17 this_tcphdr);//指示的长度与实际的不相符,指出错误 18 return; 19 } // ktos sie bawi 20 21 datalen = iplen - 4 * this_iphdr->ip_hl - 4 * this_tcphdr->th_off; 22 //tcp数据部分长度,去掉了TCP的头部 23 //ip_hl表示ip头部长度,th_off表示tcp头部长度,datalen表示tcp数据部分长度 24 if (datalen < 0) { 25 nids_params.syslog(NIDS_WARN_TCP, NIDS_WARN_TCP_HDR, this_iphdr, 26 this_tcphdr); 27 return; 28 } // ktos sie bawi,数据部分小于0,发生错误,返回 29 30 if ((this_iphdr->ip_src.s_addr | this_iphdr->ip_dst.s_addr) == 0) { 31 nids_params.syslog(NIDS_WARN_TCP, NIDS_WARN_TCP_HDR, this_iphdr, 32 this_tcphdr); 33 return; 34 } 35 if (!(this_tcphdr->th_flags & TH_ACK))//确认信息有效 36 detect_scan(this_iphdr);//如果是TCP中的ACK信息,检测是否出现攻击 37 if (!nids_params.n_tcp_streams) return; 38 if (my_tcp_check(this_tcphdr, iplen - 4 * this_iphdr->ip_hl, 39 this_iphdr->ip_src.s_addr, this_iphdr->ip_dst.s_addr)) { 40 nids_params.syslog(NIDS_WARN_TCP, NIDS_WARN_TCP_HDR, this_iphdr, 41 this_tcphdr); 42 return; 43 }//检测数据包的有效性 44 #if 0 45 check_flags(this_iphdr, this_tcphdr); 46 //ECN 47 #endif
1 //经过以上处,初步判断tcp包正常,进行入队操作,插入队列前,先进行此包的状态判断,判断此数据包处于何种状态 2 if (!(a_tcp = find_stream(this_tcphdr, this_iphdr, &from_client))) { 3 /*是三次握手的第一个包*/ 4 /*tcp里流不存在时:且tcp数据包里的(syn=1 && ack==0 && rst==0)时,添加一条tcp流*/ 5 /*tcp第一次握手*/ 6 if ((this_tcphdr->th_flags & TH_SYN) && 7 !(this_tcphdr->th_flags & TH_ACK) && 8 !(this_tcphdr->th_flags & TH_RST)) 9 add_new_tcp(this_tcphdr, this_iphdr);//发现新的TCP流,进行添加。 10 /*第一次握手完毕返回*/
1 //在此处添加TCP流,但此处有隐患,对数据进行保存操作后,有可能数据没释放,实际应用中碰到中 2 return; 3 } 4 if (from_client) { 5 snd = &a_tcp->client; 6 rcv = &a_tcp->server; 7 } 8 else { 9 rcv = &a_tcp->client; 10 snd = &a_tcp->server; 11 } 12 /********************************************************************** 13 14 三次握手的第二次握手 15 16 ************************************************************************/ 17 18 /*tcp 三次握手, SYN ==1,ACK==1,tcp第二次握手(server -> client的同步响应)*/ 19 20 //来了一个SYN包 21 if ((this_tcphdr->th_flags & TH_SYN)) { 22 //syn包是用来建立新连接的,所以,要么来自客户端且没标志(前面处理了),要么来自服务端且加ACK标志 23 //所以这里只能来自服务器,检查服务器状态是否正常,不正常的话果断忽略这个包 24 if (from_client || a_tcp->client.state != TCP_SYN_SENT || 25 a_tcp->server.state != TCP_CLOSE || !(this_tcphdr->th_flags & TH_ACK)) 26 return; 27 /*第二次回应包的ACK 值为第一个包的序列号+1,在初始化的时候已经加一*/ 28 //忽略流水号错误的包 29 if (a_tcp->client.seq != ntohl(this_tcphdr->th_ack)) 30 return; 31 32 /*第二个包服务端赋值*/ 33 /*a_tcp 中服务端赋值,*/ 34 //tcp流中有2个方向上的数据,此事可以给一个方向上的一些数据赋值 35 a_tcp->server.state = TCP_SYN_RECV; 36 a_tcp->server.seq = ntohl(this_tcphdr->th_seq) + 1; 37 a_tcp->server.first_data_seq = a_tcp->server.seq; 38 a_tcp->server.ack_seq = ntohl(this_tcphdr->th_ack); 39 a_tcp->server.window = ntohs(this_tcphdr->th_win); 40 41 /*对于tcp 选项的赋值*/ 42 //初始化客户端和服务器的时间截 43 44 45 if (a_tcp->client.ts_on) 46 { 47 a_tcp->server.ts_on = get_ts(this_tcphdr, &a_tcp->server.curr_ts); 48 if (!a_tcp->server.ts_on) 49 a_tcp->client.ts_on = 0; 50 } else 51 a_tcp->server.ts_on = 0;//初始化窗口大小 52 if (a_tcp->client.wscale_on) 53 { 54 a_tcp->server.wscale_on = get_wscale(this_tcphdr, &a_tcp->server.wscale); 55 if (!a_tcp->server.wscale_on) 56 { 57 a_tcp->client.wscale_on = 0; 58 a_tcp->client.wscale = 1; 59 a_tcp->server.wscale = 1; 60 } 61 } 62 else 63 { 64 a_tcp->server.wscale_on = 0; 65 a_tcp->server.wscale = 1; 66 } 67 /*第二次握手完毕,返回*/ return; } 68 /* 69 (如果有数据存在或者序列号不等于确认号的)并且 70 71 序列号在窗口之外 72 已经确认过的序号 73 74 */ 75 if ( ! ( !datalen && ntohl(this_tcphdr->th_seq) == rcv->ack_seq ) && 76 ( !before(ntohl(this_tcphdr->th_seq), rcv->ack_seq + rcv->window*rcv->wscale) || 77 before(ntohl(this_tcphdr->th_seq) + datalen, rcv->ack_seq) ) ) 78 return;/*发送th_rst 重新开启一个连接*/ 79 //如果是rst包,ok,关闭连接 80 //将现有数据推给注册的回调方,然后销毁这个会话。 if ((this_tcphdr->th_flags & TH_RST)) { 81 /*是tcp 数据*/ 82 if (a_tcp->nids_state == NIDS_DATA) { struct lurker_node *i; a_tcp->nids_state = NIDS_RESET; 83 //下面回调所有的钩子 84 for (i = a_tcp->listeners; i; i = i->next) 85 (i->item) (a_tcp, &i->data); 86 } 87 free_tcp(a_tcp); 88 return; 89 } 90 /* PAWS check */ 91 /* PAWS(防止重复报文)check 检查时间戳*/ 92 if (rcv->ts_on && get_ts(this_tcphdr, &tmp_ts) && before(tmp_ts, snd->curr_ts)) 93 return; 94 /********************************************************************** 95 第三次握手包 96 97 ********************************************************************** 98 */ 99 100 /* 101 102 103 从client --> server的包 104 105 是从三次握手的第三个包分析开始的,进行一部分数据分析,和初始化 106 连接状态 107 108 */ 109 if ((this_tcphdr->th_flags & TH_ACK)) { //如果是从客户端来的,且两边都在第二次握手的状态上 110 if (from_client && a_tcp->client.state == TCP_SYN_SENT &&a_tcp->server.state == TCP_SYN_RECV) 111 {//在此情况下,流水号又对得上,好的,这个包是第三次握手包,连接建立成功 112 if (ntohl(this_tcphdr->th_ack) == a_tcp->server.seq) 113 {a_tcp->client.state = TCP_ESTABLISHED; 114 //更新客户端状态 115 a_tcp->client.ack_seq = ntohl(this_tcphdr->th_ack); 116 //更新ack序号 117 { 118 struct proc_node *i; 119 struct lurker_node *j; 120 void *data; 121 a_tcp->server.state = TCP_ESTABLISHED; 122 a_tcp->nids_state = NIDS_JUST_EST; 123 /*开始全双工传输,client server 连接已经建立起来了*/ 124 125 /*三次握手tcp ip 连接建立*/ 126 for (i = tcp_procs; i; i = i->next) 127 { //此处根据调用者的设定来判断哪些数据需要在回调时返回
1 char whatto = 0; 2 char cc = a_tcp->client.collect; 3 char sc = a_tcp->server.collect; 4 char ccu = a_tcp->client.collect_urg; 5 char scu = a_tcp->server.collect_urg; /*进入回调函数处理*/ 6 7 /* 8 9 如果在相应端口出现 10 11 client.collect ++ ; 12 13 测审计次数据 14 对应用来说tcp 连接已经建立 15 16 */ 17 (i->item) (a_tcp, &data); 18 if (cc < a_tcp->client.collect) 19 whatto |= COLLECT_cc; 20 if (ccu < a_tcp->client.collect_urg) 21 whatto |= COLLECT_ccu; 22 if (sc < a_tcp->server.collect) 23 whatto |= COLLECT_sc; 24 if (scu < a_tcp->server.collect_urg) 25 whatto |= COLLECT_scu; 26 if (nids_params.one_loop_less) 27 { if (a_tcp->client.collect >=2) 28 { 29 a_tcp->client.collect=cc; 30 whatto&=~COLLECT_cc; 31 } 32 if (a_tcp->server.collect >=2 ) 33 { 34 a_tcp->server.collect=sc; 35 whatto&=~COLLECT_sc; 36 } 37 } 38 /*加入监听队列,开始数据接收*/ 39 if (whatto) 40 { 41 j = mknew(struct lurker_node); 42 j->item = i->item; 43 j->data = data; 44 j->whatto = whatto; 45 j->next = a_tcp->listeners; 46 a_tcp->listeners = j; 47 } 48 } 49 if (!a_tcp->listeners) 50 {/*不存在监听着*/ 51 free_tcp(a_tcp); 52 return; 53 } 54 a_tcp->nids_state = NIDS_DATA; 55 } 56 } 57 // 58 return; 59 } 60 } 61 /* 62 ************************************************************ 63 64 挥手过程 65 66 ************************************************************* 67 68 */ 69 70 /*数据结束的包的判断*/ 71 if ((this_tcphdr->th_flags & TH_ACK)) { 72 /* 从数据传输过程不断更新服务器客户端的ack_seq 73 一直到接收到fin 包,数据传输结束 74 75 */ 76 //先调用handle_ack更新ack序号 77 handle_ack(snd, ntohl(this_tcphdr->th_ack)); 78 //更新状态,回调告知连接关闭,然后释放连接 79 if (rcv->state == FIN_SENT) 80 rcv->state = FIN_CONFIRMED; 81 if (rcv->state == FIN_CONFIRMED && snd->state == FIN_CONFIRMED 82 { 83 struct lurker_node *i; 84 a_tcp->nids_state = NIDS_CLOSE; 85 for (i = a_tcp->listeners; i; i = i->next) 86 (i->item) (a_tcp, &i->data); 87 free_tcp(a_tcp); 88 return; 89 } 90 } 91 /* 92 93 ************************************************************* 94 数据处理过程 95 ************************************************************* 96 97 */ 98 99 if (datalen + (this_tcphdr->th_flags & TH_FIN) > 0) /* 100 101 a_tcp -----a_tcp 客户端连接包 102 this_tcphdr tcp 包头 103 snd-----发送包 104 rcv -----接收包 105 106 (char *) (this_tcphdr) + 4 * this_tcphdr->th_off -----数据包内容 107 datalen---------数据包长度 108 109 110 */ 111 //就将数据更新到接收方缓冲区 112 tcp_queue(a_tcp, this_tcphdr, snd, rcv, (char *) (this_tcphdr) + 4 * this_tcphdr->th_off, datalen, skblen); 113 //更新窗口大小 114 snd->window = ntohs(this_tcphdr->th_win); 115 //如果缓存溢出(说明出了问题),果断释放连接 116 if (rcv->rmem_alloc > 65535) prune_queue(rcv, this_tcphdr); if (!a_tcp->listeners) free_tcp(a_tcp);}
附上tcphdr结构体的定义
struct tcphdr { guint16 source; guint16 dest; guint32 seq; guint32 ack_seq; guint16 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 guint16 window; guint16 check; guint16 urg_ptr; };