热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

用scapy构建假的TCP隧道提高传输性能

TCP一定要是TCP吗?它可能是个trick!豪雨滂沱,作文一篇,当笑话看看就好。前几天写了两篇与本文相关的随笔

TCP一定要是TCP吗? 它可能是个trick!

豪雨滂沱,作文一篇,当笑话看看就好。

前几天写了两篇与本文相关的随笔:
https://blog.csdn.net/dog250/article/details/106881244
https://blog.csdn.net/dog250/article/details/106955747


想让数据包按照TCP的样子被传输和被处理,非常复杂。

然而,TCP是一个端到端有状态协议,这意味着中间转发设备没有能力处理TCP的细节,如果在端系统也不需要处理TCP细节的时候, 一个流只需要让中间转发设备看起来像TCP就行了!

什么时候端系统不需要处理TCP细节,以及为什么要让一个本不是TCP的流看起来像TCP呢?

我们要明白中间转发设备的一些行为。中间转发设备会针对TCP做出一些动作,比如:

  • 流量高峰期对除TCP之外的其它协议进行丢包限速(过度对TCP丢包会引发端系统盲CC算法的过激反应,加剧网络拥塞)。
  • 按照TCP四元组对单流进行限速。
  • 按照TCP四元组对单流进行整形。

这些动作基于以下的事实:

  • 行为最终会作用于端到端系统,端到端系统会正确处理TCP细节,使网络收敛。

而我们可以通过忽略端到端处理,从这些动作中得到收益,而不是限制:

  • 给UDP封装一个TCP头使其在高峰期免于被限速。
  • 为同一个流封装不同四元组的TCP头而免于被限速。
  • 为同一个流封装不同四元组的TCP头而免于被整形。

本文给出一个 将任意流量伪装成TCP流量 的POC。我称为 dummy TCP隧道。

本来我是想用Netfilter做的,但是我厌倦了写内核模块,本来我是想用eBPF做的,但是我觉得有点哗众取宠,于是,我用scapy+tun来做,因为简单!

下面是dummy TCP隧道python代码(仅侧重于数据面):

#!/usr/bin/python
# dtun.py
from scapy.all import *
import socket
import fcntlIFF_TUN = 0x0001
IFF_NO_PI = 0x1000
TUNSETIFF = 0x400454casrc = '0.0.0.0'
peer = '0.0.0.0'
sport = 0
dport = 0
seq = 12345 # 本应random,但为了简单,算了
ack_seq = 12345 # 本应random,但为了简单,算了# 从网络接收dummy TCP隧道封装好的报文,直接解除TCP头,送到tun网卡
def net2tun(packet):global ack_seq, src, peer, sport, dportflags = packet[TCP].flags# 处理模拟SYNACK的if分支if flags & 0x02 != 0 and flags & 0x10 == 0:ip = IP(src = src, dst = peer)# 收到SYN包,获取ack,直接回复SYNACKtcp = ip/TCP(sport = sport, dport = dport, flags = "SA", seq = seq, ack = ack_seq)ack_seq = packet[TCP].seqsend(tcp)# 处理正常通信的if分支elif flags & 0x02 == 0:os.write(tun.fileno(), str(packet[TCP].payload))ack_seq = packet[TCP].seq + len(packet[TCP].payload)def recv():filter = "src " + peer + " and tcp and tcp port 12345"sniff(filter = filter,iface="enp0s8", prn = net2tun)# 模拟TCP握手,其作用主要是协商序列号以及让中间状态设备初始化流表。
def handshake(src, dst):global seq, ack_seq, sport, dportip = IP(src = src, dst = dst)tcp = ip/TCP(sport = sport, dport = dport, flags = "S", seq = seq)pkt = sr1(tcp, iface = "enp0s8")ack_seq = pkt[TCP].ackprint pkt[TCP].seqprint pkt[TCP].ack# 直接封装TCP头后,发到网络
def tun2net(src, dst, payload):global seq, ack_seqip = IP(src = src, dst = dst)tcp = ip/TCP(sport = sport, dport = dport, flags = "A", seq = seq, ack = ack_seq)/payloadseq += len(payload)send(tcp)if __name__ == "__main__":global peertunip = sys.argv[1]peer = sys.argv[2]src = sys.argv[3]syn = sys.argv[4]dport = int("12345")sport = int("12345")tun = open('/dev/net/tun', 'r+w')iface = "tun0"# 打开并设置tun设备ifr = struct.pack('16sH', iface, IFF_TUN|IFF_NO_PI)fcntl.ioctl(tun, TUNSETIFF, ifr)ip = socket.inet_aton(tunip)sockfd = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0);ifr = struct.pack('16sH2s4s8s', iface, socket.AF_INET, '\x00'*2, ip, '\x00'*8)fcntl.ioctl(sockfd, 0x8916, ifr)ifr = struct.pack('16sH2s4s8s', iface, socket.AF_INET, '\x00'*2, socket.inet_aton("255.255.255.0"), '\x00' * 8)fcntl.ioctl(sockfd, 0x891C, ifr)ifr = struct.pack('16sH', iface, IFF_UP)fcntl.ioctl(sockfd, SIOCSIFFLAGS, ifr)try:threading.Thread(target = recv).start()if syn == "1":handshake(src, peer)# 从tun设备接收裸数据包,封装TCP头,直接发到dummy TCP隧道while True:packet = os.read(tun.fileno(), 2048)tun2net(src, peer, packet)except KeyboardInterrupt:os._exit(0)

