重點:這篇為轉載,作者在這塊分析的很好,雖然現在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; };