前提:
如果想看明白本文在说什么,必须首先听一下我的两个也许不太正确的结论,那就是:1.如今的所谓现代操作系统实在太复杂了,保护模式的特权模型也许从一开始就设计错了!
2.Linux内核的出现助长了宏内核思想的快速蔓延,实际上微内核的思路更加正确(微软的操作系统本来是微内核的,可是,却融入了很多宏内核的框架)!
以下我来解释一下上面的两点。首先看保护模式,是Intel的x86体系助长了特权模型的全面胜利,使用这种模型的弊端如今已经显现,比如UNIX/Linux的root权限就是这种模式的体现,这种模型的粒度太粗,一旦获取权限,权力又过大,无法使能更加策略化的访问控制规则。Intel的保护模式采用了特权模型,CPU在任一时刻处在某个特权模式下,如果处在0环,那么它就被给与了最高特权,无人制约,一般的设备驱动代码都运行在0环,特权模式最终会带来集权,虽然Intel设计了4个特权环,但是一般的操作系统仅仅使用了0环和3环,所有的操作系统内核代码均享受刑不上大夫的特权,操作系统的设计者会将所有的和用户程序无关的系统全局的东西都放到0特权环运行,和用户的应用程序分开。简而言之,所谓的特权模式就是要么有特权要么无特权,对于和用户应用无关的系统级别的操作,要么你什么都做不了,要么就是什么都可以做!依照这种模型,宏内核也是情理之中了,所有的系统级别的和应用无关的代码,统一归到一个叫做操作系统内核的结构中。对于网络协议栈,当然和具体的应用程序无关,当然是操作系统内核的一部分,对于这部分,你怎能指望一个App可以对其为所欲为的定制和修改或者替换呢?
不能只破不立。既然基于特权的保护模式不好,那么什么好呢?个人认为基于认证机制的保护模式会更好些,起码更安全。试想一段拥有特权的代码出问题了或者被入侵了,会怎么样?谁能把它怎么样?正因为没人能把它怎么样才会出现如此多的诸如如何利用程序漏洞获取root之类的网络蠕虫。为何不采用认证机制呢?代码在执行前首先进行签名验证或者其它的认证机制,这样即便一段本来有某个任务执行权限的代码被入侵,它将无法通过认证,进而无法执行,对于程序bug,由于每一个操作都是基于认证的,因此其影响也是局部性的,不会像Linux内核的panic那样严重,对于进程间的内存隔离也一样,凭什么内核空间就能访问任意进程的内存,为何不能是不问出处,只要有令牌就能访问的那样大同?!这样操作系统就真的成了操作系统,再也没有内核的概念了,所谓的内核也就降级成了一个服务者,就像办证大厅里面的工作人员一样,没有什么特权,但是你却相信他或她能给你办证。正确的操作系统的各个服务部分应该组件化,它应该是一个服务于App的各个服务组件的集合,批次之间通过特定的渠道联络,不管是App还是系统服务组件之间,通信的前提是安全认证通过,没有特权通道!这就是微内核的思想,其背后有两大要旨:系统服务和App是平等的;系统服务组件是可替换的。一切的鉴权基于认证而不是预设的特权!
网络协议栈无疑也是一个系统服务组件,除此之外没有特殊性,然而在宏内核之下,修改或者替换它的难度在于你必须拿到特权。在没有特权以及怎么努力都无法拿到特权的情况下,你只能另起炉灶了,如何另起炉灶,这就是本文的目的。
开始:
目前的小型便携式终端越来越丰富,早就不再是PC+手机的含义了。那么在这些终端上开发应用当然也就是逃不过的了,普通的应用开发还是比较容易的,毕竟如果一个平台要想占据移动互联网的一块领地,开放式的生态系统是必须的,厂商或者设区一定提供了完备的接口以及文档。然而如果你要开发的是一个和底层高度相关的服务,恐怕这些就不够了,以下是一些例子:
1.Android的底层是Linux,应用接口却是Java,为了支持C代码,其提供了NDK,然而C代码如何和操作系统的网络操作接口互动呢?比如,没有root的时候,如何执行iproute2命令设置一些策略路由以及带src参数的路由项;
2.Android系统上如何开发基于TAP模式虚拟网卡的应用;
3.iOS平台如何调用底层的网络操作命令;
...
这些都是实实在在的问题,厂商不开放这些机制的原因可能是在接口设计的时候自认为取了一个最大集,他们过于关注普通应用接口是为了防止底层操作带来的混乱以及其它不安全因素。这个是可以理解的,然而上述问题还是得要有办法解决。
对我个人而言,我并不需要系统导出诸如文件系统,内存管理等机制给我,我唯一需要的接口就是网络操作接口,既然既有平台的接口没有给开放出来,那就想办法绕开它,把它旁路掉,这使我想起了一些词汇,tinytcp,uIP,lwIP...这些小型甚至微型的TCP/IP协议栈旨在部署在嵌入式系统之上,特别的,对于uIP而言,其WIKI页面上还有以下一句:
uIP's native software interface is designed for small computer systems with no operating system.
注意加黑的那一句,它可以部署在没有操作系统的嵌入式平台上,这句话特别重要,如果一个操作比如Android,iOS等不让你触动底层的操作系统接口的话,对于开发者而言,它和没有操作系统有什么区别?!权当把这些封闭或者半开放的东西当成“with no operating system”的平台吧!
那么接下来要做的就是想办法把这些协议栈移植到这些恼人的平台之上,如果真的是“with no operating system”的平台,还比较容易些,好就没有从无到有的喂养一个系统了,随便选一款小小协议栈,无非就是构建一个交叉编译环境,之后没完没了的debug,panic,debug,panic,虽辛苦,但毕竟痛并快乐着。可是这些Android,iOS类的平台,并不是对于每一个嵌入式协议栈都适合,所以它们还真不如那些没有系统的裸板呢...这就涉及到嵌入式协议栈的选型问题,由于平台的限制,所有的操作都必须在用户态进行,也就是说不能和内核以及不开放的接口有任何关系。整个协议栈完全在用户态实现,然后和既有的Android,iOS之类的平台只保留一个接口进行通信,那就是把数据包导进用户态的协议栈,对于Android而言,可以用TUN模式的虚拟网卡接口,然后在用户态封装一个以太层,对于iOS而言,好像也可以用TUN虚拟网卡,但是具体如何使用我还不知道,我是看到App Store里面有OpenVPN后才知道iOS可能有TUN这回事的,当然它也可以不用TUN抓包而使用pcap也是一样的。
本文的目的是告诉你如何在封闭或半封闭的非root非越狱非特权模式的平台上为所欲为地操作网络,你唯一需要做的就是想办法把数据导入到你的用户态的嵌入式协议栈,我的选择是lwIP,它完全被编译成了一个用户态服务的一部分,但是如果仅仅为了解决TUN到TAP的适配,那么uIP就足够了。
所有的针对那些限制性极强的平台的网络操作需求如今只归并到了一个接口的需求,即只要满足能让我创建并操作TUN/TAP网卡或者使用pcap接口这二者中的一个即可,使用该接口和我移植的用户态协议栈通信,所有的网络策略化处理全都在用户态协议栈进行,如前文所述这个用户态协议栈是自己的程序的一部分,和系统级的网络处理没有任何关系!无论如何,Android和iOS是可以满足上述要求的,总的关键在于如果我们替换不了一个机制或者说替换工作量太大,那么就写一个完全自己可控的实现,然后把数据流想办法引入到自己的实现中,此处唯一的难点就是最后一句。
其实本文的思想很常规,如果你的PC机由于没有开放相应API做不到一件事比如说VPN加密怎么办呢?很简单,在PC机前面接一个VPN盒子来完成加密,PC的网卡和VPN盒子内口对连!只不过,手机不像PC,它的VPN盒子是一个App安装于手机本身,它也没有PC和VPN盒子之间的那根线,而代之以TUN/TAP或者pcap,仅此而已!
如果你要是问:万一这个用户态协议栈用到的一个功能没有权限呢?我知道有些人特别爱问这种问题,他们巴不得赶紧拆了你的台,有点偏的教育理念培养了很多只破不立的人。针对这种问题我的回答是:第一,这个问题跑偏了;第二,你的协议栈没写好或者没移植好;第三,这个问题我没碰到,因为我已经试过了!