此类的设备诸如网桥bridge、聚合接口bond,以及OVS虚拟网桥等。首先以网桥bridge为例,其配置命令如下:
$ sudo ip link add name br1 type bridge
$ sudo ip link set ens38 master br1
第一条命令创建虚拟网桥设备br1;第二条命令添加物理接口ens38到网桥br1上。对于第二条命令,内核中有函数br_add_if完成其处理,如下所示。
int br_add_if(struct net_bridge *br, struct net_device *dev, struct netlink_ext_ack *extack)
{
struct net_bridge_port *p;
p = new_nbp(br, dev);
if (IS_ERR(p))
return PTR_ERR(p);
err = netdev_rx_handler_register(dev, br_handle_frame, p);
if (err)
goto err4;
dev->priv_flags |= IFF_BRIDGE_PORT;
}
注意其中的函数netdev_rx_handler_register,其将为此物理设备(如ens38)注册一下网桥bridge特定的接收函数br_handle_frame,参数为此物理设备对应的网桥端口结构net_bridge_port(p),用于处理网桥相关的操作。
如下所示,br_handle_frame赋值给了网络设备的rx_handler函数指针,网桥端口接口赋予了rx_handler_data。
int netdev_rx_handler_register(struct net_device *dev, rx_handler_func_t *rx_handler, void *rx_handler_data)
{
if (netdev_is_rx_handler_busy(dev))
return -EBUSY;
if (dev->priv_flags & IFF_NO_RX_HANDLER)
return -EINVAL;
/* Note: rx_handler_data must be set before rx_handler */
rcu_assign_pointer(dev->rx_handler_data, rx_handler_data);
rcu_assign_pointer(dev->rx_handler, rx_handler);
return 0;
}
在内核的核心接收函数__netif_receive_skb_core中,检查数据包的接收设备是否注册了rx_handler回调函数,如果为真,首先调用rx_handler进行处理,例如对于网桥的子接口设备,此函数为br_handle_frame。
static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc, struct packet_type **ppt_prev)
{
struct packet_type *ptype, *pt_prev;
rx_handler_func_t *rx_handler;
rx_handler = rcu_dereference(skb->dev->rx_handler);
if (rx_handler) {
switch (rx_handler(&skb)) {
case RX_HANDLER_CONSUMED:
ret = NET_RX_SUCCESS;
goto out;
case RX_HANDLER_ANOTHER:
goto another_round;
case RX_HANDLER_EXACT:
deliver_exact = true;
case RX_HANDLER_PASS:
break;
default:
BUG();
}
}
}
对于聚合Bond接口,类似于网桥bridge接口,使用如下的命令创建聚合接口,以及为其添加子接口ens38:
$ sudo ip link add name bond01 type bond
$ sudo ip link set ens38 master bond01
内核中使用函数bond_enslave处理Bond子接口的添加操作。由代码可见,内核使用netdev_rx_handler_register函数为Bond子接口注册了接收处理函数bond_handle_frame。
int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev, struct netlink_ext_ack *extack)
{
struct bonding *bond = netdev_priv(bond_dev);
const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
res = netdev_rx_handler_register(slave_dev, bond_handle_frame, new_slave);
if (res) {
netdev_dbg(bond_dev, "Error %d calling netdev_rx_handler_register\n", res);
goto err_detach;
}
}
对于openvswitch来说,以下命令创建ovs网桥,并添加ens38子接口。
$ ovs-vsctl add-br br0
$ ovs-vsctl add-port br0 ens38
或
$ ovs-vsctl add-br br0 -- add-port br0 ens38
内核函数ovs_netdev_link负责处理ovs网桥子接口的添加操作。其通过函数netdev_rx_handler_register为子接口ens38注册了接收函数netdev_frame_hook,参数为ens38所对应的vport类型结构。
struct vport *ovs_netdev_link(struct vport *vport, const char *name)
{
vport->dev = dev_get_by_name(ovs_dp_get_net(vport->dp), name);
if (!vport->dev) {
err = -ENODEV;
goto error_free_vport;
}
err = netdev_rx_handler_register(vport->dev, netdev_frame_hook, vport);
if (err)
goto error_master_upper_dev_unlink;
}
以下的命令创建hsr类型接口,其子接口为ens38和ens39:
$ sudo ip link add name hsr01 type hsr slave1 ens38 slave2 ens39
内核函数hsr_portdev_setup负责将子接口添加到hsr桥中。其为子接口设备注册处理函数hsr_handle_frame:
static int hsr_portdev_setup(struct net_device *dev, struct hsr_port *port)
{
res = netdev_rx_handler_register(dev, hsr_handle_frame, port);
if (res)
goto fail_rx_handler;
}
内核中其它的Master/Slave结构的接口如下:
接口 | 处理函数 |
---|---|
failover | net_failover_handle_frame |
team | team_handle_frame |
macvlan | macvlan_handle_frame |
netvsc | netvsc_vf_handle_frame |
rmnet | rmnet_rx_handler |
ipvtap | tap_handle_frame |
ipvlan | ipvlan_handle_frame |
macvtap | tap_handle_frame |
macsec | macsec_handle_frame |
virt_wifi | virt_wifi_rx_handler |
内核版本:5.0