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規則,然後創建一個用戶態進程,代碼如下:

#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,如此策略化的事情用腳本封裝是比較好的選擇:

 

#!/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個用戶,精確本身沒有錯,錯在知道精確的尺度和粒度以及層次更加重要。
       不要認爲什麼東西都可以通過“設置”解決,遠遠不是!你需要的是:刪除!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章