基于Linux路由的访问控制

声明

关于访问控制,人民已经讨论了很多的方案,但是千万不要觉得某种方案是放之四海而皆准的,没有这样的东西!RBAC根本不适宏内核协议栈的操作系统(UNIX,Linux,and so on…我可能在之前的文章把它们弄反了,…),但是这并不是每个人民中的一员都知道,包括我自己!

总之,数据包在内核协议栈的处理过程中一定要符合“快,爽”的风格,不能导致后续的数据包排队,因为不同的数据包可能属于不同的“业务逻辑”。你不能假设后面数据包所代表的业务逻辑会容忍前面数据包耽搁太多的时间,每人签署任何协议,对于网络设备而言,每一个数据包都是平等的,如果高级一点的设备,每一个数据流都是平等的,记住,设备是为转发数据而生,数据的目的地并不是此设备!

所有的中间设备都TMD是浮云,一个所谓的WEB加速服务器如果不是利用了cache,那它就是骗局!有什么能和线速转发更快的呢?没有!CAO TMD的没有!只要Client和Server中间有一台设备,那它就TMD增加了延迟,别指望它能减少延迟,除非上帝便秘!因此,我们在设计中间设备的时候,目标绝对不是神话般的减少延迟,而是“在你能控制的范围内尽可能的快”,所以,应用程序的那种处理方式就不合适了,别指望用什么库可以提高效率,记住,最终的控制权永远在运营商手中,永远在那些不懂编程的网管手中,随便一个人剪断网线,再怎么TCP也不能保证你能优雅的发送FIN,谁能控制网络才能决定一切,网络的效率永远比端到端的程序的效率更重要!

本文的目的在于,你能通过一种方式,尽可能保证本地的处理,不考虑别的。在周六的一天的大雨中,激动,悸动!我不屑于网管嘴里的种种术语,也不怕编程,对于网管和程序员,也许奥托.冯.俾斯麦只能说,哀其不幸,怒其不争!

我虽然不懂OpenSSL,但是我是程序员!

之后

前面的一篇文章《Linux路由表的抽象扩展应用于nf_conntrack》中指出了可以用Linux的IP路由机制来实现访问控制列表,即Linux的ACL,在那篇文章中,只是说明了实现的可行性,然而最终,我却使用Linux的IP路由机制为任意的nf_conntrack保存了一个info字符串而已。本文来具体描述一下如何真的实现访问控制。

不是有iptables了吗

为何还要实现一套新的机制?因为:

1.iptables基于Netfilter实现,因此只能实现串行过滤,即一条一条的匹配;

2.匹配速度依赖于iptables规则的配置顺序,无法在内核实现统一的优化处理;

3.我不喜欢iptables,它在多核时代已经过时了,虽然可以基于Netfilter开发一个并行版本,但是太难。

因此,为了解决上面的问题,我决定实现一套另外的不依赖Netfilter的访问控制机制。很显然,我的目标之一就是在匹配的过程中,多个核心可以被调度起来。

实现原理

实现一套针对IP数据报的访问控制机制并不是简单的事情,然而最基本的框架却是很简单,那就是基于一个数据包的源IP地址和目标IP地址来决定这个数据包可以做什么。当然,除了IP地址之外,IP数据报文中的任意一个字段都可以参与匹配,甚至TCP/UDP协议头的字段也可以参与匹配,是的,但是本文不涉及那种复杂的情况,那些都很容易从本文的思想中扩展出来,因此本文仅仅将IP地址作为匹配要素。

如《Linux路由表的抽象扩展应用于nf_conntrack》 所述,一个Action可以保存在一个路由项当中,但是如果引入另外一个匹配元素目标IP的时候,这个Action将不能再存在于路由项当中了,它将作为源IP地址关联的路由项和目标IP地址关联的路由项之间的媒介,指示“该源路由项包含的所有IP地址可以对该目标路由项包含的所有IP地址实施Action”。Action可以是通过,拒绝,地址转换等所有可能的动作。如果不存在一个Action在两个路由项之间关联,则执行默认Action。可想而知,一个Action必须身兼两职,一方面它加入源路由项的Action链表,另一方面它加入目标路由项的Action链表中以关联二者。借用RBAC权限模型的术语,我可以把一个数据包的源IP地址看成是角色,而其访问的目标IP地址则可以被看作是资源,这里只是借用一下术语,真正的RBAC要比我这个复杂得多。一会儿我会说,我这个模型着眼在内核实现ACL的方式以及算法,而不是ACL模型本身。

数据结构要比图示更直接。由于每一个Action都会同时被链接到两个链表,因此一个Action命名为Xnode即可:

struct nf_action_node { //struct list_head list; // 和算法相关,见注解1 struct hlist_node nf_dst; struct hlist_node nf_src; struct nf_fib_node *dst_node; //反向指针,算法优化,见查找算法小节 struct nf_fib_node *src_node; //反向指针,算法优化,见查找算法小节 int extra; //int extra_len; //char extra[0];};

其中的nf_dst用于链接到目标路由表的某个路由项,而nf_src用于链接到源路由表的某个路由项,之所以使用hash list而不是普通的list_head是为了高效,我可不想在内核中遍历链表…当然,除了hash list,你也可以使用任何比较高效的数据结构,比如XX树之类的,另外,,路由表本身也可以不用hash,本文只是恰好使用了它而已,你完全可以使用TRIE树。上述的action数据结构中,值得注意的是最后注释掉的两行,我的原意是可以将任何类型作为action,也就是说你可以将extra定义为任何数据结构,但是由于这只是一篇blog,并没有逼着我完美的实现,因此点到为止,不再实现。另外,src/dst_node两个字段则是为了方便从nf_action_node找到它链接的两个路由项,虽然增加了两个node之间的耦合,但是却提高了效率,我们不是在OOD,而是在内核编程。

我们已经知道,一个nf_action_node同时链接到两个hlist中,这两个hlist分为归两个路由项所有,接下来就要定义路由项了:

struct nf_fib_node { struct hlist_node nf_fn_hash; struct hlist_head *act_list; __be32 fn_key; int len; unsigned int hash_value; struct nf_action_node *def;};

更多详情见请继续阅读下一页的精彩内容:

可以以心感悟,以此沉淀,足矣;耳听佳音,目极美好,

基于Linux路由的访问控制

相关文章:

你感兴趣的文章:

标签云: