2017/05/06

【关于题外话在最后】

写作本文主要基于两点,首先是因为我前段时间写了几篇关于XXN的新解,收到了很多的邮件反馈,我也思考了很多,另一个方面是因为很多人问我怎么用QQ,P2P搭建一个IP层的Tunnel,我的回答是“我也不知道”。我确实不知道,根本就没有试过,只是有个这样那样的想法…我主要是没有动力和能力去Hack这些非Linux上的东西…所以说,我写这篇文章,用UNIX的方法 “将多个小工具结合起来” 实现我的那些没有实现的想法,抛砖引玉一下。

声明:

本文没有技术含量,甚至不会有什么代码,本文只是一些Linux bash和netcat的使用技巧之堆砌,但我觉得只有这种是理解Everything over App的最好方式,虽然我没有能力将TAP对接到QQ上,但我有能力将其对接到netcat以及ssh上,而netcat,ssh和QQ并没有什么本质的区别,结论是,只要你能找一个通信软件,Tunnel便可以嫁接于其上。

Tunnel的种类

一般而言,传统的Tunnel都是用新的IP报文来运输原始IP报文的,这种新的IP报文往往封装一个新的四层头。但是也有用TCP,UDP来运输原始IP报文的,典型的例子就是OpenXXN,BadXXN等。
事实上,你可以用任何的报文来运输原始IP报文,在上一篇文章《从一个简单的聊天程序SimpleChat看XXX技术》中,我提到你可以用QQ,P2P协议来运输IP报文,并且给出了一个应景的设想。本文将通过简单的实例来实现一下这种设想。

发展阶段

像类似PPPoE,L2TP什么的,提出了一个很好的框架和概念,说明IP over XXX的可行性,然后OpenXXN掀开了一次Revolution,说明IP可以over UDP,并且和SSL/TLS进行了深度结合,小众的BadXXN应该是另一次Revolution的,但是作者好像没有把握住机会,和OpenXXN不同的是,BadXXN旨在提供灵活且独立的点对点组网能力。

在研究了BadXXN之后,我自己写了一个简单的Demo,即SimpleXXN
https://github.com/marywangran/overlay

其实,BadXXN的思路很简单,这个和我们平时的聊天软件的思路完全一致,不同的是聊天软件运输的是我们键盘输入的消息,而BadXXN运输的则是从TAP网卡中读取的IP报文。借着这个思路,我觉得任何的可以进行网络通信的软件都可以运输IP报文,只需要将程序的输入和输出重定向到TAP网卡的输入和输出即可,这非常简单。

准备工作

如果说TAP网卡在文件系统中以一个文件的形式存在,那么事情会非常简单,比如tap0网卡在文件系统中有一个字符设备文件/dev/tap0,那么以scp为例,我便可以通过以下的命令来运输通过tap0传输的IP报文或者以太帧:

scp /dev/tap0 root@192.168.44.129:/dev/tap0

然而自打Linux 2.6.x(8<x<32)起,内核便不再提供这种机制了。至少在2.6.8的内核中,有一个ethertap的模块,它提供了我上述所说的“理想方法”,你只需要以下的命令便可以构建一个tap0文件:

modprobe ethertap
ifconfig tap0 10.10.10.10/24 up
mknod /dev/tap0 c 36 16
...

可是到了2.6.32内核,ethertap模块没有了,只剩下了tun模块,使用tun模块,无法直接建立tap0的设备文件,必须通过ioctl来显式设置它:

modprobe tun
tunctl -u root -t tap0
# 注意,tun模块让你无法通过命令构建一个可以直接读写的tap0设备文件,你必须写一个程序显式执行open/ioctl对,然后操作其文件描述符进行读写。
...

