免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 5249 | 回复: 16
打印 上一主题 下一主题

Netfliter状态跟踪之动态协议的实现浅析(tftp实现) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2006-10-15 22:56 |只看该作者 |倒序浏览
之所以叫“浅析”,主要是分析其流程,很多细节的地方没有一一注解出来,之所以以tftp为范本来剖析,主要是因为它简单,呵呵,这篇贴子,作为旧贴
http://bbs.chinaunix.net/viewthread.php?tid=815129&extra=page%3D1%26filter%3Ddigest
的一个补充,好为对Netfliter的状态跟踪分析的结束……也希望,下一步“Netfliter的地址转换的实现”能早点写出来……

注:这些贴子,包括iptables,Netfilter的包过滤,Netfliter的状态检测,都只是笔记性质的贴子,供有共同兴趣的朋友一起讨论,其中有不少错误的地方,希望大家指正,(并不是谦虚,我自己也在不断地改正和完善)!另,照旧,源码版本是2.6.12

1、模块的注册

源码在ip_conntrack_tftp.c中:
init函数中定义了
  1. static struct ip_conntrack_helper tftp[MAX_PORTS];
复制代码
并初始化它,并注册它:
  1. memset(&tftp[i], 0, sizeof(struct ip_conntrack_helper));
  2. ……
  3. ret=ip_conntrack_helper_register(&tftp[i]);
复制代码


tftp是一个数组,最大允许MAX_PORTS个,并且变量ports_c决定其个数,因为它做为注册时for循环的终值,目前,只注册了一个tftp。

tftp是一个ip_conntrack_helper类型,我在后文中,会把它叫做“helper”模块,也就是说,初始化函数中,调用ip_conntrack_helper_register函数注册了一个tftp的helper模块。

在tftp的成员的赋初始化值的时候,我们可以对照理解struct ip_conntrack_helper结构的许多重要的成员:
  1.         tftp[i].tuple.dst.protonum = IPPROTO_UDP;                //协议
  2.         tftp[i].tuple.src.u.udp.port = htons(ports[i]);                //目标端口,即69,这样,UDP:69成为认识tftp的唯一标志
  3.         tftp[i].mask.dst.protonum = 0xFF;                        //目标地址掩码,以及下面一个源端口掩码,以做比较之用
  4.         tftp[i].mask.src.u.udp.port = 0xFFFF;
  5.         tftp[i].max_expected = 1;                                //最大expect,这是什么东东?后面会详解
  6.         tftp[i].timeout = 5 * 60; /* 5 minutes */                //超时时间
  7.         tftp[i].me = THIS_MODULE;
  8.         tftp[i].help = tftp_help;                                //这个函数指针是最重要的东东了,后面再来分析它的具体作用       
复制代码


ip_conntrack_helper_register函数实质上是把该模块添加进以全局变量helpers为首的链表中去:

  1. int ip_conntrack_helper_register(struct ip_conntrack_helper *me)
  2. {
  3.         BUG_ON(me->timeout == 0);
  4.         WRITE_LOCK(&ip_conntrack_lock);
  5.         list_prepend(&helpers, me);
  6.         WRITE_UNLOCK(&ip_conntrack_lock);

  7.         return 0;
  8. }
复制代码


OK,tftp的helper模块被注册了,它什么时候被调用?以及它有什么用呢??

回忆在连接跟踪的初时化时,注册的两个钩子:
/*连接跟踪初始化时,注册helper Hook*/
static struct nf_hook_ops ip_conntrack_helper_out_ops = {
        .hook                = ip_conntrack_help,
        .owner                = THIS_MODULE,
        .pf                = PF_INET,
        .hooknum        = NF_IP_POST_ROUTING,
        .priority        = NF_IP_PRI_CONNTRACK_HELPER,        /*此优先级比同Hook上的ip_confirm的高*/
};

static struct nf_hook_ops ip_conntrack_helper_in_ops = {
        .hook                = ip_conntrack_help,
        .owner                = THIS_MODULE,
        .pf                = PF_INET,
        .hooknum        = NF_IP_LOCAL_IN,
        .priority        = NF_IP_PRI_CONNTRACK_HELPER,
};

