openvpn sitetosite模式配置

更新时间:2023-12-08 11:05:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

OPEN VPN SITE TO SITE 模式配置

一. 网络拓扑结构

客户端1Eth0:202.68.92.1vpn连接使用Vpn-server 端口:1194Eth1:192.168.20.1内网网关ip公网连接ip或者域名都可以Eth0:202.194.68.1vpn连接使用服务器资源Eth1:192.168.10.1内网网关ip客户端2Eth0:202.98.146.1vpn连接使用Eth1:192.168.30.1内网网关ip利用openvpn部署在三个网关机器上,使其对应的内网机器可以互通。

二.部署配置

首先配置ip转发 /etc/sysctl.conf添加 net.ipv4.ip_forward = 1

2.1 tun模式组网配置

服务端配置server.conf local202.194.68.1 prototcp devtun ca ca.crt

cert server.crt keyserver.key dh dh2048.pem

server 10.8.0.0 255.255.255.0 ifconfig-pool-persist ipp.txt

push \推送服务端后面的子网给客户端 client-config-dir /etc/openvpn/ccd route 192.168.20.0 255.255.255.0 route 192.168.30.0 255.255.255.0 client-to-client keepalive 10 120 tls-authta.key 0 comp-lzo

max-clients 100 user nobody group nobody persist-key persist-tun verb 3

配置/etc/openvpn/ccd/client1 iroute192.168.20.0 255.255.255.0 route 192.168.30.0 255.255.255.0 配置 /etc/openvpn/ccd/client2 iroute192.168.30.0 255.255.255.0 route 192.168.20.0 255.255.255.0

注: iroute:openvpn内部路由规则,tun模式下服务端会检查数据包来源是否为vpn主机发送的,如果不是则丢弃,配置iroute后可以使子网内的数据包在服务端通过检查

解释起来就是internal route,其实就是独立于系统路由之外的OpenVPN的路由,该路由起到了访问控制的作用,特别是是在多对一即server模式的OpenVPN拓扑中,该机制可以在防止地址欺骗的同时更加灵活的针对每一个接入的客户端进行单独配置。在多对一的情况下,必须要有机制检查访问内网资源的用户就是开始接入的那个用户,由于OpenVPN是第三层的VPN,而且基于独立于OpenVPN进程之外的虚拟网卡,那么一定要防止单独的客户端盗用其它接入客户端的地址的情况。在特定客户端的上下文中配置iroute选项,它是一个ip子网,默认是客户端虚拟ip地址掩码是32位,你可以在保证路由以及IP地址不混乱的前提下任意配置它,OpenVPN仅仅让载荷数据包的源IP地址在iroute选项中配置的子网内的主机通过检查,其它数据载荷一律drop。比如客户端虚拟IP地址是172.16.0.2,而OpenVPN服务器针对该客户端的iroute参数是10.0.0.0/24,那么只要载荷数据包的源IP地址在10.0.0.0/24这个子网中,一律可以通过检查。

iroute是OpenVPN内部维护的一个路由,它主要用于维护和定位多个客户端所在的子网以及所挂接的子网,鉴于此,OpenVPN对所谓的网对网拓扑的支持其实超级灵活,它能做到这个虚拟专用网到哪里终止以及从哪里开始。

配置iroute后可以在服务端启动日志中发现服务端增加了子网ip跟客户端之间的规则。

·客户端配置文件相似: 例子为客户端1的配置文件 cat /etc/openvpn/client.conf client devtun prototcp

remote 202.194.68.1 1194 resolv-retry infinite nobind persist-key persist-tun ca ca.crt

cert cecgw-client1.crt#配置为客户端对应的证书文件 key cecgw-client1.key#配置为客户端对应的证书文件 remote-cert-tls server tls-authta.key 1 comp-lzo verb 3

;route 192.168.30.0 255.255.255.0也可以不用服务端push路由在这里配置对应的路由规则

配置完成后使用启动命令启动vpn连接就可以了 openvpn –cd /etc/openvpn –config对应的配置文件 tun模式路由表如下:

服务端:192.168.0.0 的可以忽略

客户端2:

客户端1:

2.2 tap模式配置

tap模式不需要配置iroute规则,只需要配置路由规则即可实现site to site模式 服务端配置server.conf local202.194.68.1 prototcp dev tap ca ca.crt

cert server.crt keyserver.key dh dh2048.pem

server 10.8.0.0 255.255.255.0 ifconfig-pool-persist ipp.txt

push \推送服务端后面的子网给客户端 client-config-dir /etc/openvpn/ccd

route 192.168.20.0 255.255.255.0 10.8.0.2 route 192.168.30.0 255.255.255.0 10.8.0.3 client-to-client keepalive 10 120 tls-authta.key 0 comp-lzo

max-clients 100 user nobody group nobody persist-key persist-tun verb 3

配置/etc/openvpn/ccd/client1 route 192.168.30.0 255.255.255.0 配置 /etc/openvpn/ccd/client2 route 192.168.20.0 255.255.255.0

·客户端配置文件相似: 例子为客户端1的配置文件 cat /etc/openvpn/client.conf client devtun prototcp

remote 202.194.68.1 1194 resolv-retry infinite nobind persist-key persist-tun ca ca.crt

cert cecgw-client1.crt#配置为客户端对应的证书文件 key cecgw-client1.key#配置为客户端对应的证书文件 remote-cert-tls server tls-authta.key 1 comp-lzo verb 3

服务端的路由规则生成

192.168.10.0 255.255.255.0 10.8.0.1 192.168.20.0 255.255.255.0 10.8.0.2 192.168.30.0 255.255.255.0 10.8.0.3

客户端1的路由规则会生成

192.168.10.0 255.255.255.0 10.8.0.1 192.168.30.0 255.255.255.0 10.8.0.1 192.168.20.0 255.255.255.0 0.0.0.0

客户端2的路由规则会生成

192.168.10.0 255.255.255.0 10.8.0.1 192.168.20.0 255.255.255.0 10.8.0.1 192.168.30.0 255.255.255.0 0.0.0.0

附:vpn连接后拓扑图

客户端1Eth0:202.68.92.1vpn连接使用10.8.0.2Vpn-server 端口:1194Eth1:192.168.20.1内网网关ip公网连接ip或者域名都可以Eth0:202.194.68.1vpn连接使用10.8.0.110.8.0.3服务器资源Eth1:192.168.10.1内网网关ip客户端2Eth0:202.98.146.1vpn连接使用Eth1:192.168.30.1内网网关ip

tuntap模式的区别:

在计算机网络中,TUN与TAP是操作系统内核中的虚拟网络设备。不同于普通靠硬件网路板卡实现的设备,这些虚拟的网络设备全部用软件实现,并向运行于操作系统上的软件提供与硬件的网络设备完全相同的功能。

TAP 等同于一个以太网设备,它操作第二层数据包如以太网数据帧。TUN模拟了网络层设备,操作第三层数据包比如IP数据封包。

操作系统通过TUN/TAP设备向绑定该设备的用户空间的程序发送数据,反之,用户空间的程序也可以像操作硬件网络设备那样,通过TUN/TAP设备发送数据。在后种情况下,TUN/TAP设备向操作系统的网络栈投递(或“注入”)数据包,从而模拟从外部接受数据的过程

网络资料:

广播到底通过还是不通过OpenVPN呢?tap处理二层,tun处理三层,虽然tun两端ip是同一个子网,但是其二层却不是,广播是无法进行的,但是tap可以传输广播;由于windows的虚拟网卡驱动的特殊性,为了让windows也能进入vpn,OpenVPN和虚拟网卡驱动作了特殊且复杂的处理。本文详述之(注意,本文不介绍OpenVPN的各种专业术语,比如路由模式和桥接模式之类,需要的话请参考

OpenVPN的文档或者FAQ)。

