概述
数据链路层是Linux网络协议栈中连接物理层和网络层的关键桥梁,主要负责处理以太网帧、ARP协议解析、VLAN标签处理等功能。Linux数据链路层的核心组件和实现机制。
1. 数据链路层架构
1.1 数据链路层的核心职责
数据链路层在Linux网络栈中承担以下关键功能:
- 以太网帧处理:封装和解封装以太网帧头部
- MAC地址管理:处理物理地址到逻辑地址的映射
- ARP协议:实现IP地址到MAC地址的解析
- VLAN支持:处理虚拟局域网标签
- 网桥功能:实现二层转发和学习
- 流量控制:管理数据链路层的流量
1.2 数据链路层架构图
graph TB
subgraph "网络层"
IP[IP协议栈]
ROUTE[路由子系统]
NEIGH[邻居子系统]
end
subgraph "数据链路层"
subgraph "以太网处理"
ETH_RX[以太网接收]
ETH_TX[以太网发送]
ETH_HDR[以太网头部处理]
VLAN[VLAN处理]
end
subgraph "ARP协议"
ARP_TABLE[ARP表]
ARP_RX[ARP接收]
ARP_TX[ARP发送]
ARP_CACHE[ARP缓存]
end
subgraph "网桥子系统"
BRIDGE[网桥核心]
FDB[转发数据库]
STP[生成树协议]
FLOOD[广播域]
end
subgraph "设备抽象"
NETDEV_RX[设备接收]
NETDEV_TX[设备发送]
MULTICAST[组播处理]
PROMISC[混杂模式]
end
end
subgraph "网络设备层"
QUEUE[设备队列]
DRIVER[设备驱动]
NAPI[NAPI轮询]
end
%% 连接关系 - 接收路径
DRIVER --> NETDEV_RX
NETDEV_RX --> ETH_RX
ETH_RX --> VLAN
VLAN --> ETH_HDR
ETH_HDR --> ARP_RX
ETH_HDR --> IP
ARP_RX --> ARP_TABLE
ARP_TABLE --> ARP_CACHE
ETH_RX --> BRIDGE
BRIDGE --> FDB
BRIDGE --> FLOOD
%% 连接关系 - 发送路径
IP --> ROUTE
ROUTE --> NEIGH
NEIGH --> ARP_TABLE
IP --> ETH_TX
ETH_TX --> VLAN
VLAN --> NETDEV_TX
NETDEV_TX --> QUEUE
ARP_TX --> ETH_TX
BRIDGE --> ETH_TX
%% 管理连接
NEIGH --> ARP_TX
ARP_CACHE --> NEIGH
style ETH_RX fill:#e1f5fe
style ARP_TABLE fill:#f3e5f5
style BRIDGE fill:#e8f5e8
style VLAN fill:#fff3e0
2. 以太网协议实现
2.1 以太网帧结构
/**
* 以太网帧头结构
*
* 以太网帧是数据链路层的基本传输单元,包含目标MAC地址、
* 源MAC地址和协议类型字段。
*/
struct ethhdr {
unsigned char h_dest[ETH_ALEN]; /* 目标MAC地址,6字节 */
unsigned char h_source[ETH_ALEN]; /* 源MAC地址,6字节 */
__be16 h_proto; /* 协议类型,2字节 */
} __packed;
/**
* 常用的以太网协议类型定义
*/
#define ETH_P_LOOP 0x0060 /* 以太网环回包 */
#define ETH_P_PUP 0x0200 /* Xerox PUP包 */
#define ETH_P_PUPAT 0x0201 /* Xerox PUP地址转换包 */
#define ETH_P_TSN 0x22F0 /* TSN (IEEE 1722) 包 */
#define ETH_P_IP 0x0800 /* 网际协议包 */
#define ETH_P_X25 0x0805 /* CCITT X.25 */
#define ETH_P_ARP 0x0806 /* 地址解析包 */
#define ETH_P_BPQ 0x08FF /* G8BPQ AX.25 以太网包 */
#define ETH_P_IEEEPUP 0x0a00 /* Xerox IEEE802.3 PUP包 */
#define ETH_P_IEEEPUPAT 0x0a01 /* Xerox IEEE802.3 PUP地址转换包 */
#define ETH_P_BATMAN 0x4305 /* B.A.T.M.A.N.-高级包 */
#define ETH_P_DEC 0x6000 /* DEC指定,非DIX */
#define ETH_P_DNA_DL 0x6001 /* DEC DNA转储/加载 */
#define ETH_P_DNA_RC 0x6002 /* DEC DNA远程控制台 */
#define ETH_P_DNA_RT 0x6003 /* DEC DNA路由 */
#define ETH_P_LAT 0x6004 /* DEC LAT */
#define ETH_P_DIAG 0x6005 /* DEC诊断包 */
#define ETH_P_CUST 0x6006 /* DEC客户协议 */
#define ETH_P_SCA 0x6007 /* DEC系统控制体系结构 */
#define ETH_P_TEB 0x6558 /* 透明以太网桥接 */
#define ETH_P_RARP 0x8035 /* 反向地址解析包 */
#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN扩展帧 */
#define ETH_P_IPX 0x8137 /* IPX协议 */
#define ETH_P_IPV6 0x86DD /* IPv6协议 */
#define ETH_P_PAUSE 0x8808 /* IEEE 802.3 暂停帧 */
#define ETH_P_SLOW 0x8809 /* 慢协议(IEEE 802.3) */
#define ETH_P_WCCP 0x883E /* Web-cache协调协议 */
#define ETH_P_MPLS_UC 0x8847 /* MPLS单播流量 */
#define ETH_P_MPLS_MC 0x8848 /* MPLS组播流量 */
#define ETH_P_ATMMPOA 0x884c /* MultiProtocol Over ATM */
#define ETH_P_PPP_DISC 0x8863 /* PPPoE发现协议 */
#define ETH_P_PPP_SES 0x8864 /* PPPoE会话协议 */
#define ETH_P_LINK_CTL 0x886c /* HPNA, wlan链路本地隧道 */
#define ETH_P_ATMFATE 0x8884 /* Frame-based ATM传输 */
#define ETH_P_PAE 0x888E /* 端口访问实体(IEEE 802.1X) */
#define ETH_P_AOE 0x88A2 /* ATA over Ethernet */
#define ETH_P_8021AD 0x88A8 /* 802.1ad服务VLAN */
#define ETH_P_802_EX1 0x88B5 /* 802.1本地实验1 */
#define ETH_P_TIPC 0x88CA /* TIPC */
#define ETH_P_MACSEC 0x88E5 /* 802.1AE MACsec */
#define ETH_P_8021AH 0x88E7 /* 802.1ah骨干服务标签 */
#define ETH_P_MVRP 0x88F5 /* 802.1Q MVRP */
#define ETH_P_1588 0x88F7 /* IEEE 1588时钟同步 */
#define ETH_P_NCSI 0x88F8 /* NCSI协议 */
#define ETH_P_PRP 0x88FB /* 并行冗余协议 */
#define ETH_P_FCOE 0x8906 /* 光纤通道以太网 */
#define ETH_P_TDLS 0x890D /* TDLS */
#define ETH_P_FIP 0x8914 /* FCoE初始化协议 */
#define ETH_P_80221 0x8917 /* IEEE 802.21媒体独立握手协议 */
#define ETH_P_HSR 0x892F /* 高可用性无缝冗余 */
#define ETH_P_NSH 0x894F /* 网络服务头部 */
#define ETH_P_LOOPBACK 0x9000 /* 以太网环回包,内核内部 */
#define ETH_P_QINQ1 0x9100 /* 已弃用的QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_QINQ2 0x9200 /* 已弃用的QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_QINQ3 0x9300 /* 已弃用的QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_EDSA 0xDADA /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_IFE 0xED3E /* ForCES帧间封装 */
#define ETH_P_AF_IUCV 0xFBFB /* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_802_3_MIN 0x0600 /* 如果frame length字段是 <= 1500,它是长度字段 */
#define ETH_P_802_3 0x0001 /* Dummy类型用于IEEE 802.3帧 */
#define ETH_P_AX25 0x0002 /* Dummy协议id用于AX.25 */
#define ETH_P_ALL 0x0003 /* 每个包(注意:绝不是有线的) */
#define ETH_P_802_2 0x0004 /* 802.2帧 */
#define ETH_P_SNAP 0x0005 /* 内部唯一SNAP */
#define ETH_P_DDCMP 0x0006 /* DEC DDCMP: 内部唯一 */
#define ETH_P_WAN_PPP 0x0007 /* 伪PPP用于WAN */
#define ETH_P_PPP_MP 0x0008 /* Dummy类型用于PPP MP帧 */
#define ETH_P_LOCALTALK 0x0009 /* Localtalk伪类型 */
#define ETH_P_CAN 0x000C /* CAN: 控制器局域网 */
#define ETH_P_CANFD 0x000D /* CANFD: CAN灵活数据速率*/
#define ETH_P_TR_802_2 0x0011 /* 802.2帧 */
#define ETH_P_MOBITEX 0x0015 /* Mobitex (kmod-mobitex.sf.net) */
#define ETH_P_CONTROL 0x0016 /* 通用PPP或原始帧的标签切换 */
#define ETH_P_IRDA 0x0017 /* Linux-IrDA */
#define ETH_P_ECONET 0x0018 /* Acorn Econet */
#define ETH_P_HDLC 0x0019 /* HDLC帧 */
#define ETH_P_ARCNET 0x001A /* 1A用于ARCNET :-) */
#define ETH_P_DSA 0x001B /* 分布式交换机体系结构 */
#define ETH_P_TRAILER 0x001C /* 拖车切换器 */
#define ETH_P_PHONET 0x00F5 /* Nokia Phonet帧 */
#define ETH_P_IEEE802154 0x00F6 /* IEEE802.15.4帧 */
#define ETH_P_CAIF 0x00F7 /* ST-Ericsson CAIF协议 */
#define ETH_P_XDSA 0x00F8 /* 多路复用器DSA体系结构 */
#define ETH_P_MAP 0x00F9 /* Qualcomm IPC路由器协议 */
2.2 以太网帧接收处理
/**
* eth_type_trans - 确定数据包的协议ID
* @skb: 接收到的数据包
* @dev: 接收设备
*
* 分析以太网帧头部,确定上层协议类型并设置相关字段
* 返回值:网络字节序的协议类型
*/
__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
{
unsigned short _service_access_point;
const unsigned char *rawp;
struct ethhdr *eth;
/* 重置网络头部偏移 */
skb_reset_mac_header(skb);
/* 获取以太网头部 */
eth = (struct ethhdr *)skb->data;
/* 移动数据指针跳过以太网头部 */
skb_pull_inline(skb, ETH_HLEN);
/* 设置输入设备 */
skb->dev = dev;
/*
* 判断数据包类型:单播、广播、组播或其他主机的包
*/
if (unlikely(is_multicast_ether_addr(eth->h_dest))) {
if (ether_addr_equal_64bits(eth->h_dest, dev->broadcast))
skb->pkt_type = PACKET_BROADCAST;
else
skb->pkt_type = PACKET_MULTICAST;
} else if (unlikely(!ether_addr_equal_64bits(eth->h_dest, dev->dev_addr))) {
skb->pkt_type = PACKET_OTHERHOST;
}
/*
* 一些老的网络硬件会给我们长度而不是类型字段,
* 我们可以通过查看其值来判断。
*
* 0x0600是帧长度的最大值,所以如果协议字段 > 0x0600,
* 那么它是一个类型字段,我们直接使用它。
*/
if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN)
return eth->h_proto;
/*
* 这是802.3帧,长度字段位于类型字段位置。
* 实际的协议ID可能是:
* - DSAP/SSAP,1字节每个,DSAP==SSAP表示一个协议
* - SNAP,5或8字节,需要读取以确定
* - DSAP+SSAP != SNAP的LLC,我们不处理
*/
rawp = skb->data;
if (*(unsigned short *)rawp == 0xFFFF) {
/*
* IPX的802.3,如果两个字节都是0xff,则为IPX
*/
return htons(ETH_P_802_3);
}
/*
* 实际长度 - 假设为802.2LLC
*/
_service_access_point = *(unsigned char*)rawp;
if (_service_access_point == 0xAA) {
/*
* SNAP - 检查OUI是否为以太网封装
*/
if (skb->len >= 5) {
if (rawp[1] == 0xAA && rawp[2] == 0x03 &&
rawp[3] == 0x00 && rawp[4] == 0x00 && rawp[5] == 0x00) {
/*
* 这是RFC 1042封装的以太网
*/
if (skb->len >= 8) {
return *(__be16*)(rawp + 6);
}
}
}
}
/*
* 没有其他匹配,假设它是原始802.2 LLC
*/
return htons(ETH_P_802_2);
}
/**
* eth_header - 创建以太网头部
* @skb: 要添加头部的数据包
* @dev: 网络设备
* @type: 协议类型
* @daddr: 目标硬件地址
* @saddr: 源硬件地址
* @len: 数据长度
*
* 在数据包前面添加以太网头部
* 返回值:成功返回ETH_HLEN,失败返回负值
*/
int eth_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type, const void *daddr,
const void *saddr, unsigned int len)
{
struct ethhdr *eth = (struct ethhdr *)skb_push(skb, ETH_HLEN);
if (type != ETH_P_802_3 && type != ETH_P_802_2)
eth->h_proto = htons(type);
else
eth->h_proto = htons(len);
/*
* 设置源地址
*/
if (!saddr)
saddr = dev->dev_addr;
memcpy(eth->h_source, saddr, ETH_ALEN);
if (daddr) {
memcpy(eth->h_dest, daddr, ETH_ALEN);
return ETH_HLEN;
}
/*
* 目标地址需要通过地址解析获得
*/
return -ETH_HLEN;
}
/**
* eth_rebuild_header - 重建以太网头部
* @skb: 数据包
*
* 用于在地址解析完成后重建以太网头部
* 返回值:成功返回1,失败返回0
*/
int eth_rebuild_header(struct sk_buff *skb)
{
struct ethhdr *eth = (struct ethhdr *)skb->data;
struct net_device *dev = skb->dev;
switch (eth->h_proto) {
case htons(ETH_P_IP):
return arp_find(eth->h_dest, skb);
default:
netdev_dbg(dev,
"%s: unable to resolve type %X addresses.\n",
dev->name, ntohs(eth->h_proto));
memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
break;
}
return 0;
}
/**
* 以太网头部操作函数集
*/
const struct header_ops eth_header_ops = {
.create = eth_header,
.parse = eth_header_parse,
.cache = eth_header_cache,
.cache_update = eth_header_cache_update,
};
3. ARP协议实现
3.1 ARP协议基础结构
/**
* ARP协议头部结构
*
* ARP(Address Resolution Protocol)用于将IP地址解析为MAC地址
*/
struct arphdr {
__be16 ar_hrd; /* 硬件类型,例如ARPHRD_ETHER */
__be16 ar_pro; /* 协议类型,例如ETH_P_IP */
unsigned char ar_hln; /* 硬件地址长度 */
unsigned char ar_pln; /* 协议地址长度 */
__be16 ar_op; /* 操作码,请求或应答 */
} __packed;
/* ARP操作码 */
#define ARPOP_REQUEST 1 /* ARP请求 */
#define ARPOP_REPLY 2 /* ARP应答 */
#define ARPOP_RREQUEST 3 /* RARP请求 */
#define ARPOP_RREPLY 4 /* RARP应答 */
#define ARPOP_InREQUEST 8 /* InARP请求 */
#define ARPOP_InREPLY 9 /* InARP应答 */
#define ARPOP_NAK 10 /* (ATM)ARP NAK */
/**
* ARP缓存表项结构
*
* 每个ARP表项存储IP地址到MAC地址的映射关系
*/
struct neighbour {
struct neighbour __rcu *next; /* 哈希链表下一个节点 */
struct neigh_table *tbl; /* 所属的邻居表 */
struct neigh_parms *parms; /* 参数配置 */
unsigned long confirmed; /* 最后确认时间 */
unsigned long updated; /* 最后更新时间 */
rwlock_t lock; /* 读写锁 */
refcount_t refcnt; /* 引用计数 */
struct sk_buff_head arp_queue; /* 等待ARP解析的数据包队列 */
unsigned int arp_queue_len_bytes; /* 队列字节数 */
struct timer_list timer; /* 定时器 */
unsigned long used; /* 最后使用时间 */
atomic_t probes; /* 探测次数 */
__u8 flags; /* 标志位 */
__u8 nud_state; /* 邻居状态 */
__u8 type; /* 缓存类型 */
__u8 dead; /* 死亡标记 */
u8 protocol; /* 协议类型 */
seqlock_t ha_lock; /* 硬件地址锁 */
unsigned char ha[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))]; /* 硬件地址 */
struct hh_cache hh; /* 硬件头缓存 */
int (*output)(struct neighbour *, struct sk_buff *); /* 输出函数 */
const struct neigh_ops *ops; /* 操作函数集 */
struct list_head gc_list; /* 垃圾回收列表 */
struct rcu_head rcu; /* RCU头部 */
struct net_device *dev; /* 关联的网络设备 */
u8 primary_key[0]; /* 主键(IP地址) */
};
/**
* 邻居状态定义
*/
#define NUD_INCOMPLETE 0x01 /* 正在解析中 */
#define NUD_REACHABLE 0x02 /* 可达 */
#define NUD_STALE 0x04 /* 过期但可用 */
#define NUD_DELAY 0x08 /* 延迟确认 */
#define NUD_PROBE 0x10 /* 正在探测 */
#define NUD_FAILED 0x20 /* 解析失败 */
#define NUD_NOARP 0x40 /* 无需ARP */
#define NUD_PERMANENT 0x80 /* 永久条目 */
#define NUD_NONE 0x00 /* 无效状态 */
/* 有效状态的组合 */
#define NUD_IN_TIMER (NUD_INCOMPLETE|NUD_REACHABLE|NUD_DELAY|NUD_PROBE)
#define NUD_VALID (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY)
#define NUD_CONNECTED (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE)
/**
* ARP表结构
*/
struct neigh_table {
int family; /* 协议族 */
unsigned int entry_size; /* 条目大小 */
unsigned int key_len; /* 键长度 */
__be16 protocol; /* 协议类型 */
__u32 (*hash)(const void *pkey,
const struct net_device *dev,
__u32 *hash_rnd); /* 哈希函数 */
bool (*key_eq)(const struct neighbour *, const void *pkey); /* 键比较 */
int (*constructor)(struct neighbour *); /* 构造函数 */
int (*pconstructor)(struct neighbour *); /* 代理构造函数 */
void (*pdestructor)(struct neighbour *); /* 代理析构函数 */
void (*proxy_redo)(struct sk_buff *skb); /* 代理重做 */
char *id; /* 表标识 */
struct neigh_parms parms; /* 默认参数 */
struct list_head parms_list; /* 参数列表 */
int gc_interval; /* GC间隔 */
int gc_thresh1; /* GC阈值1 */
int gc_thresh2; /* GC阈值2 */
int gc_thresh3; /* GC阈值3 */
unsigned long last_flush; /* 最后刷新时间 */
struct delayed_work gc_work; /* GC工作队列 */
struct timer_list proxy_timer; /* 代理定时器 */
struct sk_buff_head proxy_queue; /* 代理队列 */
atomic_t entries; /* 条目数量 */
rwlock_t lock; /* 读写锁 */
unsigned long last_rand; /* 最后随机数 */
struct neigh_statistics __percpu *stats; /* 统计信息 */
struct neigh_hash_table __rcu *nht; /* 哈希表 */
struct pneigh_entry **phash_buckets; /* 代理哈希桶 */
};
/* IPv4 ARP表 */
static struct neigh_table arp_tbl = {
.family = AF_INET,
.key_len = 4,
.protocol = cpu_to_be16(ETH_P_IP),
.hash = arp_hash,
.key_eq = arp_key_eq,
.constructor = arp_constructor,
.proxy_redo = arp_proxy_redo,
.id = "arp_cache",
/* 其他字段初始化... */
};
3.2 ARP数据包处理流程
/**
* arp_rcv - ARP数据包接收处理
* @skb: 接收到的数据包
* @dev: 接收设备
* @pt: 数据包类型结构
* @orig_dev: 原始设备
*
* 处理接收到的ARP数据包
* 返回值:NET_RX_SUCCESS表示成功处理
*/
static int arp_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
{
const struct arphdr *arp;
/* 检查数据包合法性 */
if (dev->flags & IFF_NOARP)
goto freeskb;
if (!pskb_may_pull(skb, arp_hdr_len(dev)))
goto freeskb;
arp = arp_hdr(skb);
/* 检查ARP头部有效性 */
if (arp->ar_hln != dev->addr_len ||
arp->ar_pln != 4 ||
arp->ar_pro != htons(ETH_P_IP) ||
arp->ar_hrd != htons(dev->type))
goto freeskb;
/* 确保数据包足够长 */
if (!pskb_may_pull(skb, sizeof(struct arphdr) +
(2 * (dev->addr_len + sizeof(u32)))))
goto freeskb;
/* 处理ARP请求和应答 */
return arp_process(dev_net(dev), NULL, skb);
freeskb:
kfree_skb(skb);
return NET_RX_DROP;
}
/**
* arp_process - ARP数据包核心处理函数
* @net: 网络命名空间
* @sock: 套接字(通常为NULL)
* @skb: ARP数据包
*
* 处理ARP请求和应答数据包的核心逻辑
*/
static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
struct in_device *in_dev = __in_dev_get_rcu(dev);
struct arphdr *arp;
unsigned char *arp_ptr;
struct rtable *rt;
unsigned char *sha; /* 发送方硬件地址 */
unsigned char *tha; /* 目标硬件地址 */
__be32 sip, tip; /* 发送方IP,目标IP */
u16 dev_type = dev->type;
int addr_type;
struct neighbour *n;
struct dst_entry *dst = NULL;
if (!in_dev)
goto out;
arp = arp_hdr(skb);
switch (dev_type) {
default:
if (arp->ar_pro != htons(ETH_P_IP) ||
htons(dev_type) != arp->ar_hrd)
goto out;
break;
case ARPHRD_ETHER:
case ARPHRD_FDDI:
case ARPHRD_IEEE802:
/*
* 以太网(10Mb)
* FDDI
* IEEE 802网络
*/
if (arp->ar_pro != htons(ETH_P_IP) ||
arp->ar_hrd != htons(ARPHRD_ETHER))
goto out;
break;
case ARPHRD_AX25:
if (arp->ar_pro != htons(AX25_P_IP) ||
arp->ar_hrd != htons(ARPHRD_AX25))
goto out;
break;
case ARPHRD_NETROM:
if (arp->ar_pro != htons(AX25_P_IP) ||
arp->ar_hrd != htons(ARPHRD_NETROM))
goto out;
break;
}
/* 解析ARP数据包内容 */
arp_ptr = (unsigned char *)(arp + 1);
sha = arp_ptr; /* 发送方硬件地址 */
arp_ptr += dev->addr_len;
memcpy(&sip, arp_ptr, 4); /* 发送方IP地址 */
arp_ptr += 4;
tha = arp_ptr; /* 目标硬件地址 */
arp_ptr += dev->addr_len;
memcpy(&tip, arp_ptr, 4); /* 目标IP地址 */
/*
* 检查是否为我们的地址
*/
if (tip == sip && inet_addr_type_dev_table(net, dev, sip) == RTN_LOCAL) {
/* 自己给自己发ARP,忽略 */
goto out;
}
if (arp->ar_op == htons(ARPOP_REQUEST) &&
ip_route_input_noref(skb, tip, sip, 0, dev) == 0) {
rt = skb_rtable(skb);
addr_type = rt->rt_type;
if (addr_type == RTN_LOCAL) {
int dont_send;
dont_send = arp_ignore(in_dev, sip, tip);
if (!dont_send && IN_DEV_ARPFILTER(in_dev))
dont_send = arp_filter(sip, tip, dev);
if (!dont_send) {
n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
if (n) {
arp_send_dst(ARPOP_REPLY, ETH_P_ARP,
sip, dev, tip, sha,
dev->dev_addr, sha, dst);
neigh_release(n);
}
}
goto out;
} else if (IN_DEV_FORWARD(in_dev)) {
if (addr_type == RTN_UNICAST &&
(arp_fwd_proxy(in_dev, dev, rt) ||
arp_fwd_pvlan(in_dev, dev, rt, sip, tip) ||
(rt->dst.dev != dev &&
pneigh_lookup(&arp_tbl, net, &tip, dev, 0)))) {
n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
if (n)
neigh_release(n);
if (NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED ||
skb->pkt_type == PACKET_HOST ||
NEIGH_VAR(in_dev->arp_parms, PROXY_DELAY) == 0) {
arp_send_dst(ARPOP_REPLY, ETH_P_ARP,
sip, dev, tip, sha,
dev->dev_addr, sha, dst);
} else {
pneigh_enqueue(&arp_tbl, in_dev->arp_parms, skb);
goto out_consume_skb;
}
goto out;
}
}
}
/* 更新发送方的ARP缓存条目 */
n = __neigh_lookup(&arp_tbl, &sip, dev, 0);
if (IN_DEV_ARP_ACCEPT(in_dev)) {
addr_type = inet_addr_type_dev_table(net, dev, sip);
if (addr_type == RTN_UNICAST)
n = __neigh_lookup(&arp_tbl, &sip, dev, 1);
}
if (n) {
int state = NUD_REACHABLE;
int override;
/* 如果地址来源被覆盖或者我们没有地址,使用应答地址 */
override = time_after(jiffies,
n->updated + NEIGH_VAR(n->parms, LOCKTIME)) ||
is_garp;
/* 确认此地址 */
if (n->nud_state == NUD_INCOMPLETE) {
if (skb && arp->ar_op == htons(ARPOP_REPLY))
neigh_update(n, sha, state,
override ? NEIGH_UPDATE_F_OVERRIDE : 0, 0);
} else {
neigh_update(n, sha, state,
override ? NEIGH_UPDATE_F_OVERRIDE : 0, 0);
}
neigh_release(n);
}
out:
consume_skb(skb);
return NET_RX_SUCCESS;
out_consume_skb:
consume_skb(skb);
return NET_RX_SUCCESS;
}
/**
* arp_send_dst - 发送ARP数据包
* @type: ARP操作类型
* @ptype: 包类型
* @dest_ip: 目标IP地址
* @dev: 发送设备
* @src_ip: 源IP地址
* @dest_hw: 目标硬件地址
* @src_hw: 源硬件地址
* @target_hw: 目标硬件地址(在ARP头部中)
* @dst: 目标路由
*/
static void arp_send_dst(int type, int ptype, __be32 dest_ip,
struct net_device *dev, __be32 src_ip,
const unsigned char *dest_hw,
const unsigned char *src_hw,
const unsigned char *target_hw,
struct dst_entry *dst)
{
struct sk_buff *skb;
/* 分配sk_buff */
skb = arp_create(type, ptype, dest_ip, dev, src_ip,
dest_hw, src_hw, target_hw);
if (!skb)
return;
skb_dst_set(skb, dst_clone(dst));
arp_xmit(skb);
}
/**
* arp_create - 创建ARP数据包
* @type: ARP操作类型
* @ptype: 以太网协议类型
* @dest_ip: 目标IP地址
* @dev: 发送设备
* @src_ip: 源IP地址
* @dest_hw: 目标硬件地址
* @src_hw: 源硬件地址
* @target_hw: ARP目标硬件地址
*
* 创建一个ARP数据包
* 返回值:成功返回sk_buff指针,失败返回NULL
*/
static struct sk_buff *arp_create(int type, int ptype, __be32 dest_ip,
struct net_device *dev, __be32 src_ip,
const unsigned char *dest_hw,
const unsigned char *src_hw,
const unsigned char *target_hw)
{
struct sk_buff *skb;
struct arphdr *arp;
unsigned char *arp_ptr;
int hlen = LL_RESERVED_SPACE(dev);
int tlen = dev->needed_tailroom;
/*
* 分配缓冲区
*/
skb = alloc_skb(arp_hdr_len(dev) + hlen + tlen, GFP_ATOMIC);
if (!skb)
return NULL;
skb_reserve(skb, hlen);
skb_reset_network_header(skb);
arp = (struct arphdr *)skb_put(skb, arp_hdr_len(dev));
skb->dev = dev;
skb->protocol = htons(ETH_P_ARP);
if (!src_hw)
src_hw = dev->dev_addr;
if (!dest_hw)
dest_hw = dev->broadcast;
/*
* 填充ARP头部
*/
if (dev_hard_header(skb, dev, ptype, dest_hw, src_hw, skb->len) < 0)
goto out;
/*
* 填充ARP操作码等字段
*/
arp->ar_hrd = htons(dev->type);
arp->ar_pro = htons(ETH_P_IP);
arp->ar_hln = dev->addr_len;
arp->ar_pln = 4;
arp->ar_op = htons(type);
arp_ptr = (unsigned char *)(arp + 1);
/* 发送方硬件地址 */
memcpy(arp_ptr, src_hw, dev->addr_len);
arp_ptr += dev->addr_len;
/* 发送方协议地址 */
memcpy(arp_ptr, &src_ip, 4);
arp_ptr += 4;
/* 目标硬件地址 */
if (target_hw)
memcpy(arp_ptr, target_hw, dev->addr_len);
else
memset(arp_ptr, 0, dev->addr_len);
arp_ptr += dev->addr_len;
/* 目标协议地址 */
memcpy(arp_ptr, &dest_ip, 4);
return skb;
out:
kfree_skb(skb);
return NULL;
}
4. 邻居子系统
4.1 邻居子系统架构
邻居子系统是Linux网络栈中管理网络邻居信息的核心组件,主要功能包括:
- 地址解析:将网络层地址解析为数据链路层地址
- 邻居状态管理:维护邻居的可达性状态
- 硬件头缓存:缓存已解析的硬件头部
- 垃圾回收:定期清理过期的邻居条目
4.2 邻居条目状态机
stateDiagram-v2
[*] --> INCOMPLETE: 创建新条目
INCOMPLETE --> REACHABLE: 收到确认
INCOMPLETE --> FAILED: 解析失败
REACHABLE --> STALE: 超时
REACHABLE --> REACHABLE: 收到确认
STALE --> DELAY: 有流量需要发送
STALE --> REACHABLE: 收到确认
DELAY --> PROBE: 延迟超时
DELAY --> REACHABLE: 收到确认
PROBE --> REACHABLE: 收到确认
PROBE --> FAILED: 探测失败
FAILED --> INCOMPLETE: 重新尝试
FAILED --> [*]: 删除条目
note right of INCOMPLETE: 正在进行地址解析
note right of REACHABLE: 地址有效且可达
note right of STALE: 地址可能过期但仍可用
note right of DELAY: 等待流量触发探测
note right of PROBE: 正在主动探测
note right of FAILED: 地址解析失败
5. VLAN支持实现
5.1 VLAN标签处理
/**
* VLAN头部结构
*/
struct vlan_hdr {
__be16 h_vlan_TCI; /* 标签控制信息:优先级(3位) + CFI(1位) + VID(12位) */
__be16 h_vlan_encapsulated_proto; /* 封装的协议类型 */
} __packed;
/**
* VLAN标签控制信息位定义
*/
#define VLAN_PRIO_MASK 0xe000 /* 优先级掩码 */
#define VLAN_PRIO_SHIFT 13 /* 优先级位移 */
#define VLAN_CFI_MASK 0x1000 /* CFI掩码 */
#define VLAN_TAG_PRESENT VLAN_CFI_MASK /* 标签存在标志 */
#define VLAN_VID_MASK 0x0fff /* VLAN ID掩码 */
#define VLAN_N_VID 4096 /* VLAN ID数量 */
/**
* vlan_get_tag - 从数据包中提取VLAN标签
* @skb: 数据包
* @vlan_tci: 输出的VLAN TCI
*
* 从数据包中提取VLAN标签信息
* 返回值:成功返回0,失败返回负值
*/
static inline int vlan_get_tag(const struct sk_buff *skb, u16 *vlan_tci)
{
if (skb_vlan_tag_present(skb)) {
*vlan_tci = skb_vlan_tag_get(skb);
return 0;
} else {
*vlan_tci = 0;
return -EINVAL;
}
}
/**
* __vlan_hwaccel_put_tag - 设置硬件加速VLAN标签
* @skb: 数据包
* @vlan_proto: VLAN协议
* @vlan_tci: VLAN标签控制信息
*
* 为支持硬件VLAN处理的网卡设置VLAN标签
*/
static inline void __vlan_hwaccel_put_tag(struct sk_buff *skb,
__be16 vlan_proto, u16 vlan_tci)
{
skb->vlan_proto = vlan_proto;
skb->vlan_tci = vlan_tci | VLAN_TAG_PRESENT;
}
/**
* vlan_do_receive - VLAN接收处理
* @skb: 接收的数据包
*
* 处理带VLAN标签的接收数据包
* 返回值:处理结果
*/
bool vlan_do_receive(struct sk_buff **skbp)
{
struct sk_buff *skb = *skbp;
__be16 vlan_proto = skb->vlan_proto;
u16 vlan_id = skb_vlan_tag_get_id(skb);
struct net_device *vlan_dev;
struct vlan_pcpu_stats *rx_stats;
vlan_dev = vlan_find_dev(skb->dev, vlan_proto, vlan_id);
if (!vlan_dev)
return false;
skb = *skbp = skb_share_check(skb, GFP_ATOMIC);
if (unlikely(!skb))
return false;
skb->dev = vlan_dev;
if (unlikely(skb->pkt_type == PACKET_OTHERHOST)) {
/* 其他主机的包,检查是否为我们的VLAN设备 */
if (ether_addr_equal_64bits(eth_hdr(skb)->h_dest, vlan_dev->dev_addr))
skb->pkt_type = PACKET_HOST;
}
if (!(vlan_dev->flags & IFF_UP)) {
kfree_skb(skb);
*skbp = NULL;
return true;
}
skb->priority = vlan_get_ingress_priority(vlan_dev, skb->vlan_tci);
skb_vlan_tag_unset(skb);
rx_stats = this_cpu_ptr(vlan_dev_priv(vlan_dev)->vlan_pcpu_stats);
u64_stats_update_begin(&rx_stats->syncp);
rx_stats->rx_packets++;
rx_stats->rx_bytes += skb->len;
if (skb->pkt_type == PACKET_MULTICAST)
rx_stats->rx_multicast++;
u64_stats_update_end(&rx_stats->syncp);
return true;
}
/**
* vlan_dev_hard_start_xmit - VLAN设备发送函数
* @skb: 要发送的数据包
* @dev: VLAN设备
*
* VLAN虚拟设备的发送函数
*/
static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data);
unsigned int len;
int ret;
/* 处理VLAN标签 */
if (veth->h_vlan_proto != vlan->vlan_proto ||
vlan->flags & VLAN_FLAG_REORDER_HDR) {
u16 vlan_tci;
vlan_tci = vlan->vlan_id;
vlan_tci |= vlan_get_egress_priority(dev, skb->priority) << VLAN_PRIO_SHIFT;
__vlan_hwaccel_put_tag(skb, vlan->vlan_proto, vlan_tci);
}
skb->dev = vlan->real_dev;
len = skb->len;
if (unlikely(netpoll_tx_running(dev)))
return vlan_netpoll_send_skb(vlan, skb);
ret = dev_queue_xmit(skb);
if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
struct vlan_pcpu_stats *stats;
stats = this_cpu_ptr(vlan->vlan_pcpu_stats);
u64_stats_update_begin(&stats->syncp);
stats->tx_packets++;
stats->tx_bytes += len;
u64_stats_update_end(&stats->syncp);
} else {
this_cpu_inc(vlan->vlan_pcpu_stats->tx_dropped);
}
return ret;
}
6. 网桥功能实现
6.1 网桥核心结构
/**
* net_bridge - Linux网桥结构
*
* 网桥是工作在数据链路层的网络设备,用于连接多个网络段
*/
struct net_bridge {
spinlock_t lock; /* 网桥锁 */
spinlock_t hash_lock; /* 哈希表锁 */
struct list_head port_list; /* 端口列表 */
struct net_device *dev; /* 网桥设备 */
struct pcpu_sw_netstats __percpu *stats; /* 统计信息 */
/* STP相关 */
unsigned long designated_root; /* 指定根桥 */
unsigned long root_path_cost; /* 根路径成本 */
unsigned long root_port; /* 根端口 */
unsigned char bridge_id[8]; /* 网桥ID */
unsigned char designated_bridge[8]; /* 指定网桥 */
/* 转发数据库 */
struct hlist_head fdb_hash[BR_HASH_SIZE]; /* FDB哈希表 */
/* 网桥参数 */
unsigned long ageing_time; /* 老化时间 */
unsigned long hello_time; /* hello时间 */
unsigned long forward_delay; /* 转发延迟 */
unsigned long max_age; /* 最大存活时间 */
u32 feature_mask; /* 特性掩码 */
struct timer_list hello_timer; /* hello定时器 */
struct timer_list tcn_timer; /* TCN定时器 */
struct timer_list topology_change_timer; /* 拓扑变化定时器 */
struct delayed_work gc_work; /* 垃圾回收工作 */
};
/**
* net_bridge_port - 网桥端口结构
*/
struct net_bridge_port {
struct net_bridge *br; /* 所属网桥 */
struct net_device *dev; /* 关联的网络设备 */
struct list_head list; /* 端口列表节点 */
unsigned long flags; /* 端口标志 */
/* STP端口状态 */
u8 state; /* 端口状态 */
u8 priority; /* 端口优先级 */
u32 path_cost; /* 路径成本 */
u32 designated_cost; /* 指定成本 */
unsigned long designated_port; /* 指定端口 */
unsigned long designated_bridge; /* 指定网桥 */
unsigned long designated_root; /* 指定根 */
struct timer_list forward_delay_timer; /* 转发延迟定时器 */
struct timer_list hold_timer; /* 保持定时器 */
struct timer_list message_age_timer; /* 消息年龄定时器 */
struct rcu_head rcu; /* RCU头部 */
};
6.2 网桥数据包处理
/**
* br_handle_frame - 网桥处理帧
* @skb: 数据包
*
* 网桥接收数据包的核心处理函数
* 返回值:处理结果
*/
rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
{
struct net_bridge_port *p;
struct sk_buff *skb = *pskb;
const unsigned char *dest = eth_hdr(skb)->h_dest;
if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
return RX_HANDLER_PASS;
if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
goto drop;
skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb)
return RX_HANDLER_CONSUMED;
memset(skb->cb, 0, sizeof(struct br_input_skb_cb));
p = br_port_get_rcu(skb->dev);
if (p->state == BR_STATE_DISABLED)
goto drop;
/* 检查是否为STP BPDU */
if (unlikely(is_link_local_ether_addr(dest))) {
u16 fwd_mask = p->br->group_fwd_mask | p->group_fwd_mask;
switch (dest[5]) {
case 0x00: /* STP */
case 0x01: /* IEEE MAC Pause */
goto drop;
case 0x0E: /* 802.1AB LLDP */
fwd_mask |= p->br->group_fwd_mask_required;
if (fwd_mask & (1u << dest[5]))
goto forward;
*pskb = skb;
return RX_HANDLER_PASS;
}
}
forward:
switch (p->state) {
case BR_STATE_FORWARDING:
return br_handle_frame_finish(net, NULL, skb);
case BR_STATE_LEARNING:
br_fdb_update(p->br, p, eth_hdr(skb)->h_source, skb->vlan_tci, false);
break;
default:
drop:
kfree_skb(skb);
}
return RX_HANDLER_CONSUMED;
}
/**
* br_handle_frame_finish - 完成网桥帧处理
* @net: 网络命名空间
* @sk: 套接字
* @skb: 数据包
*
* 完成网桥的帧处理逻辑,包括学习和转发
*/
static int br_handle_frame_finish(struct net *net, struct sock *sk,
struct sk_buff *skb)
{
struct net_bridge_port *p = br_port_get_rcu(skb->dev);
enum br_pkt_type pkt_type = BR_PKT_UNICAST;
struct net_bridge_mdb_entry *mdst;
bool local_rcv, mcast_hit = false;
struct net_bridge *br;
u16 vid = 0;
if (!p || p->state == BR_STATE_DISABLED)
goto drop;
br = p->br;
if (unlikely(is_link_local_ether_addr(eth_hdr(skb)->h_dest))) {
/* 链路本地地址处理 */
pkt_type = BR_PKT_MULTICAST;
local_rcv = true;
} else if (is_multicast_ether_addr(eth_hdr(skb)->h_dest)) {
/* 组播地址处理 */
pkt_type = BR_PKT_MULTICAST;
if (br_multicast_rcv(br, p, skb, vid))
goto drop;
} else {
/* 单播地址处理 */
pkt_type = BR_PKT_UNICAST;
}
/* 学习源MAC地址 */
br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, false);
local_rcv = !!(br->dev->flags & IFF_PROMISC);
dest = br_fdb_find_rcu(br, eth_hdr(skb)->h_dest, vid);
if (dest) {
/* 找到目标,进行转发 */
dst = dest->dst;
if (dst && dst->state == BR_STATE_FORWARDING &&
dst != p && !(dst->flags & BR_HAIRPIN_MODE) &&
!skb_warn_if_lro(skb)) {
br_forward(dst, skb, local_rcv, false);
} else {
if (!mcast_hit)
br_flood(br, skb, pkt_type, local_rcv, false);
else
br_multicast_flood(mdst, skb, local_rcv, false);
}
} else {
/* 未知目标,洪泛 */
if (!mcast_hit)
br_flood(br, skb, pkt_type, local_rcv, false);
else
br_multicast_flood(mdst, skb, local_rcv, false);
}
if (local_rcv)
return br_pass_frame_up(skb);
out:
return 0;
drop:
kfree_skb(skb);
goto out;
}
7. 性能优化策略
7.1 数据链路层优化要点
/**
* 以太网帧处理优化
*/
static inline void eth_addr_copy(u8 *dst, const u8 *src)
{
#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
*(u32 *)dst = *(const u32 *)src;
*(u16 *)(dst + 4) = *(const u16 *)(src + 4);
#else
memcpy(dst, src, ETH_ALEN);
#endif
}
/**
* ether_addr_equal_64bits - 64位对齐的MAC地址比较
* @addr1: 第一个MAC地址
* @addr2: 第二个MAC地址
*
* 利用64位对齐优化MAC地址比较性能
*/
static inline bool ether_addr_equal_64bits(const u8 addr1[6+2],
const u8 addr2[6+2])
{
#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
u64 fold = (*(const u64 *)addr1) ^ (*(const u64 *)addr2);
#ifdef __BIG_ENDIAN
return (fold >> 16) == 0;
#else
return (fold << 16) == 0;
#endif
#else
return ether_addr_equal(addr1, addr2);
#endif
}
7.2 ARP缓存优化
/**
* neigh_lookup_nodev - 查找邻居条目(无设备)
* @tbl: 邻居表
* @net: 网络命名空间
* @pkey: 协议键值
*
* 在邻居表中查找条目,不限定设备
* 返回值:邻居条目或NULL
*/
struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net,
const void *pkey)
{
struct neighbour *n;
unsigned int key_len = tbl->key_len;
u32 hash_val;
struct neigh_hash_table *nht;
NEIGH_CACHE_STAT_INC(tbl, lookups);
rcu_read_lock_bh();
nht = rcu_dereference_bh(tbl->nht);
hash_val = tbl->hash(pkey, NULL, nht->hash_rnd) >> (32 - nht->hash_shift);
for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]);
n != NULL;
n = rcu_dereference_bh(n->next)) {
if (!memcmp(n->primary_key, pkey, key_len) &&
net_eq(dev_net(n->dev), net)) {
if (!refcount_inc_not_zero(&n->refcnt))
n = NULL;
NEIGH_CACHE_STAT_INC(tbl, hits);
break;
}
}
rcu_read_unlock_bh();
return n;
}
/**
* neigh_update - 更新邻居条目
* @neigh: 邻居条目
* @lladdr: 链路层地址
* @new: 新状态
* @flags: 更新标志
* @nlmsg_pid: netlink消息PID
*
* 更新邻居条目的状态和地址信息
* 返回值:更新结果
*/
int neigh_update(struct neighbour *neigh, const u8 *lladdr,
u8 new, u32 flags, u32 nlmsg_pid)
{
u8 old;
int err;
int notify = 0;
struct net_device *dev;
int update_isrouter = 0;
trace_neigh_update(neigh, lladdr, new, flags, nlmsg_pid);
write_lock_bh(&neigh->lock);
dev = neigh->dev;
old = neigh->nud_state;
err = -EPERM;
if (!(flags & NEIGH_UPDATE_F_ADMIN) &&
(old & (NUD_NOARP | NUD_PERMANENT)))
goto out;
ext_learn_change = neigh_update_ext_learned(neigh, flags, ¬ify);
if (ext_learn_change < 0) {
err = ext_learn_change;
goto out;
}
if (!(new & NUD_VALID)) {
neigh_del_timer(neigh);
if (old & NUD_CONNECTED)
neigh_suspect(neigh);
neigh->nud_state = new;
err = 0;
notify = old & NUD_VALID;
if ((old & (NUD_INCOMPLETE | NUD_PROBE)) &&
(new & NUD_FAILED)) {
neigh_invalidate(neigh);
notify = 1;
}
goto out;
}
/* 比较链路层地址... */
if (lladdr) {
/* 更新MAC地址 */
if (!dev->addr_len ||
memcmp(lladdr, neigh->ha, dev->addr_len)) {
write_seqlock(&neigh->ha_lock);
memcpy(&neigh->ha, lladdr, dev->addr_len);
write_sequnlock(&neigh->ha_lock);
neigh_update_hhs(neigh);
if (!(new & NUD_CONNECTED))
neigh->confirmed = jiffies -
(NEIGH_VAR(neigh->parms, BASE_REACHABLE_TIME) << 1);
notify = 1;
}
}
if (new == old)
goto out;
/* 状态转换处理 */
if (new & NUD_CONNECTED)
neigh_connect(neigh);
else
neigh_suspect(neigh);
if (!(old & NUD_VALID)) {
struct sk_buff *skb;
/* 第一次建立连接,发送排队的数据包 */
while (neigh->nud_state & NUD_VALID &&
(skb = __skb_dequeue(&neigh->arp_queue)) != NULL) {
struct dst_entry *dst = skb_dst(skb);
struct neighbour *n2, *n1 = neigh;
/* 再次验证目标 */
if (dst) {
n2 = dst_neigh_lookup_skb(dst, skb);
if (n2)
n1 = n2;
}
n1->output(n1, skb);
if (n2)
neigh_release(n2);
}
__skb_queue_purge(&neigh->arp_queue);
neigh->arp_queue_len_bytes = 0;
}
out:
if (update_isrouter)
neigh_update_is_router(neigh, flags, ¬ify);
write_unlock_bh(&neigh->lock);
if (((new ^ old) & NUD_PERMANENT) || ext_learn_change)
neigh_update_gc_list(neigh);
if (notify)
neigh_update_notify(neigh, nlmsg_pid);
trace_neigh_update_done(neigh, err);
return err;
}
8. 总结
数据链路层作为网络协议栈的重要层次,通过以太网协议、ARP解析、VLAN处理和网桥功能,为上层网络层提供了可靠的二层通信基础。其高效的实现和优化策略是Linux网络栈高性能的重要保障。
8.1 关键技术要点
- 高效地址解析:ARP协议的智能缓存和状态管理
- 灵活VLAN支持:硬件和软件VLAN处理
- 智能网桥功能:学习和转发的优化实现
- 性能优化设计:缓存友好的数据结构和算法
8.2 性能优化建议
- ARP缓存调优:合理设置邻居表大小和超时参数
- VLAN硬件加速:充分利用网卡的VLAN硬件功能
- 网桥优化:合理配置STP参数和FDB大小
- 内存对齐:确保关键数据结构的缓存行对齐
本文为Linux网络栈源码分析系列的数据链路层深度解析
9. 关键函数与调用链/时序图/结构体关系
9.1 关键函数核心代码与功能说明
/*
* __netif_receive_skb_core(L2侧相关路径片段已在总览文档给出),
* 这里补充数据链路层入口关键点:eth_type_trans / vlan_do_receive / br_handle_frame。
*/
/* 以太网协议识别:将以太网帧转换为上层协议标识 */
__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev);
/* VLAN接收处理:将打标流量重定向至VLAN子设备 */
bool vlan_do_receive(struct sk_buff **skbp);
/* 网桥接收路径:学习源MAC并按FDB转发或洪泛/上送 */
rx_handler_result_t br_handle_frame(struct sk_buff **pskb);
/* ARP接收与处理:合法性校验、邻居更新、应答/代理 */
static int arp_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev);
static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb);
- 功能说明
eth_type_trans
:解析以太头并设置skb->protocol
与pkt_type
,决定后续分发。vlan_do_receive
:解析VLAN标签并切换到VLAN逻辑设备,统一后续路径。br_handle_frame
:桥转发入口,完成学习、目的查找、转发/洪泛或交付本地协议栈。arp_rcv/arp_process
:处理ARP请求/应答并更新邻居表,必要时进行代理ARP与应答。
9.2 关键函数调用链
-
接收链路(以太网→VLAN/桥→L3)
- 驱动
poll
->napi_gro_receive
->netif_receive_skb
->eth_type_trans
->vlan_do_receive
(可选) ->br_handle_frame
(桥口) 或__netif_receive_skb_core
->ip_rcv
- 驱动
-
ARP处理
netif_receive_skb
->__netif_receive_skb_core
->arp_rcv
->arp_process
->neigh_event_ns
/arp_send_dst
-
发送(含VLAN/邻居解析)
ip_finish_output2
->neigh_output
/neigh_resolve_output
->arp_solicit
->dev_queue_xmit
-> 驱动ndo_start_xmit
9.3 数据链路层时序图(接收/发送/ARP)
sequenceDiagram
participant NIC as 网卡
participant DRV as 驱动/NAPI
participant L2 as L2处理
participant BR as 网桥
participant L3 as IP层
NIC->>DRV: DMA+中断
DRV->>L2: napi_gro_receive
L2->>L2: eth_type_trans()
alt VLAN
L2->>L2: vlan_do_receive()
end
alt 桥口
L2->>BR: br_handle_frame()
BR->>L3: 上送(如目的本机)
else 直达L3
L2->>L3: __netif_receive_skb_core()->ip_rcv()
end
sequenceDiagram
participant L3 as IP层
participant NEI as 邻居
participant ARP as ARP
participant DEV as 设备层
participant DRV as 驱动
L3->>NEI: neigh_output/neigh_resolve_output
NEI->>ARP: arp_solicit(未解析)
ARP-->>NEI: 更新邻居(应答后)
NEI->>DEV: 邻居已解析-> dev_queue_xmit
DEV->>DRV: ndo_start_xmit
9.4 关键结构体关系图(L2/VLAN/桥/邻居)
classDiagram
class net_device {
+name
+netdev_ops
+qdisc
}
class vlan_dev {
+vlan_id
+real_dev: net_device
}
class net_bridge {
+port_list
+fdb_hash
}
class net_bridge_port {
+br: net_bridge
+dev: net_device
}
class neighbour {
+dev: net_device
+ha[]
+nud_state
}
vlan_dev --> net_device : 绑定底层设备
net_bridge_port --> net_bridge : 从属
net_bridge_port --> net_device : 下联口
neighbour --> net_device : 出口设备