免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12345下一页
最近访问板块 发新帖
查看: 22968 | 回复: 47

layer7-filter实现简析 [复制链接]

论坛徽章:
0
发表于 2011-03-04 17:53 |显示全部楼层
好久没有来发贴,今天看了一下l7filter过滤的实现,发上来讨论一下:
模版初始化,注册proc和netfilter match:
  1. static int __init xt_layer7_init(void)
  2. {
  3.         need_conntrack();

  4.         layer7_init_proc();
  5.         if(maxdatalen < 1) {
  6.                 printk(KERN_WARNING "layer7: maxdatalen can't be < 1, "
  7.                         "using 1\n");
  8.                 maxdatalen = 1;
  9.         }
  10.         /* This is not a hard limit.  It's just here to prevent people from
  11.         bringing their slow machines to a grinding halt. */
  12.         else if(maxdatalen > 65536) {
  13.                 printk(KERN_WARNING "layer7: maxdatalen can't be > 65536, "
  14.                         "using 65536\n");
  15.                 maxdatalen = 65536;
  16.         }
  17.         return xt_register_matches(xt_layer7_match,
  18.                                    ARRAY_SIZE(xt_layer7_match));
  19. }
复制代码
maxdatalen是最大的数据流长度。layer7使用数据流检测,若干个报文被组装到一个长度为maxdatalen的数据流中。其值默认为2048。

相应的模块注销,完成proc清理和netfilter match的注销:
  1. static void __exit xt_layer7_fini(void)
  2. {
  3.         layer7_cleanup_proc();
  4.         xt_unregister_matches(xt_layer7_match, ARRAY_SIZE(xt_layer7_match));
  5. }
