Linux網(wǎng)絡(luò)協(xié)議棧6--ipvlan

本來想將macvlan和ipvlan放一起寫建瘫,但是在測試過程中發(fā)現(xiàn)贯被,ipvlan使用起來還是挺復(fù)雜的,于是單獨(dú)作為一章來寫吏廉。
ipvlan 和 macvlan 類似,都是從一個主機(jī)接口虛擬出多個虛擬網(wǎng)絡(luò)接口衰猛。一個重要的區(qū)別就是所有的虛擬接口都有相同的 macv地址迟蜜,而擁有不同的 ip 地址。
ipvlan 有兩種不同的模式:L2 和 L3啡省。一個父接口只能選擇一種模式娜睛,依附于它的所有虛擬接口都運(yùn)行在這個模式下,不能混用模式卦睹。

L2 模式和 macvlan bridge 模式工作原理很相似畦戒,父接口作為交換機(jī)來轉(zhuǎn)發(fā)子接口的數(shù)據(jù)。同一個網(wǎng)絡(luò)的子接口可以通過父接口來轉(zhuǎn)發(fā)數(shù)據(jù)结序,而如果想發(fā)送到其他網(wǎng)絡(luò)障斋,報(bào)文則會通過父接口的路由轉(zhuǎn)發(fā)出去。

L3 模式下徐鹤,ipvlan 有點(diǎn)像路由器的功能垃环,它在各個虛擬網(wǎng)絡(luò)和主機(jī)網(wǎng)絡(luò)之間進(jìn)行不同網(wǎng)絡(luò)報(bào)文的路由轉(zhuǎn)發(fā)工作。

為了更好的理解代碼返敬,在做代碼分析之前遂庄,先按照場景看一下ipvlan的使用。

如下圖:
兩個主機(jī): host1劲赠,host2
host1上:
Ns0~Ns3 是四個network namespace涛目;
ipv0_0, ipv0_1 是在eth0上創(chuàng)建的兩個ipvlan接口秸谢;
ipv1_0, ipv1_1, ipv1_2 是在eth1上創(chuàng)建的三個ipvlan接口;
ipv2_0, ipv2_1, 是在eth1上創(chuàng)建的兩個ipvlan接口霹肝。
ipv0_0, ipv1_0, ipv2_0 在宿主機(jī)的namespace估蹄,其它的在相應(yīng)的namespace中。

注意修改一下宿主接口的rp_filter配置為0或者2(場景二沫换、三)

echo 0 > /proc/sys/net/ipv4/conf/eth0/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/eth1/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/eth2/rp_filter

場景一臭蚁、ipvlan mode l2,ipv1_1, ipv1_2 互通

image.png

即同一宿主接口的兩個ipvlan接口之間互通讯赏,2.1.1.1 ping 2.1.1.2刊棕,兩個接口是同一網(wǎng)段,arp獲取到對方的mac地址后待逞,ipv1_1的驅(qū)動發(fā)送函數(shù)通過查找ip地址(2.1.1.2)所在的ipvlan接口為ipv1_2甥角,直接走接口的接收流程。反向一樣识樱。

場景二嗤无、ipvlan mode l2,ipv1_1, Host1互通

image.png

這種場景怜庸,即如何使用ipvlan 和宿主機(jī)互通当犯,很常用的一個場景。
如圖割疾,我們從 Ns2 ping 宿主機(jī)嚎卫,即2.1.1.1 ping 170.171.0.1,流量分紅色的出方向和藍(lán)色的入方向宏榕,走的是不同的路拓诸。

先貼配置,Ns2中的路由配置:

# ip route
default via 2.1.1.254 dev ipv2_1

宿主機(jī)的配置:

# ip addr
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:00:89:49:80 brd ff:ff:ff:ff:ff:ff
    inet 1.1.1.254/24 scope global ens9
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe89:4980/64 scope link
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:00:4e:0a:de brd ff:ff:ff:ff:ff:ff
    inet 2.1.1.254/24 scope global ens10
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe4e:ade/64 scope link
       valid_lft forever preferred_lft forever
