对比MacOS上的PF与iptables

昨天同事问我怎么在Mac上配置策略路由,其实我也不知道!由于自己的实际需求,一直以来都想玩Mac网络功能,可是目光总被它炫烂的外表炫晕!今日同事这么一问我,顿时产生一种研究其究竟的欲望,还好,家里的电脑都是Mac系统(我非果粉,但老婆是),周末带女儿上完早教课,终于可以闲下来玩一番了...
       其实,MacOS绚烂的外表下面,是一辆坦克,其内核是具有学院派高贵血统的BSD UNIX,而我们知道,UNIX网络的强大,TCP/IP和UNIX的关系,不禁觉得Mac的伟大,将UNIX放在时尚OL的双膝上内窥外瞟,而这是多么伟大的一项征服!
       我的实验拓扑如下:
对比Mac OS上的PF与iptables
iMac模拟一台终端,WIFI关闭,Macbook模拟路由器,WIFI开启,两台电脑之间用一根网线相连接。
       首先,我希望在Macbook上配置NAT,将作为内网的iMac发来的数据包做源地址转换,此时希望的NAT是动态NAT或者类似Linux的MAQUERADE的那种。Macbook上的WIFI接口连接我家的路由器,因此需要将从WIFI口en1出去的数据包做SNAT,类似Linux的-o en1 -j MASQUERADE这种,我知道在BSD上可以通过多种方式来配置,有一种类似Linux iptables的工具,那就是pf,通过编辑/etc/pf.conf来实现配置是再好不过的了,因为pfctl拥有规则语法检测机制,可以帮你检查到很多错误,那么以上的这个需求可以通过下面的配置完成:
nat on en1 from 172.16.4.0/24 to any -> en1
其中,172.16.4.0/24是iMac所在段的地址,作为内网,而en1上则是所谓的外网地址192.168.1.108。实际上en1和地址段可以写成变量的形式,这里为了简单就直接写了,至于可以写成变量的形式,一会儿我要用这个和iptables和Cisco系统做对比。然后执行pfctl -e -f /etc/pf.conf就可以了!此时从iMac来ping 192.168.1.1路由器,就通了,然而从路由器却无法ping内网,这明显就是一个单向的转换,如果要做成双向的转换,那么就需要一个一一对应的转换了,在BSD中,这是通过binat实现的,主要在命令中,它加上了bi前缀,和bat区分开来,我们来看一下binat的配置:
binat on en1 from 172.16.4.10 to any -> 172.16.4.30
这样就建立了一个一一的映射关系,不管从路由器主动发起还是从iMac主动发起,都可以实现地址转换,这就是我上一篇文章费了好大劲在Linux上实现的那个功能,不管是BSD还是Cisco,都可以轻而易举的完成配置,而Linux却很难,即使使用RAWNAT也还得配置两条规则!!!PF的一对一NAT虽然也内置了match,但你可以用any来覆盖它,最关键的,它独立成了binat,而不是nat的一个配置选项。
       NAT功能已经玩转了,每一件事情我都是希望最先试成功一个最小集合,然后再慢慢拓展,当初第一次碰到iptables的时候也一样。我一直都有一个疑惑,为何iptables没有实现一对一的地址转换?注意,Netfilter仅仅是一个框架,想实现任何功能都可以,所以我没有埋怨Netfilter而怪罪iptables。这是一个基本的需求啊,试想一个WEB服务器在DMZ区对外提供服务,而它还需要主动的访问另外的外部资源,这在云环境下很常见的,如果没有一一映射NAT,光是ip_conntrack就要消耗多少资源啊,而仅仅为了转换一个地址保持一个流是没有必要的。任何其它的操作系统都可以很容易实现的功能,Linux为何没有?另一个疑问就是,Mac OS为何没有把常用的功能开放出来,比如在一块网卡上添加多个IP。我猜想,Windows是比较中庸的系统,它提供添加多个地址,但是却没有iptables,iproute2之类的强大工具,即使netsh也只能算个鸡肋,相反,Linux和Mac OS就比较极端,特别是Mac OS,它假设使用它的人不是爱折腾网络的人,一个OL或者西装革履的墨镜先生是不会托着下巴配置策略路由的...然而在它的命令行却提供了几乎完整的BSD命令集,设计者又一次假设,如果一个购买Mac的人是一个技术狂,比如像我这样,那么他总是会第一时间调出命令行的,一旦调出命令行,一切就都在他眼前了,这样,将添加多个IP这个基本不会用到的功能做进GUI还有必要么?不禁崇敬Apple人啊!这方面,Windows和Linux都应该学习啊,我就不说重量级UNIX(比如AIX,HP-UX)了,因为那玩意儿一般人也不会接触到,相反Windows,Linux的接触人员还是很多的,Windows自不必说,Linux也是一款大众系统,特别是安卓时代以后。附上本段的一个补充:说了那么多Mac OS上添加Secodary IP的问题,到底怎么添加呢?ifconfig enX $ip/$mask alias即可,关键字是alias!
       到了本文最关键的时候了。pf的配置和iptables的配置哪个更加好呢?这当然不是仁者见仁智者见智的问题,可以毫不犹豫的说,iptables完败了!写过iptables规则控制脚本的人都很头疼,就是你得为你的每一个点子写一条rule,然后噩梦就来了,你时刻要注意在什么情况下要删除这个rule,还要确保删除干净...复杂的业务逻辑往往使这一点很难得到保证,于是你就只能将业务逻辑简单化以迁就iptables...这到底是为什么??
       其根本原因就是iptables在每条规则层面上没有做到机制与策略分离,没有做到策略的可配置话,整条iptables规则的操作原子就是该条规则本身,比如你无法将一个match参数化,无法将一个target参数化,举以下例子:
iptables -A FORWARD -s 1.1.1.1 -d 2.2.2.2 -p tcp -j DROP
一旦这条规则设定了,如果有一天你控制的目标不再是2.2.2.2了,变成了3.3.3.3,那么你必须把这条规则删掉,然后再添加一条新的规则,你无法针对match对增删改操作。我们知道,一条规则蕴含的是一个判断逻辑,而具体的动作一定是要参数化的,这样才灵活,针对以上的例子规则,它蕴含的逻辑是,起始于某地到达某地的TCP包需要被丢弃!仅此而已,至于说陈述中的“某地”到底是什么,需要是可配置的。
       BSD的PF做到了这一点,它支持变量,宏等参数化配置概念。诸如最开始的NAT配置,像里面的IP地址这类配置,就可以像写BASH脚本一样定义。iptables的规则操作平面的缺失,确实是其硬伤,然而不能一棒子把它打死,我们知道ipset正是弥补这一缺失的努力成果之一,把匹配IP集合的定义移交给了ipset程序。我们期望见到的是很多的set,比如protoset,portset,stateset...在这方面,Cisco的NAT配置我们也可以看到类似的思想,Cisco剥离了ACL和NAT,ACL只负责match。
       但是并不是说PF就没有任何缺点,配置太庞杂了,比如配置策略路由:从哪个网口进来的访问包还从哪个网口出去。PF的实现方式是引入一系列硬配置route-to/reply,照这个逻辑,每一类的配置都需要引入一个配置参数咯,此法不妥啊,相反,Linux做的很好,通过mark的机制来实现策略路由,基于流的nf-mark可以标记一个流,很容易通过ip rule的fwmark来配置策略路由,策略路由本身并不是iptables负责的一块,iptables只管打mark,至于这个mark怎么用,它不管,策略路由在Linux中是单独实现的一块,和Netfilter没有任何关系。而PF则不同咯,其实它也有类似mark的tag机制,我觉得为了路由引入配置参数的方式不好。纵贯PF,它除了支持match参数化之外,其它的好像真的很乱,几乎所有的一切都可以用PF实现,NAT只是和PASS,BLOCK平行的一个action而已,另外,像什么代理之类的,统统都有相应的配置参数,我觉得这些都可以通过tag分出去的。
       遇到PF之前,总把iptables当成宝,实际上它真的就是!PF吸引人的地方在于其配置的可维护性。如果你有Mac,那么你就真的有了一个UNIX,可以折腾,任意的蹂躏,BSD UNIX强大的网络功能尽显眼前。

分享文章:对比MacOS上的PF与iptables
文章路径:http://ybzwz.com/article/pccjsi.html