libnids分析(5)——TCP重组

无限循环的抓包函数的回调函数调用了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;
};

















   

你可能感兴趣的:(tcp,Stream,struct,Security,byte)