33: ipv0_0@ens9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 52:54:00:89:49:80 brd ff:ff:ff:ff:ff:ff
    inet 192.168.114.1/32 scope host ipv1_0
       valid_lft forever preferred_lft forever
    inet6 fe80::5254:0:189:4980/64 scope link
       valid_lft forever preferred_lft forever
39: ipv1_0@ens10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether 52:54:00:4e:0a:de brd ff:ff:ff:ff:ff:ff
    inet 192.168.114.1/32 scope host ipv2_0
       valid_lft forever preferred_lft forever
    inet6 fe80::5254:0:24e:ade/64 scope link
       valid_lft forever preferred_lft forever
40: ipv2_0@ens15: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether 52:54:00:8f:77:cd brd ff:ff:ff:ff:ff:ff
    inet 192.168.114.1/32 scope host ipv3_0
       valid_lft forever preferred_lft forever
    inet6 fe80::5254:0:28f:77cd/64 scope link

# ip route
1.1.1.1 dev ipv0_0 scope link
2.1.1.1 dev ipv1_0 scope link
2.1.1.2 dev ipv1_0 scope link
3.1.1.1 dev ipv2_0 scope link

# ip neigh 
2.1.1.1 dev ipv1_0 lladdr 52:54:00:4e:0a:de

可以看到麻昼,Ns2中奠支,路由的配置我們指定了nexthop為宿主接口的ip地址,而不是直接指向ipv1_1抚芦,兩者的區(qū)別是前者arp請求網(wǎng)關(guān)ip所以會有數(shù)據(jù)報(bào)文dst mac==src mac的結(jié)果倍谜;而后者請求ping包的dst ip。
因?yàn)樗拗鳈C(jī)和Ns2不是一個廣播域叉抡,出方向數(shù)據(jù)報(bào)文尔崔,目的ip不是eth1任何一個ipvlan接口的ip,且dst mac==src mac褥民,數(shù)據(jù)報(bào)文需要通過宿主接口協(xié)議棧季春,查找路由表,找到170.171.0.1 為宿主機(jī)loopback口ip地址轴捎;而回來的包鹤盒,是無法通過eth1進(jìn)入的(進(jìn)eth1就直接發(fā)到設(shè)備外),這需要通過宿主機(jī)中和ipv1_1同宿主的ipv1_0進(jìn)入ipv1_1侦副,我們配置了“2.1.1.1 dev ipv1_0 scope link”侦锯,在宿主機(jī)上通過三層將流量轉(zhuǎn)發(fā)至ipv1_0,后面流程就入場景一一樣秦驯,報(bào)文進(jìn)入Ns2尺碰。
至于為什么配置了一個靜態(tài)的arp表項(xiàng),是因?yàn)?" who-has 2.1.1.1 tell 170.171.0.1" 的arp request進(jìn)入可以通過ipv1_0進(jìn)入eth1的ipvlan網(wǎng)絡(luò)译隘,但arp reply這個單播報(bào)文是無法通過ipv1_0出來的(arp的Target IP address不屬于任何ipvlan接口的ip)亲桥,會被從eth1發(fā)出去。所以這里配置了靜態(tài)arp固耘,防止宿主機(jī)到2.1.1.1 的arp request题篷。

場景三、ipvlan mode l2厅目,ipv1_1, ipv2_1 互通

image.png

這種場景番枚,即同一宿主機(jī)上,不同宿主接口的ipvlan接口間互通损敷,\color{red}{eth1 和 eth2不在一個二層}葫笼。
如下圖,2.1.1.1 ping 3.1.1.1拗馒,namespace中仍然配置指定下一跳ip為宿主接口ip地址的default路由路星。

# Ns2:
# ip route
default via 2.1.1.254 dev ipv1_1

宿主機(jī)配置路由和arp表項(xiàng):

#ip route
2.1.1.1 dev ipv1_0 scope link
3.1.1.1 dev ipv2_0 scope link
#ip neigh
2.1.1.1 dev ipv1_0 lladdr 52:54:00:4e:0a:de PERMANENT
3.1.1.1 dev ipv2_0 lladdr 52:54:00:8f:77:cd PERMANENT