复制代码
layer7_match是核心的匹备函数,layer7扩展了nf_conn结构,引入了协议名称,数据缓冲区和数据长度。当一个报文被匹备时,协议名称将记录这个协议,
以方便该会话的后续报文匹备检测:
  1. static bool
  2. layer7_match(const struct sk_buff *skbin, const struct xt_match_param *mt_para)
  3. {
  4.         /* sidestep const without getting a compiler warning... */
  5.         struct sk_buff * skb = (struct sk_buff *)skbin;

  6.         const struct xt_layer7_info * info = mt_para->matchinfo;
  7.         enum ip_conntrack_info master_ctinfo, ctinfo;
  8.         struct nf_conn *master_conntrack, *conntrack;
  9.         unsigned char * app_data;
  10.         unsigned int pattern_result, appdatalen;
  11.         regexp * comppattern;

  12.         /* Be paranoid/incompetent - lock the entire match function. */
  13.         //加全局锁
  14.         spin_lock_bh(&l7_lock);

  15.         //确认是能够处理的数据报文
  16.         if(!can_handle(skb)){
  17.                 DPRINTK("layer7: This is some protocol I can't handle.\n");
  18.                 spin_unlock_bh(&l7_lock);
  19.                 return info->invert;
  20.         }

  21.         /* Treat parent & all its children together as one connection, except
  22.         for the purpose of setting conntrack->layer7.app_proto in the actual
  23.         connection. This makes /proc/net/ip_conntrack more satisfying. */
  24.         //获取数据包的连接
  25.         if(!(conntrack = nf_ct_get(skb, &ctinfo)) ||
  26.            !(master_conntrack=nf_ct_get(skb,&master_ctinfo))){
  27.                 DPRINTK("layer7: couldn't get conntrack.\n");
  28.                 spin_unlock_bh(&l7_lock);
  29.                 return info->invert;
  30.         }

  31.         /* Try to get a master conntrack (and its master etc) for FTP, etc. */
  32.         /**
  33.          * 如果连接存在主连接,master_conntrack指向连接的主连接,
  34.          * 这样,如果一个连接不存在master,则conntrack/master_conntrack都指向该连接,
  35.          * 如果存在,则conntrack指向子连接自己,而master_conntrack指向其父连接
  36.         */
  37.         while (master_ct(master_conntrack) != NULL)
  38.                 master_conntrack = master_ct(master_conntrack);

  39.         /* if we've classified it or seen too many packets */
  40.         //如果对当前连接的处理超过最大报文上限,默认阀值为10,或者是主连接中已经有了协议名称
  41.         if(TOTAL_PACKETS > num_packets ||
  42.            master_conntrack->layer7.app_proto) {

  43.                 //如果主连接协议名已经标识,而子连接没有,则使用它覆盖所有子连接协议名称,
  44.                 //并且匹备协议名称。否则将主连接协议名设为"Unkown"
  45.                 //对于没有子连接的来说,两个指针都指向自己,这意味着后续的报文直接通过协议名称,
  46.                 //可以快速处理。
  47.                 pattern_result = match_no_append(conntrack, master_conntrack,
  48.                                                  ctinfo, master_ctinfo, info);

  49.                 /* skb->cb[0] == seen. Don't do things twice if there are
  50.                 multiple l7 rules. I'm not sure that using cb for this purpose
  51.                 is correct, even though it says "put your private variables
  52.                 there". But it doesn't look like it is being used for anything
  53.                 else in the skbs that make it here. */
  54.                 skb->cb[0] = 1; /* marking it seen here's probably irrelevant */

  55.                 spin_unlock_bh(&l7_lock);
  56.                 //返回匹备结果
  57.                 return (pattern_result ^ info->invert);
  58.         }

  59.         //需要一个完整的报文做检测
  60.         if(skb_is_nonlinear(skb)){
  61.                 if(skb_linearize(skb) != 0){
  62.                         if (net_ratelimit())
  63.                                 printk(KERN_ERR "layer7: failed to linearize "
  64.                                                 "packet, bailing.\n");
  65.                         spin_unlock_bh(&l7_lock);
  66.                         return info->invert;
  67.                 }
  68.         }

  69.         /* now that the skb is linearized, it's safe to set these. */
  70.         //app_data指向报文数据载荷位
  71.         app_data = skb->data + app_data_offset(skb);
  72.         //appdatalen记录数据载荷长度
  73.         appdatalen = skb_tail_pointer(skb) - app_data;

  74.         /* the return value gets checked later, when we're ready to use it */
  75.         //初始化正则表达式
  76.         comppattern = compile_and_cache(info->pattern, info->protocol);

  77.         /* On the first packet of a connection, allocate space for app data */
  78.         //如果是第一个报文,为连接会话中的app_data指针分配空间
  79.         if(TOTAL_PACKETS == 1 && !skb->cb[0] &&
  80.            !master_conntrack->layer7.app_data){
  81.                 master_conntrack->layer7.app_data =
  82.                         kmalloc(maxdatalen, GFP_ATOMIC);
  83.                 //分配空间
  84.                 if(!master_conntrack->layer7.app_data){
  85.                         if (net_ratelimit())
  86.                                 printk(KERN_ERR "layer7: out of memory in "
  87.                                                 "match, bailing.\n");
  88.                         spin_unlock_bh(&l7_lock);
  89.                         return info->invert;
  90.                 }

  91.                 master_conntrack->layer7.app_data[0] = '\0';
  92.         }

  93.         /* Can be here, but unallocated, if numpackets is increased near
  94.         the beginning of a connection */
  95.         //有这样的可能,当会话已经建立了,l7filter模块才被插入。
  96.         if(master_conntrack->layer7.app_data == NULL){
  97.                 spin_unlock_bh(&l7_lock);
  98.                 return (info->invert); /* unmatched */
  99.         }

  100.         if(!skb->cb[0]){
  101.                 int newbytes;
  102.                 //将报文载荷拷贝到会话中,连接会话中的app_data记录连接的所有数据,最大不超过maxdatalen,
  103.                 //其阀值默认为2048。
  104.                 //感觉将追加的长度判断放在add_data之前更好,这样,大多数不被匹配的报文将减少一次函数调用开销
  105.                 newbytes = add_data(master_conntrack, app_data, appdatalen);

  106.                 //连接的数据缓冲已经满了
  107.                 if(newbytes == 0) { /* didn't add any data */
  108.                         skb->cb[0] = 1;
  109.                         /* Didn't match before, not going to match now */
  110.                         spin_unlock_bh(&l7_lock);
  111.                         return info->invert;
  112.                 }
  113.         }

  114.         //协议匹备,unkown/unset做为逃生的协议名标识,前者标识不进行匹备,后者表示匹备所有
  115.         /* If looking for "unknown", then never match.  "Unknown" means that
  116.         we've given up; we're still trying with these packets. */
  117.         if(!strcmp(info->protocol, "unknown")) {
  118.                 pattern_result = 0;
  119.         /* If looking for "unset", then always match. "Unset" means that we
  120.         haven't yet classified the connection. */
  121.         } else if(!strcmp(info->protocol, "unset")) {
  122.                 pattern_result = 2;
  123.                 DPRINTK("layer7: matched unset: not yet classified "
  124.                         "(%d/%d packets)\n", TOTAL_PACKETS, num_packets);
  125.         /* If the regexp failed to compile, don't bother running it */
  126.         } else if(comppattern &&
  127.                   regexec(comppattern, master_conntrack->layer7.app_data)){
  128.                   //regexec成功则返回1,失败返回0,这里表示匹备成功。
  129.                   //每一次匹备,都使用的是连接的数据进行,数据缓冲区的释放,是放在destroy_conntrack中进行的。
  130.                 DPRINTK("layer7: matched %s\n", info->protocol);
  131.                 pattern_result = 1;
  132.         } else pattern_result = 0;

  133.         if(pattern_result == 1) {
  134.                 //匹备成功,记录协议名称。
  135.                 master_conntrack->layer7.app_proto =
  136.                         kmalloc(strlen(info->protocol)+1, GFP_ATOMIC);
  137.                 if(!master_conntrack->layer7.app_proto){
  138.                         if (net_ratelimit())
  139.                                 printk(KERN_ERR "layer7: out of memory in "
  140.                                                 "match, bailing.\n");
  141.                         spin_unlock_bh(&l7_lock);
  142.                         return (pattern_result ^ info->invert);
  143.                 }
  144.                 strcpy(master_conntrack->layer7.app_proto, info->protocol);
  145.         } else if(pattern_result > 1) { /* cleanup from "unset" */
  146.                 //专门为unset准备的
  147.                 pattern_result = 1;
  148.         }

  149.         /* mark the packet seen */
  150.         //标识数据报文已经被处理过
  151.         skb->cb[0] = 1;

  152.         spin_unlock_bh(&l7_lock);
  153.                 //返回匹备结果
  154.         return (pattern_result ^ info->invert);
  155. }
