Linux邻居子系统的细节之confirm-OpenVPN server模式的MAC地址学习

在《Linux实现的ARP缓存老化时间原理解析》一文中,我剖析了Linux协议栈IPv4的邻居子系统的转化,再次贴出那个状态机转化图,可是这个图更详细了些,因为它有一个外部输入,那就是confirm:

请注意,如果socket或者路由子系统在上层confirm了一个neighbour,那么该arp将持续留在reachable状态而可以不用转换到stale状态。这个特性是有意义的。请观察一个现象:
1.本机IP地址为192.168.1.10/24,直连的机器IP地址为192.168.1.20/24,从本机ping对端;
2.将本机的/proc/sys/net/ipv4/neigh/eth0/base_reachable_time设置为4秒,对端的对应值设置为8秒;
3.抓包发现每隔8秒左右会有一个一个arp request从对端发来,符合上述的图示;
4.抓包始终未见有本端到对端的arp request,按说应该每隔4秒左右发出一个的啊;
5.修改对端设备的IP地址为192.168.1.30/24,本端在间隔4秒左右后,马上发出了arp request;

以上现象有2个疑点:为何本端在reachable状态到期了仍然不切换状态,不重新解析对端IP地址?为何对端的IP地址修改掉或者删除掉之后,本端马上发现了这一事实,间隔reachable到期时间后发出了arp request?
       要澄清上述两个疑点,就要从下向上来逐步定位,当我设置了iptables规则在INPUT上禁止ping reply接收时,和修改删除对端IP的效果一样,ping马上发现了这一点,间隔4秒左右发出了arp request。因此问题就在ping本身,对ping进行strace,发现其sendmsg的最后一个参数为MSG_CONFIRM,查阅man手册,发现:
MSG_CONFIRM (Since Linux 2.3.15)
              Tell the link layer that forward progress happened: you got a successful reply from the other side.  If  the  link
              layer doesn‘t get this it will regularly reprobe the neighbor (e.g., via a unicast ARP).  Only valid on SOCK_DGRAM
              and SOCK_RAW sockets and currently only implemented for IPv4 and IPv6.  See arp(7) for details.

由于对端只是接收ping request,进而直接在协议栈中回应ping reply,因此不会涉及socket,并且也没有涉及路由子系统中关于redirect等关联的confirm,因此对端严格按照IPv4邻居子系统的状态图转换,而本端则每每收到ping reply,就会confirm,进而使arp表项维持在reachable状态,一旦收不到ping reply,便不会再confirm,等到reachable状态到期,便进入短暂的stale,delay状态了,进而进入probe发出arp request。
       说了这么多关于arp的一个细节,旨在解释一个tap模式的OpenVPN运行在multi模式,即server模式时的MAC地址学习的问题。
       我们知道,OpenVPN在tap模式下可以看作一个虚拟的交换机,而且是学习型的,它学习的内容为:为每一个multi_instance关联一个MAC地址链表,凡是来自该multi_instance的以太帧的源MAC地址都会进入这个链表。这个链表的作用在于为从OpenVPN服务端返回到客户端的数据包关联一个multi_instance。对于一个从OpenVPN服务端发往客户端方向的数据包,其目的MAC地址肯定在某一个OpenVPN客户端后面或者是OpenVPN客户端的tap网卡本身。OpenVPN服务端用这个MAC地址作为键值检查MAC/instance表,最终找出一个multi_instance发送出去。
       现在考虑一个主动从OpenVPN服务端发送的数据包,当它到OpenVPN进程的时候,由于事先没有从OpenVPN客户端过来的任何数据包供OpenVPN服务端学习,因此数据包必须丢弃,因为它无法对应到任何一个multi_instance。是这样吗?NO!因为你没有考虑到arp。在以太网发送任何数据包之前,都要经过arp解析对端的IP地址,而arp请求是广播,将发送到所有的multi_instance,只有有来自某个OpenVPN客户端方向的arp回应,OpenVPN服务端将会学习到一条MAC/instance表项。因此不存在数据包被drop的可能。
       但是,如果OpenVPN服务端后面的机器发送数据包的时候,IP/MAC地址映射已经存在了,那么就不会发送arp请求了,也就没有机会让OpenVPN服务端从arp回应中学习了,此时数据包将会被丢弃。是这样吗?是的,但是如果你看懂了IPv4邻居子系统的状态转换图,就会等待reachable到期时间后再次重试。由于根本不可能有数据从OpenVPN客户端发来,因此就没有机会被confirm,所有最久的等待时间就是reachable+stale+delay的定时器时间和。然而有一种情况,如果服务端这边设置了一条永久的arp表项,那就完蛋了。
       OpenVPN运行在tap模式时,可以通过arp来自动学习MAC/instance映射,那么运行在tun模式下呢?由于IPv4除了DHCP等不具备任何自动配置机制,且DHCP又不是必须使用的,因此只能手工配置,这个在OpenVPN中就是iroute选项,即Internal route。
       自动学习比较不易,那么如果你仅仅只是为了使用OpenVPN连接两个远程网络,那么就别用server模式了,使用p2p,即pointopoint模式比较好,它只是进行简单的tun-link-link-tun的转发而已。

时间: 2024-12-13 08:39:07

Linux邻居子系统的细节之confirm-OpenVPN server模式的MAC地址学习的相关文章

邻居子系统学习

