PHASE 1 · LINUX 网络栈实战

1.1网络接口全家福

eth / wlan / lo / bridge / veth / vlan / tun·tap

Phase 0 你看懂了数据包穿过哪几层。Phase 1 换挡:在一台 Linux 上,亲手把这些东西造出来、调出来。第一站是认全"网卡"——但你很快会发现,Linux 里绝大多数"网卡"根本不是硬件。其中 vethbridge 你在 Phase 0 其实已经用过了(0.3 的 namespace 实验、0.2 的 br-lan),这章把它们连同 eth/wlan/lo/vlan/tun·tap 一并讲清。

一句话定义

网络接口(Network Interface):内核暴露的"收发数据包的端点"。它可能对应一块物理网卡,也可能纯粹是软件造出来的——内核一视同仁。

网络接口 interface
物理 · 接硬件
eth 有线wlan 无线
虚拟 · 软件造
lobridgevethvlantun/tap

1接口:内核眼里的网卡

这是 Phase 1 第一个要扭转的直觉:你以为"网卡 = 那块插网线的硬件",但内核眼里,接口只是一个有名字、能进出数据包的对象。物理网卡是接口,环回是接口,你后面要造的 bridge、veth、隧道全是接口。先列出本机所有接口:

bash
ip link show          # 列出全部接口
ip -br link           # 紧凑视图(一行一个,看名字+状态最方便)

每个接口都有:一个名字(eth0、wlan0、lo…)、一个 index(内核内部编号)、一组状态标志(UP / DOWN / LOWER_UP…)、MTU(0.2 那个 1500),以及大多数都有一个 MAC 地址(也是 0.2)。

最简单的接口是 lo(loopback,环回):每台机器都有,发给它的包永远留在本机、不出网卡。127.0.0.1::1 就绑在它上面。你 curl localhost,包在 lo 上转一圈就回来了,根本不碰物理网卡——这也是本机服务间通信飞快的原因。回想 0.3 的 namespace 实验:每个 namespace 我都单独 ip link set lo up,正因为每个网络命名空间有自己独立的 lo。

类比:lo 像网络版的 /dev/null 或管道

它是内核提供的一个"虚拟端点",看着像真设备,数据却根本不出内核。这正是后面所有虚拟接口的共同气质——名字和真网卡一样,背后没有硬件。

2物理接口:eth 与 wlan

eth(以太网)wlan(Wi-Fi)是少数真正接硬件的接口,直接连着网卡驱动。各有一个硬件 MAC(0.2);eth 收发以太网帧,wlan 收发 802.11 帧但对上层伪装成以太网。

命名上有新旧两套:传统是 eth0 / wlan0;现代 systemd 用可预测命名(predictable naming)——enp3s0(en=ethernet,p3=PCI 总线 3,s0=插槽 0)、wlp2s0(wl=wireless lan)。好处是插拔、重启后名字不变。

bash
ip link set eth0 up        # 启用接口(就是 Phase 0 实验里的 set ... up)
ip link set eth0 down      # 禁用
ethtool eth0               # 有线物理层细节:速率、双工、连接状态
iw dev                     # 无线接口与连接信息(Wi-Fi)
路由器关联 router-link

路由器的物理口(一个 WAN 口、几个 LAN 口)就是这些 eth 接口。但 0.2 学过,LAN 那几个口背后被桥成一个 br-lan——也就是路由器把多个物理 eth 接口"绑"进了一个 bridge。OpenWRT 新版用 DSA(Distributed Switch Architecture)把交换芯片的每个口暴露成独立 eth 接口(lan1lan2…),再按需桥接——正是 1.1 这些接口类型在真实路由器里的样子。下一节就讲 bridge。

3bridge 与 veth:虚拟布线的两件基本元件

这是 Phase 1 真正打开局面的一节。有两件软件元件,合起来你就能在一台机器上搭出任意网络拓扑:

  • bridge = 内核里的一台软件交换机:把多个接口"插"进它,这些接口就成了同一个二层网段,像接在同一台交换机上。
  • veth = 一对虚拟网线:总是成对出现,从一端进的包从另一端出。

bridge:软件交换机

回想 0.2:交换机在二层按 MAC 转发帧,br-lan 把路由器多个 LAN 口并成一个广播域。Linux bridge 就是这台交换机的软件版。

bash
sudo ip link add br0 type bridge     # 造一个 bridge,叫 br0
sudo ip link set br0 up
sudo ip link set eth1 master br0     # 把 eth1 插进 br0(eth1 成为 br0 的一个口)
ip link show master br0              # 看谁插在 br0 上
bridge link                         # 看桥的端口

veth:成对的虚拟网线

回想 0.3:namespace 实验里,我用 ip link add v-h1 type veth peer name v-r1 造了一对 veth,两端分别塞进不同 namespace,当作连接它们的网线。veth 永远成对,从一端进的包从另一端出——天生用来"连接两个隔离的网络域"。

bash
sudo ip link add veth-a type veth peer name veth-b   # 造一对:a、b 是一根线的两头
sudo ip link set veth-b master br0                   # 例如把一端插上 br0
# 另一端可以塞进某个 namespace(0.3 那样),或留在本机
这就是 Docker / k8s 网络的底层积木

bridge 当交换机、veth 当网线、namespace(0.3,1.4 细讲)当主机——三件凑齐,就能在一台机器上搭任意拓扑。你 docker run 起一个容器,Docker 背后做的正是:建一个 namespace,造一对 veth,一端进容器、一端插上 docker0 这个 bridge。你 Phase 0 手动跑的那套 netns + veth,就是容器网络的本质。

路由器关联 router-link

一台 OpenWRT 路由器的 br-lan 就是这一节的 bridge:把 lan1lan2lan3 这些 eth 接口 master br-lan,它们就成了同一个二层网段,LAN 口之间二层互通。你在 /etc/config/network 写的 bridge device + ports,netifd 翻译成的正是这几条 ip link add … type bridge + ip link set … master

4VLAN:一条线切多个网段

VLAN(802.1Q)在以太网帧头插一个 4 字节的 VLAN tag,带一个 VLAN ID(1–4094)。同一条物理线上,不同 VLAN ID 的帧互相看不见——逻辑上等于多台独立交换机。回想 0.2 的以太网帧:这个 tag 就插在源 MAC 和类型字段之间。

为什么有用:家里一根网线 / 一台交换机,想隔离出 LAN / Guest / IoT 三个网段(各自的访问策略),不必拉三条线,打 VLAN 标签即可。在 Linux 上造 VLAN 子接口:

bash
# 在 eth0 上造一个 VLAN 10 的子接口
sudo ip link add link eth0 name eth0.10 type vlan id 10
sudo ip link set eth0.10 up
# eth0.10 收发的帧都带 VLAN tag 10;eth0 本身还能同时跑别的 VLAN

命名约定 eth0.10 = eth0 上的 VLAN 10。

路由器关联 router-link

这正是路由器做 LAN / Guest / IoT 隔离的底层手段:一个口插交换机,交换机按 VLAN 分,路由器在不同 VLAN 子接口上跑不同网段 + 不同防火墙策略。本课程后面 1.3 的"三 VLAN 家庭防火墙"专题、2.7 的 VLAN 隔离都靠它;OpenWRT 的 DSA 也是用 VLAN 在交换芯片里划隔离。

5tun/tap:用户空间的网卡

前面的接口,包都在内核里流转。tun/tap不同:它在内核和某个用户态程序之间架了个口子,把进出这个接口的包交给程序处理。两种模式:

  • tap:工作在二层,收发完整以太网帧(像一块虚拟网卡);
  • tun:工作在三层,收发 IP 包(没有以太网头)。

谁用它:VPN。一个 VPN 程序创建一个 tun 接口,系统把要走 VPN 的 IP 包路由到 tun;VPN 程序从 tun "读出"这些包,加密后通过普通 socket 发给对端;对端解密、写回它的 tun。OpenVPN 是这个模式;WireGuard 在内核里实现,但对外同样呈现一个 tun 风格的接口 wg0

bash
ip link show type wireguard          # WireGuard 接口(1.6 隧道技术细讲)
sudo ip tuntap add dev tun0 mode tun # 通用 tun(一般由 VPN 程序自动建)
类比:tun 像网络版的命名管道

内核往里写 IP 包,你的程序从里读;你的程序往里写,内核当成"收到的包"处理。VPN 的本质,就是把这些包在用户态加密后,套进另一条普通连接里发走。理解了 tun,你就理解了几乎所有 VPN 的工作方式。

路由器关联 router-link

路由器上的 WireGuard / OpenVPN(本课程 3.4 的 WireGuard mesh、2.7 的透明代理)都基于 tun 风格接口。你给路由器配一个 wg0,把要走隧道的流量路由过去——又回到了"接口 + 路由表"这套 Phase 0 的基本盘:一切流量调度,最后都落到"从哪个接口出、查哪条路由"。

本章小结

  • 接口是内核抽象,不一定是硬件;ip link 列出全部。
  • lo:本机环回,localhost 流量不出网卡;每个 namespace 各有一个。
  • eth / wlan:真接硬件;现代用可预测命名(enp3s0 / wlp2s0)。
  • bridge(软件交换机,把接口 master 进去)+ veth(成对虚拟网线)+ namespace = 在一台机器上搭任意拓扑,也是容器网络的底层积木。
  • VLAN:帧里打 tag,一条线跑多个隔离网段(LAN/Guest/IoT)。
  • tun/tap:内核把包交给用户态程序,VPN 的基础(tun=三层,tap=二层)。

动手练习

  1. ip -br link,给你机器上每个接口归类:哪些是物理(eth/wlan)、哪些是虚拟(lo/docker0/veth/…)?有没有 docker0 这种 bridge?
  2. 造一对 veth、一个 bridge,把 veth 一端插上 bridge(ip link add + master),用 bridge link 确认插上了;做完用 ip link del 清理。
  3. 思考题:你 curl http://127.0.0.1:3000 访问本机服务,这个包经过物理网卡吗?走的是哪个接口?(回想 lo。)
  4. 进阶:若装了 Docker,跑 ip linkdocker0(一个 bridge)和若干 veth;docker run 一个容器后再看,多了什么?把它和 0.3 的 namespace + veth 实验对应起来。