来看下效果。

准备两台虚拟机hostA和hostB,直连,配置如下:

# hostA
enp0s8:192.168.56.110/24
# hostB
enp0s8:192.168.56.101/24

下面在hostA和hostB上分别启动上述脚本:

# hostB (先启动B,因为它等待接收SYN)
root@zhaoya-VirtualBox:/home/zhaoya/tun# ./dtun.py 1.1.1.2 192.168.56.110 192.168.56.101 0
# hostA
[root@localhost tun]# ./dtun.py 1.1.1.1 192.168.56.101 192.168.56.110 1

此时两台机器的隧道就已经建立好了,由于并没有真正的TCP连接,为了避免端到端的RST,需要在hostA和hostB上额外添加两条过滤规则:

iptables -t mangle -A POSTROUTING -p tcp -m tcp --tcp-flags RST RST -j DROP

OK,现在可以实验了。

在hostA上ping hostB的tun网卡的地址:

[root@localhost ~]# ping 1.1.1.2 -c 2
PING 1.1.1.2 (1.1.1.2) 56(84) bytes of data.
64 bytes from 1.1.1.2: icmp_seq=1 ttl=64 time=42.1 ms
64 bytes from 1.1.1.2: icmp_seq=2 ttl=64 time=50.2 ms--- 1.1.1.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 42.196/46.243/50.290/4.047 ms

tcpdump抓包如下:

[root@localhost tun]# tcpdump -i enp0s8 tcp port 12345 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp0s8, link-type EN10MB (Ethernet), capture size 262144 bytes
# 看起来像是TCP的握手包
06:24:40.483759 IP 192.168.56.110.12345 > 192.168.56.101.12345: Flags [S], seq 12345, win 8192, length 0
06:24:40.522107 IP 192.168.56.101.12345 > 192.168.56.110.12345: Flags [S.], seq 12889, ack 12345, win 8192, length 0
# 哦,缺失了3rd ACK,嗯,有待改进...
06:24:46.525279 IP 192.168.56.101.12345 > 192.168.56.110.12345: Flags [.], seq 0:48, ack 1, win 8192, length 48
06:24:46.989491 IP 192.168.56.110.12345 > 192.168.56.101.12345: Flags [.], seq 1:85, ack 48, win 8192, length 84
06:24:47.027375 IP 192.168.56.101.12345 > 192.168.56.110.12345: Flags [.], seq 48:132, ack 85, win 8192, length 84
06:24:47.989780 IP 192.168.56.110.12345 > 192.168.56.101.12345: Flags [.], seq 85:169, ack 132, win 8192, length 84
06:24:48.034897 IP 192.168.56.101.12345 > 192.168.56.110.12345: Flags [.], seq 132:216, ack 169, win 8192, length 84

TCP流,搞得跟真事儿一样…

其实我们没有建立任何TCP连接,都是假的。

中间设备看到这种包之后,会认为这来自于一个真实的TCP连接,当它想对这个流进行限制的时候,它会实施排队,丢包等动作, 以期待端到端的TCP cc算法可以通过测量RTT,探测丢包等感知到这种抑制动作,从而作出绅士般的避让行为。 然而,根本就没有端到端的TCP处理,都是装出来的。

现在看看可以进一步做点什么。

如果我用10个不同的源地址建立10条这样的dummy TCP隧道,那么隧道的过境包每次只需要从这10条隧道里随便挑选一条就可以了。理论上,如果中间设备对TCP单流进行限速的话,这种方案可以将吞吐提高10倍!

好吧,你可能会说中间设备可以对一个IP段进行限速,那也不是没办法,只要隧道两端有一端是喇叭大张口,就可以用那一端来模拟SYN来构建多条隧道:
在这里插入图片描述
多年以前,我那个时候玩OpenU++Q–N,我一直嚷嚷着TCP隧道会造成连接崩溃,我一直嚷嚷着一定要用UDP隧道,嗯,那时其实就应该像本文这么玩,之所以没有想到这等技巧,或许是因为金融网是一个准内网,QoS各方面都有所保证,而且又不会过多对UDP有所限制,如果当时的生产环境换到公共Internet上,此等trick估计早就被我偷偷摸摸上线了。

左右手互搏,道高一尺,魔高一丈,自己打脸,然后抓着经理的领带把经理推进沟渠。


浙江温州皮鞋湿,下雨进水不会胖。