对于中转包过滤来讲,我们关心第一个钩子,它注册在NF_IP_POST_ROUTING Hook上,并且,比我们讲过的ip_confirm优先级要高。
这样,也就是数据包经过这个Hook点时,ip_conntrack_help 函数将被调用。

2.我的例子
结合一个实际的tftp传输来分析代码,先来看这个例子(该例取自《TCP/IP详解卷一》p161)

  1. 1.        192.168.0.1:1106        ->        192.168.1.1:69                udp        19        PRQ        "test1.c"
  2. 2.        192.168.1.1:1077        ->        192.168.0.1:1106        udp        516
  3. 3.        192.168.0.1:1106        ->        192.168.1.1:1077        udp        4
  4. 4.        192.168.1.1:1077        ->        192.168.0.1:1106        udp        454
  5. 5.        192.168.0.1:1106        ->        192.168.1.1:1077        udp        4
复制代码


第1行,是192.168.0.1发出了一个“读请求”,文件名是test1.c;
第2行是,192.168.1.1 回应了读请求,将文件的数据,共516字节发送给请求者,注意,这里的来源端口不是69,而变成了1077;
第3行是一个回应包
第4,5行类似;

对于第1行,即新请求一个连接,回忆我前一次的描述,连接跟踪模块会执行以下函数:
在NF_IP_PRE_ROUTING Hook处调用钩子函数ip_conntrack_in,接着进入resolve_normal_ct函数,由于这是个新连接,所以,找不
到与之对应的tuple,于是进入了init_conntrack,初始化一个连接。

  1. static struct ip_conntrack_tuple_hash *
  2. init_conntrack(const struct ip_conntrack_tuple *tuple,
  3.                struct ip_conntrack_protocol *protocol,
  4.                struct sk_buff *skb)
  5. {
  6.         struct ip_conntrack_expect *exp;
  7.        
  8.         ……
  9.         exp = find_expectation(tuple);       

  10.         if (exp) {
  11.                 DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n",
  12.                         conntrack, exp);
  13.                 /* Welcome, Mr. Bond.  We've been expecting you... */
  14.                 __set_bit(IPS_EXPECTED_BIT, &conntrack->status);
  15.                 conntrack->master = exp->master;
  16. #if CONFIG_IP_NF_CONNTRACK_MARK
  17.                 conntrack->mark = exp->master->mark;
  18. #endif
  19.                 nf_conntrack_get(&conntrack->master->ct_general);
  20.                 CONNTRACK_STAT_INC(expect_new);
  21.         } else {
  22.                 conntrack->helper = ip_ct_find_helper(&repl_tuple);

  23.                 CONNTRACK_STAT_INC(new);
  24.         }
  25.         ……
  26. }
复制代码


exp是一个struct ip_conntrack_expect类型,find_expectation看样子应该是根据该数据包对应的tuple,查找一个struct ip_conntrack_expect类型的节点,expect是什么东东?暂时不管它,因为我们目前还没有提到它,所以,find_expectation什么也查不到,那么接下来那个if...else...则会进入else判断:

  1. else
  2. {
  3.      conntrack->helper = ip_ct_find_helper(&repl_tuple);
  4.      CONNTRACK_STAT_INC(new);
  5. }
复制代码


ip_ct_find_helper函数根据当前数据包对应的repl_tuple,在helpers链表中查找是否有相应的helper模块:
PS:当前数据包的tuple是:
192.168.0.1:1106        192.168.1.1:69        udp
则repl_tuple为:
192.168.1.1:69 192.168.0.1:1106 udp

  1. static struct ip_conntrack_helper *ip_ct_find_helper(const struct ip_conntrack_tuple *tuple)
  2. {
  3.         return LIST_FIND(&helpers, helper_cmp,
  4.                          struct ip_conntrack_helper *,
  5.                          tuple);
  6. }
复制代码


比较函数是helper_cmp:
  1. static inline int helper_cmp(const struct ip_conntrack_helper *i,
  2.                              const struct ip_conntrack_tuple *rtuple)
  3. {
  4.         return ip_ct_tuple_mask_cmp(rtuple, &i->tuple, &i->mask);
  5. }
复制代码


