代码改变世界

一 DATAPATH--datapath如何接受数据包

2017-03-20 14:52  yrpapa  阅读(510)  评论(0)    收藏  举报

一 datapath是ovs的内核模块,模块初始化会执行以下代码,初始化网络设备、虚拟端口等

static int __init dp_init(void)
{
    int err; 

    BUILD_BUG_ON(sizeof(struct ovs_skb_cb) > FIELD_SIZEOF(struct sk_buff, cb));

    pr_info("Open vSwitch switching datapath %s\n", VERSION);

    err = compat_init();
    if (err)
        goto error;

    err = action_fifos_init();
    if (err)
        goto error_compat_exit;

    err = ovs_internal_dev_rtnl_link_register(); // 初始化网络设备的操作,例如创建网桥
    if (err)
        goto error_action_fifos_exit;

    err = ovs_flow_init();
    if (err)
        goto error_unreg_rtnl_link;

    err = ovs_vport_init(); 
    if (err)
        goto error_flow_exit;

    err = register_pernet_device(&ovs_net_ops);
    if (err)
        goto error_vport_exit;

    err = register_netdevice_notifier(&ovs_dp_device_notifier);
    if (err)
        goto error_netns_exit;

    err = ovs_netdev_init(); // 初始化虚拟port的操作,例如添加端口到网桥
    if (err)
        goto error_unreg_notifier;

    err = dp_register_genl();
    if (err < 0)
        goto error_unreg_netdev;

    return 0;

error_unreg_netdev:
    ovs_netdev_exit();
error_unreg_notifier:
    unregister_netdevice_notifier(&ovs_dp_device_notifier);
error_netns_exit:
    unregister_pernet_device(&ovs_net_ops);
error_vport_exit:
    ovs_vport_exit();
error_flow_exit:
    ovs_flow_exit();
error_unreg_rtnl_link:
    ovs_internal_dev_rtnl_link_unregister();
error_action_fifos_exit:
    action_fifos_exit();
error_compat_exit:
    compat_exit();
error:
    return err;
}

 二 ovs_netdev_init(),初始化虚拟port的操作,例如添加端口到网桥的操作netdev_create

static struct vport *netdev_create(const struct vport_parms *parms)
{
    printk("vport-netdev.c: netdev_create->create br or port\n");

    struct vport *vport;

    vport = ovs_vport_alloc(0, &ovs_netdev_vport_ops, parms);
    if (IS_ERR(vport))
        return vport;

    return ovs_netdev_link(vport, parms->name);
}

 

struct vport *ovs_netdev_link(struct vport *vport, const char *name)
{
    int err;

    vport->dev = dev_get_by_name(ovs_dp_get_net(vport->dp), name);
    if (!vport->dev) {
        err = -ENODEV;
        goto error_free_vport;
    }

    if (vport->dev->flags & IFF_LOOPBACK ||
        vport->dev->type != ARPHRD_ETHER ||
        ovs_is_internal_dev(vport->dev)) {
        err = -EINVAL;
        goto error_put;
    }

    rtnl_lock();
    printk("vport-netdev.c:ovs_netdev_link-->create\n");

    // ???

    err = netdev_master_upper_dev_link(vport->dev,
                       get_dpdev(vport->dp), NULL, NULL);
    if (err)
        goto error_unlock;

    printk("vport-netdev.c:ovs_netdev_link-->regist callback\n");

    // 在ovs-vsctl add-port br eth0时
    // 绑定netdev_frame_hook到netif_receive_skb的rx_handler,所有到网卡eth0的包都由
    // netdev_frame_hook处理


    err = netdev_rx_handler_register(vport->dev, netdev_frame_hook,
                     vport); // 注册钩子netdev_frame_hook到协议栈的handler
    if (err)
        goto error_master_upper_dev_unlink;

    dev_disable_lro(vport->dev);
    dev_set_promiscuity(vport->dev, 1);
    vport->dev->priv_flags |= IFF_OVS_DATAPATH;
    rtnl_unlock();

    return vport;

error_master_upper_dev_unlink:
    netdev_upper_dev_unlink(vport->dev, get_dpdev(vport->dp));
error_unlock:
    rtnl_unlock();
error_put:
    dev_put(vport->dev);
error_free_vport:
    ovs_vport_free(vport);
    return ERR_PTR(err);
}

 注册钩子netdev_frame_hook到协议栈的handler,netdev_frame_hook中调用netdev_port_receive,接收数据包,从此,ovs就介入了处理

 

钩子被注册到哪里了,首先看一下linux内核处理网络数据包的流程:

 一 不考虑数据包到达网卡的细节,从数据包被封装为sk_buff后,net/core/dev.c的netif_receive_skb开始处理数据包,最终调用到__netif_receive_skb_core