Ns2 中 2.1.1.1 ping 3.1.1.1流程:
1、走默認(rèn)路由诱桂,從ipv1_1 發(fā)下一跳ip地址的arp request(target ip=2.1.1.254)洋丐;
2、arp reply返回eth1 mac地址挥等,封裝 icmp 報(bào)文發(fā)送垫挨;
3、ipv1_1驅(qū)動發(fā)送函數(shù)触菜,檢查報(bào)文src mac == dst mac九榔,又無法找到3.1.1.1 對應(yīng)的ipvlan接口(同一宿主接口eth1),于是將報(bào)文送入eth1涡相,走接收流程協(xié)議棧哲泊,查找宿主機(jī)namespace路由表,出接口為 ipv2_0催蝗;
4切威、這一跳,我們配置了靜態(tài)arp丙号,不會有arp廣播報(bào)文先朦,否者將在 ipv2_0 上發(fā)送 “who-has 3.1.1.1 tell 170.171.0.1”的arp request缰冤,同場景二,這個request的reply是收不到的喳魏。
5棉浸、 ipv2_0驅(qū)動發(fā)送函數(shù),檢查報(bào)文src mac == dst mac刺彩,并找到3.1.1.1 的ipvlan接口是ipv2_1迷郑,走ipv2_1接收流程,報(bào)文到達(dá)Ns3创倔,完成嗡害。
6、回包路徑完全一樣畦攘。

場景四霸妹、ipvlan mode l2,ipv1_1, ipv0_1 互通

image.png

這種場景知押,即同一宿主機(jī)上抑堡,不同宿主接口的ipvlan接口間互通,\color{red}{eth1 和 eth0在一個二層}朗徊。這一點(diǎn)區(qū)別是為了演示通過宿主接口轉(zhuǎn)發(fā)首妖,而不需要走宿主機(jī)轉(zhuǎn)發(fā)。
如下圖爷恳,2.1.1.1 ping 1.1.1.1有缆,namespace中配置\color{red}{指定出接口}的default路由。

# Ns0:
# ip route
default dev ipv0_1 scope link

# Ns2:
# ip route
default dev ipv1_1 scope link

Ns0 中 1.1.1.1 ping 2.1.1.1流程:
1温亲、走默認(rèn)路由棚壁,從ipv0_1 發(fā)送target ip=2.1.1.1 的arp request,由于eth0 和 eth1是二層互通的栈虚,arp request會到達(dá)eth1袖外,在到達(dá)Ns2的ipv1_1;
2魂务、arp reply沿愿路徑返回ipv1_1的 mac地址曼验,封裝 icmp 報(bào)文發(fā)送;
3粘姜、ipv0_1驅(qū)動發(fā)送函數(shù)鬓照,檢查報(bào)文src mac != dst mac(不屬于任何ipvlan接口),走eth0發(fā)送進(jìn)入switch孤紧,轉(zhuǎn)發(fā)至eth1豺裆,查找2.1.1.1 為eth1的ipvlan接口ipv1_1的ip地址,將報(bào)文送入ipv1_1号显;
4臭猜、應(yīng)答報(bào)文的回程流程完全一樣躺酒。

可以看到這個場景簡單很多。

看一下相關(guān)代碼

ipvlan接口發(fā)包流程

ipvlan_start_xmit--> ipvlan_queue_xmit蔑歌,分為三種模式羹应,l2、l3丐膝、l3s。


int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
{
    struct ipvl_dev *ipvlan = netdev_priv(dev);
    struct ipvl_port *port = ipvlan_port_get_rcu_bh(ipvlan->phy_dev);

    if (!port)
        goto out;

    if (unlikely(!pskb_may_pull(skb, sizeof(struct ethhdr))))
        goto out;

    switch(port->mode) {
    case IPVLAN_MODE_L2:
        return ipvlan_xmit_mode_l2(skb, dev);
    case IPVLAN_MODE_L3:
    case IPVLAN_MODE_L3S:
        return ipvlan_xmit_mode_l3(skb, dev);
    }

    /* Should not reach here */
    WARN_ONCE(true, "ipvlan_queue_xmit() called for mode = [%hx]\n",
              port->mode);
out:
    kfree_skb(skb);
    return NET_XMIT_DROP;
}

