Linux 网络设备 - Bridge & Veth Pair
nanshan 2025-06-12 14:49 7 浏览 0 评论
我们继续介绍 Linux 中常见的网络设备,今天主要讲的是 Linux Bridge 和 Veth Pair,理解清楚这两种设备对后续理解容器化网络会比较有帮助。
1.veth pair 两端互通
我们先看一下 veth pair,这是成对出现的虚拟网络设备,可以简单想象这是两块网卡,中间有一条网线相连。
通过 ip 命令,可以创建 veth pair,我们创建veth0和veth1,分别绑定 IP 并置为 UP 状态:
$ ip link add veth0 type veth peer name veth1
$ ip link add veth0 type veth peer name veth1
$ ip addr add 10.1.1.100/24 dev veth0
$ ip addr add 10.1.1.101/24 dev veth1
$ ip link set veth0 up
$ ip link set veth1 up
ip a查看一下网络设备状态,veth0和veth1已经正常创建并绑定 IP:
$ ip a
...
20: veth1@veth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 36:c9:5b:1a:6d:9b brd ff:ff:ff:ff:ff:ff
inet 10.1.1.101/24 scope global veth1
valid_lft forever preferred_lft forever
inet6 fe80::34c9:5bff:fe1a:6d9b/64 scope link
valid_lft forever preferred_lft forever
21: veth0@veth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether c6:5e:14:55:f5:b1 brd ff:ff:ff:ff:ff:ff
inet 10.1.1.100/24 scope global veth0
valid_lft forever preferred_lft forever
inet6 fe80::c45e:14ff:fe55:f5b1/64 scope link
valid_lft forever preferred_lft forever
我们尝试一下将 vair pair 的一段置为 DOWN,再查看一下网络设备状态,可以看到我只是将veth0置为 DOWN,但是随之veth1也进入 M-DOWN 的状态。因为 veth pair 的两块网络设备彼此对向,当veth0置为 DOWN 时,意味着它无法接受数据,随之另一端veth1也就不能正常工作,所以内核会将其自动置为 M-DOWN,以保持两端的一致性。
$ ip link set veth1 down
$ ip a
20: veth1@veth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
link/ether 36:c9:5b:1a:6d:9b brd ff:ff:ff:ff:ff:ff
inet 10.1.1.101/24 scope global veth1
valid_lft forever preferred_lft forever
21: veth0@veth1: <NO-CARRIER,BROADCAST,MULTICAST,UP,M-DOWN> mtu 1500 qdisc noqueue state LOWERLAYERDOWN group default qlen 1000
link/ether c6:5e:14:55:f5:b1 brd ff:ff:ff:ff:ff:ff
inet 10.1.1.100/24 scope global veth0
valid_lft forever preferred_lft forever
inet6 fe80::c45e:14ff:fe55:f5b1/64 scope link
valid_lft forever preferred_lft forever
我们说veth0和veth1逻辑上有一根网线相连,所以理论上通过veth0 ping veth1是能通的,我们试一下:
ping -c 1 -I veth0 10.1.1.101
PING 10.1.1.101 (10.1.1.101) from 10.1.1.100 veth0: 56(84) bytes of data.
From 10.1.1.100 icmp_seq=1 Destination Host Unreachable
--- 10.1.1.101 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms
可以看到并不能 ping 通,我们在veth0抓包看下:
tcpdump -n -i veth0
14:41:23.473479 ARP, Request who-has 10.1.1.101 tell 10.1.1.100, length 28
14:41:24.496485 ARP, Request who-has 10.1.1.101 tell 10.1.1.100, length 28
14:41:25.520479 ARP, Request who-has 10.1.1.101 tell 10.1.1.100, length 28
原因是veth0和veth1虽然处在同一网段,但由于是第一次通信,所以 ARP 表中并不存在10.1.1.101的 MAC 信息,需要先发送 ARP Request,但是并没有收到响应。这是因为 Ubuntu 内核中一些默认配置限制导致的,我们先放开限制:
$ echo 1 > /proc/sys/net/ipv4/conf/veth0/accept_local
$ echo 1 > /proc/sys/net/ipv4/conf/veth1/accept_local
现在就能 ping 通了:
$ ping -c 1 -I veth0 10.1.1.101
PING 10.1.1.101 (10.1.1.101) from 10.1.1.100 veth0: 56(84) bytes of data.
64 bytes from 10.1.1.101: icmp_seq=1 ttl=64 time=0.140 ms
--- 10.1.1.101 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
我们在veth0和veth1抓包看一下过程:
# shell-0
$ tcpdump -n -i veth0 icmp
14:54:33.638414 IP 10.1.1.100 > 10.1.1.101: ICMP echo request, id 12, seq 1, length 64
# shell-1
$ tcpdump -n -i veth1 icmp
14:54:33.638420 IP 10.1.1.100 > 10.1.1.101: ICMP echo request, id 12, seq 1, length 64
# shell-2
$ ping -c 1 -I veth0 10.1.1.101
PING 10.1.1.101 (10.1.1.101) from 10.1.1.100 veth0: 56(84) bytes of data.
64 bytes from 10.1.1.101: icmp_seq=1 ttl=64 time=0.043 ms
--- 10.1.1.101 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
从shell-0和shell-1的微小的时间戳上的差异,我们可以看到当执行 ping 命令时,veth0先接收到 ICMP Request,随即立刻发送给对端的veth1,但是在两个设备上都没有看到 ICMP Reply,可 ping 命令确实显示 1 packets transmitted。 其实原因很简单,ICMP Reply 是走的 loopback 的口。
$ tcpdump -n -i lo icmp
14:54:33.638441 IP 10.1.1.101 > 10.1.1.100: ICMP echo reply, id 12, seq 1, length 64
我们来看一下数据包的流转过程:
流程大致如下:
- 首先 ping 程序构造 ICMP Request,通过 Socket API 发送给内核的网络协议栈;
- 在 ping 中我们通过-I veth0指定走veth0网卡,所以协议栈会将数据包交给veth0;
- 由于veth0和veth1间有逻辑上存在的网线,数据包会直接交到veth1;
- veth1接收到数据包后也不做处理,转手交给内核协议栈;
- 内核协议栈在接收到数据包后,发现10.1.1.101是本地的 IP,所以会构造 ICMP Reply的包,查看路由表之后发现10.1.1.101应该走 loopback 的口(ip route show table local);
- loopback 接收到 ICMP Reply 后,转发给内核协议栈;
- 最终协议栈将数据包交给 ping 进程,ping 成功收到 ICMP Reply包;
当给veth0和veth1配置 IP 时,内核会自动在local表中添加路由:
$ ip route show table local
local 10.1.1.100 dev veth0 proto kernel scope host src 10.1.1.100
local 10.1.1.101 dev veth1 proto kernel scope host src 10.1.1.101
broadcast 10.1.1.255 dev veth1 proto kernel scope link src 10.1.1.101
broadcast 10.1.1.255 dev veth0 proto kernel scope link src 10.1.1.100
...
2.宿主机与容器间的网络
我们知道在容器里大量用到 namespace 来实现容器间的隔离性,对容器网络也是一样的,我们先看一下在宿主机默认的 namespace 下的网络设备:
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:54:00:cb:f0:b3 brd ff:ff:ff:ff:ff:ff
inet 192.168.31.92/24 brd 192.168.31.255 scope global dynamic noprefixroute enp1s0
valid_lft 42114sec preferred_lft 42114sec
inet6 fe80::b5fc:b1f8:2b4:a62d/64 scope link noprefixroute
valid_lft forever preferred_lft forever
然后我们创建一个 netns ns1,再查看下ns1中的网络设备:
$ ip netns add ns1
$ ip netns exec ns1 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
root@bridge1:~#
可以看到在ns1中,只有一个默认创建lo设备,除此之外,所有的路由规则、防火墙规则等也与默认 namespace 是完全隔离的。
这时候我们想将ns1的网络与宿主机打通,就可以考虑使用 veth pair:
# 创建 namespace ns1
$ ip netns add ns1
# 创建 veth pair
$ ip link add veth0 type veth peer name veth1
# 将`veth1`的 netns 设置为`ns1`
$ ip link set veth1 netns ns1
# 分别初始化`veth0`和`veth1`的设置
$ ip addr add 10.1.1.100/24 dev veth0
$ ip link set veth0 up
$ ip netns exec ns1 ip addr add 10.1.1.101/24 dev veth1
$ ip netns exec ns1 ip link set veth1 up
这时候查看默认 namespace 和ns1的网络设备,可以看到veth0和veth1设备配置正常:
$ ip a
...
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:54:00:cb:f0:b3 brd ff:ff:ff:ff:ff:ff
inet 192.168.31.92/24 brd 192.168.31.255 scope global dynamic noprefixroute enp1s0
valid_lft 41661sec preferred_lft 41661sec
inet6 fe80::b5fc:b1f8:2b4:a62d/64 scope link noprefixroute
valid_lft forever preferred_lft forever
10: veth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether c6:5e:14:55:f5:b1 brd ff:ff:ff:ff:ff:ff link-netns ns1
inet 10.1.1.100/24 scope global veth0
valid_lft forever preferred_lft forever
inet6 fe80::c45e:14ff:fe55:f5b1/64 scope link
valid_lft forever preferred_lft forever
$ ip netns exec ns1 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
9: veth1@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 36:c9:5b:1a:6d:9b brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.1.1.101/24 scope global veth1
valid_lft forever preferred_lft forever
inet6 fe80::34c9:5bff:fe1a:6d9b/64 scope link
valid_lft forever preferred_lft forever
然后尝试下通过ns1的veth1接口来 ping 默认 namespace 下的veth0,并在veth0上抓包:
# shell-0
$ ip netns exec ns1 ping -c 1 10.1.1.100
PING 10.1.1.100 (10.1.1.100) 56(84) bytes of data.
64 bytes from 10.1.1.100: icmp_seq=1 ttl=64 time=0.143 ms
64 bytes from 10.1.1.100: icmp_seq=2 ttl=64 time=0.046 ms
--- 10.1.1.100 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 1026ms
# shell-1
$ tcpdump -n -i veth0 icmp
08:39:52.182061 IP 10.1.1.101 > 10.1.1.100: ICMP echo request, id 44179, seq 1, length 64
08:39:52.182131 IP 10.1.1.100 > 10.1.1.101: ICMP echo reply, id 44179, seq 1, length 64
因为 veth pair 的特性,所以即便veth0和veth1处在不同的 namespace,它们之间也可以正常通信,并且在veth0网卡上我们可以看到完整的 ICMP Request 和 ICMP Reply 数据包。
还记得在上面的例子中,ICMP Reply 是走的 loopback 的口吗?在当前的场景里,因为veth0和veth1已经在不同的 namespace 下了,所以当veth0在回包时,去路由表local查询,并不会认为veth1 10.1.1.100是本地接口,所以会正常从veth0回包,我们可以看下这两个 namespace 的路由表来验证判断:
$ ip route show table local
local 10.1.1.100 dev veth0 proto kernel scope host src 10.1.1.100
broadcast 10.1.1.255 dev veth0 proto kernel scope link src 10.1.1.100
...
$ ip netns exec ns1 ip route show table local
local 10.1.1.101 dev veth1 proto kernel scope host src 10.1.1.101
broadcast 10.1.1.255 dev veth1 proto kernel scope link src 10.1.1.101
3.容器间的网络
我们刚才看到了在宿主机默认 namespace 和容器 namespace ns1之间,通过 veth pair 可以打通网络,逻辑上就相当于宿主机和容器是两台独立的主机,通过一条网线连接在了一起。
正常一台宿主机里不会只有一个容器,宿主机和容器是一对多的关系,如果要打通容器之间的网络,单纯的依赖 veth pair 也可以做到,但配置就比较复杂,所以在这里我们引入 bridge 来实现。
Linux 的 bridge 是内核提供的虚拟以太网桥,原理上类似于物理交换机,工作在第二层。我们在 Linux 上创建一个 bridge,然后将容器通过 veth pair 插入到这个 bridge 上,以实现容器间的网络互通。
首先创建并启用 bridge br0:
$ brctl addbr br0
$ ip link set br0 up
然后通过 namespace 来模拟容器网络:
# ns0作为容器网络,将 veth pair 的一端放入ns0,另一端插入交换机br0
$ ip netns add ns0
$ ip link add veth0 type veth peer name veth0_br
$ ip link set veth0 netns ns0
$ ip netns exec ns0 ip addr add 10.1.1.100/24 dev veth0
$ ip link set veth0_br up
$ ip netns exec ns0 ip link set veth0 up
$ brctl addif br0 veth0_br
# `ns1`作为容器网络,将 veth pair 的一端放入`ns1`,另一端插入交换机`br0`
$ ip netns add ns1
$ ip link add veth1 type veth peer name veth1_br
$ ip link set veth1 netns ns1
$ ip netns exec ns1 ip addr add 10.1.1.101/24 dev veth1
$ ip link set veth1_br up
$ ip netns exec ns1 ip link set veth1 up
$ brctl addif br0 veth1_br
可以看到容器网络可以正常打通:
ip netns exec ns0 ping -c1 10.1.1.101
PING 10.1.1.101 (10.1.1.101) 56(84) bytes of data.
64 bytes from 10.1.1.101: icmp_seq=1 ttl=64 time=0.058 ms
--- 10.1.1.101 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
4.容器访问外部网络
在刚才的例子中,处在不同 namespace 的容器网络可以通过 veth pair 与 bridge 相连的方式来相互打通,但是如果容器内想要访问外部的网络,就还需要做一些额外的配置。
这里说明一下我的网络环境,我用来测试的 Ubuntu 的机器是插在一个路由器上的,这个路由器的 IP 是 192.168.31.1,DHCP 的网段是 192.168.31.0/24,为了使容器网络能跟外部网络通信,这里我们采用简单一点的方式,就是将容器的网卡 IP 设置成物理网卡的 IP 处在同一网段。
首先创建 bridge br0:
$ brctl addbr br0
$ ip link set br0 up
然后创建 namespace ns0作为容器网络,创建 veth pair veth0和veth0_br,其一端放入ns0,另一端插入网桥br0(这里注意一下我给veth0配置的 IP 192.168.31.100是物理网络的网段)。
$ ip netns add ns0
$ ip link add veth0 type veth peer name veth0_br
$ ip link set veth0 netns ns0
$ ip netns exec ns0 ip addr add 192.168.31.100/24 dev veth0
$ ip link set veth0_br up
$ ip netns exec ns0 ip link set veth0 up
$ brctl addif br0 veth0_br
这里还有一个重要的配置,是在ns0中将默认路由的网关配置为192.168.31.1这个物理网络的网关。
$ ip netns exec ns0 ip route add default via 192.168.31.1 dev veth0
$ ip netns exec ns0 route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.31.1 0.0.0.0 UG 0 0 0 veth0
192.168.31.0 0.0.0.0 255.255.255.0 U 0 0 0 veth0
此时br0上已经插入了veth0_br网卡,但我们知道如果仅仅是这样是无法与外部网络通信的,我们还需要将物理网卡也插入网桥,这样逻辑上物理路由器、物理网卡、veth0_br和veth0会处在一个二层内。
ip addr add 192.168.31.92/24 dev br0
ip addr del 192.168.31.92/24 dev enp1s0
brctl addif br0 enp1s0
通过这样的配置,容器就能正常访问外部网络了。
$ ip netns exec ns0 ping -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 39.156.66.10: icmp_seq=1 ttl=46 time=37.3 ms
--- 39.156.66.10 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 2003ms
今天介绍了通过 bridge + veth pair 来实现单节点的容器网络的思路,后续会介绍跨节点的场景下的实现。
相关推荐
- MongoDB 从入门到实战:.NET 平台完整指南
-
一、什么是MongoDBMongoDB是一种功能强大且灵活的NoSQL数据库,适用于处理大规模的半结构化数据和高并发场景。它不依赖于固定的表结构和关系模型,而是以文档的形式存储数据,每个文档可...
- NET Framework安装失败的原因及解决方法
-
大家好我是艾西,一个做服务器租用的游戏爱好者兼网络架构系统环境问题网络工具人。在我们平时使用PC安装某些程序会出现.NETFramework缺失的提示,那么也会有很多的小伙伴搞不懂什么原因导致的,这...
- 这可是全网eNSP安装最完整,最详细的图解,没有之一(常见问题)
-
eNSP安装大纲eNSP安装详细图解篇幅较长,会分三篇更完。急需安装的朋友可以在文末获取图解文档和所需软件工具。ENSP安装常见问题和解决方案Vbox安装错误eNSP在安装的过程当中,经常会出现一...
- 如何在windows 2012安装.NET Framework3.5
-
Windowsserver2012R2,自带的是.NETFramework4.5,如果想装SQLserver2008或者SQLserver2012需要安装.ENTFramework...
- 3款国内可用的「Chrome」扩展下载网站
-
身为程序员,有几个不使用Chrome浏览器提升下编码效率呢?Chrome拥有众多丰富强大的扩展程序,今天给大家分享三个国内可用的Chrome扩展下载网站,收藏一下吧,不然下次就找不到我咯!C...
- 下载 Windows 10 应用商店程序离线包方法
-
有厂商为了图方便,会把Windows10应用商店里面的UMP应用改成EXE程序版本。例如之前「网易云音乐」UMP版本简洁清爽,获得不少用户推荐,后来官方懒得更新了,直接把UMP版本...
- 极速安装!NET Framework 3.5零距离指南!
-
.NETFramework3.5是一款由微软开发的应用程序框架,它为许多Windows应用程序提供了基础支持。它的新版本带来了许多令人兴奋的功能和改进,比如增强的XML和JSON处理能力以及强大的...
- Microsoft.NET离线运行库合集发布 2021
-
软件介绍.NET是微软具有战略意义的框架,也是装机必不可少的框架,想要一个一个安装略显繁琐,再加上很多电脑小白不知道怎么下载,不小心就下载到某某高速加载器,这个运行库极大解决了这个问题,采用微软官方....
- 缺少.net framework 3.5怎么办?(缺少.net4.5.1或以上环境)
-
很多电脑用户在玩某些程序游戏时都会遇到一个头痛的问题,弹出缺少“NETFramework3.5”的提示。微软从Windows8开始默认屏蔽了“.NET3.5”,如果用户有需要就必须选择在线安装...
- Windows11无法正常安装.net 3.5组件的解决方法
-
最近因公司部分电脑升级至Windows11之后,重新安装某些需要加载.net3.5组件的应用软件时,都提示无法完成加载或安装.net3.5而导致无法完成安装。使用离线安装包亦一样无法完成安装。一...
- 离线安装.Net Framework 3.5(离线安装.net framework 4.0)
-
前言.Net3.5已经越来越少用到了,但是偶尔还是会遇到一些老软件需要。而Win10、Win11的系统,直接在控制面板的里添加,经常会添加失败!解决方法首先需要一个系统的ISO镜像来提取sxs文件夹:...
- Jenkins 11个使用技巧,90%以上的人没用过
-
一、Performance插件兼容性问题自由风格项目中,有使用Performance插件收集构建产物,但是截至到目前最新版本(Jenkinsv2.298,Performance:v3.19),此...
- 6款Linux常用远程连接工具,你最中意哪一款?
-
点击上方头像关注我,每周上午09:00准时推送,每月不定期赠送技术书籍。本文2106字,阅读约需6分钟Hi,大家好。远程连接的实现方法有很多,概括地说有两种,一种是用系统自带的远程连接,另外一种是用...
- Linux常用远程连接工具介绍,总有一款适合你
-
作为运维或者网工最常用就是ssh远程和远程桌面工具,本文就介绍几个常用的远程连接工具,你在用哪一款呢SecureCRT介绍:我觉得这个是最好的SSH工具,没有之一。SecureCRT支持SSH,同时支...
- 终极软路由网络设置,ESXi虚拟机安装iKuai+openWrt双路由系统
-
本内容来源于@什么值得买APP,观点仅代表作者本人|作者:BigBubbleGum本文是软路由系列的第五篇,也是折腾时间最长的一篇,在ESXi下分别独立安装和使用iKuai和openWrt...
你 发表评论:
欢迎- 一周热门
-
-
如何在安装前及安装后修改黑群晖的Mac地址和Sn系列号
-
爱折腾的特斯拉车主必看!手把手教你TESLAMATE的备份和恢复
-
[常用工具] OpenCV_contrib库在windows下编译使用指南
-
Ubuntu系统Daphne + Nginx + supervisor部署Django项目
-
极空间如何无损移机,新Z4 Pro又有哪些升级?极空间Z4 Pro深度体验
-
WindowsServer2022|配置NTP服务器的命令
-
WIN11 安装配置 linux 子系统 Ubuntu 图形界面 桌面系统
-
解决Linux终端中“-bash: nano: command not found”问题
-
UOS服务器操作系统防火墙设置(uos20关闭防火墙)
-
NBA 2K25虚拟内存不足/爆内存/内存占用100% 一文速解
-
- 最近发表
- 标签列表
-
- linux 查询端口号 (58)
- docker映射容器目录到宿主机 (66)
- 杀端口 (60)
- yum更换阿里源 (62)
- internet explorer 增强的安全配置已启用 (65)
- linux自动挂载 (56)
- 禁用selinux (55)
- sysv-rc-conf (69)
- ubuntu防火墙状态查看 (64)
- windows server 2022激活密钥 (56)
- 无法与服务器建立安全连接是什么意思 (74)
- 443/80端口被占用怎么解决 (56)
- ping无法访问目标主机怎么解决 (58)
- fdatasync (59)
- 405 not allowed (56)
- 免备案虚拟主机zxhost (55)
- linux根据pid查看进程 (60)
- dhcp工具 (62)
- mysql 1045 (57)
- 宝塔远程工具 (56)
- ssh服务器拒绝了密码 请再试一次 (56)
- ubuntu卸载docker (56)
- linux查看nginx状态 (63)
- tomcat 乱码 (76)
- 2008r2激活序列号 (65)