美文网首页
Linux网络协议栈7--macvlan

Linux网络协议栈7--macvlan

作者: 苏苏林 | 来源:发表于2020-10-25 20:52 被阅读0次

macvlan是linux的一种虚拟网络接口,macvlan 允许你在主机的一个网络接口上配置多个虚拟的网络接口,这些网络 interface 有自己独立的 mac 地址,也可以配置上 ip 地址进行通信。macvlan 下的虚拟机或者容器网络和主机在同一个网段中,共享同一个广播域。
macvlan 和 bridge 比较相似,但因为它省去了 bridge 的存在,所以配置和调试起来比较简单,而且效率也相对高。除此之外,macvlan 自身也完美支持 VLAN。

macvlan 虚拟网卡设备包括5种模式:
private 模式:在这种模式下,macvlan设备不能接受寄生在同一个物理网卡的其他macvlan设备的数据包,即使是其他macvlan设备通过物理网卡发送出去并通过hairpin设备返回的包。
vepa 模式:在这种模式下,macvlan设备不能直接接受寄生在同一个物理网卡的其他macvlan设备的数据包,但是其他macvlan设备可以将数据包通过物理网卡发送出去,然后通过hairpin设备返回的给其他macvlan设备。
passthru 模式:在这种模式下,每一个物理设备只能寄生一个macvlan设备
bridge 模式:在这种模式下,寄生在同一个物理设备的macvlan设备可以直接通讯,不需要外接的hairpin设备帮助。
source 模式: 在这种模式下,寄生在物理设备的这类macvlan设备,只能接受指定的源 mac source的数据包,其他数据包都不接受。

macvlan在协议栈中两个重要的数据结构:
创建macvlan接口的时候,struct macvlan_dev 会作为macvlan接口设备数据结构net_device的私有数据结构创建(netdev_priv(dev)获取)保存单个macvlan接口的信息。
同时会为其宿主接口(如果是在其上创建的第一个macvlan接口)的net_device挂载特殊设备接收处理函数rx_handler=macvlan_handle_frame,以及这个函数需要用到的参数rx_handler_data,就是macvlan_port。保存的是这个宿主接口以及其下的macvlan接口的整体信息,最重要的当然是查找报文dmac所对应的macvlan接口。
所有相关处理都在macvlan_common_newlink函数中。

struct macvlan_port {
    struct net_device   *dev;
    struct hlist_head   vlan_hash[MACVLAN_HASH_SIZE]; // macvlan设备macvlan_dev结构hash表,用于查找
    struct list_head    vlans; // macvlan设备macvlan_dev结构链表,用于遍历
    struct rcu_head     rcu;
    struct sk_buff_head bc_queue;   // 广播报文队列

    struct work_struct  bc_work;    // 广播报文处理任务进程
    bool            passthru;    
    int         count;
    struct hlist_head   vlan_source_hash[MACVLAN_HASH_SIZE]; // source模式用到
    DECLARE_BITMAP(mc_filter, MACVLAN_MC_FILTER_SZ);
};


struct macvlan_dev {
    struct net_device   *dev;      //macvlan网卡设备回指
    struct list_head    list;
    struct hlist_node   hlist;     
    struct macvlan_port *port;    // struct macvlan_port 回指
    struct net_device   *lowerdev;  // 宿主结构设备 回指
    void            *fwd_priv;           // 物理网卡支持硬件加速时用到
    struct vlan_pcpu_stats __percpu *pcpu_stats;

    DECLARE_BITMAP(mc_filter, MACVLAN_MC_FILTER_SZ);

    netdev_features_t   set_features;
    enum macvlan_mode   mode;
    u16         flags;
    /* This array tracks active taps. */
    struct macvtap_queue    __rcu *taps[MAX_MACVTAP_QUEUES];
    /* This list tracks all taps (both enabled and disabled) */
    struct list_head    queue_list;
    int         numvtaps;
    int         numqueues;
    netdev_features_t   tap_features;
    int         minor;
    int         nest_level;
#ifdef CONFIG_NET_POLL_CONTROLLER
    struct netpoll      *netpoll;
#endif
    unsigned int        macaddr_count;
};

宿主接口接收函数,macvlan_handle_frame。


/* called under rcu_read_lock() from netif_receive_skb */
static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
{
    struct macvlan_port *port;
    struct sk_buff *skb = *pskb;
    const struct ethhdr *eth = eth_hdr(skb);
    const struct macvlan_dev *vlan;
    const struct macvlan_dev *src;
    struct net_device *dev;
    unsigned int len = 0;
    int ret;
    rx_handler_result_t handle_res;

    port = macvlan_port_get_rcu(skb->dev);
    // 宿主接口接收到的广播报文,有可能是外部设备发送的,也可能是内部设备发送的,如外部设备hairpin模式返回的
    if (is_multicast_ether_addr(eth->h_dest)) {
        unsigned int hash;

        skb = ip_check_defrag(dev_net(skb->dev), skb, IP_DEFRAG_MACVLAN);
        if (!skb)
            return RX_HANDLER_CONSUMED;
        *pskb = skb;
        eth = eth_hdr(skb);
        // source模式,不管是广播还是单播,无脑根据smac匹配接收
        macvlan_forward_source(skb, port, eth->h_source);
        src = macvlan_hash_lookup(port, eth->h_source);
        // private 模式不允许接收本地的接口(同一宿主接口派生出来的)发出的报文,passthru 模式只有一个派生接口,
        // 这两种类型等同于只向自己发,不需要入队列了。
        if (src && src->mode != MACVLAN_MODE_VEPA &&
            src->mode != MACVLAN_MODE_BRIDGE) {
            /* forward to original port. */
            vlan = src;
            ret = macvlan_broadcast_one(skb, vlan, eth, 0) ?:
                  netif_rx(skb);
            handle_res = RX_HANDLER_CONSUMED;
            goto out;
        }

        hash = mc_hash(NULL, eth->h_dest);
        if (test_bit(hash, port->mc_filter))
            macvlan_broadcast_enqueue(port, src, skb);

        return RX_HANDLER_PASS;
    }
    /*source模式,不管是广播还是单播,无脑根据smac匹配接收。
      而且不影响正常根据mac地址匹配做转发的流程。
      所以这里如果smac 和 dmac同时和一个source类型的接口匹配了,岂不是接收了两份??但实际上没有,需要仔细看看什么原因。
    */ 
    macvlan_forward_source(skb, port, eth->h_source);
    if (port->passthru)
        // passthru 模式只有一个派生接口,直接取链表中第一个数据
        vlan = list_first_or_null_rcu(&port->vlans,
                          struct macvlan_dev, list);
    else
        // 其它在hash表中查
        vlan = macvlan_hash_lookup(port, eth->h_dest);
    if (vlan == NULL)
        return RX_HANDLER_PASS;

    dev = vlan->dev;
    if (unlikely(!(dev->flags & IFF_UP))) {
        kfree_skb(skb);
        return RX_HANDLER_CONSUMED;
    }
    len = skb->len + ETH_HLEN;
    skb = skb_share_check(skb, GFP_ATOMIC);
    if (!skb) {
        ret = NET_RX_DROP;
        handle_res = RX_HANDLER_CONSUMED;
        goto out;
    }

    *pskb = skb;
    skb->dev = dev;
    skb->pkt_type = PACKET_HOST;

    ret = NET_RX_SUCCESS;
    // 单播,修改了后skb->dev为macvlan接口,返回RX_HANDLER_ANOTHER,
    // __netif_receive_skb_core 会重走自己的流程,等于在macvlan接口上再走一遍协议栈。
    handle_res = RX_HANDLER_ANOTHER;
out:
    macvlan_count_rx(vlan, len, ret == NET_RX_SUCCESS, false);
    return handle_res;
}

macvlan接口发包流程


static netdev_tx_t macvlan_start_xmit(struct sk_buff *skb,
                      struct net_device *dev)
{
    unsigned int len = skb->len;
    int ret;
    struct macvlan_dev *vlan = netdev_priv(dev);

