OpenVPN 中虛擬ip地址的自訂分配–總結

來源:互聯網
上載者:User

OpenVPN中自訂IP地址分配有兩種實現方式,一種是寫一個plugin,plugin_call這個調用可以被添加到任何地方,OpenVPN中的plugin掛載點也可在任意位置定義,因此利用plugin來自訂IP地址分配策略就有兩種方式,第一種方式是可以新定義一個plugin掛載點,緊接著動態分配IP的代碼,然後在plugin中實現策略,當然具體到如何將已經分配的ip地址的指標傳入plugin中還需要前文中介紹的方式,將ip地址的記憶體位址作為環境變數加入到env_set,然後...,這種方式需要修改OpenVPN的代碼,要麼自己定義一個掛載點,要麼在已有的CLIENT_CONNECT中加入新的記憶體位址環境變數;第二種方式是一種標準的方式,也就是OpenVPN建議的方式,那就是在multi_connection_established中做文章,OpenVPN的代碼不需要做任何修改:
static void multi_connection_established (struct multi_context *m, struct multi_instance *mi)
{
...
    if (plugin_defined (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT)) {
        struct argv argv = argv_new (); //下面將要產生一個臨時檔案的名字,該檔案名稱中包含隨機數。
        const char *dc_file = create_temp_filename (mi->context.options.tmp_dir, "cc", &gc); //由於這個將要產生的檔案只在此掛載點被使用一次,用來讓OpenVPN得到配置資訊,比如IP地址或者路由之類,因此用完就要被刪除,可以也可能刪除失敗,因此為了防止別的client連入時誤用此檔案,因此需要隨機產生一個檔案,從而使得檔案名稱不可預測。
        argv_printf (&argv, "%s", dc_file); //將臨時檔案名稱加入plugin的argv參數中
        delete_file (dc_file); //既然我們已經得到了需要建立的檔案名稱,刪除建立的檔案
        if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT,&argv...) != OPENVPN_PLUGIN_FUNC_SUCCESS) {
           ... //multi_client_connect_post中將實現抉擇,如果plugin定義了IP地址,那麼將在此函數中刪除動態分配的ip地址
            multi_client_connect_post (m, mi, dc_file, option_permissions_mask, &option_types_found);//此函數會讀取由plugin產生的設定檔進行參數配置
              ...
    }
...
}
然後在plugin中這麼實現即可:(首先要在open回呼函數中註冊OPENVPN_PLUGIN_CLIENT_CONNECT掛載點)
OPENVPN_EXPORT int openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[])
{
        int rv = -1;
        struct plugin_context *context = (struct plugin_context *) handle; //plugin_context自訂,可以包含你需要的任何變數
        const char *YYY = get_env ("XXX", envp); //get_env函數是一個遍曆匹配的過程,XXX是你希望得到的用於IP分配決策的client端變數,比如可以是認證的CN項,也可以是其真實的ip地址
        ... //還可以從envp中得到任何別的變數,不僅僅是client端的,只要是env_set中的均可以得到
        ... //實現虛擬IP分配邏輯,可以通過資料庫,可以通過unix-pam機制,...不過記住最終的虛擬ip需要寫入argv[1]代表的檔案中,也就是OpenVPN傳入的那個檔案名稱帶有隨機數的臨時檔案中,寫法很簡單:sprintf(cmd, "echo 'ifconfig-push %s'>>%s", 虛擬ip, dc_file);system(cmd);
}
dc_file中不僅僅可以包含client端的虛擬ip地址資訊,任何OpenVPN接受的配置資訊都可以,比如說路由資訊也是可以的。如此就完成了虛擬IP地址的自訂分配過程,如果不太擅長寫C語言,那麼還有另一種對等的方式可以使用,這就是寫一個指令碼:
#!/bin/bash
...
#同樣取出需要的環境變數(env_set中的變數),用$XXX取出即可
#可以取很多
...
...可以通過perl/python等連結資料庫或者不連結資料庫而使用別的策略...
echo ifconfig-push 分配結果 >> $1
echo ... >> $1
#end
是不是更簡單?以上指令碼的方式在OpenVPN中的依據是:
if (mi->context.options.client_connect_script && cc_succeeded) {
    struct argv argv = argv_new ();
    const char *dc_file = NULL;
    setenv_str (mi->context.c2.es, "script_type", "client-connect");
    dc_file = create_temp_filename (mi->context.options.tmp_dir, "cc", &gc);
    delete_file (dc_file);
    argv_printf (&argv, "%sc %s", mi->context.options.client_connect_script, dc_file, cert);
    if (openvpn_execve_check (&argv, mi->context.c2.es, S_SCRIPT, "client-connect command failed")) {
        multi_client_connect_post (m, mi, dc_file...);
    ...
}
最後我們注意一點,那就是和OPENVPN_PLUGIN_CLIENT_CONNECT這個宏對應的還有OPENVPN_PLUGIN_CLIENT_CONNECT_V2,並且在if (plugin_defined (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT))前面還有一個deprecated callback的注釋,這個注釋其實也是我們想辦法通過在env_set中添加一個IP地址記憶體位址的原因之一,正如注釋所說,plugin的OPENVPN_PLUGIN_CLIENT_CONNECT掛載點是通過一個臨時檔案來傳輸資訊的,而讀寫檔案是一件很麻煩的事情,整個讀寫過程不被OpenVPN地址空間監視,除了基於檔案系統本身的系統調用測試,你甚至不知道檔案建立/刪除成功了沒有,進程之外的IO行為很容易被別的進程截獲,更改,另外,檔案IO是一個很耗時的過程,在大負載下會降低效能,更何況,OpenVPN當前是一個串列的大進程,本來的設計缺陷加上每個client一兩次檔案IO更是雪上加霜。鑒於上述問題,OpenVPN推出了V2系列plugin掛載點,僅針對client串連這個掛載點來說,V2系列的plugin回呼函數多了一個傳出參數struct plugin_return,更改了V1系列掛載點plugin對env_set可讀不可寫的缺陷:
struct plugin_return {  //返回參數,也就是plugin的傳出參數
    int n;
    struct openvpn_plugin_string_list *list[MAX_PLUGINS];
};
struct openvpn_plugin_string_list { //傳出參數容器中的元素,連結成一個鏈表
    struct openvpn_plugin_string_list *next;
    char *name;
    char *value;
};
if (plugin_defined (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT_V2)) {
    struct plugin_return pr;  //定義一個傳出參數
    plugin_return_init (&pr);
    if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT_V2, NULL, &pr, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS) {
    ...//下面的調用中接著調用plugin_return_get_column來搜集被plugin配置或者更改的資訊,然後用options_string_import將之配置入OpenVPN的變數中
        multi_client_connect_post_plugin (m, mi, &pr, option_permissions_mask, &option_types_found);
    ...
}
static void multi_client_connect_post_plugin (...) {
    struct plugin_return config;
    plugin_return_get_column (pr, &config, "config"); //搜集被觸及的變數
      /* Did script generate a dynamic config file? */ //一看就知道此等代碼的作者也少不了複製-粘貼
      if (plugin_return_defined (&config)) {
        for (i = 0; i < config.n; ++i) { //遍曆每一個被觸及的變數
            if (config.list[i] && config.list[i]->value)
                    options_string_import (...); //將plugin中觸及的變數轉化為內部資料結構的欄位
    }
      ...
}
plugin的方式有V2系列調用,而指令碼的方式卻沒有,因為指令碼本身就在獨立的進程地址空間中運行,除了使用共用記憶體之外不可能有指令碼“傳出參數”一說的,綜合對比檔案和共用記憶體傳輸資料的複雜性和效能,還是不要使用共用記憶體了,所以依然使用檔案。
     實現自訂分配IP地址的方式除了plugin以及指令碼方式之外還有很多,實際上動態分配IP只是分配虛擬IP地址的一種方式罷了,在很多應用中,管理client比較重要,因此很多時候都是自訂的虛擬IP地址分配而不是通常我們自己玩時的動態IP地址分配。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.