深入理解Linux网络随笔(七):容器网络虚拟化--Veth设备对

深入理解Linux网络随笔(七):容器网络虚拟化

微服务架构中服务被拆分成多个独立的容器,docker网络虚拟化的核心技术为:Veth设备对、Network Namespace、Bridg。

Veth设备对

veth设备是一种 成对 出现的虚拟网络接口,作用是 在 Linux 网络命名空间或不同网络栈之间建立一个虚拟的点对点连接,实现数据通信。例如实现容器与宿主机间的通信、不同neths间传递流量。

如图所示:

虚拟网络设备并不会直接连接物理网络设备,而是一端连接到协议栈,另一端连接到另一个 veth 设备。从一对 veth 设备中发出的数据包会直接传送到另一个 veth 设备。每个 veth 设备都可以配置 IP 地址,并作为 路由的一个接口,可以进行IP层通信。
在这里插入图片描述

特点:

(1)成对出现:创建时总是 两个设备*成对出现,例如 veth0veth1,它们之间类似于一条网络隧道

(2)工作方式:一端发送的流量会从另一端收到,就像网线直连一样

(3)不处理 L2/L3 转发:veth 设备 不会执行交换、路由*等功能,只是简单地在两端传输数据包

底层源码分析

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

veth设备的初始化通过函数veth_init进行。

static __init int veth_init(void)
{//注册veth_link_ops veth设备的操作方法return rtnl_link_register(&veth_link_ops);
}

veth_link_ops中定义了veth设备的操作回调函数。

static struct rtnl_link_ops veth_link_ops = {.kind		= DRV_NAME,//设备类型.priv_size	= sizeof(struct veth_priv),//私有数据大小.setup		= veth_setup,//设备启动.validate	= veth_validate,//检查netlink请求参数的合法性.newlink	= veth_newlink,//处理新建的veth设备回调函数.dellink	= veth_dellink,//处理删除的veth设备回调函数.policy		= veth_policy,//netlink配置参数解析策略.maxtype	= VETH_INFO_MAX,// netlink 解析时允许的最大属性编号.get_link_net	= veth_get_link_net,// 获取 veth 设备所在的网络命名空间.get_num_tx_queues	= veth_get_num_queues,// 获取设备的 TX 队列数.get_num_rx_queues	= veth_get_num_queues,// 获取设备的 RX 队列数
};

veth设备创建调用veth_newlink函数。

static int veth_newlink(struct net *src_net, struct net_device *dev,struct nlattr *tb[], struct nlattr *data[],struct netlink_ext_ack *extack)
{int err;struct net_device *peer;struct veth_priv *priv;char ifname[IFNAMSIZ];struct nlattr *peer_tb[IFLA_MAX + 1], **tbp;unsigned char name_assign_type;struct ifinfomsg *ifmp;struct net *net;.....//创建peer设备peer = rtnl_create_link(net, ifname, name_assign_type,&veth_link_ops, tbp, extack);if (IS_ERR(peer)) {put_net(net);return PTR_ERR(peer);}....//注册peer设备err = register_netdevice(peer);...//获取dev的私有数据privpriv = netdev_priv(dev);//将dev的指针赋值给peer的私有数据peer->priv,建立连接,通过dev访问peer设备rcu_assign_pointer(priv->peer, peer);//初始化dev的TX、RX队列err = veth_init_queues(dev, tb);if (err)goto err_queues;//获取 peer 设备的 priv 结构体priv = netdev_priv(peer);//让 peer->priv->peer 指向 dev,建立 veth 设备对的双向连接rcu_assign_pointer(priv->peer, dev);//初始化peer设备的队列err = veth_init_queues(peer, tb);if (err)goto err_queues;.....
}

启动veth设备,通过veth_netdev_ops操作表找到发送过程中的回调函数veth_xmit。

static void veth_setup(struct net_device *dev)
{ether_setup(dev);dev->priv_flags &= ~IFF_TX_SKB_SHARING;dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;dev->priv_flags |= IFF_NO_QUEUE;dev->priv_flags |= IFF_PHONY_HEADROOM;//veth操作列表dev->netdev_ops = &veth_netdev_ops;dev->ethtool_ops = &veth_ethtool_ops;dev->features |= NETIF_F_LLTX;dev->features |= VETH_FEATURES;dev->vlan_features = dev->features &~(NETIF_F_HW_VLAN_CTAG_TX |NETIF_F_HW_VLAN_STAG_TX |NETIF_F_HW_VLAN_CTAG_RX |NETIF_F_HW_VLAN_STAG_RX);dev->needs_free_netdev = true;dev->priv_destructor = veth_dev_free;dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS;dev->max_mtu = ETH_MAX_MTU;dev->hw_features = VETH_FEATURES;dev->hw_enc_features = VETH_FEATURES;dev->mpls_features = NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE;netif_set_tso_max_size(dev, GSO_MAX_SIZE);
}
static const struct net_device_ops veth_netdev_ops = {.ndo_init            = veth_dev_init,.ndo_open            = veth_open,.ndo_stop            = veth_close,.ndo_start_xmit      = veth_xmit,//veth发送函数.ndo_get_stats64     = veth_get_stats64,.ndo_set_rx_mode     = veth_set_multicast_list,.ndo_set_mac_address = eth_mac_addr,
#ifdef CONFIG_NET_POLL_CONTROLLER.ndo_poll_controller	= veth_poll_controller,
#endif.ndo_get_iflink		= veth_get_iflink,.ndo_fix_features	= veth_fix_features,.ndo_set_features	= veth_set_features,.ndo_features_check	= passthru_features_check,.ndo_set_rx_headroom	= veth_set_rx_headroom,.ndo_bpf		= veth_xdp,.ndo_xdp_xmit		= veth_ndo_xdp_xmit,.ndo_get_peer_dev	= veth_peer_dev,
};
通信过程

数据包发送过程中到达网络设备层会进入dev_hard_start_xmit函数,遍历链表上的所有skb包调用xmit_one发送数据包。

//网络设备数据包发送路径(TX Path) 的关键部分,负责调用底层驱动的 ndo_start_xmit() 发送数据包
static int xmit_one(struct sk_buff *skb, struct net_device *dev,struct netdev_queue *txq, bool more)
{unsigned int len;int rc;//监测是否有协议栈上层监听if (dev_nit_active(dev))//AF_PACKET 套接字正在监听,发送数据包副本给监听进程dev_queue_xmit_nit(skb, dev);
//记录数据包长度len = skb->len;//触发tracepoint机制,记录数据包发送开始trace_net_dev_start_xmit(skb, dev);//调用底层驱动的ndo_start_xmit()方法发送数据包rc = netdev_start_xmit(skb, dev, txq, more);trace_net_dev_xmit(skb, rc, dev, len);return rc;
}

获取驱动设备回调函数集合ops,结构体net_device_ops,调用__netdev_start_xmit发送数据包。

static inline netdev_tx_t netdev_start_xmit(struct sk_buff *skb, struct net_device *dev,struct netdev_queue *txq, bool more)
{const struct net_device_ops *ops = dev->netdev_ops;netdev_tx_t rc;//发送数据包rc = __netdev_start_xmit(ops, skb, dev, more);if (rc == NETDEV_TX_OK)txq_trans_update(txq);return rc;
}

在这里会首先判断当前cpu发送队列是否还有数据待处理,然后调用驱动的ndo_start_xmit函数发送数据包,回调函数veth_xmit,lo是loopback_xmit。也就是在veth启动的时候注册的回调函数。

static inline netdev_tx_t __netdev_start_xmit(const struct net_device_ops *ops,struct sk_buff *skb, struct net_device *dev,bool more)
{__this_cpu_write(softnet_data.xmit.more, more);return ops->ndo_start_xmit(skb, dev);
}

veth_xmit核心是获取veth设备数据,将数据发送到对端设备。

static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
{struct veth_priv *rcv_priv, *priv = netdev_priv(dev);struct veth_rq *rq = NULL;int ret = NETDEV_TX_OK;struct net_device *rcv;int length = skb->len;bool use_napi = false;int rxq;rcu_read_lock();//获取veth设备rcv = rcu_dereference(priv->peer);...//获取rcv设备私有数据rcv_priv = netdev_priv(rcv);//获取skb队列索引rxq = skb_get_queue_mapping(skb);if (rxq < rcv->real_num_rx_queues) {rq = &rcv_priv->rq[rxq];//rq绑定了napi,且数据包适合GRO,开启NAPI机制轮询数据包use_napi = rcu_access_pointer(rq->napi) &&veth_skb_is_eligible_for_gro(dev, rcv, skb);}skb_tx_timestamp(skb);//尝试将skb转发到对端veth设备if (likely(veth_forward_skb(rcv, skb, rq, use_napi) == NET_RX_SUCCESS)) {//未使用NAPI机制,更新统计信息if (!use_napi)dev_sw_netstats_tx_add(dev, 1, length);} else {
.....
}

veth_forward_skb会根据数据包选择不同路径,数据包转发到对端设备dev_forward_skb,对端开启XDP则调用veth_xdp_rx,普通数据包调用netif_rx

static int veth_forward_skb(struct net_device *dev, struct sk_buff *skb,struct veth_rq *rq, bool xdp)
{return __dev_forward_skb(dev, skb) ?: xdp ?veth_xdp_rx(rq, skb) :__netif_rx(skb);
}

函数调用关系:__dev_forward_skb-->__dev_forward_skb2-->____dev_forward_skb

//处理dev设备的转发数据包
static int __dev_forward_skb2(struct net_device *dev, struct sk_buff *skb,bool check_mtu)
{//实际数据包转发处理int ret = ____dev_forward_skb(dev, skb, check_mtu);if (likely(!ret)) {//将skb所属设备设置成刚才取到的veth对端设备rcvskb->protocol = eth_type_trans(skb, dev);//修正skb校验和skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);}return ret;
}

eth_type_trans设置完成会继续执行__netif_rx路径,函数调用逻辑netif_rx_internal-->enqueue_to_backlog,在这里获取每个CPU核心对应的softnet_data数据结构,将skb添加到等待队列input_pkt_queue,在函数__test_and_set_bit检查sd->backlog.state是否已包含 NAPI_STATE_SCHEDNAPI_STATE_SCHED是NAPI轮询处理的一个 状态标志位防止同一个 NAPI 任务被重复调度,未设置调用napi_schedule_rps触发NAPI调度,触发软中断 NET_RX_SOFTIRQ

static int enqueue_to_backlog(struct sk_buff *skb, int cpu,unsigned int *qtail)
{enum skb_drop_reason reason;struct softnet_data *sd;unsigned long flags;unsigned int qlen;//丢包原因:未指定reason = SKB_DROP_REASON_NOT_SPECIFIED;sd = &per_cpu(softnet_data, cpu);if (!__test_and_set_bit(NAPI_STATE_SCHED, &sd->backlog.state))napi_schedule_rps(sd);
.....
}

在这里根据是否开启RPS机制走不同的路径,RPS 是 Linux 内核的一种 多核负载均衡机制将收到的数据包分配到多个 CPU 进行处理,避免所有网络流量只由单个 CPU 处理,减少 CPU 瓶颈,在这里通过rps_ipi_list将数据包从一个CPU转发到另外一个CPU上,提高多核环境下的负载均衡,减少CPU之间的竞争。如果没有开启RPS机制,数据包会在当前CPU软中断上下文中处理NAP任务。

static int napi_schedule_rps(struct softnet_data *sd)
{struct softnet_data *mysd = this_cpu_ptr(&softnet_data);
// RPS将接收的数据包调度到 不同的 CPU 进行处理
#ifdef CONFIG_RPS
//sd不等于mysd,说明要在另一个 CPU 上执行 NAPI 任务,而不是本 CPU 处理if (sd != mysd) {//rps_ipi_list 负责存储要在其他 CPU 处理的 softnet_data 队列,并通过 NET_RX_SOFTIRQ 触发软中断来完成调度。sd->rps_ipi_next = mysd->rps_ipi_list;mysd->rps_ipi_list = sd;//出发软中断,处理softnet_data队列的NAPI任务__raise_softirq_irqoff(NET_RX_SOFTIRQ);return 1;}
//本地CPU处理
#endif /* CONFIG_RPS */
//直接调度 mysd->backlog 设备的 NAPI 任务,并安排 net_rx_action() 来执行数据包处理。__napi_schedule_irqoff(&mysd->backlog);return 0;
}

实践操作

Linux 中创建 veth 设备对,设备名veth,指定的虚拟网卡类型为veth,创建的另一端设备名为veth1。

ip link add veth0 type veth peer name veth1

使用ip link show可以看到veth0veth1 设备已创建,但还未启用。

在这里插入图片描述

veth设备需要配置IP地址才能进行通信,为veth0veth1 设备配置ip。

sudo ip addr add 192.168.1.1/24 dev veth0
sudo ip addr add 192.168.1.2/24 dev veth1

启动设备

sudo ip link set veth1 up
sudo ip link set veth0 up

使用ip link show可以看到veth0veth1 设备状态已变成开启

在这里插入图片描述

为了使veth设备之间能够顺利通信,需要关闭反向路径过滤(rp_filter)并设置允许接收本机数据包。root角色下修改系统配置如下:

!

ping测试veth0veth1 设备间通信

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/73353.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电气制作行业

电气制作是一个涉及多种技能和工艺的领域&#xff0c;主要包括电气设备的组装、布线、调试等工作。以下是电气制作的一般流程和相关要点&#xff1a; 设计与规划 - 需求分析&#xff1a;明确电气设备的功能、性能要求&#xff0c;以及使用环境、安全标准等因素。 - 电路设计…

【Flutter】数据库实体类构造函数加密注意事项

源代码&#xff1a; AccountEntity( {required String account, required String password,}) : account encrypter.encrypt(account,iv: iv).base64, password encrypter.encrypt(password,iv: iv).base64,; 解密代码&#xff1a; static final encrypter Encrypter(AES…

PMP冲刺每日一题(30)

试题1 标题&#xff1a;在项目执行期间&#xff0c;一名团队成员识别到由以前未被识别为项目相关方的职能经理提交了新需求。项目经理应该怎么做? A、与项目发起人开会&#xff0c;获得反馈 B、启动实施整体变更控制过程 C、对需求执行成本效益分析 D、将该职能经理添加进沟通…

一文讲通锁标记对象std::adopt_lock盲点

一文讲通锁标记对象std::adopt_lock盲点 1. 核心概念2. 代码详解1. 单个锁2. 多重锁(可以用来预防死锁)3. 条件变量的互斥控制4. 复杂示例: 多生产者-多消费者模型(超纲了&#xff0c; 可不看&#xff0c;哈哈哈哈) 3. 小结 1. 核心概念 在C中&#xff0c; std::adopt_lock是一…

LVI-SAM、VINS-Mono、LIO-SAM算法的阅读参考和m2dgr数据集上的复现(留作学习使用)

ROS一键安装参考&#xff1a; ROS的最简单安装——鱼香一键安装_鱼香ros一键安装-CSDN博客 opencv官网下载4.2.0参考&#xff1a;https://opencv.org/releases/page/3/ nvidia驱动安装:ubuntu18.04 安装显卡驱动 - 开始战斗 - 博客园 cuda搭配使用12 cuda安装1&#xff1a;Ub…

基于jspm校园安全管理系统(源码+lw+部署文档+讲解),源码可白嫖!

摘要 随着信息时代的来临&#xff0c;过去信息校园安全管理方式的缺点逐渐暴露&#xff0c;本次对过去的校园安全管理方式的缺点进行分析&#xff0c;采取计算机方式构建校园安全管理系统。本文通过阅读相关文献&#xff0c;研究国内外相关技术&#xff0c;提出了一种集安全教…

基于NXP+FPGA轨道交通3U机箱结构牵引控制单元

基于NXPFPGA轨道交通异步电机牵引控制单元(TCU-IM) 异步电机牵引控制单元&#xff08;TCU-IM&#xff09;用于牵引逆变器-异步电机构成的牵引电传动系统&#xff0c;可采用车控或架控方式。执行高性能异步电机复矢量控制策略&#xff0c;具有响应迅速、有效可靠的防空转滑行控制…

《CircleCI:CircleCI:解锁软件开发持续集成(CI)和持续部署(CD)高效密码》:此文为AI自动生成

《CircleCI&#xff1a;CircleCI&#xff1a;解锁软件开发持续集成&#xff08;CI&#xff09;和持续部署&#xff08;CD&#xff09;高效密码》&#xff1a;此文为AI自动生成 一、CircleCI 初印象 在当今软件开发的快节奏赛道上&#xff0c;持续集成&#xff08;CI&#xff…

基于MySQL有用户管理的音乐播放器

基于MySQL的音乐器 带有用户登录功能验证用户身份&#xff0c;用户注册等操作还有用户音乐列表&#xff0c;以及增删查改操作 INSERT into users(username,passwd,phone_number,created_time,role) VALUES(‘张三’,‘123456’,‘123’,‘2025-3-11’,‘1’) 三张表&#xf…

差分专题练习 ——基于罗勇军老师的《蓝桥杯算法入门C/C++》

一、1.重新排序 - 蓝桥云课 算法代码&#xff1a; #include <bits/stdc.h> using namespace std; const int N 1e5 3;int a[N], d[N], cnt[N];int main() {int n; scanf("%d", &n);for (int i 1; i < n; i) scanf("%d", &a[i]);int m…

AI+视频监控电力巡检:EasyCVR视频中台方案如何赋能电力行业智能化转型

随着电力行业的快速发展&#xff0c;电力设施的安全性、稳定性和运维效率变得至关重要。传统视频监控系统在实时性、智能化及多系统协同等方面面临严峻挑战。EasyCVR视频中台解决方案作为一种先进的技术手段&#xff0c;在电力行业中得到了广泛应用&#xff0c;为电力设施的监控…

【哈希表与字符串的算法之路:思路与实现】—— LeetCode

文章目录 两数之和面试题01.02.判定是否为字符重排存在重复元素存在重复元素||字母异位词分组最长公共前缀和最长回文子串二进制求和字符串相乘 两数之和 这题的思路很简单&#xff0c;在读完题目之后&#xff0c;便可以想到暴力枚举&#xff0c;直接遍历整个数组两遍即可&…

RabbitMQ入门:从安装到高级消息模式

文章目录 一. RabbitMQ概述1.1 同步/异步1.1.1 同步调用1.1.2 异步调用 1.2 消息中间件1.2.1 概念1.2.2 作用1.2.3 常见的消息中间件1.2.4 其他中间件 1.3 RabbitMQ1.3.1 简介1.3.2 特点1.3.3 方式1.3.4 架构1.3.5 运行流程 二. 安装2.1 Docker 安装 RabbitMQ 三. 简单队列&…

kernel与modules解耦

一、耦合&#xff1a; linux的kernel与modules存在耦合版本匹配&#xff0c;在版本不匹配&#xff08;内核重新编译后&#xff0c;或者驱动模块编译依赖的内核版本跟运行版本不匹配&#xff09;时候&#xff0c;会存在insmod 驱动模块失败的情形&#xff1b; 二、解耦&#xff…

物理约束神经网络(PINN)和有限元方法哪个更接近“真正的物理规律”?还是两者只是不同的数学表达?

物理约束神经网络(Physics-Informed Neural Networks, PINN)和有限元方法(Finite Element Method, FEM)是两种在科学计算和工程模拟中广泛应用的数值方法。PINN 依赖深度学习来近似微分方程的解,并在训练过程中将物理约束作为损失项融入网络,而 FEM 通过将连续介质的物理…

UI程序的std::cout重定向输出到Visual Studio的debug输出窗口

常用代码。 UI程序的std::cout重定向输出到Visual Studio的debug输出窗口 #include <iostream> #include <streambuf> #include <vector> #include <string> #include <afxwin.h> //MFC// 自定义 streambuf 类&#xff0c;用于重定向输出到 Vis…

Python开发合并多个PDF文件

前言 在我们的工作中&#xff0c;可能有以下场景需要用到合并多个PDF&#xff1a; 文档归档&#xff1a;在企业或组织中&#xff0c;常常需要将相关的文档&#xff08;如合同、报告、发票等&#xff09;合并为一个PDF文件&#xff0c;以便于归档和管理。 报告生成&#xff1a;在…

DeepSeek 助力 C++ 开发:探索智能编程新境界

这篇文章就会详细讲讲 DeepSeek 在 C 开发里到底能怎么用&#xff0c;从上面说的写代码、找错误、优化性能&#xff0c;到管理项目这些方面&#xff0c;还会给出好多实际的代码例子&#xff0c;讲讲实际用起来是啥情况。目的就是给那些做 C 开发的人&#xff0c;一份全面又详细…

C#-使用VisualStudio编译C#工程

一.创建csproj文件 二.创建源cs文件 三.生成解决方案 四.运行解决方案 五.VisualStudio功能列表 <1.代码格式化: CtrlKD完成代码整体格式化 <2.窗口布局 窗口->重置窗口布局 <3.引用查找&关联 <4.包管理 <5.日志输出级别 工具->选项->项目解决方案…

Kafka相关的面试题

以下是150道Kafka相关的面试题及简洁回答&#xff1a; Kafka基础概念 1. 什么是Kafka&#xff1f; Kafka是一个分布式、可扩展、容错的发布-订阅消息系统&#xff0c;最初由LinkedIn开发&#xff0c;现为Apache项目。它适用于高吞吐量的场景&#xff0c;如大数据处理和实时数据…