static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
{
    struct packet_type *ptype, *pt_prev;
    rx_handler_func_t *rx_handler;
    struct net_device *orig_dev;
    bool deliver_exact = false;
    int ret = NET_RX_DROP;
    __be16 type;

    net_timestamp_check(!netdev_tstamp_prequeue, skb);

    trace_netif_receive_skb(skb);

    orig_dev = skb->dev;

    skb_reset_network_header(skb);
    if (!skb_transport_header_was_set(skb))
        skb_reset_transport_header(skb);
    skb_reset_mac_len(skb);

    pt_prev = NULL;

another_round:
    skb->skb_iif = skb->dev->ifindex;

    __this_cpu_inc(softnet_data.processed);

    if (skb->protocol == cpu_to_be16(ETH_P_8021Q) ||
        skb->protocol == cpu_to_be16(ETH_P_8021AD)) {
        skb = skb_vlan_untag(skb);
        if (unlikely(!skb))
            goto out; 
    }    

#ifdef CONFIG_NET_CLS_ACT
    if (skb->tc_verd & TC_NCLS) {
        skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
        goto ncls;
    }
#endif

    if (pfmemalloc)
        goto skip_taps;

    list_for_each_entry_rcu(ptype, &ptype_all, list) {
        if (pt_prev)
            ret = deliver_skb(skb, pt_prev, orig_dev);
        pt_prev = ptype;
    }

    list_for_each_entry_rcu(ptype, &skb->dev->ptype_all, list) {
        if (pt_prev)
            ret = deliver_skb(skb, pt_prev, orig_dev);
        pt_prev = ptype;
    }

skip_taps:
#ifdef CONFIG_NET_INGRESS
    if (static_key_false(&ingress_needed)) {
        skb = sch_handle_ingress(skb, &pt_prev, &ret, orig_dev);
        if (!skb)
            goto out;

        if (nf_ingress(skb, &pt_prev, &ret, orig_dev) < 0)
            goto out;
    }
#endif
#ifdef CONFIG_NET_CLS_ACT
    skb->tc_verd = 0;
ncls:
#endif
    if (pfmemalloc && !skb_pfmemalloc_protocol(skb))
        goto drop;

    if (skb_vlan_tag_present(skb)) {
        if (pt_prev) {
            ret = deliver_skb(skb, pt_prev, orig_dev);
            pt_prev = NULL;
        }
        if (vlan_do_receive(&skb))
            goto another_round;
        else if (unlikely(!skb))
            goto out;
    }

    rx_handler = rcu_dereference(skb->dev->rx_handler); // 如果是ovs的虚拟端口收到的数据包,rx_handler就是注册的钩子netdev_frame_hook,从此就进入ovs的处理
    if (rx_handler) {
        if (pt_prev) {
            ret = deliver_skb(skb, pt_prev, orig_dev);
            pt_prev = NULL;
        }
        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();
        }
    }
 if (unlikely(skb_vlan_tag_present(skb))) {
        if (skb_vlan_tag_get_id(skb))
            skb->pkt_type = PACKET_OTHERHOST;
        /* Note: we might in the future use prio bits
         * and set skb->priority like in vlan_do_receive()
         * For the time being, just ignore Priority Code Point
         */
        skb->vlan_tci = 0;
    }

    type = skb->protocol;

    /* deliver only exact match when indicated */
    if (likely(!deliver_exact)) {
        deliver_ptype_list_skb(skb, &pt_prev, orig_dev, type,
                       &ptype_base[ntohs(type) &
                           PTYPE_HASH_MASK]);
    }

    deliver_ptype_list_skb(skb, &pt_prev, orig_dev, type,
                   &orig_dev->ptype_specific);

    if (unlikely(skb->dev != orig_dev)) {
        deliver_ptype_list_skb(skb, &pt_prev, orig_dev, type,
                       &skb->dev->ptype_specific);
    }

    if (pt_prev) {
        if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
            goto drop;
        else
            ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
    } else {
drop:
        if (!deliver_exact)
            atomic_long_inc(&skb->dev->rx_dropped);
        else
            atomic_long_inc(&skb->dev->rx_nohandler);
        kfree_skb(skb);
        /* Jamal, now you will not able to escape explaining
         * me how you were going to use this. :-)
         */
        ret = NET_RX_DROP;
    }

out:
    return ret;
}

 如果是ovs的虚拟端口收到的数据包,rx_handler就是注册的钩子netdev_frame_hook,从此就进入ovs的处理

 二 钩子是怎么注册的,看一下netdev_rx_handler_register

int netdev_rx_handler_register(struct net_device *dev,
                   rx_handler_func_t *rx_handler,
                   void *rx_handler_data)
{
    ASSERT_RTNL();

    if (dev->rx_handler)
        return -EBUSY;

    /* 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); // dev也就是虚拟端口的rx_handler指针指向了rx_handler指向的方法,也就是netdev_frame_hook

    return 0;
}