TCP#2: 西厢记和西厢计划 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
felix021
V2EX    程序员

TCP#2: 西厢记和西厢计划

  •  
  •   felix021
    felix021 2020-04-20 12:07:13 +08:00 2900 次点击
    这是一个创建于 2002 天前的主题,其中的信息可能已经有所发展或是发生改变。

    TCP#2: 西厢记和西厢计划

    自那日听琴之后,多日不见莺莺,张生害了相思病,趁红娘探病之机会,托她捎信给莺莺,莺莺回信约张生月下相会。夜晚,小姐莺莺在后花园弹琴,张生听到琴声,攀上墙头一看,是莺莺在弹琴。急欲与小姐相见,便翻墙而入,莺莺见他翻墙而入,反怪他行为下流,发誓不再见他,致使张生病情愈发严重。

    《西厢记》

    上篇《 TCP:学得越多越不懂》发出来以后,有朋友很委婉地说:“如果能结合现实生产场景会有意义一点。”

    经过深刻的反思,我决定虚心接受建议,写一点理论结合实践的内容。

    == 回忆杀 ==

    曾经在猫扑和天涯冲浪的网虫应该都还记得,谷歌当时还是 Goooooogle,是可以直接访问的。

    但是如果想搜索一些奇怪的词汇(比如),一点击"手气不错",浏览器马上就会显示无法访问,并且这个现象会持续几分钟。

    连接被重置

    载入页面时到服务器的连接被重置

    于是很多小伙伴就换到一个号称自己更懂中文的搜索引擎了。

    (该爬虫当年有个广告拍得不错: https://v.qq.com/x/page/r0137s2op5j.html

    作为一个曾被新自由主义( Neoliberalism )洗脑的年轻人,我在寻找“自由”的路上发现了墙的存在,也知道了这是方校长的杰作。

    但是墙到底是个什么样的存在呢?

    == 防火墙 ==

    我们的防火墙,其名源自《 The Great Firewall of China: How to Build and Control an Alternative Version of the Internet 》这本书。

    虽然名字叫防火墙( Firewall,简称 FW ),但严格来说,(在早期)它其实是一个入侵检测系统( Instrusion Detection System,简称 IDS )。

    和 FW 不同的是,IDS 是监听设备,不需要部署在链路中间,只要能把流量旁路引出供它分析即可。

    通过旁路分析,IDS 可以在不影响现有流量的情况下部署(只要路由器 /交换机上有镜像端口即可),在 IDS 出现异常时(例如在流量高峰 IDS 设备性能不足时 )也不会导致网络中断。

    曾经有人发现,在流量特别大的时候,墙的检测功能有时会失效,因此推测其是旁路引流进行分析的(符合 IDS 的特征)。

    既然是旁路的,就无法直接 Drop 数据包,为了达到阻断通信的目的,需要利用协议的特性来实现。

    == RST 大法 ==

    看了上篇《 TCP:学得越多越不懂》的同学,对报文的控制位里的 RST 可能还有点印象,在遇到异常情况时,可用于通知对方重置连接(细节详见 RFC 793 ):

    If the receiving TCP is in a non-synchronized state (i.e. SYN-SENT, SYN-RECEIVED), it returns to LISTEN on receiving an acceptable reset. If the TCP is in one of the synchronized states (ESTABLISHED, FIN-WAIT1, FIN-WAIT2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT), it aborts the connection and informs its user

    https://tools.ietf.org/html/rfc793

    有些同学可能像我一样懒得读英文原文,所以翻译一下:

    • 如果连接状态处于“非连接完成”状态(例如 SYN-SEND, SYN-RECEIVED ),当收到 reset 时会将状态返回 LISTEN ;

    • 如果 TCP 状态是 ESTABLISHED, FIN-WAIT-1, ..., LAST-ACK, TIME-WAIT 其中之一时,放弃连接并通知用户。

    忘了上述状态含义的话,可以再回顾下这张状态流转图:

     +---------+ ---------\ active OPEN | CLOSED | \ ----------- +---------+<---------\ \ create TCB | ^ \ \ snd SYN passive OPEN | | CLOSE \ \ ------------ | | ---------- \ \ create TCB | | delete TCB \ \ V | \ \ +---------+ CLOSE | \ | LISTEN | ---------- | | +---------+ delete TCB | | rcv SYN | | SEND | | ----------- | | ------- | V +---------+ snd SYN,ACK / \ snd SYN +---------+ | |<----------------- ------------------>| | | SYN | rcv SYN | SYN | | RCVD |<-----------------------------------------------| SENT | | | snd ACK | | | |------------------ -------------------| | +---------+ rcv ACK of SYN \ / rcv SYN,ACK +---------+ | -------------- | | ----------- | x | | snd ACK | V V | CLOSE +---------+ | ------- | ESTAB | | snd FIN +---------+ | CLOSE | | rcv FIN V ------- | | ------- +---------+ snd FIN / \ snd ACK +---------+ | FIN |<----------------- ------------------>| CLOSE | | WAIT-1 |------------------ | WAIT | +---------+ rcv FIN \ +---------+ | rcv ACK of FIN ------- | CLOSE | | -------------- snd ACK | ------- | V x V snd FIN V +---------+ +---------+ +---------+ |FINWAIT-2| | CLOSING | | LAST-ACK| +---------+ +---------+ +---------+ | rcv ACK of FIN | rcv ACK of FIN | | rcv FIN -------------- | Timeout=2MSL -------------- | | ------- x V ------------ x V \ snd ACK +---------+delete TCB +---------+ ------------------------>|TIME WAIT|------------------>| CLOSED | +---------+ +---------+ TCP Connection State Diagram Figure 6. 

    (tcp 连接状态图,截取自 rfc 793)

    这就是上篇里提到的“我们敬爱的防火墙很爱用它”的原因了:

    当检测到“入侵行为”时(例如 HTTP 报文中出现了)发送 RST,按照 RFC 793 规范的 TCP 协议栈实现,收到 RST 后就应当放弃本次连接。

    于是你就在浏览器上看到连接被重置(reset)了。

    == 反 RST 大法 ==

    那么,如果我忽略 RST 包,不就可以不被墙欺骗吗?

    实际上,用 iptables 来实现这一点很简单:

    $ iptables -A INPUT -p tcp --tcp-flags RST RST -j DROP

    很不幸,方校长的团队对此的解决方法也非常简单,只要向双方都发送 RST 包就可以了。

    当然如果在服务器一端也忽略 RST,就可以成功绕过墙的忽悠据说剑桥大学有人实验验证过,确实可行。

    可惜的是,用户通常没法控制服务器端忽略 RST,因此这个方法的实用价值不高。

    但是这个思路为西厢计划做好了铺垫。

    == 西厢计划 ==

    我看到这个项目的名字的时候 ,真佩服作者的脑洞。

    了解这个计划的原理之后,就更佩服作者的脑洞了。

    前面说到,墙是在检测到某个关键词的时候才会发送 RST 包。

    为了检测关键词,它需要工作在应用层( HTTP 协议)。

    而为了工作在应用层,它需要维护 TCP 连接的状态。

    由于那时的设备性能比较弱(所以会出现高峰期检测失效的情况),为了提高吞吐量,方校长团队的方案是:实现一个简化的 TCP 栈。

    RFC 793 规范中定义了很多有效性检测,例如检测序列号是否有效来过滤 old duplicates 等,以保证通信的可靠性。

    但这不是墙的需求,因此可以去掉很多规则,从而提高分析性能。

    那么,如果我可以欺骗墙,这个连接已经被关闭,那么后续该连接的包就会被墙认为是网络中滞留的无效包,绕过关键词检测。

    具体该怎么办呢?

    == 第一阶段 ==

    上篇提到了一个细节:

    虽然 ISN=4000,但是发送方发送的第一个包,SEQ 是 4001 开始的,TCP 协议规定 SYN 需要占一个序号(虽然 SYN 并不是实际传输的数据),所以前面示意图中 ACK 的 seq 是 x+1 。同样,FIN 也会占用一个序号,这样可以保证 FIN 报文的重传和确认不会有歧义。

    TCP:学得越多越不懂 https://mp.weixin.qq.com/s/xyPUEFUr_v9sSKKqlBkI7w

    我们知道,在三次握手的最后一步,A 本应发送一个 ACK(seq=y+1)。

    但如果这时候 A 发送了一个 FIN 呢?

    B 收到以后,由于此时连接尚未建立,会直接忽略这个包。

    而墙实现的 TCP 栈比较简陋,它认为 A 已经关闭了链接,因此 A 后续发送的包就不会再触发关键词检测。

    但是注意,TCP 是双向的,虽然 A 主动关闭连接,但是 B 仍然可能有数据要发送(划重点:面试题“为什么 TCP 断开连接需要 4 次”的答案),因此还需要欺骗墙说在 B 这侧也终止链接了。

    这又该怎么办呢?

    == 第二阶段 ==

    显然我们不能让服务器直接发一个 FIN,否则这个连接就真完了。

    幸运的是,RFC 793 给了一个“梯子”:

    If the connection is in any non-synchronized state (LISTEN, SYN-SENT, SYN-RECEIVED), and the incoming segment acknowledges something not yet sent (the segment carries an unacceptable ACK), or ...(省略)..., a reset is sent.

    Reset Generation, RFC 793 [Page 35]

    翻译:如果连接处于“非连接完成”状态,收到一个无效的 ACK,应当发出一个 reset 。

    如果 A 在三次握手的最后一步,没有按规范要求发送 ACK(seq=y+1),而是发送 ACK(seq=y),那么 B 在收到以后就会按照协议的要求回复一个 RST:

    这时我们可以在 A 上用“反 RST 大法”,忽略服务端返回的 RST,这个连接就不受影响。

    但是墙的 TCP 栈认为客户端会按照协议终止连接,于是就不再有必要检测服务端后续的报文了。

    == 大结局 ==

    <delete>从此张生和崔莺莺过上了幸福的生活。</delete>

    方校长的团队当然不会放任这种事情的发生,西厢计划没过多久就失效了。

    随着技术的进步、性能的提升,现在墙似乎已经集成到了链路中、可以直接 DROP 数据包,不再需要 RST 大法了。

    不过为了业务需要,企业可以向电信主管部门申请 VPN 用于正常的生产经营。

    例如字节跳动,为了建设 21 世纪数字丝绸之路,通过技术出海,在 40 多个国家和地区排在应用商店总榜前列,包括韩国、印尼、马来西亚、俄罗斯、土耳其等“一带一路”沿线的主要国家。

    如果你也想过上幸福的生活,不妨投个简历,一起为一带一路做贡献吧。

    关于字节跳动面试的详情,可参考我之前写的《程序员面试指北:面试官视角》

    https://mp.weixin.qq.com/s/Byvu-w7kyby-L7FBCE24Uw

    ~ 投递链接 ~

    网盟广告(穿山甲)-后端开发(上海) https://job.toutiao.com/s/sBAvKe

    网盟广告(穿山甲)-后端开发(北京) https://job.toutiao.com/s/sBMyxk

    网盟广告(穿山甲)-广告策略研发(上海) https://job.toutiao.com/s/sBDMAK

    其他地区、其他职能线 https://job.toutiao.com/s/sB9Jqk

    参考文章

    [1] “西厢计划”原理小解 https://blog.youxu.info/2010/03/14/west-chamber/

    [2] 从 Linux 协议栈代码和 RFC 看西厢计划原理 https://blog.csdn.net/dog250/article/details/7246895

    [3] RFC 793 - TRANSMISSION CONTROL PROTOCOL https://tools.ietf.org/html/rfc793

    欢迎关注我的公众号

    微信扫码

     
    29 条回复    2020-04-20 20:11:04 +08:00
    wysnylc
        1
    wysnylc  
       2020-04-20 12:25:41 +08:00
    哦吼,天网觉醒
    laoyur
        2
    laoyur  
       2020-04-20 12:30:48 +08:00
    二维码的实现有点意思
    Mac
        3
    Mac  
       2020-04-20 12:34:14 +08:00
    这二维码怎么生成的,能支持多少个内容字符?
    felix021
        4
    felix021  
    OP
       2020-04-20 12:37:28 +08:00   1
    @Mac @laoyur google 搜一下 qr code ascii 有几个提供在线生成的服务。

    linux 下也有个 libqrencode3 可以搞这个,google-authenticator 就是用它来生成终端下的 qrcode 的。
    tabris17
        5
    tabris17  
       2020-04-20 12:43:16 +08:00
    怎么记得几年前就见过这玩意儿?
    tabris17
        6
    tabris17  
       2020-04-20 12:44:13 +08:00
    wocao,十年前的东西了
    felix021
        7
    felix021  
    OP
       2020-04-20 12:44:54 +08:00 via Android
    @tabris17 对,毕竟咱年纪大了。。
    LGA1150
        8
    LGA1150  
       2020-04-20 12:50:52 +08:00 via Android   1
    @felix021 https://github.com/LGA1150/netfilter-spooftcp
    我实现了个纯内核态的客户端,现在还可以用……
    felix021
        9
    felix021  
    OP
       2020-04-20 12:59:04 +08:00 via Android
    @LGA1150 这个厉害了,膜拜大佬,回头我看看 paper
    jomenxiao
        10
    jomenxiao  
       2020-04-20 13:51:20 +08:00
    猛然想起大学时光里,还折腾过的西厢计划
    lzyliangzheyu
        11
    lzyliangzheyu  
       2020-04-20 13:56:28 +08:00
    讲的生动形象
    vtea
        12
    vtea  
       2020-04-20 14:15:52 +08:00 via iPhone
    印象里 08,09 年那时候用过这个,后来不是凉凉了吗
    vtea
        13
    vtea  
       2020-04-20 14:30:37 +08:00 via iPhone
    @vtea 楼主,不好意思,看个标题就评论了,原来是科普帖
    felix021
        14
    felix021  
    OP
       2020-04-20 14:46:29 +08:00
    @vtea 嗯,我就瞎扯扯,这项目是凉了好久了……
    felix021
        15
    felix021  
    OP
       2020-04-20 14:46:39 +08:00
    @jomenxiao 暴露年龄了
    misaka19000
        16
    misaka19000  
       2020-04-20 14:51:58 +08:00
    @laoyur #2
    @Mac #3

    我也写过这种生成器,其实很简单

    https://github.com/RitterHou/Lilith
    lizheming
        17
    lizheming  
       2020-04-20 15:04:48 +08:00   1
    果然是 felix 大大,发的广告都这么硬核!赞一个~
    sexoutsex2011
        18
    sexoutsex2011  
       2020-04-20 15:28:28 +08:00
    往事如烟了
    est
        19
    est  
       2020-04-20 15:30:56 +08:00   1
    这是老文章改皮之后又发出来骗流量的吧。
    Xusually
        20
    Xusually  
       2020-04-20 15:33:30 +08:00
    看到标题中#2,以为西厢计划要出新一代了呢。。

    满满的回忆啊。
    mywaiting
        21
    mywaiting  
       2020-04-20 15:36:04 +08:00
    @LGA1150 为了你这个回复,我把整个招聘贴都收藏了.........

    回头测试一下
    felix021
        22
    felix021  
    OP
       2020-04-20 15:44:29 +08:00
    @Xusually 这么多年了炒这个冷饭确实是是有点中二了 哈哈
    aoyoo
        23
    aoyoo  
       2020-04-20 17:22:37 +08:00
    还以西厢有下一版本了呢. 说难听点, 你这不是骗人进来看帖么...
    felix021
        24
    felix021  
    OP
       2020-04-20 17:24:12 +08:00
    @aoyoo 我不知道西厢记竟然这么多人知道,我还以为挺小众的一个项目。。
    LGA1150
        25
    LGA1150  
       2020-04-20 17:36:45 +08:00
    @aoyoo INTANG 算是下一版本吧?
    aoyoo
        26
    aoyoo  
       2020-04-20 19:18:09 +08:00
    @felix021 程序员群体里知道的比例比一般人要大的多吧. V2EX 大把程序员...
    felix021
        27
    felix021  
    OP
       2020-04-20 19:29:49 +08:00
    @aoyoo 嗯,但是不知道的程序员也不少,这个项目也是有点老了
    littlewing
        28
    littlewing  
       2020-04-20 19:42:55 +08:00
    虽然是炒冷饭,这个方法现在也用不了了
    但是评论里又有几个人真的把这个方法搞明白了?把 TPC/IP 搞明白了?
    glasslion
        29
    glasslion  
       2020-04-20 20:11:04 +08:00
    @felix021 当时 twitter 中文圈上, 差不多人尽皆知
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5541 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 38ms UTC 08:58 PVG 16:58 LAX 01:58 JFK 04:58
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86