实际转向给了ip_ct_tuple_mask_cmp函数:
  1. static inline int ip_ct_tuple_mask_cmp(const struct ip_conntrack_tuple *t,
  2.                                        const struct ip_conntrack_tuple *tuple,
  3.                                        const struct ip_conntrack_tuple *mask)
  4. {
  5.         return !(((t->src.ip ^ tuple->src.ip) & mask->src.ip)
  6.                  || ((t->dst.ip ^ tuple->dst.ip) & mask->dst.ip)
  7.                  || ((t->src.u.all ^ tuple->src.u.all) & mask->src.u.all)
  8.                  || ((t->dst.u.all ^ tuple->dst.u.all) & mask->dst.u.all)
  9.                  || ((t->dst.protonum ^ tuple->dst.protonum)
  10.                      & mask->dst.protonum));
  11. }
复制代码


对照一下tftp模块初始化时的helper的各成员值和当前数据包repl_tuple(192.168.1.1:69 192.168.0.1:1106 udp),可以发现,最终tftp注册的helper模块将被正确地查找出来!!
这样,当前tftp的连接conntrack的helper指针就指向了tftp模块。这一点非常重要。
  1. conntrack->helper = ip_ct_find_helper(&repl_tuple);
复制代码


这个数据包继续前进,当它进入NF_IP_POST_ROUTING Hook点时,会进入ip_conntrack_help函数:

/*根据数据包,查找对应的连接,如果此连接有关链的helper模块,则调用help函数*/
  1. static unsigned int ip_conntrack_help(unsigned int hooknum,
  2.                                       struct sk_buff **pskb,
  3.                                       const struct net_device *in,
  4.                                       const struct net_device *out,
  5.                                       int (*okfn)(struct sk_buff *))
  6. {
  7.         struct ip_conntrack *ct;
  8.         enum ip_conntrack_info ctinfo;

  9.         /* This is where we call the helper: as the packet goes out. */
  10.         ct = ip_conntrack_get(*pskb, &ctinfo);
  11.         if (ct && ct->helper) {
  12.                 unsigned int ret;
  13.                 ret = ct->helper->help(pskb, ct, ctinfo);
  14.                 if (ret != NF_ACCEPT)
  15.                         return ret;
  16.         }
  17.         return NF_ACCEPT;
  18. }
复制代码


这个函数只有一件事,就是发现了tftp的这个连接(192.168.0.1:1106        192.168.1.1:69        udp),有相应的helper模块,于是,调用helper模块的help函数,于是,我们再回来看ip_conntrack_tftp.c中,这个help函数的实现:
  1. static int tftp_help(struct sk_buff **pskb,
  2.                      struct ip_conntrack *ct,
  3.                      enum ip_conntrack_info ctinfo)
  4. {
  5.         struct tftphdr _tftph, *tfh;
  6.         struct ip_conntrack_expect *exp;
  7.         unsigned int ret = NF_ACCEPT;

  8.         tfh = skb_header_pointer(*pskb,
  9.                                  (*pskb)->nh.iph->ihl*4+sizeof(struct udphdr),
  10.                                  sizeof(_tftph), &_tftph);
  11.         if (tfh == NULL)
  12.                 return NF_ACCEPT;

  13.         switch (ntohs(tfh->opcode)) {
  14.         /* RRQ and WRQ works the same way */
  15.         case TFTP_OPCODE_READ:
  16.         case TFTP_OPCODE_WRITE:
  17.                 DEBUGP("");
  18.                 DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
  19.                 DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);

  20.                 exp = ip_conntrack_expect_alloc();
  21.                 if (exp == NULL)
  22.                         return NF_DROP;

  23.                 exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
  24.                 exp->mask.src.ip = 0xffffffff;
  25.                 exp->mask.dst.ip = 0xffffffff;
  26.                 exp->mask.dst.u.udp.port = 0xffff;
  27.                 exp->mask.dst.protonum = 0xff;
  28.                 exp->expectfn = NULL;
  29.                 exp->master = ct;

  30.                 DEBUGP("expect: ");
  31.                 DUMP_TUPLE(&exp->tuple);
  32.                 DUMP_TUPLE(&exp->mask);
  33.                 if (ip_nat_tftp_hook)
  34.                         ret = ip_nat_tftp_hook(pskb, ctinfo, exp);
  35.                 else if (ip_conntrack_expect_related(exp) != 0) {
  36.                         ip_conntrack_expect_free(exp);
  37.                         ret = NF_DROP;
  38.                 }
  39.                 break;
  40.         case TFTP_OPCODE_DATA:
  41.         case TFTP_OPCODE_ACK:
  42.                 DEBUGP("Data/ACK opcode\n");
  43.                 break;
  44.         case TFTP_OPCODE_ERROR:
  45.                 DEBUGP("Error opcode\n");
  46.                 break;
  47.         default:
  48.                 DEBUGP("Unknown opcode\n");
  49.         }
  50.         return NF_ACCEPT;
  51. }