l2 mode的發(fā)送處理钾菊,大致分為三種情況:
1帅矗、發(fā)往內(nèi)部(宿主接口+其上創(chuàng)建的所有ipvlan接口,dmac==smac)煞烫,且dst ip屬于其它ipvlan接口浑此。這種情況下可以查詢到目的ipvlan接口,走他的ipvlan_rcv_frame接收報(bào)文滞详。
2凛俱、發(fā)往內(nèi)部(宿主接口+其上創(chuàng)建的所有ipvlan接口,dmac==smac)料饥,且dst ip不屬于其它ipvlan接口蒲犬。這種情況下可以查不到目的ipvlan接口,走宿主接口(物理口)的dev_forward_skb岸啡,基本走一遍非NAPI接收流程原叮,在宿主接口的namespace中走協(xié)議棧,查路由表三層轉(zhuǎn)發(fā)巡蘸。
3奋隶、發(fā)往外部。調(diào)用dev_queue_xmit發(fā)往網(wǎng)絡(luò)悦荒。

這里暫時(shí)不看廣播和組播報(bào)文的處理唯欣。再提一下,什么情況下mac會不相等搬味,一種情況境氢,dip和sip不是同一網(wǎng)段,且dip的路由指向出方向ipvlan接口碰纬,而不是同網(wǎng)段的ip地址产还。如上面的測試場景4,我也是根據(jù)代碼分支做的測試嘀趟。

static int ipvlan_xmit_mode_l2(struct sk_buff *skb, struct net_device *dev)
{
    const struct ipvl_dev *ipvlan = netdev_priv(dev);
    struct ethhdr *eth = eth_hdr(skb);
    struct ipvl_addr *addr;
    void *lyr3h;
    int addr_type;
    /* 這里就是上面各個場景中經(jīng)常提到的脐区,判斷報(bào)文的dmac==smac,以判斷是否發(fā)往內(nèi)部接口(宿主接口+其上創(chuàng)建的所有vlan接口)她按。
    */
    if (ether_addr_equal(eth->h_dest, eth->h_source)) {
        // 根據(jù)報(bào)文信息查詢接收ipvlan接口牛隅,注意這里不止是ip報(bào)文炕柔,arp reply也會走到這里,其查詢ip取的是arp報(bào)文的target ip address媒佣。
        lyr3h = ipvlan_get_L3_hdr(skb, &addr_type);
        if (lyr3h) {
            addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true);
            if (addr)
                // 如上情況1匕累,發(fā)往內(nèi)部且dst ip屬于其它ipvlan接口,測試場景一走這里默伍。
                return ipvlan_rcv_frame(addr, &skb, true);
        }
        skb = skb_share_check(skb, GFP_ATOMIC);
        if (!skb)
            return NET_XMIT_DROP;

        /* Packet definitely does not belong to any of the
         * virtual devices, but the dest is local. So forward
         * the skb for the main-dev. At the RX side we just return
         * RX_PASS for it to be processed further on the stack.
         */
        // 如上情況2欢嘿,發(fā)往內(nèi)部且dst ip不屬于其它ipvlan接口。測試場景二也糊、三走這里炼蹦。
        // skb->dev換成物理接口后走一遍協(xié)議棧。
        return dev_forward_skb(ipvlan->phy_dev, skb);

    } else if (is_multicast_ether_addr(eth->h_dest)) {
        ipvlan_skb_crossing_ns(skb, NULL);
        ipvlan_multicast_enqueue(ipvlan->port, skb);
        return NET_XMIT_SUCCESS;
    }
    // 如上情況2狸剃,發(fā)往外部掐隐,測試場景四走這里。
    ipvlan_skb_crossing_ns(skb, ipvlan->phy_dev);
    return dev_queue_xmit(skb);
}

l3 mode的發(fā)送處理钞馁,這個模式把宿主接口當(dāng)成一個路由器虑省,完全不支持廣播,這個模式下的接口也比l2模式下的ipvlan接口多了一個 NOARP屬性僧凰,也不會發(fā)送廣播報(bào)文探颈。如果dip屬于同宿主接口的一個ipvlan接口,轉(zhuǎn)發(fā)給這個接口接收训措,否者膝擂,在宿主接口上查找路由,走ip協(xié)議棧的ip_local_out流程處理報(bào)文隙弛。

