背景信息
因?yàn)楣ぷ餍再|(zhì)的變動(dòng)褐啡,許久不更新文章了。最近在做裸金屬管理的項(xiàng)目鳖昌,因?yàn)橘Y源限制备畦,缺少物理機(jī)低飒,而且物理機(jī)啟動(dòng)太慢,不利于開(kāi)發(fā)懂盐,所以選擇用虛擬機(jī)來(lái)模擬物理機(jī)褥赊。在搭建測(cè)試環(huán)境的過(guò)程中,遇到了一些問(wèn)題莉恼,在解決這些問(wèn)題的過(guò)程中拌喉,有很多網(wǎng)絡(luò)方面的收貨,經(jīng)過(guò)一些思考和閱讀源碼解開(kāi)了心中的很多疑惑俐银。比如下面這些問(wèn)題尿背,你是否想的清楚呢?
- 將物理網(wǎng)卡添加到linx bridge中捶惜,linux bridge是如何接管物理網(wǎng)卡的呢田藐?
- 將物理網(wǎng)卡添加到ovs橋中,ovs橋是如何接管物理網(wǎng)卡的呢吱七?
- 為什么物理網(wǎng)卡不能同時(shí)加入到linux bridge和ovs橋呢汽久?
- 在linux系統(tǒng)中,為什么不能創(chuàng)建多個(gè)vlan id相同的vlan子接口呢踊餐?
- 將網(wǎng)卡加入到bond中景醇,又在bond上創(chuàng)建了vlan子接口,流量又是如何轉(zhuǎn)發(fā)的呢吝岭?
資源部署架構(gòu)
這是在一臺(tái)物理機(jī)部署的很多虛擬機(jī)啊易,其中虛擬機(jī)1颗品、虛擬機(jī)2和裸金屬服務(wù)虛機(jī)是直接在物理機(jī)上使用libvirt啟動(dòng)的虛擬機(jī)帐姻,虛擬機(jī)3忘伞、虛擬機(jī)4、虛擬機(jī)5是通過(guò)kubevirt啟動(dòng)的虛機(jī)微峰,kubevirt使用的網(wǎng)絡(luò)方案是kube-ovn的underlay的方式舷丹,即ovs橋接管bond0網(wǎng)卡,虛擬機(jī)通過(guò)ovs橋蜓肆,直接連到物理網(wǎng)絡(luò)上颜凯。
首先需要介紹一些前置的知識(shí),才能更好的理解這些內(nèi)容仗扬。
- 裸金屬管理:裸金屬管理的最終目的是 能夠像虛擬機(jī)一樣管理虛擬機(jī)症概,包括裸金屬服務(wù)器的啟停、監(jiān)控早芭、自動(dòng)安裝系統(tǒng)彼城、修改密碼、自動(dòng)擴(kuò)容根分區(qū)等等。裸金屬管理的方案選擇的是使用開(kāi)源的ironic募壕,又因?yàn)閕ronic依賴(lài)于openstack環(huán)境调炬,所以選擇使用bifrost,bifrost是用于自動(dòng)部署ironic服務(wù)舱馅,并使得ironic不依賴(lài)于openstack環(huán)境缰泡。
- kubevirt : kubevirt是基于kubernetes的輕量虛擬化管理平臺(tái),使得用戶(hù)能夠在kubernetes環(huán)境中同時(shí)使用容器和虛擬機(jī)代嗤。
- ovs :全稱(chēng)是openvswitch棘钞,簡(jiǎn)單來(lái)說(shuō),就是一個(gè)虛擬的交換機(jī)干毅,通過(guò)sdn接管ovs宜猜,可以實(shí)現(xiàn)很強(qiáng)大的網(wǎng)絡(luò)功能。
- 虛擬機(jī)模擬物理機(jī):物理機(jī)的自動(dòng)裝機(jī)包括dhcp溶锭、ipmi控制從網(wǎng)絡(luò)啟動(dòng)宝恶、開(kāi)關(guān)機(jī)等符隙,可用使用vbmcd來(lái)模擬ipmi趴捅,通過(guò)這些模擬方案,可以實(shí)現(xiàn)用虛擬機(jī)來(lái)模擬物理機(jī)霹疫,搭建裸金屬管理的開(kāi)發(fā)環(huán)境拱绑。
網(wǎng)絡(luò)邏輯關(guān)系
現(xiàn)在把目光聚集到網(wǎng)絡(luò)層面,看一下各個(gè)網(wǎng)絡(luò)設(shè)備之間的關(guān)系丽蝎。
從類(lèi)別上分猎拨,enp1和enp2屬于物理網(wǎng)卡,vlan子接口和bond0屬于虛擬網(wǎng)絡(luò)設(shè)備屠阻,linux bridge和ovs橋?qū)儆谔摂M交換機(jī)红省。
從主從關(guān)系講,vlan子接口是linux bridge的從設(shè)備国觉,bond0是ovs橋的從設(shè)備吧恃,enp1和enp2是bond0的從設(shè)備。
有幾個(gè)特殊的點(diǎn)麻诀,需要提一下痕寓,vlan子接口并不是bond0的從設(shè)備,這個(gè)后面會(huì)說(shuō)明我的分析蝇闭,bond0的角色既是主設(shè)備呻率,又是從設(shè)備。
網(wǎng)絡(luò)協(xié)議棧
網(wǎng)絡(luò)協(xié)議棧十分復(fù)雜呻引,這里并不會(huì)詳細(xì)分析網(wǎng)絡(luò)協(xié)議棧的實(shí)現(xiàn)礼仗,只是簡(jiǎn)要介紹一個(gè)網(wǎng)絡(luò)協(xié)議棧的處理流程,方便對(duì)于后文的理解。
為了簡(jiǎn)便和擴(kuò)展元践,網(wǎng)絡(luò)進(jìn)行了分層挪丢,就是大家熟悉的二層卢厂、三層乾蓬、四層、七層慎恒。其中任内,二、三融柬、四是在內(nèi)核態(tài)處理的死嗦,七層是在用戶(hù)態(tài)處理的。在內(nèi)核態(tài)處理的這部分就是網(wǎng)絡(luò)協(xié)議棧的核心內(nèi)容粒氧。
已收包為例越除,物理網(wǎng)卡收到包后,進(jìn)行拆包外盯,解析二層的類(lèi)型摘盆,調(diào)用對(duì)應(yīng)的二層的處理函數(shù),二層收到包后饱苟,進(jìn)行拆包孩擂,解析三層包的類(lèi)型后,調(diào)用對(duì)應(yīng)的三層處理去處理箱熬,三層收到包后类垦,進(jìn)行拆包,解析四層包的類(lèi)型后城须,調(diào)用對(duì)應(yīng)的四層函數(shù)去處理蚤认。每一層都有對(duì)應(yīng)的入口函數(shù),如ip_rcv是三層的入口函數(shù)糕伐,tcp_rcv是四層tcp的入口函數(shù)砰琢。
網(wǎng)絡(luò)協(xié)議棧也是可以重入的,如各種封包赤炒,已vxlan發(fā)送數(shù)據(jù)包為例氯析,當(dāng)數(shù)據(jù)包分別經(jīng)過(guò)四層、三層莺褒、二層的處理后掩缓,到達(dá)vxlan驅(qū)動(dòng),vlan驅(qū)動(dòng)處理時(shí)遵岩,又調(diào)用相應(yīng)的函數(shù)重新回到了四層處理你辣,接下來(lái)又通過(guò)三層巡通、二層的處理,最后通過(guò)物理網(wǎng)卡發(fā)送出去舍哄。
vlan子接口
vlan網(wǎng)卡是一個(gè)虛擬的網(wǎng)絡(luò)設(shè)備宴凉,下面簡(jiǎn)要介紹vlan子接口的創(chuàng)建、vlan虛擬網(wǎng)卡的收發(fā)包流程表悬、以及為什么不能創(chuàng)建相同vlanid的子接口弥锄。
創(chuàng)建vlan虛機(jī)網(wǎng)卡
ip link add link enp1 name enp1.100 type vlan id 100
上面就是創(chuàng)建vlan子接口的命令,其中 enp1 是物理網(wǎng)卡的名稱(chēng)蟆沫,enp1.100是vlan子接口的名稱(chēng)(名稱(chēng)可以隨便起籽暇,enp1.100只是可以簡(jiǎn)明的表示出來(lái)邏輯關(guān)系),id 100表示封vlan包時(shí)使用的id是100饭庞。之后當(dāng)要發(fā)送的數(shù)據(jù)包達(dá)到enp1.100時(shí)戒悠,vlan驅(qū)動(dòng)會(huì)將數(shù)據(jù)包加上vlan tag,之后轉(zhuǎn)交給主設(shè)備enp1物理網(wǎng)卡進(jìn)行發(fā)送舟山。
vlan驅(qū)動(dòng)收發(fā)包
- vlan驅(qū)動(dòng)收包
__netif_receive_skb_core
函數(shù)是二層處理中一個(gè)很重要的函數(shù)绸狐,下面簡(jiǎn)要分析一下這個(gè)函數(shù)(代碼只展示最關(guān)鍵部分)。
static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc,
struct packet_type **ppt_prev)
{
if (skb_vlan_tag_present(skb)) {
vlan_do_receive(&skb);
}
rx_handler = rcu_dereference(skb->dev->rx_handler);
rx_handler(&skb)累盗;
deliver_skb(skb, pt_prev, orig_dev)
}
其中寒矿,skb_vlan_tag_present
用于判斷數(shù)據(jù)包有沒(méi)有vlan,如果有的話(huà)幅骄,調(diào)用vlan_do_receive進(jìn)行處理劫窒,rx_handler
就是判斷一個(gè)設(shè)備是否是從設(shè)備的關(guān)鍵因素,如將一個(gè)物理網(wǎng)卡加入到linux bridge中拆座,linux bridge的驅(qū)動(dòng)就會(huì)將物理網(wǎng)卡的rx_handler
進(jìn)行賦值。本來(lái)這里處理完后冠息,應(yīng)該向上傳遞數(shù)據(jù)包了挪凑,但是因?yàn)?code>rx_handler不為空,所以會(huì)調(diào)用linux bridge的相關(guān)函數(shù)被處理逛艰,簡(jiǎn)單點(diǎn)說(shuō)躏碳,就是數(shù)據(jù)包被linux bridge截胡了。
- vlan驅(qū)動(dòng)發(fā)包
vlan驅(qū)動(dòng)的發(fā)包函數(shù)是vlan_dev_hard_start_xmit
散怖,下面簡(jiǎn)要分析這個(gè)函數(shù)菇绵,代碼只展示最關(guān)鍵的流程。
static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct vlan_dev_priv vlan = vlan_dev_priv(dev);
skb->dev = vlan->real_dev;
vlan_tci = vlan->vlan_id;
vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb->priority);
__vlan_hwaccel_put_tag(skb, vlan->vlan_proto, vlan_tci);
ret = dev_queue_xmit(skb);
return ret;
通過(guò)vlan_dev_priv
獲取到vlan設(shè)備的私有數(shù)據(jù)镇眷,將skb->dev
賦值為vlan子接口的主設(shè)備咬最,再通過(guò)vlan
獲取到vlan id,調(diào)用__vlan_hwaccel_put_tag
進(jìn)行打vlan的tag欠动,最后通過(guò)dev_queue_xmit
將數(shù)據(jù)包轉(zhuǎn)給主設(shè)備進(jìn)行發(fā)送永乌。
創(chuàng)建相同的vlan id的子網(wǎng)卡
在不同的系統(tǒng)上惑申,呈現(xiàn)的錯(cuò)誤并不一樣。
- ubuntu 22.04 上
root@VM-0-5-ubuntu:~# ip -V
ip utility, iproute2-5.15.0, libbpf 0.5.0
root@VM-0-5-ubuntu:~# ip link add link eth0 name vlan98 type vlan id 98
Error: 8021q: VLAN device already exists.
- centos7.6
[root@10 ~]# ip -V
ip utility, iproute2-ss170501
[root@10 ~]# ip link add link bond0 name vlan98 type vlan id 98
RTNETLINK answers: File exists
ubuntu22.04 因?yàn)閕p命令的版本比較高翅雏,很好的將錯(cuò)誤的原因展示了出來(lái)圈驼,centos7.6中ip只是提示已存在。這個(gè)錯(cuò)誤來(lái)自于內(nèi)核望几,可以看這個(gè)函數(shù)vlan_check_real_dev:
int vlan_check_real_dev(struct net_device *real_dev,
__be16 protocol, u16 vlan_id,
struct netlink_ext_ack *extack)
{
if (vlan_find_dev(real_dev, protocol, vlan_id) != NULL) {
NL_SET_ERR_MSG_MOD(extack, "VLAN device already exists");
return -EEXIST;
}
}
從用戶(hù)的需求角度看绩脆,確實(shí)是有創(chuàng)建多個(gè)相同vlan id的vlan子接口的需求,但是內(nèi)核為什么不支持呢橄抹? 這其實(shí)跟內(nèi)核的實(shí)現(xiàn)有關(guān)衙伶,內(nèi)核完全是可以支持的,但是本著機(jī)制和策略分離的原則害碾,vlan驅(qū)動(dòng)只是做vlan隔離矢劲,用戶(hù)需要多個(gè)相同vlan id的子接口,可以通過(guò)bridge進(jìn)行將vlan子接口與用戶(hù)的虛擬網(wǎng)卡進(jìn)行橋接即可(純屬猜測(cè))慌随。
橋接原理
如上文分析芬沉,將物理網(wǎng)卡進(jìn)行橋接,相應(yīng)的橋接驅(qū)動(dòng)都會(huì)改變物理網(wǎng)卡的rx_handler
函數(shù)阁猜,進(jìn)而在__netif_receive_skb_core
函數(shù)處理時(shí)攔截物理網(wǎng)卡的數(shù)據(jù)包丸逸。bond、ovs剃袍、bridge都是相同的原理黄刚。
- ovs
ovs-vsctl add-port br-int port0
這條命令是將port0網(wǎng)卡加入到br-int的ovs橋上時(shí),對(duì)應(yīng)的ovs驅(qū)動(dòng)會(huì)做處理民效,在ovs_netdev_link
函數(shù)會(huì)調(diào)用netdev_rx_handler_register
將物理網(wǎng)卡的rx_handler
賦值為netdev_frame_hook
憔维。
struct vport *ovs_netdev_link(struct vport *vport, const char *name)
{
vport->dev = dev_get_by_name(ovs_dp_get_net(vport->dp), name);
err = netdev_rx_handler_register(vport->dev, netdev_frame_hook,
vport);
return vport;
- linux bridge
brctl add-if br0 port0
這條命令是將網(wǎng)卡port0加入到linux bridge的br0上。對(duì)應(yīng)的linux-bridge驅(qū)動(dòng)會(huì)做處理畏邢。
int br_add_if(struct net_bridge *br, struct net_device *dev,
struct netlink_ext_ack *extack)
{
err = netdev_rx_handler_register(dev, br_handle_frame, p);
}
- 將物理網(wǎng)卡同時(shí)接入linux bridge和ovs橋
調(diào)用橋接時(shí)业扒,會(huì)調(diào)用netdev_is_rx_handler_busy
進(jìn)行檢測(cè),這個(gè)函數(shù)的注釋也寫(xiě)的很清楚舒萎,如果網(wǎng)卡的rx_handler
已經(jīng)有值了程储,這里就會(huì)返回true
。從邏輯的角度也能想清楚臂寝,如果對(duì)于一個(gè)函數(shù)變量重復(fù)賦值章鲤,必然導(dǎo)致變量值被覆蓋,這顯然也是不合理的咆贬。
/**
* netdev_is_rx_handler_busy - check if receive handler is registered
* @dev: device to check
*
* Check if a receive handler is already registered for a given device.
* Return true if there one.
*
* The caller must hold the rtnl_mutex.
*/
bool netdev_is_rx_handler_busy(struct net_device *dev)
{
ASSERT_RTNL();
return dev && rtnl_dereference(dev->rx_handler);
}
流量走向總結(jié)
從最上面的部署關(guān)系圖看到败徊,bond0是ovs橋的從設(shè)備,vlan虛擬網(wǎng)卡的上層設(shè)備是bond0素征,如果一個(gè)數(shù)據(jù)包是帶vlan tag的集嵌,在內(nèi)核中是如何處理的呢萝挤,從__netif_receive_skb_core
的處理也可以看到,是先調(diào)用vlan_do_recieve
進(jìn)行處理根欧,再看網(wǎng)卡的rx_handler
是否有值怜珍。
總結(jié)
本文簡(jiǎn)要的介紹了vlan虛擬網(wǎng)卡和各種橋接的原理,但是并沒(méi)有太多的去關(guān)注細(xì)節(jié)凤粗。希望本文的解讀酥泛,可以幫助各位更好的理解網(wǎng)絡(luò)的基本原理,讓心中懸而未決的疑問(wèn)更加的清晰嫌拣,最后祝各位看官看的開(kāi)心柔袁、生活愉快。創(chuàng)作不易异逐,假如文章對(duì)你有幫助捶索,記得點(diǎn)贊支持。如果有其他問(wèn)題灰瞻,可以直接評(píng)論腥例,或者加微信進(jìn)行更深入的交流。