Linux 4.10中两个新特性与我的一段故事
在2014年到2015年期间,我在负责研发一款无线安全网关。事实上就是一个VPN。接入设备包含手机,xPad,盒子...这些设备的OS除了iOS之外,基本上都是基于Linux的Android。这个网关一般用于各种须要高性能加密通信的场合,在传输数据之前须要比較强的认证,服务端支持4G的加密带宽。支持复杂的接入控制和訪问控制,支持复杂的Qos。另外,还能够将两台甚至多台VPN网关设备对接形成一个复杂的网状混合拓扑,支持网中网,功能非常强大...广告做到此为止。
如今開始说说接入设备。也就是client的一些事。
在研发以及部署期间,事实上最头疼的不是网关端,而是接入设备端。也就是那些Androidclient。
典型的需求是。为一组App构建一个沙盒。沙盒里的App走VPN通道到网关,而沙盒外的App不受不论什么影响。比方说,交警使用的移动警务App的数据显然要进行保护,而交警在轮班期间无聊时看看新浪微博则不应该受到影响。且移动警务App和新浪微博不能互訪。
...
我以前花了比較多的精力去跟客户解释我们的Androidclient多么的“智能”,能够保证特定的且仅仅是特定的App的数据才会进入VPN隧道,这事实上是MDM厂商的活儿,对于我的产品,这仅仅是个附加的功能。
于是,为了实现这个功能,就不得不不断调研Android的某个版本号在非root情况下能否够操作路由表。操作iptables(这个可能性不大)。可不能够NAT,假设能够的话,是不是通过一个本地监听的代理程序能够完毕导流的工作...对于以TUN网卡实现的VPN而言。事实上这一切工作归根结底仅仅是一个需求:让特定的App数据发出时走特定的路由!
可惜。在之前的Android内核版本号中,这么一个简单的需求实现起来非常不易。如今Linux 4.10内核带来了一个新的特性,即为不同的uid配置不同的策略路由,这足以解决我的问题,仅仅是晚了大概两三年:
Add support for per-UID routing. It allows the administrator to configure rules such as:
# ip rule add uidrange 100-200 lookup 123.
This functionality has been in use by all Android devices since 5.0. It is primarily used to impose per-app routing policies (on Android, every app has its own UID) without having to resort to rerouting packets in iptables, which breaks getsockname() and MTU/MSS calculation, and generally disrupts end-to-end connectivity
还有一个比較激动人心的特性来自于路由和iptables的联动。
早在2014年年底,我在OpenVPN的程序里面内置了一张路由表,这是为了防止有人手工加入一条路由,这样就能够訪问不该訪问的资源了。
或许你会认为在服务端做足做够訪问控制不是非常好吗?是的。这个我也知道,我自诩精通OpenVPN。从源代码到配置再到优化重构。怎么可能不知道这个。令我激动的是Linux内核终于提供了一个不依赖OpenVPN的通用机制来实现这个需求:
Introduce an nftables rt expression for routing related data with support for nexthop (i.e. the directly connected IP address that an outgoing packet is sent to), which can be used either for matching or accounting, e.g.
# nft add rule filter postrouting ip daddr 192.168.1.0/24 rt nexthop != 192.168.0.1 drop
this will drop any traffic to 192.168.1.0/24 that is not routed via 192.168.0.1
看吧,这是使用nftables而不是iptables配置的。
这并非我对nftables的又一个鼓噪点,而是想表达iptables已经逐步迁移到nftables了。要是在两年前。我肯定会写一个iptables的模块,可是如今不会了。
...
如今说说我的还有一段故事。可从中窥见非常多网络技术。
以前有四个难题让我没日没夜几个月,只是幸运的是。这些问题终于都被我用Netfilter攻克了:
1.路由查找与转发开销比較大
起初我是借助优化后的nf_conntrack来解决这个问题的。
总的来讲就是将IP层的处理进行短路,借鉴于Cisco的CEF技术分离出一个转发表,然后将表项保存在conntrack项中。
后来我干脆抛弃了conntrack,而是直接使用了DxRPro这个结构来完毕查找,详细參见《以DxR算法思想为基准设计出的路由项定位结构图解》。
当初之所以使用conntrack是由于它能够高效定位一个流并保存流信息,而转发项能够保存在流中...假设有一种比定位流更加高效的方式,那就不须要conntrack了。事实证明我找到了这样的更高效的方式。
2.过滤条目太多,导致了收包性能急剧下降
饱受诟病的iptables filter被我彻底放弃了。取而代之的是nf-HiPac以及ipset。后来我又发现了更好的方式。当我在路由查找中把conntrack放弃了之后,我将其引入到了ACL匹配。详细来讲就是,我把流的首包ACL结果保存在conntrack表项中,后面直接使用,无需再匹配。当然。我的conntrack是被我优化过的,不是原生的。
3.Linux中无法配置双向静态NAT。
关于Linux的NAT,它本来就是保存在conntrack中的,仅对首包匹配NAT规则。然后对于兴许的包直接从conntrack取出后使用。所以说就无法是静态的。也无法是双向的。
我的做法是简单的实现了一个新的NAT机制:https://github.com/marywangran/static-stateless-2-way-NAT-on-Linux-with-iptables
全部以上这三个问题。除了最后一个,总的思路,就是一次查找,保存后多次使用。
面对这三个问题,解题思路以及解决方式差点儿占领了我这个博客的大量的篇幅。我也因此得到了公司的认能够及新的工作机会。
然而假设就此停步不前,那就可悲了。
网络技术一直都在迅速发展。每天都有新的东西冒出来,必须不断学习才干永远被公司认可,永远不缺新的机会。
4.Linux Bridge问题
我清晰地记得我在2010年3月19日去我上一家公司面试时被问到的一个问题:你对Linux Bridge有了解吗?我说有了解,于是我就试着说说看...事实上那个时候我也仅仅是看过Linux Bridge的源代码而已,好在面试官(后来成了我职业生涯中都要感谢并永远不会忘记的恩师...)所在的业务组并不以这个Bridge为主导,所以我也就蒙混过关了。充其量当时我也就知道Brdige的大致原理。更进一步的东西也说不清。
我本以为从此以后再也与Linux网络无缘了,可是非也...到了2011年底,我要做的事情奠定了我今后的技术路线。
说实话,我做的VPN产品可能囊括了全部的Linux网络技术,从下到上说吧,PCIE,MSI。软中断负载均衡,Bonding。Spanning Tree。Bridge调用IP Netfilter,IP层Netfilter。IP路由。策略路由,TCP分发,TUN字符设备,TLS加密解密(对称加密,非对称加密,摘要...),HTTP。HTTPS(我尽力去学习,却终于也没有学会http相关的应用层...),VRRP,HSRP,OSPF...期间我学会了ECC椭圆曲线算法。也知道了什么叫做双证书认证,可是我认为对我最重要的是我在2013年就改动了Linux Bridge的逻辑。使得其符合了SDN的思想!
What?Why?
2013年的时候,SDN已经被提出非常多年了。可是我并不知道这个概念,我确实是在2013年中下才知道的这个概念,好像当时还写了几篇软文(一般不懂装懂的人都会写软文),可是明确了SDN深意之时却在多年以后。
...
我关注SDN是由于我认为它跟我当时正在做的东西的思路不谋而合。我当时在网络层须要一种集中的控制机制,无论是路由策略还是QoS,都须要在一个中心端来集中控制,然后把策略下发到各个子节点。子节点没有什么智能逻辑,它仅仅是盲目运行中心端下发的策略而已,我这样的做法看似是与分布式网络控制机制背道而驰的,但我厌倦了Spanning Tree。RIP这样的不可控的东西。同一时候我也不喜欢TCP,不喜欢区块链,我须要找到一个理论依托来支持我的独裁式集中控制机制。后来在一天下班的路上,我用手机看技术站点,发现了SDN这个概念...
这个跟Bridge有什么关系呢?
我当时希望数据包绕过IP路由。直接运行集中式的VPN中心端下发的策略就可以。这些策略不仅仅包含路由,还包含别的非常多其他的。比方是二层透传还是三层路由。所以说我须要自己定义一个匹配链,每个数据包都要去匹配这个匹配链。一旦命中便运行该匹配项指示的动作。相似iptables那样。只是是直接借用了conntrack连接跟踪。这样匹配链就能够直接用conntrack本身了,然后在conntrack项中添加一个action字段,数据包进入内核协议栈之后会被无条件关联一个conntrack表项,然后直接运行该conntrack表项指示的动作就可以。
可是大家都知道。nf_conntrack是作用于PREROUTING点的。这已经位于IP层了。我希望的是在Bridge层就做匹配,然后依据匹配好的conntrack表项的action来决定是直接Bridge层转发还是broute到IP层。幸好。Linux Bridge有一个call ip netfilter机制(详见/proc/sys/net/bridge/bridge-nf-call-iptables),正合我意。可是这个call点是在决定了是否Redirect之后才运行的,于是我不得不颠倒二者的位置,參见《关于bridge-nf-call-iptables的设计问题》。
在我的这个设计中,系统的IP路由机制差点儿是不起作用的。
子节点VPN守护进程会从中心节点接收转发策略,并注入到conntrack表项,然后就能够来包转发了。我编程编的不好。但我对Linux内核协议栈流程非常了解。所以我能够做到利用一切现成的东西。小修小改就可以满足我的需求。我借用了conntrack,我借用了bridge-nf-call-iptables...
那个时候我还不知道OVS(即Openvswitch),要是知道有这么个玩意儿,我连conntrack,Bridge都不用了,细致看看,OVS实现我上面的需求。是不是非常直接呢?本质上来讲,OVS就是强化版的Linux Bridge,以前须要你前挪后移搬运的功能如今都集成在OVS内部了。OVS内部就有一个流表,并且还是多级的,能够接收SDN控制器注入的流表项,一切都是已经标准化的,现成的东西也多。我要是早几年知道有这么个OVS,那得降低多大的工作量啊。
我还没提VxLAN呢,也没提LISP,事实上本质上它们都是建立大二层的技术。我做事任意,崇尚草根之风,用OpenVPN建立了大二层,盖在了上海市的一个城域网中,这不就是Overlay么...
至于NFV。也只是是一个名词。我非常怀念我的那个花了无数个日日夜夜做成的东西,它里面包含了太多的东西,假设掌握了它,你也就掌握了网络的核心技术之脉络,在开发期间,接触了各种Linux内核网络的东西。在部署期间。有幸与CCNP/IE们各种交锋,掌握了核心网的非常多东西,甚至在送货的路上,深夜回家的路上。都会突然冒出非常多想法...故事还没有讲完。但我不想再讲下去了。对于给我机会做这事情的经理而言,除了感谢,还是感谢!
以下准备说说BGP和DCI的事情...假设明天有时间的话。