static int ipvlan_xmit_mode_l3(struct sk_buff *skb, struct net_device *dev)
{
    const struct ipvl_dev *ipvlan = netdev_priv(dev);
    void *lyr3h;
    struct ipvl_addr *addr;
    int addr_type;
    // 這里直接根據(jù)三層頭(dip或arp的target ip)查ipvlan接口架馋,不會檢查mac,也不會處理廣播報(bào)文
    lyr3h = ipvlan_get_L3_hdr(skb, &addr_type);
    if (!lyr3h)
        goto out;

    addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true);
    if (addr)
        // 存在對應(yīng)的ipvlan接口全闷,走接口的接收叉寂,送到ipvlan接口
        return ipvlan_rcv_frame(addr, &skb, true);

out:
    // 沒找到ipvlan接口的情況,交給宿主口(物理口)总珠,走三層轉(zhuǎn)發(fā)
    ipvlan_skb_crossing_ns(skb, ipvlan->phy_dev);
    return ipvlan_process_outbound(skb);
}


static int ipvlan_process_outbound(struct sk_buff *skb)
{
    struct ethhdr *ethh = eth_hdr(skb);
    int ret = NET_XMIT_DROP;

    /* l3 mode是不處理廣播報(bào)文的屏鳍,接口是NOARP的,報(bào)文進(jìn)入接口后的dmac和smac都是接口的mac地址 */
    if (is_multicast_ether_addr(ethh->h_dest)) {
        pr_warn_ratelimited("Dropped {multi|broad}cast of type= [%x]\n",
                    ntohs(skb->protocol));
        kfree_skb(skb);
        goto out;
    }

    /* The ipvlan is a pseudo-L2 device, so the packets that we receive
     * will have L2; which need to discarded and processed further
     * in the net-ns of the main-device.
     */
    if (skb_mac_header_was_set(skb)) {
        skb_pull(skb, sizeof(*ethh));
        skb->mac_header = (typeof(skb->mac_header))~0U;
        skb_reset_network_header(skb);
    }

    if (skb->protocol == htons(ETH_P_IPV6))
        ret = ipvlan_process_v6_outbound(skb);
    else if (skb->protocol == htons(ETH_P_IP))
        ret = ipvlan_process_v4_outbound(skb);
    else {
        pr_warn_ratelimited("Dropped outbound packet type=%x\n",
                    ntohs(skb->protocol));
        kfree_skb(skb);
    }
out:
    return ret;
}

static int ipvlan_process_v4_outbound(struct sk_buff *skb)
{
    const struct iphdr *ip4h = ip_hdr(skb);
    struct net_device *dev = skb->dev;
    struct net *net = dev_net(dev);
    struct rtable *rt;
    int err, ret = NET_XMIT_DROP;
    struct flowi4 fl4 = {
        .flowi4_oif = dev->ifindex,
        .flowi4_tos = RT_TOS(ip4h->tos),
        .flowi4_flags = FLOWI_FLAG_ANYSRC,
        .daddr = ip4h->daddr,
        .saddr = ip4h->saddr,
    };
    // 查找路由表
    rt = ip_route_output_flow(net, &fl4, NULL);
    if (IS_ERR(rt))
        goto err;

    if (rt->rt_type != RTN_UNICAST && rt->rt_type != RTN_LOCAL) {
        ip_rt_put(rt);
        goto err;
    }
    skb_dst_set(skb, &rt->dst);
    // 這里走的是local_out 流程的協(xié)議棧
    err = ip_local_out(net, skb->sk, skb);
    if (unlikely(net_xmit_eval(err)))
        dev->stats.tx_errors++;
    else
        ret = NET_XMIT_SUCCESS;
    goto out;
err:
    dev->stats.tx_errors++;
    kfree_skb(skb);
out:
    return ret;
}

image.png