复制代码


这个函数很简单,它只关注tftp操作码的读和写,发现,如果是这两个操作码的话,就先分配一个struct ip_conntrack_expect结构:
  1.         exp = ip_conntrack_expect_alloc();
复制代码


然后,初始化它:
  1.         exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
  2.         exp->mask.src.ip = 0xffffffff;
  3.         exp->mask.dst.ip = 0xffffffff;
  4.         exp->mask.dst.u.udp.port = 0xffff;
  5.         exp->mask.dst.protonum = 0xff;
  6.         exp->expectfn = NULL;
  7.         exp->master = ct;
复制代码


最后,将它注册:
  1.         ip_conntrack_expect_related(exp) != 0
复制代码

       
是到了解释expect的时候了:
        对于tftp来讲,它的请求连接是:
        192.168.0.1:1106        ->        192.168.1.1:69                udp
        我们希望它同其它普通协议一样,应答包是:
        192.168.1.1:69        ->        192.168.0.1:1106        udp
        而不是:
        192.168.1.1:1077        ->        192.168.0.1:1106        udp
       
所以,这个expect就用来存储,该连接所“期望”的应答包,仅此而已,这也是给它的成员tuple初始化时,初始化的是当前连接的应答的tuple的原因:
  1. exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
复制代码

后面的那些mask,用于比较用。master指针让expect指向了当前连接。

至于注册,它与注册helper一样,是一个插入链表的过程:

  1. int ip_conntrack_expect_related(struct ip_conntrack_expect *expect)
  2. {
  3.         struct ip_conntrack_expect *i;
  4.         int ret;

  5.         DEBUGP("ip_conntrack_expect_related %p\n", related_to);
  6.         DEBUGP("tuple: "); DUMP_TUPLE(&expect->tuple);
  7.         DEBUGP("mask:  "); DUMP_TUPLE(&expect->mask);

  8.         WRITE_LOCK(&ip_conntrack_lock);
  9.         list_for_each_entry(i, &ip_conntrack_expect_list, list) {
  10.                 if (expect_matches(i, expect)) {
  11.                         /* Refresh timer: if it's dying, ignore.. */
  12.                         if (refresh_timer(i)) {
  13.                                 ret = 0;
  14.                                 /* We don't need the one they've given us. */
  15.                                 ip_conntrack_expect_free(expect);
  16.                                 goto out;
  17.                         }
  18.                 } else if (expect_clash(i, expect)) {
  19.                         ret = -EBUSY;
  20.                         goto out;
  21.                 }
  22.         }

  23.         /* Will be over limit? */
  24.         if (expect->master->helper->max_expected &&
  25.             expect->master->expecting >= expect->master->helper->max_expected)
  26.                 evict_oldest_expect(expect->master);

  27.         ip_conntrack_expect_insert(expect);
  28.         ret = 0;
  29. out:
  30.         WRITE_UNLOCK(&ip_conntrack_lock);
  31.         return ret;
  32. }
复制代码

首先看是否已经有相应节点,如没有,则插入之,不同的是,这次的链表首部是ip_conntrack_expect_list。

OK,数据包
192.168.0.1:1106        ->        192.168.1.1:69                udp
接下来就进入ip_confirm,然后离开本机。

当回来的数据传输的包进入Netfliter:
  1. 192.168.1.1:1077        ->        192.168.0.1:1106        udp
复制代码


