以太网协议(Ethernet)深入解析:从底层原理到实战应用
交换机的核心协议,用于定义数据帧的格式和传输方式。
-
目录
-
以太网基础概念
-
以太网帧结构详解
-
MAC地址系统
-
以太网物理层
-
交换技术原理
-
碰撞域与广播域
-
实战:C语言实现以太网帧处理
-
实战:构建简单交换机
-
以太网安全与攻击防护
-
未来发展趋势
1. 以太网基础概念
1.1 历史发展
1973 : 施乐帕洛阿尔托研究中心发明以太网1980 : DEC、Intel、Xerox联合推出DIX以太网标准1983 : IEEE 802.3标准正式发布1995 : 100BASE-T(快速以太网)标准化1998 : 千兆以太网(802.3z)发布2002 : 10千兆以太网(802.3ae)发布2016 : 802.3bs(400GbE)标准发布2022 : 800GbE和1.6TbE开始部署
1.2 核心特点
-
介质访问控制:CSMA/CD(载波侦听多路访问/碰撞检测)
-
拓扑结构:总线型 → 星型(现代)
-
传输速率:10Mbps → 100Mbps → 1Gbps → 10Gbps → 40Gbps → 100Gbps
-
传输介质:同轴电缆 → 双绞线(UTP/STP) → 光纤
1.3 以太网工作模型
+--------------+ +--------------+ | 应用层 | | 应用层 | +--------------+ +--------------+ | 传输层 | | 传输层 | +--------------+ +--------------+ | 网络层 | | 网络层 | +--------------+ +--------------+ | 数据链路层 | | 数据链路层 | +--------------+ +--------------+ | 物理层 | | 物理层 | +------+-------+ +-------+------+| |+---------------------+物理介质
2. 以太网帧结构详解
2.1 以太网帧格式
// C语言中的以太网帧结构表示
typedef struct {uint8_t dest_mac[6]; // 目标MAC地址 (6字节)uint8_t src_mac[6]; // 源MAC地址 (6字节)uint16_t ethertype; // 以太网类型 (2字节)uint8_t payload[1500]; // 负载数据 (46-1500字节)uint32_t crc; // 帧校验序列 (4字节) } EthernetFrame;
-
-
2.2 字段说明
字段 长度 说明 前导码 7字节 10101010...用于同步时钟 SFD 1字节 帧起始定界符(10101011) 目标MAC 6字节 接收方MAC地址 源MAC 6字节 发送方MAC地址 EtherType 2字节 上层协议标识(0x0800=IPv4, 0x86DD=IPv6) 载荷 46-1500字节 传输的数据 FCS 4字节 循环冗余校验(CRC-32) 2.3 EtherType常用值
十六进制 协议 0x0800 IPv4 0x0806 ARP 0x86DD IPv6 0x8100 VLAN标记 0x88CC LLDP 0x8864 PPPoE发现阶段 0x8863 PPPoE会话阶段
3. MAC地址系统
3.1 MAC地址格式
3.2 特殊MAC地址
MAC地址 类型 用途 FF:FF:FF:FF:FF:FF 广播地址 发送到所有设备 01:80:C2:00:00:00 组播地址 STP生成树协议 01:00:0C:CC:CC:CD 组播地址 CDP/VTP协议 00:00:0C:xx:xx:xx Cisco设备 Cisco默认OUI
4. 以太网物理层
4.1 编码技术对比
标准 编码方式 特点 10BASE-T 曼彻斯特编码 1低→高, 0高→低 100BASE-TX 4B/5B → MLT-3 三电平编码 1000BASE-T 4D-PAM5 五电平编码 10GBASE-T 16-DPAM 复杂调制方案 4.2 常见以太网标准
标准 速率 传输距离 介质 应用 10BASE-T 10Mbps 100m Cat3+ 已淘汰 100BASE-TX 100Mbps 100m Cat5 小型网络 1000BASE-T 1Gbps 100m Cat5e+ 主流标准 10GBASE-T 10Gbps 55-100m Cat6a/Cat7 数据中心 40GBASE-SR4 40Gbps 100m 多模光纤 骨干网 100GBASE-SR4 100Gbps 70m 多模光纤 数据中心骨干
5. 交换技术原理
5.1 交换机工作原理
5.2 交换方式对比
交换方式 特点 延迟 错误处理 典型应用 存储转发 完整接收帧后处理 高 CRC验证 现代交换机 直通交换 读取目标MAC即转发 低 无错误校验 高速环境 无碎片 接收64字节后转发 中 过滤碎片 过渡方案 5.3 MAC地址表操作
// C语言实现简单MAC地址表 #include <stdio.h> #include <string.h> #include <time.h> #define MAX_ENTRIES 1024 typedef struct {uint8_t mac[6];int port;time_t last_seen; } MACEntry; MACEntry mac_table[MAX_ENTRIES]; int entry_count = 0; // 更新或添加MAC条目 void update_mac_table(uint8_t mac[6], int port) {time_t now = time(NULL);// 检查现有条目for (int i = 0; i < entry_count; i++) {if (memcmp(mac_table[i].mac, mac, 6) == 0) {mac_table[i].port = port;mac_table[i].last_seen = now;return;}}// 添加新条目if (entry_count < MAX_ENTRIES) {memcpy(mac_table[entry_count].mac, mac, 6);mac_table[entry_count].port = port;mac_table[entry_count].last_seen = now;entry_count++;} } // 老化机制(删除过时条目) void age_mac_table(int max_age) {time_t now = time(NULL);int j = 0;for (int i = 0; i < entry_count; i++) {if (now - mac_table[i].last_seen <= max_age) {if (i != j) {mac_table[j] = mac_table[i];}j++;}}entry_count = j;//未过期的总数 } // 查找MAC对应的端口 int find_port_by_mac(uint8_t mac[6]) {for (int i = 0; i < entry_count; i++) {if (memcmp(mac_table[i].mac, mac, 6) == 0) {return mac_table[i].port;}}return -1; // 未找到 }
6. 碰撞域与广播域
6.1 概念对比
6.2 VLAN技术
7. 实战:C语言实现以太网帧处理
7.1 原始套接字初始化
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <linux/if_packet.h> #include <net/ethernet.h> #include <net/if.h> #include <arpa/inet.h>// 创建原始套接字 int create_raw_socket(const char *interface) {int sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));if (sockfd < 0) {perror("socket");return -1;}// 获取接口索引struct ifreq ifr;memset(&ifr, 0, sizeof(ifr));strncpy(ifr.ifr_name, interface, IFNAMSIZ);if (ioctl(sockfd, SIOCGIFINDEX, &ifr) < 0) {perror("ioctl(SIOCGIFINDEX)");close(sockfd);return -1;}// 绑定到接口struct sockaddr_ll sll;memset(&sll, 0, sizeof(sll));sll.sll_family = AF_PACKET;sll.sll_ifindex = ifr.ifr_ifindex;sll.sll_protocol = htons(ETH_P_ALL);if (bind(sockfd, (struct sockaddr*)&sll, sizeof(sll)) < 0) {perror("bind");close(sockfd);return -1;}// 设置混杂模式if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) == -1) {perror("ioctl(SIOCGIFFLAGS)");close(sockfd);return -1;}ifr.ifr_flags |= IFF_PROMISC;if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) == -1) {perror("ioctl(SIOCSIFFLAGS)");close(sockfd);return -1;}return sockfd; }
7.2 以太网帧解析器
#include <linux/if_ether.h>void parse_ethernet_frame(const uint8_t *packet, size_t len) {// 检查最小帧长度if (len < sizeof(struct ethhdr)) {fprintf(stderr, "Frame too small: %zu bytes\n", len);return;}const struct ethhdr *eth_header = (struct ethhdr*)packet;// 打印源MAC和目标MACprintf("Ethernet Header:\n");printf(" Destination: %02x:%02x:%02x:%02x:%02x:%02x\n", eth_header->h_dest[0], eth_header->h_dest[1],eth_header->h_dest[2], eth_header->h_dest[3],eth_header->h_dest[4], eth_header->h_dest[5]);printf(" Source: %02x:%02x:%02x:%02x:%02x:%02x\n", eth_header->h_source[0], eth_header->h_source[1],eth_header->h_source[2], eth_header->h_source[3],eth_header->h_source[4], eth_header->h_source[5]);// 打印类型/长度uint16_t eth_type = ntohs(eth_header->h_proto);printf(" Type: 0x%04x", eth_type);// 识别常见协议switch(eth_type) {case ETH_P_IP:printf(" (IPv4)\n");parse_ip_packet(packet + sizeof(struct ethhdr), len - sizeof(struct ethhdr));break;case ETH_P_IPV6:printf(" (IPv6)\n");// 解析IPv6包break;case ETH_P_ARP:printf(" (ARP)\n");parse_arp_packet(packet + sizeof(struct ethhdr), len - sizeof(struct ethhdr));break;// 更多协议解析...default:printf(" (Unknown)\n");} }// ARP包解析示例 void parse_arp_packet(const uint8_t *packet, size_t len) {if (len < sizeof(struct arphdr)) {fprintf(stderr, "ARP packet too small: %zu bytes\n", len);return;}struct arphdr *arp_header = (struct arphdr*)packet;printf("ARP Packet:\n");printf(" Hardware Type: %u\n", ntohs(arp_header->ar_hrd));printf(" Protocol Type: 0x%04x\n", ntohs(arp_header->ar_pro));printf(" Operation: %s\n", (ntohs(arp_header->ar_op) == ARPOP_REQUEST) ? "Request" : (ntohs(arp_header->ar_op) == ARPOP_REPLY) ? "Reply" : "Other");// 解析ARP数据部分... }
7.3 帧发送函数
#include <linux/if_ether.h>void parse_ethernet_frame(const uint8_t *packet, size_t len) {// 检查最小帧长度if (len < sizeof(struct ethhdr)) {fprintf(stderr, "Frame too small: %zu bytes\n", len);return;}const struct ethhdr *eth_header = (struct ethhdr*)packet;// 打印源MAC和目标MACprintf("Ethernet Header:\n");printf(" Destination: %02x:%02x:%02x:%02x:%02x:%02x\n", eth_header->h_dest[0], eth_header->h_dest[1],eth_header->h_dest[2], eth_header->h_dest[3],eth_header->h_dest[4], eth_header->h_dest[5]);printf(" Source: %02x:%02x:%02x:%02x:%02x:%02x\n", eth_header->h_source[0], eth_header->h_source[1],eth_header->h_source[2], eth_header->h_source[3],eth_header->h_source[4], eth_header->h_source[5]);// 打印类型/长度uint16_t eth_type = ntohs(eth_header->h_proto);printf(" Type: 0x%04x", eth_type);// 识别常见协议switch(eth_type) {case ETH_P_IP:printf(" (IPv4)\n");parse_ip_packet(packet + sizeof(struct ethhdr), len - sizeof(struct ethhdr));break;case ETH_P_IPV6:printf(" (IPv6)\n");// 解析IPv6包break;case ETH_P_ARP:printf(" (ARP)\n");parse_arp_packet(packet + sizeof(struct ethhdr), len - sizeof(struct ethhdr));break;// 更多协议解析...default:printf(" (Unknown)\n");} }// ARP包解析示例 void parse_arp_packet(const uint8_t *packet, size_t len) {if (len < sizeof(struct arphdr)) {fprintf(stderr, "ARP packet too small: %zu bytes\n", len);return;}struct arphdr *arp_header = (struct arphdr*)packet;printf("ARP Packet:\n");printf(" Hardware Type: %u\n", ntohs(arp_header->ar_hrd));printf(" Protocol Type: 0x%04x\n", ntohs(arp_header->ar_pro));printf(" Operation: %s\n", (ntohs(arp_header->ar_op) == ARPOP_REQUEST) ? "Request" : (ntohs(arp_header->ar_op) == ARPOP_REPLY) ? "Reply" : "Other");// 解析ARP数据部分... }
8. 实战:构建简单交换机
8.1 简单交换机架构
#include <pthread.h> #include <sys/epoll.h>#define MAX_PORTS 8 #define MAX_FDS 10typedef struct {int port_num;char name[IFNAMSIZ];int sockfd;uint8_t mac[6]; } SwitchPort;typedef struct {SwitchPort ports[MAX_PORTS];int port_count;MACEntry mac_table[MAX_ENTRIES];int running; } EthernetSwitch;// 初始化交换机 int init_switch(EthernetSwitch *sw) {// 创建epoll实例int epoll_fd = epoll_create1(0);if (epoll_fd < 0) {perror("epoll_create1");return -1;}// 添加端口监控...struct epoll_event ev;for (int i = 0; i < sw->port_count; i++) {ev.events = EPOLLIN;ev.data.fd = sw->ports[i].sockfd;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sw->ports[i].sockfd, &ev) < 0) {perror("epoll_ctl");return -1;}}return epoll_fd; }// 交换机主循环 void switch_main_loop(EthernetSwitch *sw) {int epoll_fd = init_switch(sw);struct epoll_event events[MAX_FDS];while (sw->running) {int nfds = epoll_wait(epoll_fd, events, MAX_FDS, 100); // 100ms超时for (int i = 0; i < nfds; i++) {// 处理接收到的帧process_incoming_frame(sw, events[i].data.fd);}// 每10秒老化MAC地址表static time_t last_aging = 0;time_t now = time(NULL);if (now - last_aging > 10) {age_mac_table(&sw->mac_table, MAC_AGING_TIME);last_aging = now;}}close(epoll_fd); }// 帧处理函数 void process_incoming_frame(EthernetSwitch *sw, int port_fd) {// 找到端口索引int port_index = -1;for (int i = 0; i < sw->port_count; i++) {if (sw->ports[i].sockfd == port_fd) {port_index = i;break;}}if (port_index == -1) return;// 接收帧uint8_t buffer[2048];ssize_t len = recv(port_fd, buffer, sizeof(buffer), 0);if (len <= 0) return;// 解析以太网头部struct ethhdr *eth_header = (struct ethhdr*)buffer;// 更新MAC地址表(源MAC对应此端口)update_mac_table(&sw->mac_table, eth_header->h_source, port_index);// 检查目标MACint dest_port = find_port_by_mac(&sw->mac_table, eth_header->h_dest);// 广播或多播处理if (is_broadcast_mac(eth_header->h_dest) || is_multicast_mac(eth_header->h_dest)) {// 泛洪到所有其他端口for (int i = 0; i < sw->port_count; i++) {if (i != port_index) {send_ethernet_frame(sw->ports[i].sockfd, sw->ports[i].mac,eth_header->h_dest,ntohs(eth_header->h_proto),buffer + sizeof(struct ethhdr),len - sizeof(struct ethhdr));}}}// 已知单播处理else if (dest_port >= 0) {send_ethernet_frame(sw->ports[dest_port].sockfd,sw->ports[dest_port].mac,eth_header->h_dest,ntohs(eth_header->h_proto),buffer + sizeof(struct ethhdr),len - sizeof(struct ethhdr));}// 未知单播处理(泛洪)else {for (int i = 0; i < sw->port_count; i++) {if (i != port_index) {send_ethernet_frame(sw->ports[i].sockfd, sw->ports[i].mac,eth_header->h_dest,ntohs(eth_header->h_proto),buffer + sizeof(struct ethhdr),len - sizeof(struct ethhdr));}}} }
8.2 使用Linux网桥配置
# 创建网桥 sudo ip link add name br0 type bridge sudo ip link set br0 up# 将端口加入网桥 sudo ip link set eth0 master br0 sudo ip link set eth1 master br0# 查看网桥状态 bridge link show# 配置STP生成树协议 echo 1 | sudo tee /sys/class/net/br0/bridge/stp_state
9. 以太网安全与攻击防护
9.1 常见攻击方式及防护
攻击类型 原理 防护措施 MAC洪泛 发送大量虚假MAC耗尽交换机表空间 端口安全(MAC数量限制) ARP欺骗 发送虚假ARP响应劫持流量 ARP检测、静态ARP绑定 VLAN跳跃 通过双VLAN标签突破隔离 Native VLAN配置 STP攻击 发送恶意BPDU接管网络 BPDU Guard、根防护 MAC欺骗 伪造合法MAC地址 端口安全、动态ARP检测 9.2 端口安全实现代码
// 简单端口安全实现 typedef struct {uint8_t allowed_macs[MAX_MAC_PER_PORT][6];int mac_count;bool sticky;ViolationAction action; } PortSecurity;// 检查端口帧的MAC是否合法 int check_port_security(PortSecurity *ps, uint8_t *src_mac) {// 学习模式下自动添加if (ps->sticky && ps->mac_count < MAX_MAC_PER_PORT) {memcpy(ps->allowed_macs[ps->mac_count], src_mac, 6);ps->mac_count++;return 0;}// 检查MAC是否在允许列表中for (int i = 0; i < ps->mac_count; i++) {if (memcmp(ps->allowed_macs[i], src_mac, 6) == 0) {return 0;}}// 违规处理handle_security_violation(ps->action);return -1; }void handle_security_violation(ViolationAction action) {switch(action) {case ACTION_SHUTDOWN:// 关闭端口break;case ACTION_RESTRICT:// 限制流量break;case ACTION_PROTECT:// 记录日志但允许通过syslog(LOG_WARNING, "MAC violation detected");break;} }
10. 未来发展趋势
10.1 TSN(时间敏感网络)
10.2 关键技术演进
-
高速以太网:800GbE (2022) → 1.6TbE (2025)
-
节能以太网:EEE (802.3az) 空闲时节能90%
-
PoE发展:802.3bt (90W) 支持更多设备
-
单对以太网:10BASE-T1S/T1L (汽车、工业物联网)
-
自动化运维:AI驱动的故障预测与优化
10.3 RDMA与RoCE
RDMA优势:
-
零拷贝技术:绕过内核直接访问内存
-
低延迟:低于5μs延迟
-
CPU卸载:网卡处理网络协议栈
-