网桥接口处在非混杂模式下,只能接收目的MAC地址为自身的数据包,也就是说如果数据包的目的MAC为其它地址,将会被丢弃掉。对于单网口的设备这样没有问题,但是对于存在多个网卡的交换设备,如果从一个网口接收到的数据包为需要转发的数据包,其去往另外一个接口,由于目的mac不等于接收网卡的MAC地址就会被丢弃导调导致网络不通。
Linux内核为了处理这一问题,采取了为一个非混杂模式的接口,添加多个硬件单播MAC地址的方法,即一个接口对应多个MAC地址(MAC地址列表)。
struct net_device {
struct netdev_hw_addr_list uc; /* Unicast mac addresses */
}
Linux内核为了处理这一问题,采取了为一个非混杂模式的接口,添加多个硬件单播MAC地址的方法,即一个接口对应多个MAC地址(MAC地址列表)。
struct net_device {
struct netdev_hw_addr_list uc; /* Unicast mac addresses */
}
网桥代码中函数br_port_clear_promisc负责处理混杂模式的关闭, 如果网桥的子接口不支持单播过滤功能(网卡驱动不支持IFF_UNICAST_FLT),即其混杂模式不能关闭,不用再做处理。
static void br_port_clear_promisc(struct net_bridge_port *p)
{
if (!br_promisc_port(p) || !(p->dev->priv_flags & IFF_UNICAST_FLT))
return;
err = br_fdb_sync_static(p->br, p);
dev_set_promiscuity(p->dev, -1);
p->flags &= ~BR_PROMISC;
}
当网卡设备支持单播过滤时,在关闭接口混杂模式之前,需要先同步好FDB表内相关的静态转发表项,以防关闭混杂模式后导致数据流中断。br_fdb_sync_static函数负责将fdb表中的静态表项同步到网卡设备的单播地址列表中(uc),这样,当关闭混杂模式时,目的地址只要在dev->uc列表中的数据包,还可以继续正常转发。dev_uc_add函数会将这些地址同步到网卡硬件的过滤列表中。
int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p)
{
for (i = 0; i < BR_HASH_SIZE; i++) {
hlist_for_each_entry(fdb, &br->hash[i], hlist) {
if (!fdb->is_static)
continue;
err = dev_uc_add(p->dev, fdb->addr.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);
}
最后看一下FDB表中都由哪些静态表项。首先是网桥接口自身的MAC地址生成的表项(毫无疑问目的mac为网桥自身的数据包,子接口必须接收上来);第二是网桥增加子接口时,由子接口的MAC地址生成的表项;然后就是用户配置的静态转发表项。
ip link add br_test type bridge
ip link set eth0 master br_test
bridge fdb add 00:01:02:03:04:05 dev eth0 master static
内核版本
linux-4.15