所谓邻居,是指在同一个IP局域网内的主机,或者邻居之间在三层上仅相隔一跳的距离.而邻居子系统,则提供了三层协议地址与二层协议地址之间的映射关系,此外还提供二层首部缓存,以加速发送数据包. 在发送数据的时候,先进行路由查找,如果找到目的地址的路径,再查看邻居表中是否存在相应的映射关系,如果没有则新建邻居项:然后判断邻居项是否为可用状态,如不可用则把数据报存至发送缓存队列后发送请求:在接收到请求应答后,将对应邻居项置为可用,并将其缓存队列中的数据包发送出去:如果在指定时间内未收到相应包,则将对应邻居

Linux usb子系统(二):USB设备驱动usb-skeleton.c

usb驱动分为通过usbfs操作设备的用户空间驱动,内核空间的内核驱动.两者不能同时进行,否则容易引发对共享资源访问的问题,死锁!使用了内核驱动,就不能在usbfs里驱动该设备. 下面转载的一篇分析usb-skeleton.c文章. 初次接触与OS相关的设备驱动编写,感觉还挺有意思的,为了不至于忘掉看过的东西,笔记跟总结当然不可缺,更何况我决定为嵌入式卖命了.好,言归正传,我说一说这段时间的收获,跟大家分享一下Linux的驱动开发.但这次只先针对Linux的USB子系统作分析,因为周五研讨老板催

Linux usb子系统(三):通过usbfs操作设备的用户空间驱动

内核中提供了USB设备文件系统(usbdevfs,Linux 2.6改为usbfs,即USB文件系统),它和/proc类似,都是动态产生的.通过在/etc/fstab文件中添加如下一行:none /proc/bus/usb usbfs defaults或者输入命令:mount -t usbfs none /proc/bus/usb可以实现USB设备文件系统的挂载. 一个典型的/proc/bus/usb/devices文件的结构如下(运行的arm Linux 2.6.37内核上的机器上插入了一个u

Linux时间子系统(十七) ARM generic timer驱动代码分析

一.前言 关注ARM平台上timer driver(clocksource chip driver和clockevent chip driver)的驱动工程师应该会注意到timer硬件的演化过程.在单核时代,各个SOC vendor厂商购买ARM core的IP,然后自己设计SOC上的peripherals,这里面就包括了timer的硬件.由于没有统一的标准,各个厂商的设计各不相同,这给驱动工程师带来了工作量.然而,如果仅仅是工作量的话就还好,实际上,不仅仅如此.linux的时间子系统要求硬件t

Linux usb子系统(一):子系统架构

摘自:http://www.360doc.com/content/15/0519/05/22854460_471598740.shtml 摘自:https://www.cnblogs.com/cslunatic/p/3726053.html Linux usb子系统(一):子系统架构 一.USB协议基础知识   前序:USB概念概述 USB1.0版本速度1.5Mbps(低速USB) USB1.1版本速度12Mbps(全速USB)  USB2.0版本速度480Mbps(高速USB). USB 分为

linux中断子系统:中断号的映射与维护

写在前沿: 好久好久没有静下心来整理一些东西了,开始工作已有一个月,脑子里想整理的东西特别多.记录是一种很好的自我学习方式,静下来多思考多总结,三年的工作目标不能发生变化,作为职场菜鸟即将进入全世界半导体第一的Intel working,是机遇更是一种挑战,困难也是可想而知.脚踏实地.仰望星空,以结果为导向,以目标为准则,争取每天进步一点点. Linux内核版本:3.4.39 一. linux中断子系统的irq_desc初始化 linux内核最初的中断初始化过程入口为start_kernel.在

Linux时间子系统之八:动态时钟框架(CONFIG_NO_HZ、tickless)

在前面章节的讨论中,我们一直基于一个假设:Linux中的时钟事件都是由一个周期时钟提供,不管系统中的clock_event_device是工作于周期触发模式,还是工作于单触发模式,也不管定时器系统是工作于低分辨率模式,还是高精度模式,内核都竭尽所能,用不同的方式提供周期时钟,以产生定期的tick事件,tick事件或者用于全局的时间管理(jiffies和时间的更新),或者用于本地cpu的进程统计.时间轮定时器框架等等.周期性时钟虽然简单有效,但是也带来了一些缺点,尤其在系统的功耗上,因为就算系统目

Linux时间子系统之六:高精度定时器(HRTIMER)的原理和实现

上一篇文章,我介绍了传统的低分辨率定时器的实现原理.而随着内核的不断演进,大牛们已经对这种低分辨率定时器的精度不再满足,而且,硬件也在不断地发展,系统中的定时器硬件的精度也越来越高,这也给高分辨率定时器的出现创造了条件.内核从2.6.16开始加入了高精度定时器架构.在实现方式上,内核的高分辨率定时器的实现代码几乎没有借用低分辨率定时器的数据结构和代码,内核文档给出的解释主要有以下几点: 低分辨率定时器的代码和jiffies的关系太过紧密,并且默认按32位进行设计,并且它的代码已经经过长时间的优化

Linux输入子系统框架分析(1)

在Linux下的输入设备键盘.触摸屏.鼠标等都可以用输入子系统来实现驱动.输入子系统分为三层,核心层和设备驱动层,事件层.核心层和事件层由Linux输入子系统本身实现,设备驱动层由我们实现.我们在设备驱动层将输入事件上报给核心层input.c,核心层找到匹配的事件层,将事件交给事件层处理,事件层处理完后传递到用户空间. 我们最终要搞清楚的是在用户空间调用open和read最终在内核中是怎样处理的,向内核上报的事件又是谁处理的,处理完后是怎样传递到用户空间的? 上面两个图是输入子系统的框架. 下面