网络设备的MAC地址

内核中为网络设备定义了5中MAC地址类型,如下所示,其中NETDEV_HW_ADDR_T_SLAVE类型目前没有使用。局域网LAN类型与存储SAN类型的MAC地址保存在net_device结构体的dev_addrs链表中;UNICAST与MULTICAST类型的MAC地址分别保存在uc和mc链表中。

#define NETDEV_HW_ADDR_T_LAN        1    // Local Area Network
#define NETDEV_HW_ADDR_T_SAN        2    // Storage Area Network
#define NETDEV_HW_ADDR_T_SLAVE      3    //
#define NETDEV_HW_ADDR_T_UNICAST    4    // Unicast
#define NETDEV_HW_ADDR_T_MULTICAST  5    // Multicast

struct net_device {
    struct netdev_hw_addr_list  uc;
    struct netdev_hw_addr_list  mc;
    struct netdev_hw_addr_list  dev_addrs;
    
    unsigned char       *dev_addr;
    unsigned char       broadcast[MAX_ADDR_LEN];
}

以上我们已看到有这些MAC地址存在,在使用ifconfig或者ip link show命名时,显示的设备MAC地址是哪个呢?由函数rtnl_fill_ifinfo可知,其取值为net_device结构体的dev_addr变量(单播MAC)和broadcast变量(广播MAC)的值。另外在驱动程序中也会使用到MAC地址dev_addr,用于判断接收数据包是否为到本机。 这么看来好像dev_addr/boradcast地址与之前的三个MAC地址链表并没有关系。以下看看硬件地址的初始化过程,了解下二者的关联。

static int rtnl_fill_ifinfo(...)
{
    if (nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr) ||
        nla_put(skb, IFLA_BROADCAST, dev->addr_len, dev->broadcast))
        goto nla_put_failure;
}
__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
{
    if (unlikely(!ether_addr_equal_64bits(eth->h_dest, dev->dev_addr)))
        skb->pkt_type = PACKET_OTHERHOST;
}

硬件地址初始化

在网络设备初始化时,初始化三类MAC地址:分别为局域网(LAN)MAC地址、单播(UNICAST)MAC地址和多播(MULTICAST)MAC地址。

struct net_device *alloc_netdev_mqs(...)
{
    dev_addr_init(dev);
    dev_mc_init(dev);
    dev_uc_init(dev);
}

由硬件地址链表dev_addrs初始化函数可见,dev_addr其实是指向了dev_addrs地址链表中的第一个MAC地址,此地址就是我们在应用层使用IP命令查看网络设备信息时显示的单播MAC地址。对于广播MAC地址,内核在以太网设备创建时,将其值设置为全F,由函数eth_broadcast_addr实现。

在初始化dev_addrs链表时,首先加入了一个全0的MAC地址,因此dev_addr目前为全0的MAC。在驱动初始化时,例如e1000网卡驱动函数e1000_probe,其会从网卡的EEPROM中读取到真实的MAC地址,赋值到dev_addr中。

int dev_addr_init(struct net_device *dev)
{
    __hw_addr_init(&dev->dev_addrs);
    memset(addr, 0, sizeof(addr));
    err = __hw_addr_add(&dev->dev_addrs, addr, sizeof(addr), NETDEV_HW_ADDR_T_LAN);
    if (!err) {
        ha = list_first_entry(&dev->dev_addrs.list, struct netdev_hw_addr, list);
        dev->dev_addr = ha->addr;
    }
}
void ether_setup(struct net_device *dev)
{
    eth_broadcast_addr(dev->broadcast);
}

存储SMAC地址

对于INTEL网卡驱动IXGBE来说,其会通过函数ixgbe_get_san_mac_addr_generic获取EEPROM中的SAN MAC地址,之后添加到net_device的dev_addrs链表中,类型为NETDEV_HW_ADDR_T_SAN。在其后FCoE创建接口时,会从dev_addrs链表中能够取出SAN类型的MAC地址使用。

static int fcoe_interface_setup(struct fcoe_interface *fcoe, struct net_device *netdev)
{
    for_each_dev_addr(real_dev, ha)
        if ((ha->type == NETDEV_HW_ADDR_T_SAN) && (is_valid_ether_addr(ha->addr))) {
            memcpy(fip->ctl_src_addr, ha->addr, ETH_ALEN);
            break;
        }
}

单播MAC地址

单播MAC地址保存在net_device结构体的uc链表中,函数dev_uc_add负责添加单播MAC地址。由其在内核代码的调用可以看到目前vlan设备、macvlan设备、ipvlan设备与macsec设备等在添加单播MAC地址。

无论是单播MAC还是多播MAC都是用作MAC地址的接收过滤来使用,具体由函数__dev_set_rx_mode进行设置。将需要过滤的MAC地址下发到网驱动处理,例如e1000驱动中的e1000_set_rx_mode函数。但是,对于不支持单播过滤的网卡,有两种处理情况:1)如果存在MAC地址需要过滤,打开设备的混杂接收模式;2)如果没有MAC地址需过滤,关闭混杂模式。

非混杂模式下,仅接收目的MAC与net_device结构中dev_addr值相同的数据包。

int dev_uc_add(struct net_device *dev, const unsigned char *addr)
{
    err = __hw_addr_add(&dev->uc, addr, dev->addr_len, NETDEV_HW_ADDR_T_UNICAST);
    if (!err)
        __dev_set_rx_mode(dev);
}

多播MAC地址

多播MAC地址保存在net_device结构体的mc链表中,由函数dev_mc_add负责添加多播MAC地址。与单播MAC地址相同,通过__dev_set_rx_mode函数可以设置网卡的多播地址接收过滤功能。

static int __dev_mc_add(struct net_device *dev, const unsigned char *addr, bool global)
{
    err = __hw_addr_add_ex(&dev->mc, addr, dev->addr_len, NETDEV_HW_ADDR_T_MULTICAST, global, false, 0);
    if (!err)
        __dev_set_rx_mode(dev);
}

内核版本

Linux-4.15

猜你喜欢

转载自blog.csdn.net/sinat_20184565/article/details/81189671