總結(jié)一下:
1局服、l3模式下钓瞭,ipvlan接口是NOARP的,不會收發(fā)ARP報(bào)文淫奔。l2模式下山涡,則會處理廣播報(bào)文;
2、l3模式下鸭丛,發(fā)送到外部的報(bào)文竞穷,只會走三層。二l2模式鳞溉,如果dmac==smac瘾带,才會進(jìn)入宿主物理接口走三層,否則將宿主物理接口當(dāng)二層接口轉(zhuǎn)發(fā)二層報(bào)文出設(shè)備熟菲;
3看政、l3模式下走三層時(shí)處理外部報(bào)文時(shí),直接查路由抄罕,走三層轉(zhuǎn)發(fā)允蚣。而l2模式下,會模擬宿主物理接口收包流程贞绵,走非NAPI的收包流程厉萝。

宿主物理口收包流程

還是分為l2恍飘,l3, l3s三種模式榨崩。


rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb)
{
    struct sk_buff *skb = *pskb;
    struct ipvl_port *port = ipvlan_port_get_rcu(skb->dev);

    if (!port)
        return RX_HANDLER_PASS;

    switch (port->mode) {
    case IPVLAN_MODE_L2:
        return ipvlan_handle_mode_l2(pskb, port);
    case IPVLAN_MODE_L3:
        return ipvlan_handle_mode_l3(pskb, port);
    case IPVLAN_MODE_L3S:
        return RX_HANDLER_PASS;
    }

    /* Should not reach here */
    WARN_ONCE(true, "ipvlan_handle_frame() called for mode = [%hx]\n",
              port->mode);
    kfree_skb(skb);
    return RX_HANDLER_CONSUMED;
}

l2模式下,根據(jù)三層頭章母,查找ipvlan接口母蛛,找到,就走接口接收流程乳怎,送到接口處理彩郊。沒有對應(yīng)的ipvlan接口,返回RX_HANDLER_PASS蚪缀,在__netif_receive_skb_core 函數(shù)中秫逝,會繼續(xù)走三層協(xié)議棧處理。

static rx_handler_result_t ipvlan_handle_mode_l2(struct sk_buff **pskb,
                         struct ipvl_port *port)
{
    struct sk_buff *skb = *pskb;
    struct ethhdr *eth = eth_hdr(skb);
    rx_handler_result_t ret = RX_HANDLER_PASS;
    void *lyr3h;
    int addr_type;

    if (is_multicast_ether_addr(eth->h_dest)) {
        if (ipvlan_external_frame(skb, port)) {
            struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);

            if (nskb) {
                ipvlan_skb_crossing_ns(nskb, NULL);
                ipvlan_multicast_enqueue(port, nskb);
            }
        }
    } else {
        struct ipvl_addr *addr;
        // 根據(jù)三層頭询枚,查找ipvlan接口
        lyr3h = ipvlan_get_L3_hdr(skb, &addr_type);
        if (!lyr3h)
            return ret;

        addr = ipvlan_addr_lookup(port, lyr3h, addr_type, true);
        if (addr)
            // 找到违帆,就走接口接收流程,送到接口處理
            ret = ipvlan_rcv_frame(addr, pskb, false);
    }
    // 沒有對應(yīng)的ipvlan接口金蜀,返回RX_HANDLER_PASS刷后,在__netif_receive_skb_core 函數(shù)中,會繼續(xù)走三層協(xié)議棧處理渊抄。
    return ret;
}

l3模式尝胆,只是不處理廣播和組播報(bào)文,其它的一樣护桦。


static rx_handler_result_t ipvlan_handle_mode_l3(struct sk_buff **pskb,
                         struct ipvl_port *port)
{
    void *lyr3h;
    int addr_type;
    struct ipvl_addr *addr;
    struct sk_buff *skb = *pskb;
    rx_handler_result_t ret = RX_HANDLER_PASS;

    lyr3h = ipvlan_get_L3_hdr(skb, &addr_type);
    if (!lyr3h)
        goto out;

    addr = ipvlan_addr_lookup(port, lyr3h, addr_type, true);
    if (addr)
        ret = ipvlan_rcv_frame(addr, pskb, false);

out:
    return ret;
}