这意味着我不得不写一个程序了,而这令不会编程的我感到悲哀!我把该文件命名为tunio.c,如下所示:

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <linux/if_tun.h>
#include <sys/select.h>
#include <sys/ioctl.h>
#include <fcntl.h>struct frame {unsigned int len;char data[2000];
};// 为了便于重定向操作,我在本程序中只使用了3个文件描述符:TAP网卡字符设备,标准输入,标准输出
int main(int argc, char **argv)
{int fd = -1;struct ifreq ifr;size_t len;char buf[2004];struct frame frm;int i;fd_set rd_set;if( (fd = open("/dev/net/tun", O_RDWR)) < 0) {exit(-1);}memset(&ifr, 0, sizeof(ifr));ifr.ifr_flags |= IFF_NO_PI;ifr.ifr_flags |= IFF_TAP;snprintf(ifr.ifr_name, IFNAMSIZ, "%s", "tap0");ioctl(fd, TUNSETIFF, (void *)&ifr);while(1) {int nfds;int j;FD_ZERO(&rd_set);FD_SET(0, &rd_set);FD_SET(fd, &rd_set);char *tmp;nfds = select(1024, &rd_set, NULL, NULL, NULL);for (j = 0;j < nfds; j++) {// 如果标准输入有动静的话if(FD_ISSET(0, &rd_set)) {unsigned int dlen;// 从标准输入中读取数据长度len = read(0, &dlen, sizeof(unsigned int));// 按照指示的长度读取原始IP报文或者数据帧len = read(0, frm.data, dlen);// 将IP报文或者数据帧写入TAP设备len = write(fd, frm.data, len);}// 如果虚拟网卡字符设备有动静if(FD_ISSET(fd, &rd_set)) {// 从网卡读取IP报文或者以太帧len = read(fd, buf, sizeof(buf));// 为了对端可以区分数据边界,加入了长度头frm.len = len;memcpy(frm.data, buf, len);tmp = (char *)&frm;// 将加入长度头的原始数据输出到标准输出for (i = 0; i < len+sizeof(unsigned int); i++) {printf("%c", tmp[i]);}}// 刷新输出fflush(NULL);}}return 0;
}

将以上的代码编译成tunio即可:

gcc tunio.c -o tunio

然后就可以用这个tunio和其它的既有程序进行基于UNIX哲学的组合从而构建出一个看起来像那么回事的Tunnel了。

在继续之前,我不得不说一下不能继续使用ethertap的原因。

一个好好的ethertap为什么就下课了呢?其实很大的原因在于ethertap所依赖的Netlink机制被重构了。在2.4版本以及2.6的早期版本的内核中,Netlink消息是通过字符设备进行读写的,然而后来Netlink便不再采用字符设备了,而是采用socket API进行读写了,而socket并没有导出文件到文件系统这也是众所周知,socket仅仅导出文件到进程。然而这跟ethertap下课有关吗?有的!

ethertap之所以可以导出文件到文件系统,就是使用的Netlink字符设备,你看看上述的mknod命令,其major设备号36就是Netlink的major设备号:

#define NETLINK_MAJOR        36

在init_netlink中:

if (register_chrdev(NETLINK_MAJOR,"netlink", &netlink_fops)) {...

在ethertap模块代码中,你看不到任何关于字符设备的逻辑,事实上ethertap的字符设备文件的读写逻辑已经被Netlink接管,这个从hard_xmit以及netif_rx这两个协议栈的读写接口中便可获知,它们都是直接与Netlink接口。

好了,现在Netlink不再采用字符设备的方式了,改由API调用的方式控制了,那么ethertap便面临两个选择,一个是也跟着采用API的方式进行控制,而这个与tun模块功能重复,另一个选择是为ethertap单独注册一个字符设备类型,或者将其注册到misc设备,但这样会使字符设备集合越来越臃肿…再者说了,不还有tun的吗?编个程序封装一下读写控制逻辑有那么难吗?所以说,只能下课了…

其实,虽然ethertap好用,但其实现方式是有问题的,试想如果大家都为每一个功能注册一个Netlink号码,或者注册一种字符设备,那么几乎可以肯定,这种横向的平坦拓展方式总有一天会让Netlink或者字符设备管理机制不堪重负…之前的ioctl问题就是一个例子。

下面开始真正有意思的东西。

使用netcat实现一个简单的Demo

小用一下这个被誉为“瑞士军刀”的小巧nc,我来展示一下IP报文是如何通过nc构建的连接运输到另一个任意节点的。

我把tunio的标准输出重定向到netcat的标准输入,然后把netcat的标准输出重定向到tunio的标准输入,事情就成了,这让整个事情变成了一个环:

是不是很简单呢?

那么怎么实现呢?虽然逻辑上上图已经足够简单,但是实现上还是需要一个pipe作为过渡的,因此我们先在两台机器上分别创建一个pipe:

mkfifo /tmp/tmp_fifo

接下来的玩法完全取决于你对netcat的熟悉程度,本文不讲netcat,只说Tunnel,所以直接给出命令:

# 机器1:
cat /tmp/tmp_fifo | ./tunio 2>&1 | nc -l 1567 >/tmp/tmp_fifo
ifconfig tap0 10.10.10.10/24 up
# 机器2:
cat /tmp/tmp_fifo | ./tunio 2>&1 | nc 192.168.44.100 1567 >/tmp/tmp_fifo
ifconfig tap0 10.10.10.20/24 up

然后你来试试在机器2上去ping机器1的tap0的地址,绝对通了。这下,我们在物理网络的TCP连接上成功构建了一个承载以太帧的Ethernet over TCP的Overlay,显然这还远远不够,我们来抓包看看。在机器2上执行:

curl 10.10.10.10

同时在机器1上抓包:

tcpdump -i any tcp port 1567 -n -w overlay1.pcap

打开这个overlay1.pcap,我们可见:

好吧,虽然很完美的完成了隧道封装,然而没有实现加密…某种意义上,这并不是XXN,具体为啥我在这里多说无益。技术归技术,我们现在的目标是实现加密传输。这简直太简单了。

在继续展示加密隧道之前,我必须来点题外话为netcat做点推广。在前几篇文章中,我提到了QQ,P2P,甚至差点连飞鸽传书都扯上,但这些对Linux用户来讲,都是被鄙视的。在Linux上难道不是netcat这把瑞士军刀最靠谱吗?如果两人都使用Linux,最轻便的聊天工具就是netcat,根本不用装别的什么乱七八糟的东西。

实现一个加密隧道:使用ncat

当然,按照UNIX的传统方法,使用netcat和一个加密解密的命令加上输入输出重定向,就能在上节例子的基础上实现加密隧道,比如可以nc…|mcrypt…,然而我没有成功!所以我不得不使用别的方案。

幸亏有一个好用的ncat可以使用,我直接就用了。其实还有一个cryptcat可用,但我没有用成功,如果用成功的,希望可以告诉我。我本人对bash重定向的掌握一直都是半吊子水平,所以用起来当然没有玩Netfilter,XXN这般得心应手。决定使用ncat,命令如下:

# 机器1:
cat /tmp/tmp_fifo | ./tunio 2>&1 | ncat -vnl 333 --ssl >/tmp/tmp_fifo
#【以下特意给出屏幕输出,以显示正在初始化加密套件】
ifconfig tap0 10.10.10.10/24 up


# 机器2:
cat /tmp/tmp_fifo | ./tunio 2>&1 |ncat -nv 192.168.44.100 333 --ssl  >/tmp/tmp_fifo
#【以下特意给出屏幕输出,以显示正在初始化加密套件】
ifconfig tap0 10.10.10.20/24 up

同上面的netcat明文实例一样,依然在机器2上执行curl,在机器1上抓包,得到如下结果:


我不可能去解释这些密文是什么个含义,总之,这就是一条加密的隧道,构建成功了。至于说加密强度如何,不属于本文的范畴。

实现一个加密隧道:使用ssh

其实一开始我是希望使用scp来做tap0字符设备文件的传输的,中间我采用了一个fifo pipe来过渡,但是没有成功,貌似scp的源文件必须是Regular file才可以,不能是pipe。后来我决定放弃,进而寻找别的方案。毕竟,搞清楚scp并不是我的目的,其实我完全可以修改scp的源码使之适应pipe传输的,但是这样做毫无意义。

放弃了scp后,我转向了直接使用ssh来执行远程命令,ssh执行远程命令要比scp更加通用,而不仅仅只是一个“文件传输”机制。废了大力之后,也算是小成功了,仍然是上述的机制,我的命令如下:

# 机器1:
mkfifo /tmp/tmp_fifo
./tunio </tmp/tmp_fifo |ssh root@192.168.44.129 "cat >/tmp/tmp_fifo"
ifconfig tap0 10.10.10.10/24
# 机器2:
mkfifo /tmp/tmp_fifo
./tunio </tmp/tmp_fifo |ssh root@192.168.44.100 "cat >/tmp/tmp_fifo"
ifconfig tap0 10.10.10.20/24

这个更加对称,比使用netcat/ncat的方式更加对称,更加优雅,但是效率去不咋地。抓包我就不给出了,大家自己品鉴吧。

本质

总的来讲,扯以上这些,我就是想说明一个观点,构建一个加密的隧道其实非常简单,方法更是多种多样,如果有人问你什么是XXN,你给他展示一下用Linux2.4内核ethertap模块+netcat+mcrypt或者快速手写一个tunio,然后配合ncat或者配合ssh构建一条加密隧道,那么你就算彻底理解了XXN的本质,如果你能快速让QQ和tunio这类程序对接,那你就是百折不扣的高手!当然,我达不到这样的水平,我只是指路人,我只是鼓手。当然,如果你真的拿netcat,ncat,ssh和tunio,ethertap这类对接了,很多学院派,科班生会觉得你这根本不是XXN,因为在他们眼里,XXN就是L2TP,PPTP,…,MPLS之流,你这自己搭建的,根本就什么都不是…其实我觉得这种学院派才是什么都不懂。

XXN在概念上满足两点即可,第一就是V(虚拟),第二是P(专用),这两点分别用Overlay和加解密技术完全可以完美表达。至于是L2TP,…等,它们只是成熟的标准之作罢了,但完全没有做到大道至简。

感谢老一辈程序员使我可以写就本文

这部分本想放在文章最前面的,但是怕喧宾夺主,所以移到了最后。

说实话,本文根本没有任何技术含量,但是绝对可以引发人们的思考。在我2006年刚刚参加工作的时候,我什么都不懂。但我跟一帮上世纪90年代以及21世纪前5年的程序员一起学到了很多的东西,比如用鼠标线上网,用串口联网,…他们说以前的时候,以太网卡和网线是稀缺的,反而PS/2,RS232是常见的,人们普遍都是使用物理单机,然后依靠软盘,刻录光盘,后来的U盘作为介质来传输数据,这就好比我之前说的用卡车运数据时一样的。

如果说我在一块没有接线网卡(假设它没有接线依然可以运行)上抓到了一个数据帧,然后把这个帧放到了一个U盘里,将U盘通过卡车,轮船运送到了外国,在外国有人将这个数据帧注入到了一个没有接线的网卡里,那么在协议栈看来,这个主机就跟从网线上收到了这个数据帧是一样的…所以说,网线只是个介质。

在2010年的时候,我第一次知道了TAP网卡这种东西,发现IP数据报文还可以通过一个字符设备读到用户态应用程序,进而被加密后通过socket传输出去,我觉得这太妙了!这难道不是跟用串口联网一样的道理吗?用串口和用socket唯一的区别就是,前者将原始IP数据写进了串口,而后者将原始IP数据写入socket,在应用程序看来,这没什么不同,都是写入了一个文件而已。之所以可以这么理解,得益于UNIX的两个传统,一个是IO的文件本质,一切皆文件,读写文件即可,第二个是分层的协议栈模型,这使得Overlay成为了可能!

好吧,我来总结一下,其实没什么好总结的,几乎都一样:

  • 串口联网:IP字符设备fd<—>APP<—>串口字符设备
  • OpenXXN:IP字符设备fd(tun/tap)<—>OpenXXN<—>socket
  • 本文范例:IP字符设备fd(tun/tap)<—>netcat/ncat/ssh<—>socket

大家都一样,最终大家都回归到了最开始的状态!中间的IPSec之流实乃昙花一现也。

鉴于此,我将我本文的简单返璞归真的方法自诩为另一种革命,承接于IPXXc,OpenXXN,BadXXN。如果你能玩的得心应手,就可以出神入化,随心所欲了,你的XXN数据将不再建立在协议的基础上,它甚至没有自己的协议,完全传输裸数据,因此也就没有任何的可以识别的Pattern。

不管是用USB联网,还是用串口联网,或者用任意APP上网,我们都要记住,这完全得益于一切皆文件以及分层协议栈的馈赠啊。你和想建立XXN连接的主机之间,无非有两种连接方式,第一种就是物理直连,即通过网线,串口线,USB线,无线等连接起来的,另一种就是逻辑连接,即虽然不是物理介质之间连接的,但却是可以通过TCP/IP协议栈连接在一起的,比如用一对socket就能互相通信。不管以上哪一类,XXN连接在乎的是只要能和对端保证TCP/IP可达即可。

推荐一本书,《全球城市史》,今天周六刚刚看完,虽然篇幅比较短小,但确实很好看,从这本书里思考,你可以看到我本文一样的观点。古代大家自给自足,都待在家里做工,后来大家集中式地先后走向工厂,走向写字楼,走向园区,如今随着交通和通讯技术越来越发达,大家再次回到了家里工作,同样都是在家工作,但层次却完全不同,可能在未来几十年,我们会再次回到男耕女织的生活,但是和古代的男耕女织却完全不同,古代的男耕女织是无组织的个体行为,而未来的男耕女织却是一个整体组织中的一环…以前,没有网线,网卡匮乏,大家用串口,USB,鼠标线等做Overlay,如今网络资源过剩,大家用TCP/IP做Overlay…网络技术在更高的层次上实现了回归。

想象一下大型机工作站时代,大家通过哑终端接入这些超级计算机,自己的终端可能离计算机很远,终端仅仅提供输入输出功能,根本没有计算功能,慢慢的计算资源逐渐移到了终端,这便开启了个人计算机时代,接下来,随着计算需求的增加,计算资源有一次集中了,这次不叫大型机了,这次叫云端,同样的,云计算也是在更高的层次实现了回归。

用netcat,SSH构建IP层Tunnel相关推荐

  1. 用netcat,SSH构建IP层Tunnel(转载)

    2017/05/06 [关于题外话在最后] 写作本文主要基于两点,首先是因为我前段时间写了几篇关于XXN的新解,收到了很多的邮件反馈,我也思考了很多,另一个方面是因为很多人问我怎么用QQ,P2P搭建一 ...

  2. linux 内核网络协议栈--数据从接收到IP层(二)

    此处主要讲的是从数据来到,中断到最终数据包被处理的过程. 首先来介绍一下IO端口访问问题,内核提供了这样一组函数处理: /kernel/io.c中 inb( ).inw( ).inl( )函数 分别从 ...

  3. IP 层收发报文简要剖析4--ip 报文发送

    无论是从本地输出的数据还是转发的数据报文,经过路由后都要输出到网络设备,而输出到网络设备的接口就是dst_output(output)函数 路由的时候,dst_output函数设置为ip_output ...

  4. 一文讲解Linux 内核网络协议栈-数据从接收到ip层

    [推荐阅读] 一文了解Linux上TCP的几个内核参数调优 一文剖析Linux内核中内存管理 分析linux启动内核源码 此处主要讲的是从数据来到,中断到最终数据包被处理的过程. 0:首先来介绍一下I ...

  5. 利用ISCSI存储技术构建IP存储网络(概念篇)

    一.iSCSI的概念iSCSI是一种在Internet协议上,特别是以太网上进行数据块传输的标准,它是一种基于IP Storage理论的新型存储技术,该技术是将存储行业广泛应用的SCSI接口技术与IP ...

  6. linux内核网络协议栈--ip层报文转发之ip_local_out()函数(六)

    IP层本地报文发送有两个函数ip_local_out和ip_local_out_sk,实际实现两者是等同的,因为本地发送的报文,skb必然关联着一个sock对象. 1.ip_local_out函数 s ...

  7. linux 内核网络协议栈--IP层开始直到包被处理(三)

    先看看ip头结构: struct iphdr struct iphdr {#if defined(__LITTLE_ENDIAN_BITFIELD) // 小端__u8 ihl:4, // 首部长度( ...

  8. auto drop ssh failed ip address

    #/bin/bash #auto drop ssh failed ip address #author by efoni 2018.7 SEC_FILE=/var/log/secure #如下为截取s ...

  9. DL之DNN优化技术:采用三种激活函数(sigmoid、relu、tanh)构建5层神经网络,权重初始值(He参数初始化和Xavier参数初始化)影响隐藏层的激活值分布的直方图可视化

    DL之DNN优化技术:采用三种激活函数(sigmoid.relu.tanh)构建5层神经网络,权重初始值(He参数初始化和Xavier参数初始化)影响隐藏层的激活值分布的直方图可视化 目录

最新文章

  1. iOS沙盒路径及路径下数据的存储和读取
  2. python关闭线程根据id_python之线程相关操作
  3. idea找不到Hide empty Middle Packages???
  4. * 有1、2、3、4四个数字,能组成多少个互不相同且无重复数字的三位数? * 把这些数都输出出来,并且输出总共的个数。
  5. 20190616 IDEA-每次修改JS文件都需要重启Idea才能生效解决方法
  6. SpringCloudAlibaba--Seata简单案例
  7. C++ STL map和multimap的简单使用
  8. Linux学习之在线安装mysql
  9. 【数据结构】顺序线性表的几种常用方法
  10. Android 12新功能:使用SplashScreen优化启动体验
  11. python批量png转ico
  12. 【重磅】英国脱离欧盟,英国首相卡梅伦宣布辞职
  13. Java--实现简单的音频(mp3格式)播放
  14. Project build error: Non-resolvable parent POM
  15. 关于网站域名备案流程
  16. 关于提高信息传输率三个方法之一——增加频带宽度
  17. python docx转换成txt文本
  18. 国庆回家计划满满最后却摆烂这件事(解决?)
  19. 腾讯T3团队整理,好文推荐
  20. ROS melodic+Astra s编译运行ros_astra_camera实录(踩坑没填完)

热门文章

  1. 真实案例分享:MOS管电源开关电路,遇到上电冲击电流超标
  2. 微信小程序云开发-批量上传文件到云储存空间
  3. 可运营快递查询微信小程序源码
  4. 工业控制系统的安全防护建议
  5. Multisim--软件相关使用技巧
  6. win10---血战上海滩
  7. 项目编译不成功原因之一的引用jar包问题
  8. cocos2d-x公开课视频已经发布
  9. 分享5个宝藏小网站,工作学习都能用到
  10. luogu P4233 射命丸文的笔记