使用OpenVPN时,有几点需要注意:
- 如果不是OpenVPN客户端将自己的虚拟IP地址作为源地址发出的数据包,而是由其forward的数据包,那么就要在数据进入虚拟网卡之前做一个SNAT了,否则OpenVPN服务器将会拒绝接收这种数据包;
- 如果使用的是tap虚拟网卡模式,那么一定要将OpenVPN服务器的虚拟ip设置成网关而不能仅仅设置一个出口设备,因为tap模式需要进行arp,如果目的地址不在OpenVPN服务器上或者即使在OpenVPN服务器上但是其arp_ignore设置了不同的值,arp都会不成功进而无法发送数据;
- 在开启了client-to-client的情况下并且使用tun模式时,不要以为所有的client和server均在同一子网内,tun是点对点的,没有子网的概念,所以一个client或者其后的主机为了访问另一个client c2后面的资源,不能将网关设置成c2,除非做复杂的源/目的地址转换,因为OpenVPN服务器将不认识c2后面的目的地址。
- 如果使用tap模式,并且开启了c2c,那么所有的client连同server共同组成一个虚拟子网,内部arp可流通,作为一个虚拟以太网和真实的以太网是一样的,OpenVPN服务器就是一个以太网交换机,可以设置任意的client或者server作为网关,这个意义上,OpenVPN服务器是一个三层交换机。
具体的代码都在multi_process_incoming_link中:
bool multi_process_incoming_link (struct multi_context *m, struct multi_instance *instance, const unsigned int mpp_flags)
{
struct context *c;
struct mroute_addr src, dest;
unsigned int mroute_flags;
struct multi_instance *mi;
bool ret = true;
...
if (BLEN (&c->c2.buf) > 0) {
process_incoming_link (c); //此操作进行解密,vpn作为客户段来说只调用这一个函数而不进行下面的地址判断
if (TUNNEL_TYPE (m->top.c1.tuntap) == DEV_TYPE_TUN) {
mroute_flags = mroute_extract_addr_from_packet (&src, //从ip数据报中解析出源/目的ip地址
&dest,
NULL,
NULL,
&c->c2.to_tun,
DEV_TYPE_TUN);
//确保源ip地址是自己的一个客户端的,这就是说,要在客户端进行源地址转换
else if (multi_get_instance_by_virtual_addr (m, &src, true) != m->pending) {
...//打印错误日志
c->c2.to_tun.len = 0; //不再往虚拟网卡写入
} else if (m->enable_c2c) {
...//多播情况,tun模式下,通过判断ip地址类型设置多播标志,tun没有arp
else { //以目的ip地址作为键值查找以目的地址为虚拟地址的自己的客户端,如果没有做目的地址转换的话,此处将找不到任何客户端,最终数据将发往虚拟网卡,等待内核标准路由系统的路由,很可能就发往默认网关了
mi = multi_get_instance_by_virtual_addr (m, &dest, true);
if (mi) {
multi_unicast (m, &c->c2.to_tun, mi); //向目的地址单播
register_activity (c, BLEN(&c->c2.to_tun));
c->c2.to_tun.len = 0;
}
}
}
} else if (TUNNEL_TYPE (m->top.c1.tuntap) == DEV_TYPE_TAP) {
mroute_flags = mroute_extract_addr_from_packet (&src, //从以太网数据帧中解析出源/目的mac地址
&dest,
NULL,
NULL,
&c->c2.to_tun,
DEV_TYPE_TAP);
if (multi_learn_addr (m, m->pending, &src, 0) == m->pending) {
if (m->enable_c2c) { //以下是广播/多播情况,比如tap模式下的arp就是广播,可以看出mroute_extract_addr_ether处理了arp,设置了多播标志,OpenVPN中,多播和广播统一处理
if (mroute_flags & (MROUTE_EXTRACT_BCAST|MROUTE_EXTRACT_MCAST)) {
multi_bcast (m, &c->c2.to_tun, m->pending, NULL);
} else { //下面根据目的mac来判断目的地是否是同一个虚拟子网的,如果是,则将以太帧发过去
mi = multi_get_instance_by_virtual_addr (m, &dest, false);
if (mi) {
multi_unicast (m, &c->c2.to_tun, mi); //单播发送以太帧
register_activity (c, BLEN(&c->c2.to_tun));
c->c2.to_tun.len = 0; //不再写入虚拟网卡
}
}
}
...
} else {
c->c2.to_tun.len = 0;
}
}
}
//所有其它情况都将受到的数据包写入虚拟网卡中,一旦写入虚拟网卡以后,数据包的流向就不再受OpenVPN的控制了,而是完全受内核路由表的控制
ret = multi_process_post (m, m->pending, mpp_flags);
...
}
在multi_process_incoming_tun中有下面一个判断:
multi_set_pending (m, multi_get_instance_by_virtual_addr (m, &dest, dev_type == DEV_TYPE_TUN));
if (m->pending) --->后续处理
这是判断目的地址的有效性,作为OpenVPN服务器来讲,从虚拟网卡中来的数据包就是要发往OpenVPN客户端的数据包,因此需要判断目的地址,和multi_process_incoming_link的判断相反。
结论是:tun模式按照ip地址路由,tap模式按照mac地址路由。在客户端,没有服务器端这么复杂的判断,只是经过“in-tun–out-link-加密,in-link–out-tun-解密”的过程即可