复制代码
can_handle返回l7filter支持处理的协议,包括tcp/udp/icmp:
  1. static int can_handle(const struct sk_buff *skb)
  2. {
  3.         if(!ip_hdr(skb)) /* not IP */
  4.                 return 0;
  5.         if(ip_hdr(skb)->protocol != IPPROTO_TCP &&
  6.            ip_hdr(skb)->protocol != IPPROTO_UDP &&
  7.            ip_hdr(skb)->protocol != IPPROTO_ICMP)
  8.                 return 0;
  9.         return 1;
  10. }
复制代码
compile_and_cache函数初始化正则表达式,将用户空间的表达式转换为内核可识别的形式
为了加速正则表达式的转换——不可能每个报文都要转换一次。函数引入了缓存机制,不过这个缓存是一维链表形式的。这意味着加了若干条规则后,
这个链将变得很长。
  1. static regexp * compile_and_cache(const char * regex_string,
  2.                                   const char * protocol)
  3. {
  4.         struct pattern_cache * node               = first_pattern_cache;
  5.         struct pattern_cache * last_pattern_cache = first_pattern_cache;
  6.         struct pattern_cache * tmp;
  7.         unsigned int len;

  8.         //遍历表达式缓存表,试图找到缓存节点
  9.         while (node != NULL) {
  10.                 if (!strcmp(node->regex_string, regex_string))
  11.                 return node->pattern;

  12.                 last_pattern_cache = node;/* points at the last non-NULL node */
  13.                 node = node->next;
  14.         }

  15.         /* If we reach the end of the list, then we have not yet cached
  16.            the pattern for this regex. Let's do that now.
  17.            Be paranoid about running out of memory to avoid list corruption. */
  18.         //为新的表达式分配空间
  19.         tmp = kmalloc(sizeof(struct pattern_cache), GFP_ATOMIC);

  20.         //分配失败
  21.         if(!tmp) {
  22.                 if (net_ratelimit())
  23.                         printk(KERN_ERR "layer7: out of memory in "
  24.                                         "compile_and_cache, bailing.\n");
  25.                 return NULL;
  26.         }

  27.         //初始化各成员
  28.         tmp->regex_string  = kmalloc(strlen(regex_string) + 1, GFP_ATOMIC);
  29.         tmp->pattern       = kmalloc(sizeof(struct regexp),    GFP_ATOMIC);
  30.         tmp->next = NULL;

  31.         if(!tmp->regex_string || !tmp->pattern) {
  32.                 if (net_ratelimit())
  33.                         printk(KERN_ERR "layer7: out of memory in "
  34.                                         "compile_and_cache, bailing.\n");
  35.                 kfree(tmp->regex_string);
  36.                 kfree(tmp->pattern);
  37.                 kfree(tmp);
  38.                 return NULL;
  39.         }

  40.         /* Ok.  The new node is all ready now. */
  41.         node = tmp;

  42.         //更新表达式节点缓存
  43.         if(first_pattern_cache == NULL) /* list is empty */
  44.                 first_pattern_cache = node; /* make node the beginning */
  45.         else
  46.                 last_pattern_cache->next = node; /* attach node to the end */

  47.         //编译正则表达式
  48.         /* copy the string and compile the regex */
  49.         len = strlen(regex_string);
  50.         DPRINTK("About to compile this: \"%s\"\n", regex_string);
  51.         node->pattern = regcomp((char *)regex_string, &len);
  52.         if ( !node->pattern ) {
  53.                 if (net_ratelimit())
  54.                         printk(KERN_ERR "layer7: Error compiling regexp "
  55.                                         "\"%s\" (%s)\n",
  56.                                         regex_string, protocol);
  57.                 /* pattern is now cached as NULL, so we won't try again. */
  58.         }

  59.         strcpy(node->regex_string, regex_string);
  60.         return node->pattern;
  61. }
