TCP重组process_tcp

nids_run调用了pcap_loop,无止境地循环抓包,抓到的包传给回调函数nids_pcap_handle来处理,而回调函数最终都会调用gen_ip_proc来根据不同类型协议的包进行处理重组,process_tcp,process_udp等等

首先是IP和TCP的结构体,可能不同的linux操作系统定义不同,下面头文件CentOS6.4里的定义

IP结构体:/usr/include/netinet/ip.h

107 struct ip
108   {
109 #if __BYTE_ORDER == __LITTLE_ENDIAN
110     unsigned int ip_hl:4;               /* header length */
111     unsigned int ip_v:4;                /* version */
112 #endif
113 #if __BYTE_ORDER == __BIG_ENDIAN
114     unsigned int ip_v:4;                /* version */
115     unsigned int ip_hl:4;               /* header length */
116 #endif
117     u_int8_t ip_tos;                    /* type of service */
118     u_short ip_len;                     /* total length */
119     u_short ip_id;                      /* identification */
120     u_short ip_off;                     /* fragment offset field */
121 #define IP_RF 0x8000                    /* reserved fragment flag */
122 #define IP_DF 0x4000                    /* dont fragment flag */
123 #define IP_MF 0x2000                    /* more fragments flag */
124 #define IP_OFFMASK 0x1fff               /* mask for fragmenting bits */
125     u_int8_t ip_ttl;                    /* time to live */
126     u_int8_t ip_p;                      /* protocol */
127     u_short ip_sum;                     /* checksum */
128     struct in_addr ip_src, ip_dst;      /* source and dest address */
129   };

TCP结构体:/usr/include/netinet/tcp.h

struct tcphdr
  {
    u_int16_t th_sport;         /* source port */
    u_int16_t th_dport;         /* destination port */
    tcp_seq th_seq;             /* sequence number */
    tcp_seq th_ack;             /* acknowledgement number */
#  if __BYTE_ORDER == __LITTLE_ENDIAN
    u_int8_t th_x2:4;           /* (unused) */
    u_int8_t th_off:4;          /* data offset */
#  endif
#  if __BYTE_ORDER == __BIG_ENDIAN
    u_int8_t th_off:4;          /* data offset */
    u_int8_t th_x2:4;           /* (unused) */
#  endif
    u_int8_t th_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
    u_int16_t th_win;           /* window */
    u_int16_t th_sum;           /* checksum */
    u_int16_t th_urp;           /* urgent pointer */
};

下面就是process_tcp,在libnids里tcp.c里面:

void
process_tcp(u_char * data, int skblen)  //数据和长度
{
  struct ip *this_iphdr = (struct ip *)data;
  struct tcphdr *this_tcphdr = (struct tcphdr *)(data + 4 * this_iphdr->ip_hl);
  //通过IP部分偏移,指到了TCP头部的位置
  int datalen, iplen;   //TCP数据部分的长度,IP包的长度
  int from_client = 1;
  unsigned int tmp_ts;
  struct tcp_stream *a_tcp;     //TCP流全部信息
  struct half_stream *snd, *rcv;        //每个方向的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数据部分的长度:IP包长度减去IP头部长度减去TCP头部长度
  if (datalen < 0) {
    nids_params.syslog(NIDS_WARN_TCP, NIDS_WARN_TCP_HDR, this_iphdr,
                       this_tcphdr);
    return;
  } // ktos sie bawi

  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))
          //检测TCP中的ACk信息,是否是攻击
    detect_scan(this_iphdr);
        //下面都是检查数据包是否是没用的包
  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数据包正常,首先对数据包的状态进行判断
