libnids的哈希及查找



哈希的键值

在Libnids中对tcp数据流的哈希关键字是“源IP、源端口、目的IP、目的端口”:

  1. //该结构描述地址及端口  
  2. struct tuple4         
  3. {  
  4.   u_short source;   //源端口  
  5.   u_short dest;     //目的端口  
  6.   u_int saddr;      //源IP  
  7.   u_int daddr;      //目的IP  
  8. };  
//该结构描述地址及端口
struct tuple4       
{
  u_short source;   //源端口
  u_short dest;     //目的端口
  u_int saddr;      //源IP
  u_int daddr;      //目的IP
};

哈希表结构

这里的哈希表是由tcp_stream_table定义的:

  1. static struct tcp_stream **tcp_stream_table;  
static struct tcp_stream **tcp_stream_table;
       tcp_stream_table存放的是指针,这些指针分别指向了一个双向链表,双向链表的节点存的是一个个的tcp_stream,而tcp_stream里含有指针,指向了half_stream;在half_stream里存放的才是tcp流的数据。每次一个新的tcp数据流到达,将其插入到双向链表的表头位置。

哈希函数及哈希表长度

文件“tcp.c"中通过如下函数实现哈希,它调用的函数mkhash在源文件hash.c中,参考文章:Libnids的哈希函数

  1. //该函数调用mkhash将源/目的IP和端口号hash为一个int数,该数已经  
  2. //具有了足够的随机性,然后在通过取余操作得到其在哈希表中的位置  
  3. static int  
  4. mk_hash_index(struct tuple4 addr)  
  5. {  
  6.   int hash=mkhash(addr.saddr, addr.source, addr.daddr, addr.dest);  
  7.   return hash % tcp_stream_table_size;  
  8. }  
//该函数调用mkhash将源/目的IP和端口号hash为一个int数,该数已经
//具有了足够的随机性,然后在通过取余操作得到其在哈希表中的位置
static int
mk_hash_index(struct tuple4 addr)
{
  int hash=mkhash(addr.saddr, addr.source, addr.daddr, addr.dest);
  return hash % tcp_stream_table_size;
}
某次捕获并还原tcp数据流之前,将mk_hash_index函数添加如下输出信息,重新安装Libnids(安装过程参考 Libnids在Ubuntu下的安装 ):

  1. static int  
  2. mk_hash_index(struct tuple4 addr)  
  3. {  
  4.   struct sockaddr_in saddr, daddr;  
  5.   saddr.sin_addr.s_addr = addr.saddr;  
  6.   daddr.sin_addr.s_addr = addr.daddr;  
  7.   char ip[20];  
  8.   printf("ip, port: %s %d, %s %d\n", inet_ntoa(saddr.sin_addr), addr.source,  
  9.            inet_ntop(AF_INET, &daddr.sin_addr,ip,sizeof(ip)), addr.dest);  
  10.   int hash=mkhash(addr.saddr, addr.source, addr.daddr, addr.dest);  
  11.   printf("hash: %d, tcp_stream_table_size#: %d\n\n", hash, tcp_stream_table_size);  
  12.   return hash % tcp_stream_table_size;  
  13. }  
static int
mk_hash_index(struct tuple4 addr)
{
  struct sockaddr_in saddr, daddr;
  saddr.sin_addr.s_addr = addr.saddr;
  daddr.sin_addr.s_addr = addr.daddr;
  char ip[20];
  printf("ip, port: %s %d, %s %d\n", inet_ntoa(saddr.sin_addr), addr.source,
           inet_ntop(AF_INET, &daddr.sin_addr,ip,sizeof(ip)), addr.dest);
  int hash=mkhash(addr.saddr, addr.source, addr.daddr, addr.dest);
  printf("hash: %d, tcp_stream_table_size#: %d\n\n", hash, tcp_stream_table_size);
  return hash % tcp_stream_table_size;
}
此时捕获并还原tcp流,可以得到如下的部分截图信息:由图可以看出,哈希表长度为1040,以及每次hash后的数值:


哈希表的查找

  1. struct tcp_stream *  
  2. find_stream(struct tcphdr * this_tcphdr, struct ip * this_iphdr,  
  3.         int *from_client)  
  4. {  
  5.   struct tuple4 this_addr, reversed;  
  6.   struct tcp_stream *a_tcp;  
  7.   
  8.   this_addr.source = ntohs(this_tcphdr->th_sport);  
  9.   this_addr.dest = ntohs(this_tcphdr->th_dport);  
  10.   this_addr.saddr = this_iphdr->ip_src.s_addr;  
  11.   this_addr.daddr = this_iphdr->ip_dst.s_addr;  
  12.   a_tcp = nids_find_tcp_stream(&this_addr);  
  13.   if (a_tcp) {  
  14.     *from_client = 1;  
  15.     return a_tcp;  
  16.   }  
  17.   reversed.source = ntohs(this_tcphdr->th_dport);  
  18.   reversed.dest = ntohs(this_tcphdr->th_sport);  
  19.   reversed.saddr = this_iphdr->ip_dst.s_addr;  
  20.   reversed.daddr = this_iphdr->ip_src.s_addr;  
  21.   a_tcp = nids_find_tcp_stream(&reversed);  
  22.   if (a_tcp) {  
  23.     *from_client = 0;  
  24.     return a_tcp;  
  25.   }  
  26.   return 0;  
  27. }  
struct tcp_stream *
find_stream(struct tcphdr * this_tcphdr, struct ip * this_iphdr,
	    int *from_client)
{
  struct tuple4 this_addr, reversed;
  struct tcp_stream *a_tcp;

  this_addr.source = ntohs(this_tcphdr->th_sport);
  this_addr.dest = ntohs(this_tcphdr->th_dport);
  this_addr.saddr = this_iphdr->ip_src.s_addr;
  this_addr.daddr = this_iphdr->ip_dst.s_addr;
  a_tcp = nids_find_tcp_stream(&this_addr);
  if (a_tcp) {
    *from_client = 1;
    return a_tcp;
  }
  reversed.source = ntohs(this_tcphdr->th_dport);
  reversed.dest = ntohs(this_tcphdr->th_sport);
  reversed.saddr = this_iphdr->ip_dst.s_addr;
  reversed.daddr = this_iphdr->ip_src.s_addr;
  a_tcp = nids_find_tcp_stream(&reversed);
  if (a_tcp) {
    *from_client = 0;
    return a_tcp;
  }
  return 0;
}
  1. struct tcp_stream *  
  2. nids_find_tcp_stream(struct tuple4 *addr)  
  3. {  
  4.   int hash_index;  
  5.   struct tcp_stream *a_tcp;  
  6.   
  7.   hash_index = mk_hash_index(*addr);  
  8.   for (a_tcp = tcp_stream_table[hash_index];  
  9.        a_tcp && memcmp(&a_tcp->addr, addr, sizeof (struct tuple4));  
  10.        a_tcp = a_tcp->next_node);  
  11.   return a_tcp ? a_tcp : 0;  
  12. }  
struct tcp_stream *
nids_find_tcp_stream(struct tuple4 *addr)
{
  int hash_index;
  struct tcp_stream *a_tcp;

  hash_index = mk_hash_index(*addr);
  for (a_tcp = tcp_stream_table[hash_index];
       a_tcp && memcmp(&a_tcp->addr, addr, sizeof (struct tuple4));
       a_tcp = a_tcp->next_node);
  return a_tcp ? a_tcp : 0;
}