复制代码
match_no_append函数用于匹备已经标识存在协议名的连接,或者是超过最大检测数据包限制的会话:
  1. static int match_no_append(struct nf_conn * conntrack,
  2.                            struct nf_conn * master_conntrack,
  3.                            enum ip_conntrack_info ctinfo,
  4.                            enum ip_conntrack_info master_ctinfo,
  5.                            const struct xt_layer7_info * info)
  6. {
  7.         /* If we're in here, throw the app data away */
  8.         //会话数据缓冲区已经用不着了,释放内存,置空
  9.         if(master_conntrack->layer7.app_data != NULL) {

  10.         #ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG
  11.                 if(!master_conntrack->layer7.app_proto) {
  12.                         char * f =
  13.                           friendly_print(master_conntrack->layer7.app_data);
  14.                         char * g =
  15.                           hex_print(master_conntrack->layer7.app_data);
  16.                         DPRINTK("\nl7-filter gave up after %d bytes "
  17.                                 "(%d packets):\n%s\n",
  18.                                 strlen(f), TOTAL_PACKETS, f);
  19.                         kfree(f);
  20.                         DPRINTK("In hex: %s\n", g);
  21.                         kfree(g);
  22.                 }
  23.         #endif

  24.                 kfree(master_conntrack->layer7.app_data);
  25.                 master_conntrack->layer7.app_data = NULL; /* don't free again */
  26.         }

  27.         //主连接已经标识了协议名称了
  28.         if(master_conntrack->layer7.app_proto){
  29.                 //如果连接还没有标识,则使用主连接的覆盖之
  30.                 /* Here child connections set their .app_proto (for /proc) */
  31.                 if(!conntrack->layer7.app_proto) {
  32.                         conntrack->layer7.app_proto =
  33.                           kmalloc(strlen(master_conntrack->layer7.app_proto)+1,
  34.                             GFP_ATOMIC);
  35.                         if(!conntrack->layer7.app_proto){
  36.                                 if (net_ratelimit())
  37.                                         printk(KERN_ERR "layer7: out of memory "
  38.                                                         "in match_no_append, "
  39.                                                         "bailing.\n");
  40.                                 return 1;
  41.                         }
  42.                         strcpy(conntrack->layer7.app_proto,
  43.                                 master_conntrack->layer7.app_proto);
  44.                 }

  45.                 //匹备协议名
  46.                 return (!strcmp(master_conntrack->layer7.app_proto,
  47.                                 info->protocol));
  48.         }
  49.         else {
  50.                 /* If not classified, set to "unknown" to distinguish from
  51.                 connections that are still being tested. */
  52.                 //否则将协议标识为unkown,则为不可识别,这也意味着,每一个unkown的连接,都会进入
  53.                 //这个函数进行匹备协议名
  54.                 master_conntrack->layer7.app_proto =
  55.                         kmalloc(strlen("unknown")+1, GFP_ATOMIC);
  56.                 if(!master_conntrack->layer7.app_proto){
  57.                         if (net_ratelimit())
  58.                                 printk(KERN_ERR "layer7: out of memory in "
  59.                                                 "match_no_append, bailing.\n");
  60.                         return 1;
  61.                 }
  62.                 strcpy(master_conntrack->layer7.app_proto, "unknown");
  63.                 return 0;
  64.         }
  65. }