    if (unlikely(netpoll_tx_running(dev)))
        return macvlan_netpoll_send_skb(vlan, skb);
    // 硬件优化的代码,不用管
    if (vlan->fwd_priv) {
        skb->dev = vlan->lowerdev;
        ret = dev_queue_xmit_accel(skb, vlan->fwd_priv);
    } else {
        // 实际调用 macvlan_queue_xmit
        ret = macvlan_queue_xmit(skb, dev);
    }

    if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
        struct vlan_pcpu_stats *pcpu_stats;

        pcpu_stats = this_cpu_ptr(vlan->pcpu_stats);
        u64_stats_update_begin(&pcpu_stats->syncp);
        pcpu_stats->tx_packets++;
        pcpu_stats->tx_bytes += len;
        u64_stats_update_end(&pcpu_stats->syncp);
    } else {
        this_cpu_inc(vlan->pcpu_stats->tx_dropped);
    }
    return ret;
}


static int macvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
{
    const struct macvlan_dev *vlan = netdev_priv(dev);
    const struct macvlan_port *port = vlan->port;
    const struct macvlan_dev *dest;
    // bridge模式,如果报文是发向同一宿主接口派生的bridge模式的macvlan接口,则转发,其它都被禁止。
    if (vlan->mode == MACVLAN_MODE_BRIDGE) {
        const struct ethhdr *eth = (void *)skb->data;

        /* send to other bridge ports directly */
        // 可以看到,只有bridge模式能向 bridge模式的派生口发广播报文
        if (is_multicast_ether_addr(eth->h_dest)) {
            macvlan_broadcast(skb, port, dev, MACVLAN_MODE_BRIDGE);
            goto xmit_world;
        }

        dest = macvlan_hash_lookup(port, eth->h_dest);
        // 可以看到,只有bridge模式能向 bridge模式的派生口发单播报文
        if (dest && dest->mode == MACVLAN_MODE_BRIDGE) {
            /* send to lowerdev first for its network taps */
            dev_forward_skb(vlan->lowerdev, skb);

            return NET_XMIT_SUCCESS;
        }
    }

xmit_world:
    // 其它情况,直接走宿主物理口发送,所以macvlan接口的性能还是很高的,除了上面的简单的查表,不会做额外的封装,很接近物理口。
    skb->dev = vlan->lowerdev;
    return dev_queue_xmit(skb);
}

相关文章

  • Linux网络协议栈7--macvlan

    macvlan是linux的一种虚拟网络接口,macvlan 允许你在主机的一个网络接口上配置多个虚拟的网络接口,...

  • docker网络基础

    网络的命名空间 linux在网络栈中引入网络命名空间,从而支持网络协议栈的多个实例。这些独立的协议栈被隔离到不同的...

  • K8S原理简介及环境搭建

    一、原理简介 名词解释 1、网络的命名空间:Linux在网络栈中引入网络命名空间,将独立的网络协议栈隔离到不同的命...

  • 网络协议、端口和Socket

    1、网络协议分层 网络层次可划分为五层因特网协议栈和七层因特网协议栈。 1.1 五层因特网协议栈 因特网协议栈共有...

  • 用户态协议栈的实现

    协议栈,指的是TCP/IP协议栈。linux系统中,协议栈是内核实现的。 Client发送数据给server,数据...

  • CentOS系统启动流程你懂否

    一、Linux内核的组成 相关概念:Linux系统的组成部分:内核+根文件系统内核:进程管理、内存管理、网络协议栈...

  • linux.network 网络协议栈

    https://blog.csdn.net/zxorange321/article/details/75676063

  • 协议栈的内部结构

    什么是协议栈? 如果说网卡是连接网络的硬件,那么协议栈就是连接网络的软件。 协议栈包括什么? 主要包括TCP,UD...

  • Linux网络子系统

    网络分层 类似于OSI模型,Linux网络协议栈分层: 网络数据传输期间发生的基本操作: 1) 当一个应用程序发送...

  • Linux网络协议栈8--vxlan

    本文记录一下vxlan接口内核收发包处理。VXLAN(Virtual Extensible LAN, 虚拟局域网扩...

网友评论

      本文标题:Linux网络协议栈7--macvlan

      本文链接:https://www.haomeiwen.com/subject/jzmnmktx.html