内核Master/Slave类型设备的接收流程

此类的设备诸如网桥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结构的接口如下:

接口处理函数
failovernet_failover_handle_frame
teamteam_handle_frame
macvlanmacvlan_handle_frame
netvscnetvsc_vf_handle_frame
rmnetrmnet_rx_handler
ipvtaptap_handle_frame
ipvlanipvlan_handle_frame
macvtaptap_handle_frame
macsecmacsec_handle_frame
virt_wifivirt_wifi_rx_handler

内核版本:5.0

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页