怎么理解tun设备建立的是“点对点”链路,因为tun隧道是三层隧道,没有二层链路,更不必说二层广播链路了,我们知道数据链路层有两种通信方式,一种是点对点的方式,比如ppp协议,另一种是广播的方式,比如以太网,tun设备建立的隧道只有两个端点,隧道中封装的是IP数据报,虽然也需要arp协议来定位隧道对端tun设备的mac,然而如果有n台机器同时连接进一个虚拟网络并且属于同一个网段的话,其它机器是不会收到这个arp报文的,因为根本就没有二层链路帮忙广播转发这个arp报文

static inline unsigned intmroute_extract_addr_from_packet (structmroute_addr *src,

structmroute_addr *dest, structmroute_addr *esrc, structmroute_addr *edest, conststruct buffer *buf, inttunnel_type) { ...

unsigned int ret = 0; verify_align_4 (buf);

if (tunnel_type == DEV_TYPE_TUN) //如果是tun模式,那么直接处理ipv4包头,不再处理广播的情况,但是可以处理ip多播 ret = mroute_extract_addr_ipv4 (src, dest, buf);

else if (tunnel_type == DEV_TYPE_TAP) //只有在tap的情况下才解析二层地址。

ret = mroute_extract_addr_ether (src, dest, esrc, edest, buf); return ret; }

在XXX_addr_ether中会调用: if (is_mac_mcast_addr (eth->dest)) ret |= MROUTE_EXTRACT_BCAST;

在以后的代码中通过这个MROUTE_EXTRACT_BCAST来判断,进而进行广播。需要注意的是,是在OpenVPN中而不是在tun/tap驱动中进行广播的,可以把tap模式下的OpenVPN进程当成一个二层交换机,而SSL出入口和tap设备出入口是交换机上的物理接口,广播数据的一种行进方向是从SSL_read(虽然OpenVPN并不调用OpenSSL的libssl的接口)中将以太帧

读出并解密,然后进入OpenVPN进行广播,同时将数据交给自己一份,就是说往自己的tap设备中写入一份,广播给别的机器的数据还是要经过隧道的方式进行的,就是说将数据经过SSL协议封装然后通过socket发送。这个发送过程是通过multi_bcast来处理的,实际上multi_bcast并不是真正要发送数据,而是将待发送的数据连同其目的地信息先放入到一个容器中,然后等到时机成熟时统一处理这个容器。所谓时机成熟就是在multi_process_outgoing_link被调用时:

multi_process_outgoing_link-->multi_get_queue,正如multi_get_queue的注释所说,这种容器不但存放广播数据,还存放client-to-client数据以及多播数据,既然说到这里了,那么就说说client-to-client的一些话题。首先下面是一段错误的

论述:

client-to-client实际上目前是通过server来充当路由器的,所有的

client-to-client的连接都要通过server进行中转,中转数据包到达server之后首先通过ethX到达应用层并且解除ssl封装,然后server将此裸ip封装后的数据包写入tun0,通过路由之后,发现目的ip地址是到一个vpn的虚拟私有网段的一个ip地址,此时就又将数据从tun0发送出去从而又被openvpn接收,此时server查看自己是否设置了client-to-client,如果没有设置的话,并且刚刚查到的虚拟私有的ip地址不是自己的话,那么就说明这是一次client到client的通信,丢弃该数据包,反之就将之写入目的虚拟私有ip对应的真实ip的ssl连接,这个连接怎么查询得到呢?毕竟一个server可以拥有很多的client,其实有两种方式,一种是通过为每一个客户端配置一个tun虚拟网卡的方式,然后通过路由来实现区分,另一种方式就是在openvpn中解决,当数据从虚拟网卡发送时,实际上出去的是一个带有标准ip头的数据报,openvpn通过字符设备读取的就是这个数据报,它自己显然可以通过读取ip头得到目的地址,然后得知对应的真实ssl连接是哪一个。对比两种方式,第一种对效率影响很小,可以实现高速转发,但是管理复杂,第二种方式单点决策,管理简单又安全,但是在应用层解析数据对性能影响很大,可以考虑并发。