复制代码
app_data_offset计算载荷的偏移位置,跳过传输层首部:
  1. /* Returns offset the into the skb->data that the application data starts */
  2. static int app_data_offset(const struct sk_buff *skb)
  3. {
  4.         /* In case we are ported somewhere (ebtables?) where ip_hdr(skb)
  5.         isn't set, this can be gotten from 4*(skb->data[0] & 0x0f) as well. */
  6.         int ip_hl = 4*ip_hdr(skb)->ihl;

  7.         if( ip_hdr(skb)->protocol == IPPROTO_TCP ) {
  8.                 /* 12 == offset into TCP header for the header length field.
  9.                 Can't get this with skb->h.th->doff because the tcphdr
  10.                 struct doesn't get set when routing (this is confirmed to be
  11.                 true in Netfilter as well as QoS.) */
  12.                 int tcp_hl = 4*(skb->data[ip_hl + 12] >> 4);

  13.                 return ip_hl + tcp_hl;
  14.         } else if( ip_hdr(skb)->protocol == IPPROTO_UDP  ) {
  15.                 return ip_hl + 8; /* UDP header is always 8 bytes */
  16.         } else if( ip_hdr(skb)->protocol == IPPROTO_ICMP ) {
  17.                 return ip_hl + 8; /* ICMP header is 8 bytes */
  18.         } else {
  19.                 if (net_ratelimit())
  20.                         printk(KERN_ERR "layer7: tried to handle unknown "
  21.                                         "protocol!\n");
  22.                 return ip_hl + 8; /* something reasonable */
  23.         }
  24. }
