函数名:process_tcp(data, skblen)
代码很长,如下:
void process_tcp(u_char * data, int skblen)//传入数据与其长度 { struct ip *this_iphdr = (struct ip *)data;//ip与tcp结构体见后面说明 struct tcphdr *this_tcphdr = (struct tcphdr *)(data + 4 * this_iphdr->ip_hl); //计算ip部分偏移指到TCP头部 int datalen, iplen;//数据部分长度,以及ip长度 int from_client = 1; unsigned int tmp_ts;//时间戳 struct tcp_stream *a_tcp;//一个TCP流的全部信息 struct half_stream *snd, *rcv; //一个方向上的TCP流,TCP分为两个方向上的,一个是客户到服务端,一个是服务端到客户 ugly_iphdr = this_iphdr; iplen = ntohs(this_iphdr->ip_len); if ((unsigned)iplen < 4 * this_iphdr->ip_hl + sizeof(struct tcphdr)) { nids_params.syslog(NIDS_WARN_TCP, NIDS_WARN_TCP_HDR, this_iphdr, this_tcphdr);//指示的长度与实际的不相符,指出错误 return; } // ktos sie bawi datalen = iplen - 4 * this_iphdr->ip_hl - 4 * this_tcphdr->th_off; //tcp数据部分长度,去掉了TCP的头部 //ip_hl表示ip头部长度,th_off表示tcp头部长度,datalen表示tcp数据部分长度 if (datalen < 0) { nids_params.syslog(NIDS_WARN_TCP, NIDS_WARN_TCP_HDR, this_iphdr, this_tcphdr); return; } // ktos sie bawi,数据部分小于0,发生错误,返回 if ((this_iphdr->ip_src.s_addr | this_iphdr->ip_dst.s_addr) == 0) { nids_params.syslog(NIDS_WARN_TCP, NIDS_WARN_TCP_HDR, this_iphdr, this_tcphdr); return; } if (!(this_tcphdr->th_flags & TH_ACK))//确认信息有效 detect_scan(this_iphdr);//如果是TCP中的ACK信息,检测是否出现攻击 if (!nids_params.n_tcp_streams) return; if (my_tcp_check(this_tcphdr, iplen - 4 * this_iphdr->ip_hl, this_iphdr->ip_src.s_addr, this_iphdr->ip_dst.s_addr)) { nids_params.syslog(NIDS_WARN_TCP, NIDS_WARN_TCP_HDR, this_iphdr, this_tcphdr); return; }//检测数据包的有效性 #if 0 check_flags(this_iphdr, this_tcphdr); //ECN #endif
//经过以上处,初步判断tcp包正常,进行入队操作,插入队列前,先进行此包的状态判断,判断此数据包处于何种状态 if (!(a_tcp = find_stream(this_tcphdr, this_iphdr, &from_client))) { /*是三次握手的第一个包*/ /*tcp里流不存在时:且tcp数据包里的(syn=1 && ack==0 && rst==0)时,添加一条tcp流*/ /*tcp第一次握手*/ if ((this_tcphdr->th_flags & TH_SYN) && !(this_tcphdr->th_flags & TH_ACK) && !(this_tcphdr->th_flags & TH_RST)) add_new_tcp(this_tcphdr, this_iphdr);//发现新的TCP流,进行添加。 /*第一次握手完毕返回*/
//在此处添加TCP流,但此处有隐患,对数据进行保存操作后,有可能数据没释放,实际应用中碰到中 return; } if (from_client) { snd = &a_tcp->client; rcv = &a_tcp->server; } else { rcv = &a_tcp->client; snd = &a_tcp->server; } /********************************************************************** 三次握手的第二次握手 ************************************************************************/ /*tcp 三次握手, SYN ==1,ACK==1,tcp第二次握手(server -> client的同步响应)*/ //来了一个SYN包 if ((this_tcphdr->th_flags & TH_SYN)) { //syn包是用来建立新连接的,所以,要么来自客户端且没标志(前面处理了),要么来自服务端且加ACK标志 //所以这里只能来自服务器,检查服务器状态是否正常,不正常的话果断忽略这个包 if (from_client || a_tcp->client.state != TCP_SYN_SENT || a_tcp->server.state != TCP_CLOSE || !(this_tcphdr->th_flags & TH_ACK)) return; /*第二次回应包的ACK 值为第一个包的序列号+1,在初始化的时候已经加一*/ //忽略流水号错误的包 if (a_tcp->client.seq != ntohl(this_tcphdr->th_ack)) return; /*第二个包服务端赋值*/ /*a_tcp 中服务端赋值,*/ //tcp流中有2个方向上的数据,此事可以给一个方向上的一些数据赋值 a_tcp->server.state = TCP_SYN_RECV; a_tcp->server.seq = ntohl(this_tcphdr->th_seq) + 1; a_tcp->server.first_data_seq = a_tcp->server.seq; a_tcp->server.ack_seq = ntohl(this_tcphdr->th_ack); a_tcp->server.window = ntohs(this_tcphdr->th_win); /*对于tcp 选项的赋值*/ //初始化客户端和服务器的时间截 if (a_tcp->client.ts_on) { a_tcp->server.ts_on = get_ts(this_tcphdr, &a_tcp->server.curr_ts); if (!a_tcp->server.ts_on) a_tcp->client.ts_on = 0; } else a_tcp->server.ts_on = 0;//初始化窗口大小 if (a_tcp->client.wscale_on) { a_tcp->server.wscale_on = get_wscale(this_tcphdr, &a_tcp->server.wscale); if (!a_tcp->server.wscale_on) { a_tcp->client.wscale_on = 0; a_tcp->client.wscale = 1; a_tcp->server.wscale = 1; } } else { a_tcp->server.wscale_on = 0; a_tcp->server.wscale = 1; } /*第二次握手完毕,返回*/ return; } /* (如果有数据存在或者序列号不等于确认号的)并且 序列号在窗口之外 已经确认过的序号 */ if ( ! ( !datalen && ntohl(this_tcphdr->th_seq) == rcv->ack_seq ) && ( !before(ntohl(this_tcphdr->th_seq), rcv->ack_seq + rcv->window*rcv->wscale) || before(ntohl(this_tcphdr->th_seq) + datalen, rcv->ack_seq) ) ) return;/*发送th_rst 重新开启一个连接*/ //如果是rst包,ok,关闭连接 //将现有数据推给注册的回调方,然后销毁这个会话。 if ((this_tcphdr->th_flags & TH_RST)) { /*是tcp 数据*/ if (a_tcp->nids_state == NIDS_DATA) { struct lurker_node *i; a_tcp->nids_state = NIDS_RESET; //下面回调所有的钩子 for (i = a_tcp->listeners; i; i = i->next) (i->item) (a_tcp, &i->data); } free_tcp(a_tcp); return; } /* PAWS check */ /* PAWS(防止重复报文)check 检查时间戳*/ if (rcv->ts_on && get_ts(this_tcphdr, &tmp_ts) && before(tmp_ts, snd->curr_ts)) return; /********************************************************************** 第三次握手包 ********************************************************************** */ /* 从client --> server的包 是从三次握手的第三个包分析开始的,进行一部分数据分析,和初始化 连接状态 */ if ((this_tcphdr->th_flags & TH_ACK)) { //如果是从客户端来的,且两边都在第二次握手的状态上 if (from_client && a_tcp->client.state == TCP_SYN_SENT &&a_tcp->server.state == TCP_SYN_RECV) {//在此情况下,流水号又对得上,好的,这个包是第三次握手包,连接建立成功 if (ntohl(this_tcphdr->th_ack) == a_tcp->server.seq) {a_tcp->client.state = TCP_ESTABLISHED; //更新客户端状态 a_tcp->client.ack_seq = ntohl(this_tcphdr->th_ack); //更新ack序号 { struct proc_node *i; struct lurker_node *j; void *data; a_tcp->server.state = TCP_ESTABLISHED; a_tcp->nids_state = NIDS_JUST_EST; /*开始全双工传输,client server 连接已经建立起来了*/ /*三次握手tcp ip 连接建立*/ for (i = tcp_procs; i; i = i->next) { //此处根据调用者的设定来判断哪些数据需要在回调时返回
char whatto = 0; char cc = a_tcp->client.collect; char sc = a_tcp->server.collect; char ccu = a_tcp->client.collect_urg; char scu = a_tcp->server.collect_urg; /*进入回调函数处理*/ /* 如果在相应端口出现 client.collect ++ ; 测审计次数据 对应用来说tcp 连接已经建立 */ (i->item) (a_tcp, &data); if (cc < a_tcp->client.collect) whatto |= COLLECT_cc; if (ccu < a_tcp->client.collect_urg) whatto |= COLLECT_ccu; if (sc < a_tcp->server.collect) whatto |= COLLECT_sc; if (scu < a_tcp->server.collect_urg) whatto |= COLLECT_scu; if (nids_params.one_loop_less) { if (a_tcp->client.collect >=2) { a_tcp->client.collect=cc; whatto&=~COLLECT_cc; } if (a_tcp->server.collect >=2 ) { a_tcp->server.collect=sc; whatto&=~COLLECT_sc; } } /*加入监听队列,开始数据接收*/ if (whatto) { j = mknew(struct lurker_node); j->item = i->item; j->data = data; j->whatto = whatto; j->next = a_tcp->listeners; a_tcp->listeners = j; } } if (!a_tcp->listeners) {/*不存在监听着*/ free_tcp(a_tcp); return; } a_tcp->nids_state = NIDS_DATA; } } // return; } } /* ************************************************************ 挥手过程 ************************************************************* */ /*数据结束的包的判断*/ if ((this_tcphdr->th_flags & TH_ACK)) { /* 从数据传输过程不断更新服务器客户端的ack_seq 一直到接收到fin 包,数据传输结束 */ //先调用handle_ack更新ack序号 handle_ack(snd, ntohl(this_tcphdr->th_ack)); //更新状态,回调告知连接关闭,然后释放连接 if (rcv->state == FIN_SENT) rcv->state = FIN_CONFIRMED; if (rcv->state == FIN_CONFIRMED && snd->state == FIN_CONFIRMED { struct lurker_node *i; a_tcp->nids_state = NIDS_CLOSE; for (i = a_tcp->listeners; i; i = i->next) (i->item) (a_tcp, &i->data); free_tcp(a_tcp); return; } } /* ************************************************************* 数据处理过程 ************************************************************* */ if (datalen + (this_tcphdr->th_flags & TH_FIN) > 0) /* a_tcp -----a_tcp 客户端连接包 this_tcphdr tcp 包头 snd-----发送包 rcv -----接收包 (char *) (this_tcphdr) + 4 * this_tcphdr->th_off -----数据包内容 datalen---------数据包长度 */ //就将数据更新到接收方缓冲区 tcp_queue(a_tcp, this_tcphdr, snd, rcv, (char *) (this_tcphdr) + 4 * this_tcphdr->th_off, datalen, skblen); //更新窗口大小 snd->window = ntohs(this_tcphdr->th_win); //如果缓存溢出(说明出了问题),果断释放连接 if (rcv->rmem_alloc > 65535) prune_queue(rcv, this_tcphdr); if (!a_tcp->listeners) free_tcp(a_tcp);}
相关结构体说明
struct timestamp { u_int8_t len; u_int8_t ptr; #if __BYTE_ORDER == __LITTLE_ENDIAN unsigned int flags:4; unsigned int overflow:4; #elif __BYTE_ORDER == __BIG_ENDIAN unsigned int overflow:4; unsigned int flags:4; #else # error "Please fix <bits/endian.h>" #endif u_int32_t data[9]; }; struct iphdr { #if __BYTE_ORDER == __LITTLE_ENDIAN unsigned int ihl:4; unsigned int version:4; #elif __BYTE_ORDER == __BIG_ENDIAN unsigned int version:4; unsigned int ihl:4; #else # error "Please fix <bits/endian.h>" #endif u_int8_t tos; u_int16_t tot_len; u_int16_t id; u_int16_t frag_off; u_int8_t ttl; u_int8_t protocol; u_int16_t check; u_int32_t saddr; u_int32_t daddr; /*The options start here. */ };tcp结构struct tcphdr { __be16 source; __be16 dest; __be32 seq; __be32 ack_seq; #if defined(__LITTLE_ENDIAN_BITFIELD) __u16 res1:4, doff:4, fin:1, syn:1, rst:1, psh:1, ack:1, urg:1, ece:1, cwr:1; #elif defined(__BIG_ENDIAN_BITFIELD) __u16 doff:4, res1:4, cwr:1, ece:1, urg:1, ack:1, psh:1, rst:1, syn:1, fin:1; #else #error "Adjust your <asm/byteorder.h> defines" #endif __be16 window; __be16 check; __be16 urg_ptr; };