推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • 本文介绍了如何在Mac上使用Pillow库加载不同于默认字体和大小的字体,并提供了一个简单的示例代码。通过该示例,读者可以了解如何在Python中使用Pillow库来写入不同字体的文本。同时,本文也解决了在Mac上使用Pillow库加载字体时可能遇到的问题。读者可以根据本文提供的示例代码,轻松实现在Mac上使用Pillow库加载不同字体的功能。 ... [详细]
  • 如何使用Python从工程图图像中提取底部的方法?
    本文介绍了使用Python从工程图图像中提取底部的方法。首先将输入图片转换为灰度图像,并进行高斯模糊和阈值处理。然后通过填充潜在的轮廓以及使用轮廓逼近和矩形核进行过滤,去除非矩形轮廓。最后通过查找轮廓并使用轮廓近似、宽高比和轮廓区域进行过滤,隔离所需的底部轮廓,并使用Numpy切片提取底部模板部分。 ... [详细]
  • 本文介绍了利用ARMA模型对平稳非白噪声序列进行建模的步骤及代码实现。首先对观察值序列进行样本自相关系数和样本偏自相关系数的计算,然后根据这些系数的性质选择适当的ARMA模型进行拟合,并估计模型中的位置参数。接着进行模型的有效性检验,如果不通过则重新选择模型再拟合,如果通过则进行模型优化。最后利用拟合模型预测序列的未来走势。文章还介绍了绘制时序图、平稳性检验、白噪声检验、确定ARMA阶数和预测未来走势的代码实现。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • r2dbc配置多数据源
    R2dbc配置多数据源问题根据官网配置r2dbc连接mysql多数据源所遇到的问题pom配置可以参考官网,不过我这样配置会报错我并没有这样配置将以下内容添加到pom.xml文件d ... [详细]
  • web.py开发web 第八章 Formalchemy 服务端验证方法
    本文介绍了在web.py开发中使用Formalchemy进行服务端表单数据验证的方法。以User表单为例,详细说明了对各字段的验证要求,包括必填、长度限制、唯一性等。同时介绍了如何自定义验证方法来实现验证唯一性和两个密码是否相等的功能。该文提供了相关代码示例。 ... [详细]
  • 第四章高阶函数(参数传递、高阶函数、lambda表达式)(python进阶)的讲解和应用
    本文主要讲解了第四章高阶函数(参数传递、高阶函数、lambda表达式)的相关知识,包括函数参数传递机制和赋值机制、引用传递的概念和应用、默认参数的定义和使用等内容。同时介绍了高阶函数和lambda表达式的概念,并给出了一些实例代码进行演示。对于想要进一步提升python编程能力的读者来说,本文将是一个不错的学习资料。 ... [详细]
  • MPLS VP恩 后门链路shamlink实验及配置步骤
    本文介绍了MPLS VP恩 后门链路shamlink的实验步骤及配置过程,包括拓扑、CE1、PE1、P1、P2、PE2和CE2的配置。详细讲解了shamlink实验的目的和操作步骤,帮助读者理解和实践该技术。 ... [详细]
  • 本文由编程笔记小编整理,主要介绍了使用Junit和黄瓜进行自动化测试中步骤缺失的问题。文章首先介绍了使用cucumber和Junit创建Runner类的代码,然后详细说明了黄瓜功能中的步骤和Steps类的实现。本文对于需要使用Junit和黄瓜进行自动化测试的开发者具有一定的参考价值。摘要长度:187字。 ... [详细]
  • NotSupportedException无法将类型“System.DateTime”强制转换为类型“System.Object”
    本文介绍了在使用LINQ to Entities时出现的NotSupportedException异常,该异常是由于无法将类型“System.DateTime”强制转换为类型“System.Object”所导致的。同时还介绍了相关的错误信息和解决方法。 ... [详细]
  • Centos7搭建ELK(Elasticsearch、Logstash、Kibana)教程及注意事项
    本文介绍了在Centos7上搭建ELK(Elasticsearch、Logstash、Kibana)的详细步骤,包括下载安装包、安装Elasticsearch、创建用户、修改配置文件等。同时提供了使用华为镜像站下载安装包的方法,并强调了保证版本一致的重要性。 ... [详细]
  • 本文整理了315道Python基础题目及答案,帮助读者检验学习成果。文章介绍了学习Python的途径、Python与其他编程语言的对比、解释型和编译型编程语言的简述、Python解释器的种类和特点、位和字节的关系、以及至少5个PEP8规范。对于想要检验自己学习成果的读者,这些题目将是一个不错的选择。请注意,答案在视频中,本文不提供答案。 ... [详细]
  • 大数据Hadoop生态(20)MapReduce框架原理OutputFormat的开发笔记
    本文介绍了大数据Hadoop生态(20)MapReduce框架原理OutputFormat的开发笔记,包括outputFormat接口实现类、自定义outputFormat步骤和案例。案例中将包含nty的日志输出到nty.log文件,其他日志输出到other.log文件。同时提供了一些相关网址供参考。 ... [详细]
author-avatar
青藤摄影876
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有