复制代码
add_data函数用于构建待检测的数据流,它将数据报文追加到连接会话的数据缓冲区中,令人疑惑的是,为什么没有区分数据流方向呢?

  1. /* add the new app data to the conntrack.  Return number of bytes added. */
  2. static int add_data(struct nf_conn * master_conntrack,
  3.                     char * app_data, int appdatalen)
  4. {
  5.         int length = 0, i;
  6.         int oldlength = master_conntrack->layer7.app_data_len;

  7.         /* This is a fix for a race condition by Deti Fliegl. However, I'm not
  8.            clear on whether the race condition exists or whether this really
  9.            fixes it.  I might just be being dense... Anyway, if it's not really
  10.            a fix, all it does is waste a very small amount of time. */
  11.         if(!master_conntrack->layer7.app_data) return 0;

  12.         /* Strip nulls. Make everything lower case (our regex lib doesn't
  13.         do case insensitivity).  Add it to the end of the current data. */
  14.         //追加数据进入连接会话中
  15.         for(i = 0; i < maxdatalen-oldlength-1 &&
  16.                    i < appdatalen; i++) {
  17.                 if(app_data[i] != '\0') {
  18.                         /* the kernel version of tolower mungs 'upper ascii' */
  19.                         master_conntrack->layer7.app_data[length+oldlength] =
  20.                                 isascii(app_data[i])?
  21.                                         tolower(app_data[i]) : app_data[i];
  22.                         length++;
  23.                 }
  24.         }
  25.         //置结束标记
  26.         master_conntrack->layer7.app_data[length+oldlength] = '\0';
  27.         //重新计算长度
  28.         master_conntrack->layer7.app_data_len = length + oldlength;

  29.         return length;
  30. }
复制代码
个人认为,layer7的性能低,是各个方面造成的,不知道优化匹备模式,能不能提高其效率——类似snort规则,引用更多的检测项。

评分

参与人数 1可用积分 +30 收起 理由
dreamice + 30 赞一个!

查看全部评分

论坛徽章:
0
发表于 2011-03-04 18:16 |显示全部楼层
它这个太简单了 ,要是用来做防火墙还是不要算了,我是自己重新一个,  正则表达式也用了强悍的pcre。
   

pcre 移植到内核,可以参阅下面我写的:

http://blogold.chinaunix.net/u3/116699/showart_2314530.html

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2011-03-04 18:36 |显示全部楼层
回复 1# 独孤九贱


    赞一下九贱兄的无私分享。

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2011-03-04 18:36 |显示全部楼层
它这个太简单了 ,要是用来做防火墙还是不要算了,我是自己重新一个,  正则表达式也用了强悍的pcre。
    ...
tuibo 发表于 2011-03-04 18:16


l7 这个框架还是有不少借鉴价值的。

BTW,你的 Blog 的代码是可以直接在内核编译的吗?

论坛徽章:
0
发表于 2011-03-04 19:10 |显示全部楼层
直接在内核编译

论坛徽章:
0
发表于 2011-03-04 21:41 |显示全部楼层
我也是在内核做的IPS,用户态下规则,但是不能下太多

论坛徽章:
0
发表于 2011-03-04 22:46 |显示全部楼层
谢谢分享!!

论坛徽章:
0
发表于 2011-03-05 02:59 |显示全部楼层
http://l7-filter.sourceforge.net/

L7-filter is a classifier for Linux's Netfilter that identifies packets based on application layer data. It can classify packets as Kazaa, HTTP, Jabber, Citrix, Bittorrent, FTP, Gnucleus, eDonkey2000, etc., regardless of port. It complements existing classifiers that match on IP address, port numbers and so on.

Our intent is for l7-filter to be used in conjunction with Linux QoS to do bandwith arbitration ("packet shaping") or traffic accounting.

PS:这个是在用户层还是在内核里面实现的?

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
发表于 2011-03-05 11:32 |显示全部楼层
L7-filter is a classifier for Linux's Netfilter that identifies packets based on application lay ...
accessory 发表于 2011-03-05 02:59



    内核层做L7的过滤

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
发表于 2011-03-05 11:33 |显示全部楼层
它这个太简单了 ,要是用来做防火墙还是不要算了,我是自己重新一个,  正则表达式也用了强悍的pcre。
    ...
tuibo 发表于 2011-03-04 18:16



    内核做正则,效率、误报、稳定性是很重要的因素
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP