第2章 以太网与二层通信
学习目标
- 理解以太网帧结构和MAC地址机制
- 掌握ARP协议的工作原理
- 了解VLAN和生成树协议
- 能够配置和排查二层网络问题
🔬 原理
以太网帧结构
标准以太网帧格式:
[目的MAC(6)] [源MAC(6)] [类型(2)] [数据(46-1500)] [CRC(4)]
字段详解:
- 目的MAC:6字节,目标设备的物理地址
- 源MAC:6字节,发送设备的物理地址
- 类型:2字节,上层协议类型(0x0800=IP,0x0806=ARP)
- 数据:46-1500字节,有效载荷
- CRC:4字节,循环冗余校验
最小/最大帧长度:
- 最小:64字节(6+6+2+46+4)
- 最大:1518字节(6+6+2+1500+4)
- 原因:确保冲突检测机制正常工作
MAC地址机制
MAC地址格式:
格式:XX:XX:XX:XX:XX:XX(十六进制)
示例:00:1B:44:11:3A:B7
地址类型:
- 单播:第一个字节最低位为0
- 多播:第一个字节最低位为1
- 广播:全1地址(FF:FF:FF:FF:FF:FF)
地址分配:
- 前3字节:OUI(组织唯一标识符)
- 后3字节:厂商分配
ARP协议
ARP(Address Resolution Protocol):IP地址到MAC地址的映射
工作过程:
- ARP请求:广播"谁是192.168.1.1?告诉我你的MAC"
- ARP应答:单播"我是192.168.1.1,MAC是xx:xx:xx"
- 缓存更新:将映射关系存入ARP表
ARP表结构:
IP地址 MAC地址 接口
192.168.1.1 00:1b:44:11:3a:b7 eth0
192.168.1.2 00:1b:44:11:3a:b8 eth0
VLAN技术
VLAN(Virtual LAN):虚拟局域网,通过Tag隔离二层广播域
VLAN Tag格式:
[目的MAC(6)] [源MAC(6)] [VLAN Tag(4)] [类型(2)] [数据] [CRC(4)]
VLAN Tag字段:
- TPID:2字节,固定值0x8100
- PCP:3位,优先级
- DEI:1位,丢弃指示
- VID:12位,VLAN ID(1-4094)
VLAN类型:
- Access端口:只属于一个VLAN
- Trunk端口:属于多个VLAN,带Tag传输
生成树协议(STP)
STP目的:防止二层环路,确保网络拓扑无环
工作原理:
- 选举根桥(最小BID)
- 每个非根桥选择根端口
- 每个网段选择指定端口
- 阻塞其他端口
️ 实现
Linux Bridge实现
Bridge数据结构:
struct net_bridge {
struct list_head port_list; // 端口列表
struct hlist_head hash[BR_HASH_SIZE]; // FDB哈希表
struct net_device *dev; // Bridge设备
// ... 更多字段
};
struct net_bridge_port {
struct net_bridge *br; // 所属Bridge
struct net_device *dev; // 端口设备
struct hlist_head hash[BR_HASH_SIZE]; // 端口FDB
// ... 更多字段
};
FDB(Forwarding Database):
- 学习MAC地址与端口的映射
- 基于源MAC学习
- 基于目标MAC转发
ARP实现
ARP表管理:
struct neigh_table arp_tbl = {
.family = AF_INET,
.key_len = 4, // IP地址长度
.hash = arp_hash,
.constructor = arp_constructor,
.pconstructor = arp_constructor,
.pdestructor = arp_destructor,
.proxy_redo = parp_redo,
.id = "arp_cache",
.parms = {
.tbl = &arp_tbl,
.reachable_time = 30 * HZ,
.data = {
[NEIGH_VAR_MCAST_PROBE] = 3,
[NEIGH_VAR_UCAST_PROBE] = 3,
[NEIGH_VAR_APP_PROBES] = 0,
[NEIGH_VAR_RETRANS_TIME] = 1 * HZ,
[NEIGH_VAR_BASE_REACHABLE_TIME] = 30 * HZ,
[NEIGH_VAR_DELAY_PROBE_TIME] = 5 * HZ,
[NEIGH_VAR_GC_STALETIME] = 60 * HZ,
[NEIGH_VAR_QUEUE_LEN] = 3,
[NEIGH_VAR_QUEUE_LEN_BYTES] = 64 * 1024,
[NEIGH_VAR_PROXY_QLEN] = 64,
[NEIGH_VAR_ANYCAST_DELAY] = 1 * HZ,
[NEIGH_VAR_PROXY_DELAY] = (8 * HZ) / 10,
[NEIGH_VAR_LOCKTIME] = 1 * HZ,
},
},
.gc_interval = 30 * HZ,
.gc_thresh1 = 128,
.gc_thresh2 = 512,
.gc_thresh3 = 1024,
};
🛠️ 命令
基础二层命令
# 查看ARP表
ip neigh show
# 查看Bridge信息
bridge link
# 查看FDB
bridge fdb show
# 传统ARP命令
arp -an
VLAN配置
# 创建VLAN接口
sudo ip link add link eth0 name eth0.10 type vlan id 10
# 配置VLAN IP
sudo ip addr add 10.0.10.1/24 dev eth0.10
# 启动VLAN接口
sudo ip link set eth0.10 up
# 查看VLAN配置
ip -d link show eth0.10
Bridge配置
# 创建Bridge
sudo ip link add name br0 type bridge
# 启动Bridge
sudo ip link set br0 up
# 添加端口到Bridge
sudo ip link set eth1 master br0
# 配置Bridge IP
sudo ip addr add 192.168.1.1/24 dev br0
# 查看Bridge状态
bridge link show
代码
ARP学习程序
// arp_learning.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <net/if.h>
struct arp_hdr {
unsigned short ar_hrd; // 硬件类型
unsigned short ar_pro; // 协议类型
unsigned char ar_hln; // 硬件地址长度
unsigned char ar_pln; // 协议地址长度
unsigned short ar_op; // 操作码
unsigned char ar_sha[6]; // 发送方硬件地址
unsigned char ar_sip[4]; // 发送方IP地址
unsigned char ar_tha[6]; // 目标硬件地址
unsigned char ar_tip[4]; // 目标IP地址
};
int main() {
int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
if (sock < 0) {
perror("socket");
return 1;
}
char buffer[1024];
while (1) {
int len = recv(sock, buffer, sizeof(buffer), 0);
if (len < 0) {
perror("recv");
continue;
}
struct arp_hdr *arp = (struct arp_hdr*)(buffer + 14);
if (ntohs(arp->ar_op) == 1) { // ARP请求
printf("ARP Request: Who has %d.%d.%d.%d? Tell %d.%d.%d.%d\n",
arp->ar_tip[0], arp->ar_tip[1], arp->ar_tip[2], arp->ar_tip[3],
arp->ar_sip[0], arp->ar_sip[1], arp->ar_sip[2], arp->ar_sip[3]);
} else if (ntohs(arp->ar_op) == 2) { // ARP应答
printf("ARP Reply: %d.%d.%d.%d is at %02x:%02x:%02x:%02x:%02x:%02x\n",
arp->ar_sip[0], arp->ar_sip[1], arp->ar_sip[2], arp->ar_sip[3],
arp->ar_sha[0], arp->ar_sha[1], arp->ar_sha[2],
arp->ar_sha[3], arp->ar_sha[4], arp->ar_sha[5]);
}
}
close(sock);
return 0;
}
编译运行:
gcc arp_learning.c -o arp_learning
sudo ./arp_learning
🧪 实验
实验1:ARP学习过程观察
目标:理解ARP协议的工作机制
步骤:
# 1. 清空ARP缓存
sudo ip neigh flush all
# 2. 查看ARP表(应该为空或很少)
ip neigh show
# 3. ping一个同网段IP
ping -c 1 192.168.1.1
# 4. 再次查看ARP表
ip neigh show
# 5. 抓包观察ARP过程
sudo tcpdump -i eth0 -nn arp
预期结果:
- 看到ARP请求和应答
- 理解广播和单播的区别
- 观察ARP表的学习过程
实验2:VLAN配置与测试
目标:掌握VLAN的配置和隔离效果
步骤:
# 1. 创建VLAN 10
sudo ip link add link eth0 name eth0.10 type vlan id 10
sudo ip addr add 10.0.10.1/24 dev eth0.10
sudo ip link set eth0.10 up
# 2. 创建VLAN 20
sudo ip link add link eth0 name eth0.20 type vlan id 20
sudo ip addr add 10.0.20.1/24 dev eth0.20
sudo ip link set eth0.20 up
# 3. 查看VLAN配置
ip -d link show eth0.10
ip -d link show eth0.20
# 4. 测试VLAN隔离
ping -I eth0.10 10.0.20.1 # 应该失败
ping -I eth0.10 10.0.10.2 # 如果存在应该成功
预期结果:
- 理解VLAN的隔离效果
- 掌握VLAN Tag的作用
- 学会VLAN配置方法
实验3:Bridge网络构建
目标:构建多容器Bridge网络
步骤:
# 1. 创建Bridge
sudo ip link add br0 type bridge
sudo ip link set br0 up
sudo ip addr add 10.0.0.1/24 dev br0
# 2. 创建3个namespace模拟容器
for i in 1 2 3; do
sudo ip netns add ns$i
sudo ip link add veth$i type veth peer name veth${i}-br
sudo ip link set veth$i netns ns$i
sudo ip link set veth${i}-br master br0
sudo ip link set veth${i}-br up
sudo ip netns exec ns$i ip link set veth$i up
sudo ip netns exec ns$i ip addr add 10.0.0.1$i/24 dev veth$i
sudo ip netns exec ns$i ip route add default via 10.0.0.1
done
# 3. 测试容器间通信
sudo ip netns exec ns1 ping -c 2 10.0.0.12
sudo ip netns exec ns1 ping -c 2 10.0.0.13
# 4. 查看Bridge学习的MAC
bridge fdb show br br0
# 5. 抓包观察ARP和ICMP
sudo tcpdump -i br0 -n
预期结果:
- 理解Bridge的MAC学习机制
- 掌握容器网络基础
- 观察二层转发过程
实验4:容器访问外网(NAT)
目标:配置NAT让容器访问外网
步骤:
# 1. 开启IP转发
sudo sysctl -w net.ipv4.ip_forward=1
# 2. 添加SNAT规则
sudo iptables -t nat -A POSTROUTING -s 10.0.0.0/24 ! -o br0 -j MASQUERADE
# 3. 测试外网访问
sudo ip netns exec ns1 ping -c 2 8.8.8.8
# 4. 查看NAT规则
sudo iptables -t nat -vnL
预期结果:
- 理解NAT的工作原理
- 掌握容器外网访问配置
- 学会iptables NAT规则
排错
常见问题排查
问题1:ARP表学习异常
# 检查ARP表
ip neigh show
# 手动添加ARP条目
sudo ip neigh add 192.168.1.100 lladdr 00:11:22:33:44:55 dev eth0
# 删除ARP条目
sudo ip neigh del 192.168.1.100 dev eth0
# 检查ARP统计
cat /proc/net/arp
问题2:VLAN配置问题
# 检查VLAN支持
cat /proc/net/vlan/config
# 检查VLAN接口状态
ip -d link show eth0.10
# 检查VLAN Tag
tcpdump -i eth0 -nn vlan
问题3:Bridge转发异常
# 检查Bridge状态
bridge link show
# 检查FDB
bridge fdb show
# 检查Bridge统计
cat /proc/net/bridge/bridge-nf-call-iptables
排错清单
- [ ] 检查物理连接(网线、网卡状态)
- [ ] 验证MAC地址配置(唯一性、格式)
- [ ] 确认ARP表学习(超时、刷新)
- [ ] 检查VLAN配置(ID、Tag、端口)
- [ ] 验证Bridge状态(端口、FDB)
- [ ] 测试二层连通性(ping、arping)
- [ ] 查看系统日志(dmesg、/var/log/syslog)