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

 

123456789888888888888

 

使用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-解密”的过程即可

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值