以上的论述错就错在OpenVPN是不可能如此复杂地实现client-to-client的,看了OpenVPN的源代码之后,总的来说OpenVPN的实现很简单,基本就是个转发器,看一下下面的流程: while (true) { ...

multi_process_io_udp (&multi);

... }

static void multi_process_io_udp (structmulti_context *m) { ...

if (status & SOCKET_WRITE) //写入socket

multi_process_outgoing_link (m, mpp_flags); else if (status & TUN_WRITE) //写入虚拟网卡字符设备 multi_process_outgoing_tun (m, mpp_flags); else if (status & SOCKET_READ) { //读取socket read_incoming_link (&m->top); multi_release_io_lock (m); if (!IS_SIG (&m->top))

multi_process_incoming_link (m, NULL, mpp_flags); }

else if (status & TUN_READ) { //读取虚拟网卡字符设备 read_incoming_tun (&m->top); multi_release_io_lock (m); if (!IS_SIG (&m->top))

multi_process_incoming_tun (m, mpp_flags); } }

其中multi_process_outgoing_link是写socket的操作,当然在真正写入之前要做SSL封装,如果顺着往该函数里面看,就会发现写往socket的数据源自于一个队列,就是multi_get_queue中处理的队列,于是问题就是谁将数据放入了队列,由于OpenVPN的逻辑就是上面的multi_process_io_udp,因此很显然是multi_process_incoming_tun将数据放入了队列,multi_process_incoming_tun最终调用了mroute_extract_addr_from_packet,这也就和本文的最开始的广播问题联系了起来。总的来说OpenVPN在multi_process_io_udp中首先形成了下面两个通道:

1.from tun/tap-->to socket 2.from socket-->to tun/tap