哈希表的插入

        如果一个数据报是正常的数据报,但是在哈希表中没有找到该数据报对应的数据流(这是三次握手的第一个数据报,标志着一个新的tcp连接的开始),那么就使用该函数添加该数据报到哈希表中。哈希表处理冲突的方法是使用双向链表,如果新来的tcp流发生冲突,将这个新tcp流插入到双向链表的表头

  1. static void  
  2. add_new_tcp(struct tcphdr * this_tcphdr, struct ip * this_iphdr)  
  3. {  
  4.   struct tcp_stream *tolink;  
  5.   struct tcp_stream *a_tcp;  
  6.   int hash_index;  
  7.   struct tuple4 addr;  
  8.     
  9.   addr.source = ntohs(this_tcphdr->th_sport);  
  10.   addr.dest = ntohs(this_tcphdr->th_dport);  
  11.   addr.saddr = this_iphdr->ip_src.s_addr;  
  12.   addr.daddr = this_iphdr->ip_dst.s_addr;  
  13.   hash_index = mk_hash_index(addr);  
  14.     
  15.   //如果该tcp流中数据报数量超过了阈值,需要先释放空间  
  16.   if (tcp_num > max_stream) {  
  17.     struct lurker_node *i;  
  18.     int orig_client_state=tcp_oldest->client.state;  
  19.     tcp_oldest->nids_state = NIDS_TIMED_OUT;  
  20.     for (i = tcp_oldest->listeners; i; i = i->next)  
  21.       (i->item) (tcp_oldest, &i->data);  
  22.     nids_free_tcp_stream(tcp_oldest);  
  23.     if (orig_client_state!=TCP_SYN_SENT)  
  24.       nids_params.syslog(NIDS_WARN_TCP, NIDS_WARN_TCP_TOOMUCH, ugly_iphdr, this_tcphdr);  
  25.   }  
  26.   a_tcp = free_streams;  
  27.   if (!a_tcp) {  
  28.     fprintf(stderr, "gdb me ...\n");  
  29.     pause();  
  30.   }  
  31.   free_streams = a_tcp->next_free;  
  32.     
  33.   tcp_num++;  //tcp流数量加1  
  34.   //更新tcp流的相关信息  
  35.   tolink = tcp_stream_table[hash_index];  
  36.   memset(a_tcp, 0, sizeof(struct tcp_stream));  
  37.   a_tcp->hash_index = hash_index;  
  38.   a_tcp->addr = addr;  
  39.   a_tcp->client.state = TCP_SYN_SENT;  
  40.   a_tcp->client.seq = ntohl(this_tcphdr->th_seq) + 1;  
  41.   a_tcp->client.first_data_seq = a_tcp->client.seq;  
  42.   a_tcp->client.window = ntohs(this_tcphdr->th_win);  
  43.   a_tcp->client.ts_on = get_ts(this_tcphdr, &a_tcp->client.curr_ts);  
  44.   a_tcp->client.wscale_on = get_wscale(this_tcphdr, &a_tcp->client.wscale);  
  45.   a_tcp->server.state = TCP_CLOSE;  
  46.   a_tcp->next_node = tolink;  
  47.   a_tcp->prev_node = 0;  
  48.   if (tolink)  
  49.     tolink->prev_node = a_tcp;  
  50.   tcp_stream_table[hash_index] = a_tcp;  
  51.   a_tcp->next_time = tcp_latest;   
  52.   a_tcp->prev_time = 0;  
  53.   if (!tcp_oldest)  
  54.     tcp_oldest = a_tcp;  
  55.   if (tcp_latest)  
  56.     tcp_latest->prev_time = a_tcp;  
  57.   tcp_latest = a_tcp;  //将最新插入哈希表的流更新为当前流  
  58. }  
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) {
    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流数量加1
  //更新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;  //将最新插入哈希表的流更新为当前流
}

删除tcp流时对hash表的处理

  1. //删除tcp数据流并释放空间  
  2. void  
  3. nids_free_tcp_stream(struct tcp_stream * a_tcp)  
  4. {  
  5.   //在tcp_stream的结构中记录了该tcp流的hash index,这里要使用该值  
  6.   int hash_index = a_tcp->hash_index;  
  7.   struct lurker_node *i, *j;  
  8.   
  9.   del_tcp_closing_timeout(a_tcp);  
  10.   //分别删除两个方向上的half_stream  
  11.   purge_queue(&a_tcp->server);  
  12.   purge_queue(&a_tcp->client);  
  13.      
  14.   //从hash表中删除该tcp流并释放空间  
  15.   if (a_tcp->next_node)  
  16.     a_tcp->next_node->prev_node = a_tcp->prev_node;  
  17.   if (a_tcp->prev_node)  
  18.     a_tcp->prev_node->next_node = a_tcp->next_node;  
  19.   else  
  20.     tcp_stream_table[hash_index] = a_tcp->next_node;  
  21.   if (a_tcp->client.data)  
  22.     free(a_tcp->client.data);  
  23.   if (a_tcp->server.data)  
  24.     free(a_tcp->server.data);  
  25.   if (a_tcp->next_time)  
  26.     a_tcp->next_time->prev_time = a_tcp->prev_time;  
  27.   if (a_tcp->prev_time)  
  28.     a_tcp->prev_time->next_time = a_tcp->next_time;  
  29.   if (a_tcp == tcp_oldest)  
  30.     tcp_oldest = a_tcp->prev_time;  
  31.   if (a_tcp == tcp_latest)  
  32.     tcp_latest = a_tcp->next_time;  
  33.     
  34.   i = a_tcp->listeners;  
  35.     
  36.   while (i) {  
  37.     j = i->next;  
  38.     free(i);  
  39.     i = j;  
  40.   }  
  41.   a_tcp->next_free = free_streams;  
  42.   free_streams = a_tcp;  
  43.   tcp_num--; //删除了tcp流后,当前的tcp流数量减1  
  44. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值