tun设备多用来在用户态模拟网络转发设备,比如隧道的端点,路由器,NAT网关等,但作为转发设备的模拟,其编程模型和作为数据始发站的服务器编程模型有着大不同。

我们先看一下最简单的tun程序模型:

tun字符设备和TCP/UDP socket连接作为文件描述符被均等地poll,将从一个fd收到的数据经过加工之后写入另一个fd。

但这种思路是错误的。是不是觉得性能底下,而tun设备有多队列模式,可以将一个tun虚拟网卡打开为多个文件描述符,下面的样子:

但还是不对。哪里出了问题呢?接下来我基于上述的 单体结构 逐步优化。

所谓的单体结构就是不考虑并发的情况下,如何让一个单独的流(单独的一个五元组)以最高效的方式通过tun模拟的转发路径。

作为转发设备和作为端设备处理数据最大的区别在于:

  • 转发设备一定要在不同线程分别处理同一条流的两个方向的数据流,以模拟全双工。
  • 端点设备最好在相同的线程处理同一个连接,以最大化cache利用率。

那么,我们把tun的多队列区分成发送(TX)和接收(RX)两个方向,两个文件描述符分别被处理,如此一来,TCP/UDP便需要在单独的线程处理网络接收:

然而还是有问题,TCP的接收和发送并不独立啊。

这里简单说一下TCP全双工的问题。

TCP被设计为一个双向全双工的协议,这是不是意味着在同一个TCP连接中,双向都能跑到带宽极限呢?

我们先看一个标准的TCP管道:

如果希望双向都跑到带宽极限,那就必须让ACK被对向的数据流捎带传输,如下图:

这显然是协议规范里说得很清楚的,并且是可以达到的一种状态。但TCP在实际使用的时候,它是个socket,TCP全双工说的是它实际发送和接收数据,但数据送到发送buffer以及从接收buffer接收数据是通过socket进行的,socket是不能同时send和recv的。除非buffer里数据足够且ACK平滑到来,否则很难控制双向的管道保持满载。

我用下面的程序进行了测试:

...pthread_create(&threads[0], NULL, receiver, NULL);pthread_create(&threads[1], NULL, sender, NULL);sleep(10000);return 0;
}void *sender(void *arg)
{while (1) {send(csd, buffer, 1400, 0);}return 0;
}void *receiver(void *arg)
{while (1) {recv(csd, rbuffer, 1400, MSG_DONTWAIT);}return 0;
}

双向带宽相比单向,会折半的。这里之所以使用1400的大小,是考虑到tun设备处理的都是网络报文,并不会按照流式数据进出socket缓冲区。当然,流式的测试你可以用netcat进行:

# 单向
nc -l -p 1234 >/dev/null
pv /dev/zero |nc -w 1 192.168.56.101 1234 >/dev/null
# 双向
nc -l -p 1234 </dev/zero >/dev/null
pv /dev/zero |nc -w 1 192.168.56.101 1234 >/dev/null

使用UDP socket可解这个问题,但是对于TCP,若要解决这个问题,建立两条独立的连接即可,一个负责发送(TX),另一个负责接收(RX),这就和tun字符设备对应了:

单流情形,这就是正确的做法了。如果是一个隧道server,会有多个client接入,那么就需要提高单线程的buffer能力了:

这类似将所有的tun RX队列构建了一个buffer队列,而所有的TCP RX socket则是另一个buffer队列。

下面是一个队列选择的细节,包括内核xmit时的tun RX队列选择和应用程序write fd时的tun TX队列选择:

对于同一条流,始终选择同一个队列,这需要多队列tun网卡的steering来协助写队列选择的eBPF程序,该机制详见:
https://lore.kernel.org/patchwork/patch/858162/

虽然以上的布置已经趋于完美,然而现实中让服务器编程框架固化的程序员很难接受创建两个TCP连接独立收发的思路,因此现实的折中是:

将上述的 单体结构 多线程化就可以应对高并发了,启动多个线程,每个线程执行一个单体逻辑,就可以用tun模拟一个高并发高性能转发设备了。

好了,线程关系摆置好了,接下来呢,绑一下CPU:

  • 物理网卡RSS队列向tun队列转交的流程绑在同一个CPU。
  • 处理tun RX队列的线程绑在入队CPPU的sibling CPU。
  • 处理TCP收包到tun TX fd write流程的线程绑定同一个CPU。
  • 处理tun TX队列接收的softirq在tun TX写线程的sibling CPU。

当然了,如果你想用buffer平滑生产和消费,上述任何一个从一个文件描述符到另一个文件描述符的处理流程均可以在buffer处中断并返回,如此一来类似Nginx的那种接力模型就是必须的了。

接下来看另一个tun应用的典型例子,NAT64。

tayga是一个NAT64玩具,它如下:

这个结构侧重数据转发逻辑上来看,是非常错误的,它不光将双向的数据流转发处理串行化了,而且也将多CPU并行处理也串行化了。

NAT64至少需要tun网络的多队列特性:

下面是NAT64单体结构正确的样子:

将上述NAT64单体结构多线程化,就是一个高性能NAT64网关了。(真的是这样吗?不一定,后面会再写一篇单独的文章…)

我常说,转发逻辑编程和服务器编程的思路截然不同,简而言之:

  • 服务器编程需要基于连接处理高并发。
  • 转发逻辑编程需要基于连接的载荷处理高并发。

如果用服务器编程的思维来看待转发逻辑,多个fd可能代表的是同一条载荷流,如何处理同一条载荷流的不同fd之间的冲突就很重要,稍不慎就会将原本全双工的流退化成半双工,形成畸变。

我也常说,转发逻辑非常适合DPDK/XDP来做,不太适合用服务器框架来做。DPDK直接针对数据流载荷,程序员知道自己在做什么,也就知道应该怎么做。然而,应用服务器处理的是抽象的文件描述符,而程序员对这些文件描述符能做的只是读和写,至于什么时候去读写,就需要更底层的知识而不是更上层的业务逻辑。

耳朵都听出茧子的一句话是 同一个CPU处理同一个连接,以保证cache命中率 ,然而如此处理一个转发流便不对了,对于一个流的转发处理,不同方向的逻辑恰恰要在不同的CPU上处理才能确保不破坏数据流的全双工特征。是吧…

非常令人遗憾的是,现如今绝大多数使用tun的开源软件均未按照正确的方式处理数据流,这可能依然是 会写代码懂网络 之间的隔阂所导致,和大多数人普遍认为的不同,懂协议栈的软件硬件实现细节绝不意味着懂网络,在任何人都可以在简历上写 精通数据面转发 的如今,不知道怎么用tun设备,这是一件憾事。我说的这些当然没有多少人关注,可能也不会得到多少认同,但这恰恰不是在反面印证了什么吗?

是不是见了tun网卡就要构建多线程多队列呢?远远不是!

为了证明这一点,我以我自己为例,以剖析我的愚蠢来说明 代码网络 之间的隔阂。


浙江温州皮鞋湿,下雨进水不会胖。

tun虚拟网卡该怎么玩不该怎么玩相关推荐

  1. sstap tun虚拟网卡没有安装_虚拟设备之TUN和TAP

    前言 最近尝试了下 Zerotier,感觉还挺好用的, 比较好奇它的实现原理,查了下,遂有了下文. TAP与TUN是什么 不同于硬件物理网卡,TAP/TUN 是在 Linux 内核 2.4.x 版本之 ...

  2. Golang Window TUN 虚拟网卡

    1. wintun Linux 2.4以后下有一种特殊的虚拟网络设备tun,用户可以直接创建虚拟网卡tun,直接以文件读写方式从设备处读取到网络层数据包(IP数据包),该网卡可以像是真实网卡一样设置I ...

  3. 提示tun虚拟网卡没有安装_Win10家庭版通过Hyper-V安装Centos7+Python3.7过程总结

    Win10专业版自带有虚拟机Hyper-V, 只需要在控制面板--程序中将其添加到应用就可使用,非常方便,但我电脑预装的是Win10家庭版,没有这个工具,但可以通过以下方法把它安装上: 新建文件Hyp ...

  4. linux TUN 虚拟网卡设备

    [网络虚拟化技术(二): TUN/TAP MACVLAN MACVTAP] https://blog.kghost.info/2013/03/27/linux-network-tun/ TUN 设备是 ...

  5. 虚拟网卡 TUN/TAP 驱动程序设计原理

    简介 虚拟网卡Tun/tap驱动是一个开源项目,支持很多的类UNIX平台,OpenVPN和Vtun都是基于它实现隧道包封装.本文将介绍tun/tap驱动的使用并分析虚拟网卡tun/tap驱动程序在li ...

  6. linux下使用tun/tap虚拟网卡

    tun/tap虚拟网卡介绍 tun是一种虚拟网络设备,tun设备一端连接着用户程序,一端连接着内核协议栈,任何时候从协议栈发到tun网卡的数据都能从用户程序中读到,而从用户程序写入/dev/net/t ...

  7. tun/tap虚拟网卡收发机制解析

    文章目录 收发机制原理 共性 虚拟网卡和物理网卡的通信 网络传输包格式 接收机制 发送机制 服务器端的转发机制 本章要研究的是openxxx的收发原理,实际上研究就是研究tun/tap网卡驱动的相关原 ...

  8. linux虚拟网卡tun,Centos7 创建虚拟网卡(tun/tap)

    创建网卡.创建网桥并建立桥接 [root@kolla ~]# cat create.sh create_br(){ tunctl -t storage -u root brctl addbr virb ...

  9. linux上使用tun/tap设备模拟一个虚拟网卡,并将该虚拟网卡连接到新创建的网桥上

    目录 linux上使用tun/tap设备模拟一个虚拟网卡,并将该虚拟网卡连接到新创建的网桥上 一.tun/tap设备简介 二.在linux上使用tun/tap设备模拟一个虚拟网卡 三. 创建网桥连接到 ...

最新文章

  1. 高精地图与自动驾驶(上)
  2. JavaScript引擎大战:Google提出StrongMode和SoundScript议案,增强V8性能
  3. 从零开始学Xamarin.Forms(四) Android 准备步骤(添加第三方Xamarin.Forms.Labs库)
  4. Boost:bind绑定访客的测试程序
  5. 是否应该频繁升级小米的系统?
  6. css React 单行省略和多行省略
  7. 在ARM Cortex-M上实现FreeRTOS性能计数器
  8. Spring框架IOC的实现
  9. python免费课程400节-太原Python编程课哪家比较有优势
  10. Android11_图片处理
  11. Xshell7安装教程
  12. 【WPF】屏幕录像、摄像头录像
  13. php swoole 教程,Swoole基础入门
  14. Spring——IoC和DI
  15. MoveIt轨迹规划问题
  16. 附子理中丸,人参健脾丸,参苓白术散、补脾益肠丸、痛泻宁颗粒
  17. 我是一个搞IT的农民工
  18. 搞懂Python切片中start、end、steps正负的区别
  19. Xilinx 8B10B转换
  20. 【jQuery - serializeArray 序列化表单值并转为键值对】

热门文章

  1. Linux性能优化—内存实战篇
  2. php做网站步骤_PHP网站安装程序制作的原理、步骤、注意事项和示例代码
  3. 橡皮擦的英语_英语从零开始怎么学(英语小白速成,必看!)
  4. bcb image 动态大小_实战|使用CSS Paint API动态创建与分辨率无关的可变背景
  5. UiPath学习第一课
  6. C#_摄像头图像转换为Bitmap格式及绘制十字线
  7. 如今甲骨文正式发布Java 14……
  8. 甲骨文扼杀 Java EE
  9. linux下的写作软件下载,极音创作 linux版
  10. c语言 温岚打开方式,环球网专访温岚:多面天后跨界作芳疗师 为你打开嗅觉的钥匙...