//是否是崭新的TCP连接,是否是三次握手的第一个包
  if (!(a_tcp = find_stream(this_tcphdr, this_iphdr, &from_client))) {
    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流不存在,而且是第一个SYN包,非ACK非RST,添加新的TCP流
    return;
  }//第一个握手包结束
  //判定TCP流的方向,发送端和接收端,而find stream里已经指定了方向
  if (from_client) {
    snd = &a_tcp->client;
    rcv = &a_tcp->server;
  }
  else {
    rcv = &a_tcp->client;
    snd = &a_tcp->server;
  }
  //此时经过了上面的if find_stream,可见已经找到了存在的TCP流
  if ((this_tcphdr->th_flags & TH_SYN)) {
          //又来一个SYN包,这里只能来自服务器,因为走到了这里,肯定不是建立
          //TCP连接的(前面已经处理),肯定来自服务器带ACK的,检查状态ing
    if (from_client || a_tcp->client.state != TCP_SYN_SENT ||
      a_tcp->server.state != TCP_CLOSE || !(this_tcphdr->th_flags & TH_ACK))
      return;
          //SEQ和ACK对不上的包,当作是错误的包
    if (a_tcp->client.seq != ntohl(this_tcphdr->th_ack))
      return;
  //第二个握手包,服务器端赋值
    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选项的赋值,初始化client和server的timestamp
    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;
        //第二个握手包结束,返回
  }

  //假如有存在SEQ和ACK不等,并且SEQ还在窗口之外或者已经确认过的序号
  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包,直接关闭连接,将数据还给注册的回调方,销毁连接
  if ((this_tcphdr->th_flags & TH_RST)) {
    if (a_tcp->nids_state == NIDS_DATA) {
                //如果是TCP数据
      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);
    }
    nids_free_tcp_stream(a_tcp);
    return;
  }

  /* PAWS check */
  //这里是检查重复包,检查timestamp
  if (rcv->ts_on && get_ts(this_tcphdr, &tmp_ts) &&
        before(tmp_ts, snd->curr_ts))
  return;

  //来一个ACK包
  if ((this_tcphdr->th_flags & TH_ACK)) {
          //从client来的包,而且两边的接收和发送状态统一
    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);
        //更新序号
        {
          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三次握手建立连接

          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;
            //进入回调函数来处理

            (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) {
                  //没有监听者
            nids_free_tcp_stream(a_tcp);
            return;
          }
          a_tcp->nids_state = NIDS_DATA;
        }
      }
      // return;
    }
  }
  //下面是四次结束过程
  //数据传输过程中不停更新client和server的seq和ack
  //直到接收到了FIN包,数据传输结束
  if ((this_tcphdr->th_flags & TH_ACK)) {
          //调用handle_ack更新ack序号
    handle_ack(snd, ntohl(this_tcphdr->th_ack));
    if (rcv->state == FIN_SENT)
                //来了FIN包,更新状态
      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);
      nids_free_tcp_stream(a_tcp);
      return;
    }
  }

  //数据处理部分
  if (datalen + (this_tcphdr->th_flags & TH_FIN) > 0)
          //将数据更新到接收方缓冲区
    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)
    nids_free_tcp_stream(a_tcp);
}

在此过程中,还有两个关键的调用查找TCP的find_stream和添加新的TCP的add_new_tcp两个函数

