libnids分析(5)——TCP重组

来源:互联网 发布:mac os x10.12懒人版 编辑:程序博客网 时间:2024/05/17 00:06

无限循环的抓包函数的回调函数调用了gen_ip_proc()函数,该函数通过判断类型进入到对应的重组阶段。下面开始分析TCP重组部分。

函数名: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;};