使用OpenVPN时的问题--用源代码进行分析

使用OpenVPN时,有几点需要注意:
1.如果不是OpenVPN客户端将自己的虚拟IP地址作为源地址发出的数据包,而是由其forward的数据包,那么就要在数据进入虚拟网卡之前做一个SNAT了,否则OpenVPN服务器将会拒绝接收这种数据包;
2.如果使用的是tap虚拟网卡模式,那么一定要将OpenVPN服务器的虚拟ip设置成网关而不能仅仅设置一个出口设备,因为tap模式需要进行arp,如果目的地址不在OpenVPN服务器上或者即使在OpenVPN服务器上但是其arp_ignore设置了不同的值,arp都会不成功进而无法发送数据;
3.在开启了client-to-client的情况下并且使用tun模式时,不要以为所有的client和server均在同一子网内,tun是点对点的,没有子网的概念,所以一个client或者其后的主机为了访问另一个client c2后面的资源,不能将网关设置成c2,除非做复杂的源/目的地址转换,因为OpenVPN服务器将不认识c2后面的目的地址。
4.如果使用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-解密”的过程即可
标签: 无
返回文章列表 文章二维码
本页链接的二维码
打赏二维码
评论列表
  1. [...]6. 使用OpenVPN时的问题--用源代码进行分析[...]

添加新评论