OpenVPN实现了自己的一套ip地址分配的方略,正如《OpenVPN中虚拟ip地址的分配》中所述,但是如果我觉得这套方式不好的话,我又怎么修改之呢,或者说如何实现自定义的ip地址分配策略呢?OpenVPN是开源的,因此修改代码是一个选择,然而任何优秀的开源项目都不会傻到非要让用户修改代码从而获得自定义特性,比如linux内核提供了lkm机制,apache提供了module机制,Eclipse的Plugin机制,OpenSSL提供了engine机制等等,OpenVPN也不例外,它提供了两种外挂扩展机制,一种是script机制,另一种是plugin机制,script机制就是在特定的事件点上运行一个脚本,在OpenVPN地址空间之外运行,而plugin机制则是在特定事件点执行一系列的动态库中的函数,在OpenVPN的地址空间内部执行,两类方式都能实现ip地址的自定义分配,本文依次叙之:
1.script方式
multi_connection_established中有以下调用:
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); //将脚本名称和dc_file的名称传入了argv,作为要执行的脚本和其参数,环境变量就是该连接client的相关信息,脚本将来要根据这些信息来生成其ip地址
if (openvpn_execve_check (&argv, mi->context.c2.es, S_SCRIPT, "client-connect command failed")) { //执行脚本,在脚本中可以执行任意的事情,如果说要想自定义分配ip的话,那么就形成一个文件,名称是dc_file,里面写入ip地址的分配规则,具体规则就不详述了,肯定是有一定格式的。然后...
multi_client_connect_post (m, mi, dc_file, option_permissions_mask, &option_types_found);//然后在该函数里面会读取脚本生成的dc_file,读入该文件中分配的ip。
++cc_succeeded_count;
}
...
}
static void multi_client_connect_post (struct multi_context *m,
struct multi_instance *mi,
const char *dc_file,
unsigned int option_permissions_mask,
unsigned int *option_types_found)
{
if (test_file (dc_file)) { //如果script真的创建了一个保存有client端ip地址的文件,那么就做下面的
options_server_import (&mi->context.options, //读取文件,取得需要的信息
dc_file,
D_IMPORT_ERRORS|M_OPTERR,
option_permissions_mask,
option_types_found,
mi->context.c2.es);
if (!delete_file (dc_file)) //既然ip已经读取了,那么就删除文件吧
...//注释:之所以调用下面的函数是因为在multi_select_virtual_addr中,如果ip地址已经根据文件分配过了,那么就不再动态分配了,并且释放掉动态分配的entry
multi_select_virtual_addr (m, mi);
multi_set_virtual_addr_env (m, mi); //设置该连接的“环境变量”
}
}
就这样,以一个dc_file文件为中介,成功实现了ip地址的自定义分配,这只是一种方案,另一种方案就是所谓的plugin机制。
2.plugin机制
OpenVPN实现了可插拔的插件--plugin,类似于linux内核中的netfilter,就是在特定的几个挂载点或者叫监控点挂载一些钩子,然后当执行路径到达这些点的时候便会执行这些钩子,和netfilter非常类似,OpenVPN的plugin也是遍历调用的,说是在特定的点上挂载钩子,其实可以在任意的位置挂载,只要对你的逻辑有用,和netfilter一样,可以轻而易举地修改源代码使得任意点都可以成为挂载点。
所谓的plugin实际上就是一个动态库,和别的钩子机制一样,必须实现几个特定的函数,并且每个plugin必须申明自己在哪些点上挂载,只是OpenVPN的plugin中实现的函数并不是一般意义上的回调函数,而是名称特定的函数,这是为了让plugin最大限度解除对OpenVPN的依赖,对函数的调用是通过dlsym进行的,在windows下当然就是GetProcAddress了,
struct plugin {
bool initialized;
const char *so_pathname;
unsigned int plugin_type_mask;
int requested_initialization_point;
openvpn_plugin_open_v1 open1; //该函数定义自己需要挂载哪些点
...
openvpn_plugin_func_v1 func1;//该函数的实现就是一个大的switch-case或者if-elif-else
...//其他的回调函输
openvpn_plugin_handle_t plugin_handle;
};
int plugin_call (const struct plugin_list *pl,
const int type,
const struct argv *av,
struct plugin_return *pr,
struct env_set *es) //env_set就是一个name-value对的集合,但是OpenVPN却没有这么设计,而是...
{
...
struct gc_arena gc = gc_new ();
int i;
const char **envp;
const int n = plugin_n (pl);
bool success = false;
bool error = false;
bool deferred = false;
...
setenv_del (es, "script_type");
envp = make_env_array(es, false, &gc); //根据环境变量集合es生成一个char*数组,类似于main函数的argv,用于调用plugin的参数
for (i = 0; i < n; ++i) {
const int status = plugin_call_item (&pl->common->plugins[i],
pl->per_client.per_client_context[i],
type,
av,
pr ? &pr->list[i] : NULL,
envp);
switch (status) {
case OPENVPN_PLUGIN_FUNC_SUCCESS: //只要一个成功就设置success标志
success = true;
break;
...
default: //只要一个成功就设置error标志
error = true;
break;
}
}
...
if (type == OPENVPN_PLUGIN_ENABLE_PF && success) //在PF情形下,只要有成功的就成功
return OPENVPN_PLUGIN_FUNC_SUCCESS;
else if (error) //只要有一个plugin执行失败则失败
return OPENVPN_PLUGIN_FUNC_ERROR;
...//由此可见OpenVPN的plugin(s)链的调用和netfilter还有所不同,netfilter是首次匹配原则,而这里却使用了不同的策略
return OPENVPN_PLUGIN_FUNC_SUCCESS;
}
在plugin中,env_set的作用十分大,这个结构体也很重要,它几乎包含了一个client对于一个server所有需要的信息,以name-value对的形式保存,也很容易理解,在OpenVPN的一个连接的生命周期中多次需要一些信息,而这些信息几乎都可以从env_set中得到,另外该结构体作为script和plugin函数参数的来源扮演了一个很重要的角色,但是也有美中不足,make_env_array函数将env_set组织成了一个char*数组,该函数很简单:
const char ** make_env_array (const struct env_set *es,
const bool check_allowed,
struct gc_arena *gc)
{
char **ret = NULL;
struct env_item *e = NULL;
int i = 0, n = 0;
if (es) { //首先得到list的长度
for (e = es->list; e != NULL; e = e->next)
++n;
}//其实这么实现很丑陋,如果能在env_set中保存一个当前的数量,就不用这么实现了
ALLOC_ARRAY_CLEAR_GC (ret, char *, n+1, gc);
if (es) { //然后将每一个es元素加入到char*数组
i = 0;
for (e = es->list; e != NULL; e = e->next) {
if (!check_allowed || env_allowed (e->string)) {
ret[i++] = e->string; //而是将name和value合成一个字符串了
}
}
}
ret[i] = NULL;
return (const char **)ret;
}
void setenv_str_ex (struct env_set *es,
const char *name,
const char *value,
... )
{
...
name_tmp = string_mod_const (name, name_include, name_exclude, name_replace, &gc);
val_tmp = string_mod_const (value, value_include, value_exclude, value_replace, &gc);
const char *str = construct_name_value (name_tmp, val_tmp, &gc); //用"="将name和value连接成了"name=value"
env_set_add (es, str); //将最新构造的env_item加入进全局的set中
...
}
最后看一下env_set:
struct env_item {
char *string; //形如"name=value"的字符串
struct env_item *next; //下一个env_item
};
struct env_set {
struct gc_arena *gc;
struct env_item *list; //真正的list
};
在OpenVPN的根目录下有一个misc.c的文件,里面实现了env_set的各种操作,有add,delete,但是缺少search和modify,这就给在plugin中修改环境变量带来了巨大的不便(不要指望在script中修改,因为它和OpenVPN根本就不在一个地址空间),如果有形如env_set_modify的话,我们就可以随意在plugin中修改环境变量了,比如修改分配给client的ip地址。只可惜在调用plugin的func之前,env_set就被转化成了一个char*数组,这么就有去无回了,也就是说plugin只能读取OpenVPN的一个连接的环境变量而无法修改,那是否真的无法修改了呢?由于plugin和OpenVPN处于一个地址空间,那么我们可以将要修改的变量的地址作为新的环境变量item加入到env_set中去,当然这需要修改OpenVPN的代码了,必须在调用plugin之前加入新的item,然后在plugin的func中取出该地址再做修改,这就是在plugin中修改ip地址的办法中的办法。
比如我们想修改OpenVPN内核为client动态分配的ip地址,那么我们只需要在调用OPENVPN_PLUGIN_CLIENT_CONNECT挂载点的plugin之前做点手脚即可,我们需要这么做:
char address_buf[10] = {0}; //作为一个地址,10个字符够了,因为32系统中最大的地址就是0XFFFFFFFF(姑且不说顶端不能被userspace访问的OS内核),化为10进制后就是10个字符
unsigned int address = (unsigned int)&mi->context.c2.push_ifconfig_local;
sprintf(address_buf, "%d", address);
setenv_str (mi->context.c2.es, "ifconfig_pool_myaddress_address", address);
然后在plugin中仿照OpenVPN自带的plugin实例plugin/examples/simple.c中的get_env取出这个地址,然后修改之即可,具体怎么修改可以随意,也可以根据其他的环境变量采用更复杂的策略生成,指针都到手了,还有什么做不到的呢?
分享到:
相关推荐
基于openvpn的web管理系统,前后端分离设计。
openvpn的几种组网方式
openvpn 2.5.10版本,通过三板斧即可安装,证书生成需要的easy-rsa3.1.5版本也在压缩包里面。
openvpn 服务端需要的文件,通过helm 3 部署
Win10系统—VPN连接无法启动虚拟网卡任务.pdf
与原版open-build-master相比,我做了稍稍修改,已经包含Open虚拟专网2.5源代码和依赖项源代码,需要VS2019、ActivePerl、WDK10,可以直接按照我写的教程进行编译,100%可编译。
在Windows和Linux操作系统中,IP地址的使用和网络连接的管理方式有所不同,这可能是导致问题的原因。以下是一些相关的知识点: 1. **IP地址原理**:IP地址是互联网上的唯一标识,用于在网络中定位设备。它遵循TCP/...
此外,由于ISP路由策略的改变或用户使用虚拟私人网络(VPN),IP定位的准确性可能会受到影响。因此,在使用IP定位技术时,需要遵守相关法律法规,尊重用户隐私,并考虑到技术的可能误差。 总的来说,IP地址精确查询...
填写完毕后打印盖章交到当地电信即可,公司里有专线的最好备案一个,如果没有域名也可以备案,这个是IP地址备案哦,不备案影响你的VPN系统登录。你的域名要在申请的地方备案,例如阿里。两个地方都要备案。
它们负责网络中的命名解析、IP地址分配以及文件共享等功能。通过这些服务模块,管理员可以有效地管理和维护网络环境,确保服务的稳定性和安全性。 2. **普通PC软件包**:这部分可能包含常用的操作系统(如Windows或...
12. **IP地址问题**:IP地址随机分配,不同地址段不影响网络访问。 遇到网络连接问题时,可以参考这份指南进行自我排查,如设置IP地址,检查网络客户端安装等。如果问题仍然存在,应及时联系网络与信息管理中心获取...
这种工具可能通过代理服务器、虚拟私人网络(VPN)服务或者动态主机配置协议(DHCP)来实现IP地址的切换。 标签“ip”明确了讨论的主题,与IP地址及其相关的技术紧密相关。 文件“IP地址随意换.au3”表明这是一个...
易语言中可以通过网络组件调用这些接口,获取返回的JSON数据,然后解析JSON以获取IP地址。 4. **P2P技术** P2P技术中,节点之间可以直接通信,可以通过向其他节点询问自身的IP来获取公网IP。在易语言中实现P2P通信...
在现代网络环境中,动态IP地址分配是一个常见的需求,尤其在企业网络中,为了节约有限的公网IP资源,通常会使用DHCP(Dynamic Host Configuration Protocol)服务来动态分配IP地址。IP自动更新程序能够帮助用户在IP...
Tap-Windows Adapter 虚拟网卡 vpn 多联 神器
在上面的代码中,首先通过x-forwarded-for请求头获取客户端IP地址,然后将其分割以获取第一个IP地址,哪个就是客户端真实IP地址。 此外,还有其他方法可以获取客户端真实IP地址,例如通过Proxy-Client-IP、WL-Proxy...
2. **基于IP网络的专用数据网络,如IP-VPN**:虚拟现实技术在IP网络上的应用,可以构建专用数据网络,如IP虚拟私有网络(IP-VPN)。这些网络允许用户在虚拟环境中进行安全的数据传输和通信,为远程工作、协同设计和...
总结,配置DHCP服务是Windows 2008网络服务的重要组成部分,它能有效地管理网络中的IP地址分配,减少管理负担,提高网络效率,并为其他关键服务如DNS、Web、FTP和RAS/VPN等提供支持。正确配置和管理DHCP服务是构建...
DHCP 服务器负责 IP 地址的动态分配。在企业环境中,可以启用 DHCP 服务器上的多播作用域和 option 66,67。 DDC 服务器 DDC 服务器是虚拟桌面交付的基础架构服务器,负责分配桌面给授权用户访问,设定访问策略等。...
IP虚拟专用网(IPVPN)是另一种虚拟现实技术在计算机通信中的应用示例。IPVPN利用现有的互联网基础设施来构建一个安全的虚拟网络环境。它通过加密技术保护数据的安全传输,使得不同地理位置的用户能够像在同一网络内...