新版本的find_stream里,哈希操作放到了一个新的函数nids_find_tcp_stream中进行

 635 struct tcp_stream *
 636 find_stream(struct tcphdr * this_tcphdr, struct ip * this_iphdr,
 637             int *from_client)
 638 {
 639   struct tuple4 this_addr, reversed;
 640   struct tcp_stream *a_tcp;
 641
 642   this_addr.source = ntohs(this_tcphdr->th_sport);
 643   this_addr.dest = ntohs(this_tcphdr->th_dport);
 644   this_addr.saddr = this_iphdr->ip_src.s_addr;
 645   this_addr.daddr = this_iphdr->ip_dst.s_addr;
 646   a_tcp = nids_find_tcp_stream(&this_addr);
 647   if (a_tcp) {//如果找到,设为1,否则一直找到最后
 //看是否为同一个TCP流主要看其地址是否一样
 648     *from_client = 1;
 649     return a_tcp;
 650   }
 651   reversed.source = ntohs(this_tcphdr->th_dport);
 652   reversed.dest = ntohs(this_tcphdr->th_sport);
 653   reversed.saddr = this_iphdr->ip_dst.s_addr;
 654   reversed.daddr = this_iphdr->ip_src.s_addr;
 655   a_tcp = nids_find_tcp_stream(&reversed);
 656   if (a_tcp) {
 657     *from_client = 0;
 658     return a_tcp;
 659   }
 660   return 0;
 661 }

  663 struct tcp_stream *
 664 nids_find_tcp_stream(struct tuple4 *addr)
 665 {
 666   int hash_index;
 667   struct tcp_stream *a_tcp;
 668
 669   hash_index = mk_hash_index(*addr);       //找到传入数据的哈希地址
 670   for (a_tcp = tcp_stream_table[hash_index];
 671        a_tcp && memcmp(&a_tcp->addr, addr, sizeof (struct tuple4));
 672        a_tcp = a_tcp->next_node);
 //如果此地址开始就是空,表明没有与这个包对应的TCP流;如果地址不为空,则
 //看地址值是否一样
 673   return a_tcp ? a_tcp : 0;
 674 }

add_new_tcp还是没有变化

static void
add_new_tcp(struct tcphdr * this_tcphdr, struct ip * this_iphdr)
{
  struct tcp_stream *tolink;
  struct tcp_stream *a_tcp;
  int hash_index;
  struct tuple4 addr;

  addr.source = ntohs(this_tcphdr->th_sport);
  addr.dest = ntohs(this_tcphdr->th_dport);
  addr.saddr = this_iphdr->ip_src.s_addr;
  addr.daddr = this_iphdr->ip_dst.s_addr;
  hash_index = mk_hash_index(addr);             //计算新的TCP流哈希地址

  if (tcp_num > max_stream) {   //TCP流大小超过了阀值,线释放空间
    struct lurker_node *i;
    int orig_client_state=tcp_oldest->client.state;
    tcp_oldest->nids_state = NIDS_TIMED_OUT;
    for (i = tcp_oldest->listeners; i; i = i->next)
      (i->item) (tcp_oldest, &i->data);
    nids_free_tcp_stream(tcp_oldest);
    if (orig_client_state!=TCP_SYN_SENT)
      nids_params.syslog(NIDS_WARN_TCP, NIDS_WARN_TCP_TOOMUCH, ugly_iphdr, this_tcphdr);
  }
  a_tcp = free_streams;
  if (!a_tcp) {
    fprintf(stderr, "gdb me ...\n");
    pause();
  }
  free_streams = a_tcp->next_free;

  tcp_num++;    //TCP流总数
  tolink = tcp_stream_table[hash_index];
  memset(a_tcp, 0, sizeof(struct tcp_stream));
  a_tcp->hash_index = hash_index;
  a_tcp->addr = addr;
  a_tcp->client.state = TCP_SYN_SENT;
  a_tcp->client.seq = ntohl(this_tcphdr->th_seq) + 1;
  a_tcp->client.first_data_seq = a_tcp->client.seq;
  a_tcp->client.window = ntohs(this_tcphdr->th_win);
  a_tcp->client.ts_on = get_ts(this_tcphdr, &a_tcp->client.curr_ts);
  a_tcp->client.wscale_on = get_wscale(this_tcphdr, &a_tcp->client.wscale);
  a_tcp->server.state = TCP_CLOSE;
  a_tcp->next_node = tolink;
  a_tcp->prev_node = 0;
  if (tolink)
    tolink->prev_node = a_tcp;
  tcp_stream_table[hash_index] = a_tcp;
  a_tcp->next_time = tcp_latest;
  a_tcp->prev_time = 0;
  if (!tcp_oldest)
    tcp_oldest = a_tcp;
  if (tcp_latest)
    tcp_latest->prev_time = a_tcp;
  tcp_latest = a_tcp;
}

发表回复