ss-libev 分析
ss-libev分析
- 0x00 ss 与 ss-libev
- 0x01 ss-redir
- 0x02 总结
0x00 ss 与 ss-libev
前面一篇文章分析了 ss 与其源码结构,参考的是 Git 上别人备份的 python 版本,但 ss 其实还有一套基于 libev 的 C 版本实现,也就是 ss-libev 。libev 是一个基于事件的循环库,有很多开源软件,比如 nodejs 就是基于 libev 实现的,而这套库相当于取代 python 版本的 eventloop.py 给出的接口。
ss-libev 相对于 python 版本而言多出了 ss-redir 和 ss-tunnel 两个客户端的选择,相当于 ss-local 的角色,ss-server 则没有变化。ss-redir 可以配套 iptables 和 tproxy 做透明代理,而 ss-tunnel 用来搭建隧道。本文就 ss-redir 的实现及其如何与 iptables 和 tproxy 配合实现透明代理进行分析。
本文不会涉及具体配置方案,如果想要结合 iptables 和 ss-redir 实现透明代理可以看这里,如果需要一个成熟的代理方案 (包括TCP和UDP),可以看这里。
0x01 ss-redir
透明代理
为什么 python 版本的 ss 无法进行透明代理,这需要从透明代理的原理开始介绍。通常来说,代理软件分为客户端(client)和服务端(server),客户端一般运行在本机,服务端运行在进行转发的服务器上。一个软件如果想通过服务器转发自己的流量,需要显式的配置该软件,比如常用的 Chrome 等。而透明代理则不需要软件自行配置代理,在进行网络通信时,需要走代理的软件流量会自动被转发到 client 端,不需要走代理的流量则被正常放行。
python 版本的 ss 只是普通的 socks5 代理,它提供一个监听端口并负责转发流量,但需要被代理软件主动与其进行 socks 协议的握手。而 ss-redir 的 client 端同样提供监听接口,但它可以处理由 iptables 或 tproxy 转发而来的流量,因此被代理软件无需设置,只要在启动 ss-redir 时同时配置好 iptables 和 tproxy 让它们把需要代理的流量转发到 ss-redir 监听的端口上即可。透明代理的实现
了解了透明代理的概念之后,我们再提出两个值得思考的问题:- iptables 和 tproxy 是如何转发流量的
- ss-redir 是如何接收转发流量的
这两个问题其实可以结合在一起来讨论。首先是对于 TCP 流量的转发,一般采用iptables。iptables 的转发很容易理解,它直接修改被转发数据包的目标地址和端口为 ss-redir 的监听地址 (比如127.0.0.1:1080),这样在转发时 ss-redir 就能接收到该数据包。但这时有一个问题,数据包的 IP 和 port 已经被修改,ss-redir 如何得到数据包被修改之前的目标 IP 和 port 呢?这一点我们可以从 ss-redir 的源码出发来分析。
ss-redir 用于处理接收数据的回调函数为accept_cb()
,其中有如下两行语句:
int serverfd = accept(listener->fd, NULL, NULL);
err = getdestaddr(serverfd, &destaddr);
第一句是获取接收到数据的 socket,而第二句调用了getdestaddr()
函数,获取源目标地址和 IP 并将结果写入 destaddr 结构体中。static int getdestaddr(int fd, struct sockaddr_storage *destaddr)
{
socklen_t socklen = sizeof(*destaddr);
int error = 0;
error = getsockopt(fd, SOL_IPV6, IP6T_SO_ORIGINAL_DST, destaddr, &socklen);
if (error) { // Didn’t find a proper way to detect IP version.
error = getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, destaddr, &socklen);
if (error) {
return -1;
}
}
return 0;
}getdestaddr() 函数内容如上,其中核心的语句是:
getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, destaddr, &socklen);
,它利用了 Linux Kernel 的连接跟踪机制,查询接收到数据包的源目标 IP 和 端口,从而使后续的转发成为可能。
那么 UDP 是不是可以采用同样的方式呢? 按照道理来说是可以的,但是根据 这篇博客 查看内核源码得到的结果, getsockopt() 函数中的 SO_ORIGINAL_DST 参数没有支持 UDP 源 IP 端口的查找。
那么 UDP 的转发流量的转发该如何做呢?这就需要采用 tproxy。tproxy 相较于 iptables 的优势在于它可以在不对数据包的包头信息进行任何改变的情况下,就重定向数据包,而这也就让我们可以直接获取目标 IP。TPROXY 原理
tproxy 的转发功能实际上也是基于 netfilter ,其通过在 mangle 表的 prerouting 中添加规则,给原本要被发送出去的数据包被打上标记并修改目的端口号,从而让数据包在路由判决中被发送给 ss-redir 的固定端口,而 ss-redir 接收到数据包后再通过某种方式获取源端口号,达到获取目标 IP 和端口的目的。
以上过程听起来不是很复杂,但是有几个核心的问题需要解决:- 如何在数据包上打标记并让其在路由判决中被正确转发
- ss-redir 如何接收与目标 IP 与自身 IP 地址不同的数据包
- ss-redir 如何获取源端口号
第一个问题涉及到 netfilter 中的 mangle 表,mangle 表的主要功能是根据规则修改数据包的标志位,以便其他规则或程序可以利用这种标志对数据包进行过滤或策略路由。下面的三行代码就是解决第一个问题的一种方式,通过在数据包的 prerouting 过程中给 UDP 数据包打上 0x2333 标记,并且让其在之后的转发过程中将有 0x2333 标记的数据包转发到本地的 1080 端口。
ip rule add fwmark 0x2333/0x2333 pref 100 table 100
ip route add local default dev lo table 100
iptables -t mangle -A PREROUTING -p udp -j TPROXY --tproxy-mark 0x2333/0x2333 --on-ip 127.0.0.1 --on-port 1080
第二个问题则是先用setsockopt函数为套接字设置IP_TRANSPARENT标识,再去监听0.0.0.0地址这样的方式来实现监听任意IP。
第三个问题则可以在 ss-redir 的源码中找答案,ss-redir 先调用setsockopt (s, IPPROTO_IP, IP_RECVORIGDSTADDR, &n, sizeof(int)) 函数为套接字设置IP_RECVORIGDSTADDR标识,然后通过 recvmsg 函数从 tproxy那边接受发过来的 msghdr 结构体信息。然后调用下图中的 get_dstaddr 函数,循环遍历cmsghdr成员最终获取到原始目标的地址和端口。在这之前,tproxy会向msghdr (附属数据结构) 填入原始目标ip和端口信息,再通过sendmsg函数发送给 ss-redir 。
0x02 总结
从源码结构上来说,ss-redir 和 ss-local 的差别并不大,因为两者的作用都是接收客户端的数据然后发送给加密服务器,以及解密服务器的数据返回给客户端。其主要差别还是在于与客户端建立连接,以及处理数据包的代码,后面的通信过程就与 python 版本差异不大了。
需要记住的是,只靠启动 ss-redir 不能搭建一个透明代理,还需要搭配其他工具和软件,比如 iptables、tproxy ,为了解析 DNS 报文可能还需要 ss-tunnel 或者其他工具等等。
本文在写作过程中参考了如下资料:
https://www.jianshu.com/p/76cea3ef249d
https://github.com/zfl9/ss-tproxy
https://blog.csdn.net/ts__cf/article/details/78942294
https://blog.csdn.net/lee244868149/article/details/45113585
ss-libev 分析相关推荐
- kafka集群under replicated分析
近期随着业务消息量增大,现网几套kafka集群频繁收到under repliacted告警,集合近期定位分析过程,主要有以下几个方面: 1. 查看是否有主机挂掉,或近期是否有主机重启,通过kafdro ...
- Android UI编程之自定义控件初步(下)——CustomEditText
概述: 基于对上一篇博客<Android UI编程之自定义控件初步(上)--ImageButton>的学习,我们对自定义控件也有了一个初步的认识.那现在我们可以再试着对EditText进行 ...
- EditText图文混排
下面就具体说一下我遇到的问题,首先是EditText里面的图文混排问题,这个问题的难点就是三点: 1.怎么插图片 2.怎么保存插入的图片和文字 3.怎么解析回图片和文字 解决: 一.怎么插入图片 在这 ...
- 【面试篇】ConcurrentHashMap1.7和1.8详解对比
ConcurrentHashMap1.7和1.8详解对比 [面试篇]数据结构-哈希表 [面试篇]HashMap常见面试题目 [面试篇]HashMap1.7和HashMap1.8的详细区别对比 [面试篇 ...
- java(基础语法)
文章目录 一. 语法 1. 数组 2. 字符串 3. 对象 (1) 语法 (2) 继承 (3) 抽象类 (4) final 常量 4. 装箱和拆箱 5. 常用类 (1) Data类 (2) Strin ...
- Android 自带的应用统计服务(UsageStatsService)
最近要弄在 framework 中弄一个统计应用使用时长的功能.刚开始想着要怎么是不是要在 ActivityManagerService(AMS)的几个 Activity 的生命周期那埋几个统计点,后 ...
- linux命令行连接wifi RTL810xE PCI Express Fast Ethernet Controller 网卡
linux命令行连接wifi - hunters007 - 博客园 https://www.cnblogs.com/hunter-007/p/11151092.html hunters007 linu ...
- 08-04 多线程之Task
目录 前言 一.Task开启线程的方式 1.Task实例化 2.Task.Run()静态方法 3.TaskFactory 二.Task由线程池管理 三.父子线程 1.默认情况 2.线程附着 四.线程优 ...
- zookeeper集群模式(十)zookeeper的lead流程
lead流程 Leader选举出来之后,会创建一个Leader实例,然后调用lead()方法进行lead流程,接下来看一下lead()方法: void lead() throws IOExceptio ...
- libev源码分析---整体设计
libev是Marc Lehmann用C写的高性能事件循环库.通过libev,可以灵活地把各种事件组织管理起来,如:时钟.io.信号等.libev在业界内也是广受好评,不少项目都采用它来做底层的事件循 ...
最新文章
- RGPNET: 复杂环境下实时通用语义分割网络
- 计算机应用中的CAI,????按计算机应用的分类,CAI应属于()应用。
- ubuntu16.04安装PCL
- 简单DNS服务器架设
- 接口测试之json中的key获取
- 英文的写作 —— 词汇的积累(环境的描写、写人)
- Ceph OSD处理客户端写操作处理流程
- 神经网络是不是分类算法,人工神经网络分类算法
- html设置为壁纸win10,Win10让桌面壁纸自动换的设置方法(图文教程)
- 电子计算机上total,计算器频幕上grand total 什么意思
- 倍福EtherCAT EK1100耦合器技术参数
- php 云片网对接,云片网络短信发送 PHP SDK 奉上
- mysql 超级用户登录_MySQL超级用户(root)密码忘记重置
- a href点击无效_a标签失效的问题
- 分享一款将中文网站源代码直接生成英文网站的工具
- nyoj 一笔画问题
- 光线传媒副总裁刘同:我们为什么要读大学?
- 实验三——密码破解技术
- 三国志战略版:Daniel_典韦分析
- OpenEuler兼容Erlang 23.0测试过程中,No OpenGL headers found, No GLU headers (glu.h) ,wxWidgets not found
热门文章
- AI System 人工智能系统 TVM深度学习编译器 DSL IR优化 计算图 编译 优化 内存内核调度优化 DAG 图优化 DFS TaiChi 函数注册机 Registry
- 正易判讀 6-3 韓長庚 著
- 免费最新微信,QQ拦截查询代码接口
- Centos7安装docker并更改阿里云下载镜像地址(附带windows10安装docker教程)
- 正则匹配过滤字母和数字
- matlab直流电动机特性曲线,他励直流电动机的机械特性曲线的分析
- Ailurus 小熊猫
- 三分钟教会你用Python爬取心仪小姐姐图片
- 【思考题】新客老客定义
- 2018黑马前端视频教程视频与源码全