本來想將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 互通
即同一宿主接口的兩個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互通
這種場景怜庸,即如何使用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 互通
這種場景番枚,即同一宿主機(jī)上,不同宿主接口的ipvlan接口間互通损敷,葫笼。
如下圖,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 互通
這種場景知押,即同一宿主機(jī)上抑堡,不同宿主接口的ipvlan接口間互通,朗徊。這一點(diǎn)區(qū)別是為了演示通過宿主接口轉(zhuǎn)發(fā)首妖,而不需要走宿主機(jī)轉(zhuǎn)發(fā)。
如下圖爷恳,2.1.1.1 ping 1.1.1.1有缆,namespace中配置的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;
}
總結(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;
}