這里再補(bǔ)充一個函數(shù)含衔,ipvlan_rcv_handle,即找到ipvlan接口后,送入ipvlan接口的處理抱慌。
ipvlan子接口的發(fā)送流程和宿主物理接口的接收流程都會走到這個函數(shù)逊桦,前者傳入的local標(biāo)記為true,后者為false抑进。
local==true强经,調(diào)用dev_forward_skb的處理,會走完整的ipvlan接口的接收流程寺渗,報(bào)文放入CPU SD結(jié)構(gòu)隊(duì)列匿情,通過觸發(fā)軟中斷,處理報(bào)文信殊。返回結(jié)果是RX_HANDLER_CONSUMED炬称,__netif_receive_skb_core結(jié)束處理。
local==false涡拘,直接返回RX_HANDLER_ANOTHER玲躯,__netif_receive_skb_core函數(shù)對這個結(jié)果的處理是再走一遍__netif_receive_skb_core函數(shù)。
其實(shí)這兩個流程最終都是走_(dá)_netif_receive_skb_core鳄乏,處理大部分一樣跷车,最大的區(qū)別是是否再次入隊(duì)列,通過軟中斷觸發(fā)處理的問題橱野。這其中應(yīng)該有什么考慮朽缴,有大神明白的話,留言指教一下吧水援。


static int ipvlan_rcv_frame(struct ipvl_addr *addr, struct sk_buff **pskb,
                bool local)
{
    struct ipvl_dev *ipvlan = addr->master;
    struct net_device *dev = ipvlan->dev;
    unsigned int len;
    rx_handler_result_t ret = RX_HANDLER_CONSUMED;
    bool success = false;
    struct sk_buff *skb = *pskb;

    len = skb->len + ETH_HLEN;
    /* Only packets exchanged between two local slaves need to have
     * device-up check as well as skb-share check.
     */
    if (local) {
        if (unlikely(!(dev->flags & IFF_UP))) {
            kfree_skb(skb);
            goto out;
        }

        skb = skb_share_check(skb, GFP_ATOMIC);
        if (!skb)
            goto out;

        *pskb = skb;
    }
    // 后面可能切換ns
    ipvlan_skb_crossing_ns(skb, dev);
    /*
    local==true密强,是指ipvlan子接口的發(fā)送流程,dev_forward_skb的處理即走完整的ipvlan接口的接收流程蜗元,
    通過軟中斷觸發(fā)或渤。返回結(jié)果是RX_HANDLER_CONSUMED,__netif_receive_skb_core結(jié)束處理奕扣。
    local==false薪鹦,是指宿主物理接口的接收流程。返回結(jié)果是RX_HANDLER_ANOTHER成畦,再走一遍__netif_receive_skb_core函數(shù)距芬。
    */ 
    if (local) {
        // 
        skb->pkt_type = PACKET_HOST;
        if (dev_forward_skb(ipvlan->dev, skb) == NET_RX_SUCCESS)
            success = true;
    } else {
        ret = RX_HANDLER_ANOTHER;
        success = true;
    }

out:
    ipvlan_count_rx(ipvlan, len, success, false);
    return ret;
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市循帐,隨后出現(xiàn)的幾起案子框仔,更是在濱河造成了極大的恐慌,老刑警劉巖拄养,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件离斩,死亡現(xiàn)場離奇詭異银舱,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)跛梗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門寻馏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人核偿,你說我怎么就攤上這事诚欠。” “怎么了漾岳?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵轰绵,是天一觀的道長。 經(jīng)常有香客問我尼荆,道長左腔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任捅儒,我火速辦了婚禮液样,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘巧还。我一直安慰自己鞭莽,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布狞悲。 她就那樣靜靜地躺著撮抓,像睡著了一般妇斤。 火紅的嫁衣襯著肌膚如雪摇锋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天站超,我揣著相機(jī)與錄音荸恕,去河邊找鬼。 笑死死相,一個胖子當(dāng)著我的面吹牛融求,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播算撮,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼生宛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了肮柜?” 一聲冷哼從身側(cè)響起陷舅,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎审洞,沒想到半個月后莱睁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年仰剿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了创淡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡南吮,死狀恐怖琳彩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情部凑,我是刑警寧澤汁针,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站砚尽,受9級特大地震影響施无,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜必孤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一猾骡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧敷搪,春花似錦兴想、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至闸与,卻和暖如春毙替,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背践樱。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工厂画, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拷邢。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓袱院,卻偏偏與公主長得像,于是被迫代替她去往敵國和親瞭稼。 傳聞我的和親對象是個殘疾皇子忽洛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評論 2 359