Linux内核中以太网设备处理逻辑
INET是Linux操作系统的TCP/IP协议套件的实现,INET使用BSD Socket接口作为与用户进程通信的手段。本文主要介绍以太网类型的设备处理过程,内核源码位于net/ethernet/eth.c
文件中。
初始化以太网设备
// header_ops包含以一系列函数指针,主要是对链路层协议头的操作,比如协议头的创建、解析、缓存等。
const struct header_ops eth_header_ops ____cacheline_aligned = {
// 创建协议头函数
.create = eth_header,
// 解析以太网源mac地址字段函数
.parse = eth_header_parse,
// 根据arp查询的结果,构造hh_cache,供邻居子系统使用
.cache = eth_header_cache,
// 更新hh_cache中的以太网头
.cache_update = eth_header_cache_update,
// 解析以太网协议字段函数
.parse_protocol = eth_header_parse_protocol,
};
// 初始化以太网设备,使用通用值填充以太网设备结构各字段
/**
* ether_setup - setup Ethernet network device
* @dev: network device
*
* Fill in the fields of the device structure with Ethernet-generic values.
*/
void ether_setup(struct net_device *dev)
{
// 链路层协议头操作函数
dev->header_ops = ð_header_ops;
// 接口的硬件类型,这个type成员由ARP用来决定接口支持什么样的硬件地址,对以太网接口正确的值是ARPHRD_ETHER
dev->type = ARPHRD_ETHER;
// 链路层头部长度,14字节
dev->hard_header_len = ETH_HLEN;
dev->min_header_len = ETH_HLEN;
// MTU默认1500字节,最小68字节
dev->mtu = ETH_DATA_LEN;
dev->min_mtu = ETH_MIN_MTU;
dev->max_mtu = ETH_DATA_LEN;
// 单个MAC地址长度
dev->addr_len = ETH_ALEN;
// 设备发送队列中可以排队的最大帧数,默认1000
dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN;
// 使能广播地址和组播地址
dev->flags = IFF_BROADCAST|IFF_MULTICAST;
// 私有flags,发送方向共享skb
dev->priv_flags |= IFF_TX_SKB_SHARING;
// 设置链路层广播地址:FF:FF:FF:FF:FF:FF
eth_broadcast_addr(dev->broadcast);
}
EXPORT_SYMBOL(ether_setup);
协议头创建
/**
* eth_header - create the Ethernet header
* @skb: buffer to alter
* @dev: source device
* @type: Ethernet type field
* @daddr: destination address (NULL leave destination address)
* @saddr: source address (NULL use device source address)
* @len: packet length (<= skb->len)
*
*
* Set the protocol type. For a packet of type ETH_P_802_3/2 we put the length
* in here instead.
*/
int eth_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type,
const void *daddr, const void *saddr, unsigned int len)
{
// 为以太网头准备空间,data指针指向以太网头部
struct ethhdr *eth = skb_push(skb, ETH_HLEN);
// 通用Ethernet II型帧
if (type != ETH_P_802_3 && type != ETH_P_802_2)
eth->h_proto = htons(type);
else
// IEEE 802.2或802.3型帧,协议字段为包长
eth->h_proto = htons(len);
/*
* Set the source hardware address.
*/
// 设置源mac地址,未指定则赋值为设备接口mac地址
if (!saddr)
saddr = dev->dev_addr;
memcpy(eth->h_source, saddr, ETH_ALEN);
// 设置目的mac地址
if (daddr) {
memcpy(eth->h_dest, daddr, ETH_ALEN);
return ETH_HLEN;
}
/*
* Anyway, the loopback-device should never use this function...
*/
// LOOPBACK接口或者未开启arp协议,则清空目的mac地址
if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
eth_zero_addr(eth->h_dest);
return ETH_HLEN;
}
return -ETH_HLEN;
}
EXPORT_SYMBOL(eth_header);
解析以太网源mac地址
/**
* eth_header_parse - extract hardware address from packet
* @skb: packet to extract header from
* @haddr: destination buffer
*/
int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr)
{
// 获取以太网头
const struct ethhdr *eth = eth_hdr(skb);
// 提取源mac地址
memcpy(haddr, eth->h_source, ETH_ALEN);
return ETH_ALEN;
}
EXPORT_SYMBOL(eth_header_parse);
解析以太网协议字段
/**
* eth_header_parse_protocol - extract protocol from L2 header
* @skb: packet to extract protocol from
*/
__be16 eth_header_parse_protocol(const struct sk_buff *skb)
{
// 获取以太网头
const struct ethhdr *eth = eth_hdr(skb);
// 提取以太网协议字段
return eth->h_proto;
}
EXPORT_SYMBOL(eth_header_parse_protocol);
缓存链路层信息
根据arp查询的结果,构造hh_cache,供邻居子系统使用。
/**
* eth_header_cache - fill cache entry from neighbour
* @neigh: source neighbour
* @hh: destination cache entry
* @type: Ethernet type field
*
* Create an Ethernet header template from the neighbour.
*/
int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh, __be16 type)
{
struct ethhdr *eth;
const struct net_device *dev = neigh->dev;
// 获取缓存中以太网头位置
eth = (struct ethhdr *)
(((u8 *) hh->hh_data) + (HH_DATA_OFF(sizeof(*eth))));
if (type == htons(ETH_P_802_3))
return -1;
// 设置缓存中以太网协议,源mac,目的mac
eth->h_proto = type;
// 源mac为设备接口mac地址
memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
// 目的mac为邻居子系统解析到的下一跳mac地址
memcpy(eth->h_dest, neigh->ha, ETH_ALEN);
/* Pairs with READ_ONCE() in neigh_resolve_output(),
* neigh_hh_output() and neigh_update_hhs().
*/
smp_store_release(&hh->hh_len, ETH_HLEN);
return 0;
}
EXPORT_SYMBOL(eth_header_cache);
更新链路层缓存信息
根据arp查询的结果,更新hh_cache,供邻居子系统使用。
/**
* eth_header_cache_update - update cache entry
* @hh: destination cache entry
* @dev: network device
* @haddr: new hardware address
*
* Called by Address Resolution module to notify changes in address.
*/
void eth_header_cache_update(struct hh_cache *hh,
const struct net_device *dev,
const unsigned char *haddr)
{
// 由ARP结果更新缓存中对应项的mac地址
memcpy(((u8 *) hh->hh_data) + HH_DATA_OFF(sizeof(struct ethhdr)),
haddr, ETH_ALEN);
}
EXPORT_SYMBOL(eth_header_cache_update);