因为端口已经变成了1077,而不是69,所以它不会同第一条连接的repl_tuple匹配(废话,当然不匹配了,否则还用搞这么复杂),所以,当然没有属于它的连接,数据包也会进入init_conntrack,初始化一个连接:

  1. static struct ip_conntrack_tuple_hash *
  2. init_conntrack(const struct ip_conntrack_tuple *tuple,
  3.                struct ip_conntrack_protocol *protocol,
  4.                struct sk_buff *skb)
  5. {
  6.         struct ip_conntrack_expect *exp;
  7.        
  8.         ……
  9.         exp = find_expectation(tuple);       

  10.         if (exp) {
  11.                 DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n",
  12.                         conntrack, exp);
  13.                 /* Welcome, Mr. Bond.  We've been expecting you... */
  14.                 __set_bit(IPS_EXPECTED_BIT, &conntrack->status);
  15.                 conntrack->master = exp->master;
  16. #if CONFIG_IP_NF_CONNTRACK_MARK
  17.                 conntrack->mark = exp->master->mark;
  18. #endif
  19.                 nf_conntrack_get(&conntrack->master->ct_general);
  20.                 CONNTRACK_STAT_INC(expect_new);
  21.         } else {
  22.                 conntrack->helper = ip_ct_find_helper(&repl_tuple);

  23.                 CONNTRACK_STAT_INC(new);
  24.         }
  25.         ……
  26. }
复制代码

这一次,find_expectation函数根据当前数据包的tuple,查找有没有对应的expect,很幸运,我们刚才注册的expect被查到了:
  1. static struct ip_conntrack_expect *
  2. find_expectation(const struct ip_conntrack_tuple *tuple)
  3. {
  4.         struct ip_conntrack_expect *i;

  5.         list_for_each_entry(i, &ip_conntrack_expect_list, list) {
  6.                 /* If master is not in hash table yet (ie. packet hasn't left
  7.                    this machine yet), how can other end know about expected?
  8.                    Hence these are not the droids you are looking for (if
  9.                    master ct never got confirmed, we'd hold a reference to it
  10.                    and weird things would happen to future packets). */
  11.                 if (ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask)
  12.                     && is_confirmed(i->master)
  13.                     && del_timer(&i->timeout)) {
  14.                         unlink_expect(i);
  15.                         return i;
  16.                 }
  17.         }
  18.         return NULL;
  19. }
复制代码

比较函数仍然是ip_ct_tuple_mask_cmp,再来看一遍它的代码:

  1. static inline int ip_ct_tuple_mask_cmp(const struct ip_conntrack_tuple *t,
  2.                                        const struct ip_conntrack_tuple *tuple,
  3.                                        const struct ip_conntrack_tuple *mask)
  4. {
  5.         return !(((t->src.ip ^ tuple->src.ip) & mask->src.ip)
  6.                  || ((t->dst.ip ^ tuple->dst.ip) & mask->dst.ip)
  7.                  || ((t->src.u.all ^ tuple->src.u.all) & mask->src.u.all)
  8.                  || ((t->dst.u.all ^ tuple->dst.u.all) & mask->dst.u.all)
  9.                  || ((t->dst.protonum ^ tuple->dst.protonum)
  10.                      & mask->dst.protonum));
  11. }
复制代码

回忆初始化tftp的expect时,作为比较用的mask的源端口并没有被赋值:
  1.                 exp->mask.src.ip = 0xffffffff;
  2.                 exp->mask.dst.ip = 0xffffffff;
  3.                 exp->mask.dst.u.udp.port = 0xffff;
  4.                 exp->mask.dst.protonum = 0xff;
  5.                 exp->expectfn = NULL;
复制代码

所以,对于这条应答的包来讲,尽管它的来源端口是1077,而不是我们希望的69,但
((t->src.u.all ^ tuple->src.u.all) & mask->src.u.all)仍然为0,所以,它仍然被查找出来了。

这样,Netfilter发现该连接有对应的expect,哈哈,终于找到你了,于是:
  1. __set_bit(IPS_EXPECTED_BIT, &conntrack->status);
复制代码


设置该连接为“关连”标志位(等回到resolve_normal_ct函数中,再将此连接设置为IP_CT_RELATED)
,这样,关连的连接就被识别出来了。并且,该连接的master指针,指向了第一条连接:
conntrack->master = exp->master;

主要的流程就这么简单!!

小结一下:
首先,特殊的协议注册一个helper,helper模块根据协议的某些特性,如(udp & dport==69),“帮助”我们发现一条连接是“特殊协议”,于是调用help函数,初始化一个“期望”连接expect,事实上,这个expect主要的作用仅仅是比较。当回来的应答包穿过Netfilter时,它被发现有一个expect,于是,它就被识别出来是一个“关联”连接了!!

