今天遇到一个很有意思的问题:clash使用隧道设备(为了形式上一致起见,下文直接用tun代替),用以在用户态直接读取L3数据包;使用raw套接字的程序,例如libpcap,也可以直接读取L3数据包。那这两个东西有什么区别?
要分析区别,不妨将二者的位置对调一下。
假如clash使用raw套接字,可以获取本机所有流量。这是因为tcp套接字算是在L4的范围内实现,而raw套接字则是L3、L2的内容,监听它的应用程序本质上和L4平起平坐。可以理解成,tcp套接字(L4套接字)工作在是地址加端口上,L3 raw套接字工作在整个IP地址上,而L2 raw套接字工作在整个MAC地址上。
但问题在于,raw套接字并不能拦截流量。打个比方,所谓“挟天子以令诸侯”,曹操要平定天下,就得先和天子讲(天子作为“代理”,拦截了曹操的outbound流量),天子讲出的话百姓才能够信服。假如曹操直接对百姓讲话,天子只是旁听,那么失败的讲话就发生了,天子即便以服人的方式再说一遍,也是于事无补的。
假如libpcap使用tun设备,可以通过修改路由表来拦截所有流量,以实现本机流量的获取。但抓到包后libpcap还得把这些outbound或inbound流量都原封不动地送到正确的网卡进行相应处理,即便该功能实现于内核,也会带来一些性能浪费。因此还不如不截取了,流量就在原先的管道里正常传输,管道上开个口子给外面人看,这不就又回到raw套接字了吗?
综上,如果以管道和里面的水作比喻,raw套接字就像在管道上开个小口,从外面能看到里面的水,但不影响水该往哪流往哪流;而隧道设备是直接把水接到自己的水库里。
参考链接: