我是靠谱客的博主 生动西装,这篇文章主要介绍OpenUOM的按需连接实现,现在分享给大家,希望可以做个参考。

写于2013/12/08

 

万恶的心跳!只是证明自己还活着...
为何不能到有事情来的时候再做,没事情时就休息呢?何必一直保持心跳呢?
上帝按照自己的形象,造出了人,人按照自己的喜好,造出了计算机,计算机也都有心跳。操作系统靠时钟中断这种心跳来推进机器的时间,然而后来Linux实现了NOHZ,即没有事情的时候,不再无谓地触发时钟中断,而是彻底halt,有事请来的时候,其它的中断会将机器唤醒,继续心跳。这种nohz机制节省了资源,最小化了bug触发率。
       IPSec UOM完全是按需连接的,隧道模式下,如果数据包过来,匹配到了加密策略,那么就看加密隧道有没有建立,如果还没有建立,则触发IKE协商,如果已经建立了,则直接通过隧道传输数据,当然IPSec也可以使用万恶的心跳...
       心跳保持的UOM长连接有几个问题,第一,如果收不到心跳,隧道就要被迫断开一次,这种断开事件会被审计为一次异常事件,因为按照正常看来,既然要保持长连接,那它就不应该断开,现在断开了,那是不应该的。第二,对于那种带宽属于稀缺资源的环境,心跳报文会占用可观的资源,比如3G用户,在没有实际数据传输的情况下,发送的心跳报文将是完全的浪费。
       UOM为何要保持长连接呢?难道随用随连不好吗?隧道的长连接难道仅仅为了展示自己是一个基础设施吗?对于网到网拓扑来讲,这可能是真实的,但是对于终端用户而言,长连接基础设施反而意味着压力,因为终端用户要为心跳报文买单。OpenUOM是一种高度灵活的UOM,它的灵活性以及好处在于你如何使它和外界关联配合,而不在于它本身!记住这一点非常重要!
       OpenUOM也可以实现按需连接,即没有数据的时候隧道断开,有数据来的时候创建隧道,并且在持续一段时间没有数据的时候再次断开隧道。这些事件中,除了”持续一段时间没有数据的时候再次断开隧道“这个功能(--inactive参数)是OpenUOM本身的功能外,其它的都需要外部的协助,首先我们要考虑的是,如何按需创建连接。
       通过iptables识别什么是”需要通过OpenUOM加密传输“的数据,识别到了以后,必须”堵住“该数据,直到隧道建立后再将它放行,堵在哪里呢?当然是堵在队列里面,如果隧道建立的时间比较久,那队列无疑会满掉,此时丢弃新来者是迫不得已的行为。有了此大方向,接下来就是具体的方案。在Linux上,几乎什么东西都不需要自己重做,以上这个功能可以通过iptables的QUEUE来实现,该target将数据包queue,用户态进程read这些排队的数据包到用户态,然后...
       然后该进程可以直接将包重新注入回内核,也可以在注入之前先去建立OpenUOM隧道,隧道建立以后再注入数据包,既然此时隧道已经建立了,路由规则该添加的也添加了,那么后续的数据包就不需要再QUEUE到用户态了,这个又是怎么实现的呢?还好,Netfilter有一个addons扩展,扩充了iptables的功能,我们需要的就是一个叫做condition的模块,该模块的使用使得多条iptables规则之间可以完成很多复杂的控制逻辑,如下一条:
iptables -t mangle  -A PREROUTING -d 192.168.1.1/32 -m condition --condition "UOM"  -j QUEUE
它的意思就是只有在UOM这个condition变量的值为1的时候,发往192.168.1.1的数据包才会去排队,否则就直接匹配下一条规则,不会被排队。到此为止,所有的准备工作基本就绪,剩余的工作即是如何把这一切拼接在一起形成方案。为了快速说明问题,我就不再使用OpenUOM举例了,我用一种等价的方式说明问题:
只有当到达192.168.1.1的数据包来的时候才建立其明细路由,持续没有到达192.168.1.1的流量5秒钟后删掉该明细路由。
这个足以说明问题了,它足够简单。虽然说即使它这么简单,最终还还是偷了懒,没有实现完全。首先添加上面列举的那条iptables规则,然后创建一个用户态进程,代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <linux/netfilter.h> #include <string.h> #include <stdlib.h> #include <libipq.h> #include <stdio.h> #include <libnetfilter_queue/libnetfilter_queue.h> #include <linux/ip.h> #include <signal.h> #define BUFSIZE 2048 static int condition = 0; void condition_handler(int num) { if (condition == 1) { condition = 0; } else { condition = 1; } } int main(int argc, char **argv) { int status; unsigned char buf[BUFSIZE]; struct ipq_handle *h; signal (SIGUSR1, condition_handler); h = ipq_create_handle(0, 2/*PROTO_IPV4*/); status = ipq_set_mode(h, IPQ_COPY_PACKET, BUFSIZE); do{ status = ipq_read(h, buf, BUFSIZE, 0); switch (ipq_message_type(buf)) { case IPQM_PACKET: { ipq_packet_msg_t *m = ipq_get_packet(buf); size_t data_len = m->data_len; //读取到有数据包后,触发执行启动UOM脚本的命令 system("/home/zhaoya/start_UOM param1 param2 paramX"); //等待start_UOM命令执行完毕,它会建立UOM隧道,建立完成发送信号给该进程 while(!condition) { pause(); } //成功建立隧道以后,将数据包原封不动地重新注入回去 status = ipq_set_verdict(h, m->packet_id, NF_ACCEPT, data_len+sizeof(struct iphdr), (char*)m->payload); break; } default: break; } } while (1); ipq_destroy_handle(h); return 0; }


上述程序很简单,逻辑上它就是读取queue的数据包,然后触发命令建立隧道,建立好隧道后,发送信号给进程,进程接着将数据包重新注入。隧道建立好以后,还需要修改UOM这个condition变量的值,使后续的数据包不再queue,如此策略化的事情用脚本封装是比较好的选择:

 

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash # 模拟创建隧道,添加一条路由 ip route add 192.168.1.1/32 via 172.16.49.88 echo 0 >/proc/net/nf_condition/UOM killall -SIGUSR1 queue # 模拟5秒没有流量 sleep 5; # 模拟断开隧道 ip route del 192.168.1.1/32 echo 1 >/proc/net/nf_condition/UOM killall -SIGUSR1 queue


经过测试,效果非常不错!但是那个用户态进程实在需要再润色润色啊。
       最后,我觉得最重要的不是如何想出解决问题的办法,而是想出如何验证你的解决方案是正确的方法,在你不可能实际证明的时候,或者当你实际证明要付出很大代价的时候,就要想出模拟的办法。但是很多人只是闷头做自己擅长的事情,如果模拟不了15454500053535300042453535004343535350043534647430043532578053535353005330535305454636365363530046464640535353500个用户,他们就要想办法模拟146560053353666535005353535305463320465479024214794546424235364657007684000646465789032558970032646320053646758320个用户,精确本身没有错,错在知道精确的尺度和粒度以及层次更加重要。
       不要认为什么东西都可以通过“设置”解决,远远不是!你需要的是:删除!

最后

以上就是生动西装最近收集整理的关于OpenUOM的按需连接实现的全部内容,更多相关OpenUOM内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(72)

评论列表共有 0 条评论

立即
投稿
返回
顶部