0
点赞
收藏
分享

微信扫一扫

Linux内核中以太网设备处理逻辑(1)-设备初始化

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     = &eth_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);
举报

相关推荐

0 条评论