如果仅仅是这两个通道的话,client-to-client通信正如上面所说的那样,可是OpenVPN中还提供了另外的通道,那就是: 3.from socket-->to socket 正如下面的调用路径所示: multi_process_incoming_link: if (BLEN (&c->c2.buf) > 0){

process_incoming_link (c); //SSL解封装,内部将c->c2.to_tun.len设置为需要写入tun/tap的数据长度,也就是说默认是要写入到虚拟网卡设备的,但是在下面的逻辑中可能将c->c2.to_tun.len重新设置为0,什么情况呢?那就是数据已经处理过了的情况,比如这是一个client-to-client的通信,就没有必要往虚拟网卡设备写入了,也证实了上面那段话的错误。既然有to_tun,那么肯定有c->c2.to_link了,只是那是将从tun/tap读出的数据写往link也就是socket的。

if (TUNNEL_TYPE (m->top.c1.tuntap) == DEV_TYPE_TUN) { mroute_flags = mroute_extract_addr_from_packet (...); ...

else if (m->enable_c2c) { //如果c2c启用的话

if (mroute_flags& MROUTE_EXTRACT_MCAST) ...//组播 else {

mi = multi_get_instance_by_virtual_addr (m, &dest, true); //server作为“路由器”找到目的client的socket if (mi) {

multi_unicast (m, &c->c2.to_tun, mi); //单播发送,实际上就是放入了队列,中转源client到目的client的通信

register_activity (c, BLEN(&c->c2.to_tun));

c->c2.to_tun.len = 0; //凡是有这个语句的表示数据已经处理过了,不需要to-tun了,或者数据出错 } } } ...

如是说,在process_incoming_link就处理了client-to-client,根本就不需要再写入tun/tap设备,然后靠路由再写入tun/tap。同时从上述调用路径继续跟踪也可以看到基于tun的隧道是不支持广播的,因为MROUTE_EXTRACT_BCAST标志只在mroute_extract_addr_ether中被设置,而后者只有在tap模式中才会被调用,

同时也只有在tap模式下调用mroute_extract_addr_ether的时候才会处理arp,并且只在一个packet filter预编译宏启用时才被启用,该宏不启用的时候在tap设备模式下arp通过正常的tap隧道被传送,而arp正是一种链路层广播。那么问题又来了,如果是tun设备模式的话,怎样找到对端地址呢?这还要看linux kernel的tun驱动程序:

static void tun_net_init(structnet_device *dev) {

structtun_struct *tun = netdev_priv(dev); switch (tun->flags & TUN_TYPE_MASK) {

case TUN_TUN_DEV: //下面设置tun设备的点对点模式 dev->hard_header_len = 0; dev->addr_len = 0; dev->mtu = 1500;

dev->type = ARPHRD_NONE; //没有arp,就是一个点对点的连接,路由时直接从出口发出,不再arp

dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; dev->tx_queue_len = 10; break;

case TUN_TAP_DEV:

dev->set_multicast_list = tun_net_mclist; *(u16 *)dev->dev_addr = htons(0x00FF);

get_random_bytes(dev->dev_addr + sizeof(u16), 4);

ether_setup(dev); break; } }

最终就回到了最初的问题,tun设备没有链路层,它是点对点的,client到server的寻址是靠隧道进行,虽然是在一个ip网段,它们也不是靠arp来寻址的,毕竟arp寻的是链路层地址,对于没有链路层的隧道两端来讲,它还寻找什么呢?可是windows的tap设备的行为是不一样的,windows的tap驱动并不会像linux的tun驱动那样按照tun或者tap模式的不同分别设置网卡,于是就必须设置一个实际上不存在的ip地址代表对端,然后以此不存在的地址作为网关发送数据,事实上发往该网关的数据全部经由虚拟网卡发送,于是就是就走上了隧道上匝道,于是就有了net30的模式。

windows的tap-win32驱动始终将带有以太头的帧发出(可以抓包,看代码确认),因此tap-win32驱动并不真的支持点对点的ip连接,真正的点对点连接一般用于专用线路上,比如SLIP协议(很简单的串行链路协议,类似HDLC),这种点对点链路其实也并不是没有链路层,而是链路层特别简单,当然了肯定没有arp/广播等机制支持多点寻址了,windows机器一般用于个人电脑,而个人电脑一般使用以太网,根本没有个人电脑使用点对点链路的,于是windows的虚拟网卡基本就是一个以太网的虚拟适配器,这从它的名称tap-win32上也能看得出来。我们看一下tap-win32驱动的IO完成: CompleteIRP:

if (p_PacketBuffer->m_SizeFlags& TP_TUN) { //如果是tun设备模式的话就不将

以太头传输给用户空间

offset = ETHERNET_HEADER_SIZE;

len = (int) (p_PacketBuffer->m_SizeFlags& TP_SIZE_MASK) - ETHERNET_HEADER_SIZE; } else { offset = 0;

len = (p_PacketBuffer->m_SizeFlags& TP_SIZE_MASK); }

可以看出,windows上目前的虚拟网卡并没有直接的点对点链路的概念(不知道今后有没有人去开发),基本还是按照老一套机制来的,发送arp来进行以太网的寻址,而对于非windows的系统上运行OpenVPN的tun设备模式来讲,arp是不需要的,也是永远不会被发送的。对于windows来说,其arp是有人回应的,那么是谁回应的呢,既然事情是由tap-win32的驱动引起的,那么就不要在OpenVPN的代码中寻找这个arp回应了,还是在tap-win32驱动本身寻找吧,再次重申,OpenVPN本来在tun设备模式下不支持链路层,为了兼容windows才定制出net30的拓扑来模拟链路层的,tun模式虽然在ip层看来所有的ip处于一个网段,但是这同一个网段的ip之间的通信靠的却不是链路层(比如以太网的arp),而是各个客户端和服务器的点对点链路,如果是tap模式,那很显然是有链路层的,并且就是以太网,arp会在client和server之间传送。回到tap-win32驱动的问题,arp是怎么发送又是怎么接收的呢? DriverEntry:

l_Properties->SendHandler = AdapterTransmit;

NDIS_STATUS

AdapterTransmit (IN NDIS_HANDLE p_AdapterContext, IN PNDIS_PACKET p_Packet, IN UINT p_Flags) { ...

if (l_Adapter->m_tun) { ETH_HEADER *e;

if (l_PacketLength< ETHERNET_HEADER_SIZE) gotono_queue;

e = (ETH_HEADER *) l_PacketBuffer->m_Data; switch (ntohs (e->proto)) { case ETH_P_ARP:

... //由于tap-win32必然要实现以太网卡的标准,实际上它就是一个以太网卡,从而arp也是必须要处理的,然而tun模式下的arp是没有意义的,于是tap-win32不得不采用一种自问自答的方式来自圆其说。 ProcessARP (l_Adapter,

(PARP_PACKET) l_PacketBuffer->m_Data, l_Adapter->m_localIP,

l_Adapter->m_remoteNetwork, l_Adapter->m_remoteNetmask,

l_Adapter->m_TapToUser.dest); //此处自答自己发出的arp请求。 default:

gotono_queue; case ETH_P_IP: ... }

if (IS_UP (l_Adapter)) //以下将数据包push到一个读队列,以便从用户空间ReadFile读取

result = QueuePush (l_Adapter->m_Extension.m_PacketQueue, l_PacketBuffer); ... }

BOOLEAN

ProcessARP (TapAdapterPointerp_Adapter, const PARP_PACKET src, const IPADDR adapter_ip, const IPADDR ip_network, const IPADDR ip_netmask, const MACADDR mac) {

if (src->m_Proto == htons (ETH_P_ARP)

&& MAC_EQUAL (src->m_MAC_Source, p_Adapter->m_MAC)

...//检查确认这个arp是发送给自己的

&&src->m_ARP_IP_Destination != adapter_ip) {

ARP_PACKET *arp = (ARP_PACKET *) MemAlloc (sizeof (ARP_PACKET), TRUE); if (arp) {

// Initialize ARP reply fields

arp->m_Proto = htons (ETH_P_ARP);

arp->m_MAC_AddressType = htons (MAC_ADDR_TYPE); arp->m_PROTO_AddressType = htons (ETH_P_IP); arp->m_MAC_AddressSize = sizeof (MACADDR); arp->m_PROTO_AddressSize = sizeof (IPADDR); arp->m_ARP_Operation = htons (ARP_REPLY); // ARP addresses

COPY_MAC (arp->m_MAC_Source, mac); //mac实际上第3个字节比p_Adapter->m_MAC要大1,这里谎称mac是从“远端”过来的

COPY_MAC (arp->m_MAC_Destination, p_Adapter->m_MAC); //“远道而来”的arp-reply的目的地显然是p_Adapter->m_MAC,也就是自己 COPY_MAC (arp->m_ARP_MAC_Source, mac);

COPY_MAC (arp->m_ARP_MAC_Destination, p_Adapter->m_MAC); arp->m_ARP_IP_Source = src->m_ARP_IP_Destination; arp->m_ARP_IP_Destination = adapter_ip;

InjectPacket (p_Adapter, (UCHAR *) arp, sizeof (ARP_PACKET)); //模拟接

收arp-reply数据帧

MemFree (arp, sizeof (ARP_PACKET)); }

return TRUE; } else

return FALSE; }

最终的InjectPacket调用NdisMEthIndicateReceive和

NdisMEthIndicateReceiveComplete来使得虚拟网卡自己以为从物理链路收到了数据。最后总结的就是,tun模式下,linux系统或者unix系统从来不发送arp,windows发送的arp也不会到达OpenVPN进程,直接在tap-win32中模拟,也正是基于此,有了诸如net30之类的网络拓扑,至于广播的问题,严

格说来tun模式没有广播,即使windows的tap-win32驱动也没有将arp广播呈现到用户空间的OpenVPN,最后,OpenVPN的体系是简单的,实现是很对称的,基本就是一个tun和link之间的转发器。

本文来源:https://www.bwwdw.com/article/u53t.html

Top