论坛徽章:
0
2 [报告]
发表于 2006-10-16 11:56 |只看该作者
这个tftp指的不是"Trivial File Transfer Protocol"吧?

论坛徽章:
0
3 [报告]
发表于 2006-10-16 12:04 |只看该作者
原帖由 albcamus 于 2006-10-16 11:56 发表
这个tftp指的不是"Trivial File Transfer Protocol"吧?


就是它呀?它与FTP有点类似,应答包会自动打开高端口回应,而不再用69,所以,Linux就需要为它做动态协议的连接跟踪,这篇文章就是借tftp协议的实现来分析整个连接跟踪模块对于动态协议跟踪的实现!

论坛徽章:
0
4 [报告]
发表于 2006-10-16 13:00 |只看该作者
原帖由 独孤九贱 于 2006-10-16 12:04 发表


就是它呀?它与FTP有点类似,应答包会自动打开高端口回应,而不再用69,所以,Linux就需要为它做动态协议的连接跟踪,这篇文章就是借tftp协议的实现来分析整个连接跟踪模块对于动态协议跟踪的实现!


谢谢,还有一个疑问:那为什么tftp需要在内核层实现呢?

论坛徽章:
0
5 [报告]
发表于 2006-10-16 13:32 |只看该作者
原帖由 albcamus 于 2006-10-16 13:00 发表


谢谢,还有一个疑问:那为什么tftp需要在内核层实现呢?


嘻嘻,不是tftp协议在内核实现
是内核中实现对tftp协议的跟踪和状态检测……
《 Netfliter状态跟踪之动态协议的实现浅析(tftp实现) 》,或许这个名字本身让人误解,我想表达的意思是“Netfilter中状态跟踪模块对动态协议的实现支持,用tftp协议的跟踪实现的代码来分析”……

论坛徽章:
0
6 [报告]
发表于 2006-10-16 13:45 |只看该作者

回复 5楼 独孤九贱 的帖子

谢谢! 偶不熟悉网络, 见笑了

论坛徽章:
0
7 [报告]
发表于 2006-10-16 16:53 |只看该作者
good,最近在研究这方面的东西,打算用conntrack实现一个ttl状态维持的东西来避免GFW强行插入的reset包,lz这篇文章刚好是最为关键的conntrack代码分析,谢了。

论坛徽章:
0
8 [报告]
发表于 2006-10-16 17:43 |只看该作者
原帖由 colddawn 于 2006-10-16 16:53 发表
good,最近在研究这方面的东西,打算用conntrack实现一个ttl状态维持的东西来避免GFW强行插入的reset包,lz这篇文章刚好是最为关键的conntrack代码分析,谢了。

我记得GFW是同时向客户端与服务器发送rst的,只在客户端避免有用麽?我曾经简单的在客户端丢掉所有rst,但是服务器端还是关闭了连接。

论坛徽章:
0
9 [报告]
发表于 2006-10-16 17:52 |只看该作者
原帖由 mingyanguo 于 2006-10-16 17:43 发表

我记得GFW是同时向客户端与服务器发送rst的,只在客户端避免有用麽?我曾经简单的在客户端丢掉所有rst,但是服务器端还是关闭了连接。

说的没错
我曾经做过一个实验,比如用 google 搜索敏感信息,抓包发现收到了 RST 以及 HTTP/1.0 的正文的第一个包,证明那个 RST 是某个第三者插入的,于是写了一个 module 来 DROP 所有 RST 包

实验的结果和 mingyanguo 说的一样,虽然 client 的 RST 收不到了,不会马上弹出无法连接的界面,但是由于 server 已经 disconnect 了,所以最后超时后提示错误

论坛徽章:
0
10 [报告]
发表于 2006-10-16 18:57 |只看该作者
原帖由 platinum 于 2006-10-16 17:52 发表

说的没错
我曾经做过一个实验,比如用 google 搜索敏感信息,抓包发现收到了 RST 以及 HTTP/1.0 的正文的第一个包,证明那个 RST 是某个第三者插入的,于是写了一个 module 来 DROP 所有 RST 包

实验的结果 ...


你们说的什么GFW,什么Reset?听不懂,哪位能否详细讲讲?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP