作者: 趙怡
1. SFC底層實現原理
port chain和ovs driver/agent
Port Chain插件架構圖:
SFC Port Chain Plugin +-------------------------------+ | +-------------------------+ | | | Port Chain API | | | +-------------------------+ | | | Port Chain Database | | | +-------------------------+ | | | Driver Manager | | | +-------------------------+ | | | Common Driver API | | | +-------------------------+ | | | | | +-------------------------+ | | | OVS Driver | | | +-------------------------+ | +---------------|--------------+ | rpc +----------------+ | OVS Agent | | ext sfc driver | +----------------+ |
目前networking-sfc項目中的基於ovs的sfc實現還是用的mpls, nsh可能要Pike版本才能支持。
以下下列配置爲例, 創建業務鏈串聯sf1, sf2, sf3 三臺nfv虛擬機, 並從VM1處發包進入業務鏈:
+------+ +------+ +------+ | SF1 | | SF2 | | SF3 | +------+ +------+ +------+ p1| |p2 p3| |p4 p5| |p6 | | | | | | VM 1-->----+ +--------+ +-----------+ +---->
根據代碼中的一些限制(參考後面"限制"章節), p1必須和logical-source-port在一個子網中, p2、p3必須在一個子網中, p4、p5必須在一個子網中。 所以, 本示例中, 我們定義p1的子網爲: 1.0.0.0/24, p2的子網爲: 2.0.0.0/24, p3的子網爲: 2.0.0.0/24, p4的子網爲: 3.0.0.0/24, p5的子網爲: 3.0.0.0/24, p6子網爲4.0.0.0/24。
$ openstack network create sfc-net-1 $ openstack network create sfc-net-2 $ openstack network create sfc-net-3 $ openstack network create sfc-net-4 $ openstack network create sfc-net-5 $ openstack network create sfc-net-6 $ openstack subnet create --network sfc-net-1 --subnet-range 1.0.0.0/24 sfc-subnet-1 $ openstack subnet create --network sfc-net-2 --subnet-range 2.0.0.0/24 sfc-subnet-2 $ openstack subnet create --network sfc-net-3 --subnet-range 3.0.0.0/24 sfc-subnet-3 $ openstack subnet create --network sfc-net-4 --subnet-range 4.0.0.0/24 sfc-subnet-4 # 創建虛擬機, 命名爲sfc-vm-1, 加入2個網絡: sfc-net-1, sfc-net-2 # 創建虛擬機, 命名爲sfc-vm-2, 加入2個網絡: sfc-net-2, sfc-net-3 # 創建虛擬機, 命名爲sfc-vm-3, 加入2個網絡: sfc-net-3, sfc-net-4 # 修改虛擬機sfc-vm-1的nova port名字爲: p1, p2 # 修改虛擬機sfc-vm-2的nova port名字爲: p3, p4 # 修改虛擬機sfc-vm-3的nova port名字爲: p5, p6 $ openstack sfc port pair create --ingress p1 --egress p2 port-pair-1 $ openstack sfc port pair create --ingress p3 --egress p4 port-pair-2 $ openstack sfc port pair create --ingress p5 --egress p6 port-pair-3 $ openstack sfc port pair group create --port-pair port-pair-1 port-pair-group-1 $ openstack sfc port pair group create --port-pair port-pair-2 port-pair-group-2 $ openstack sfc port pair group create --port-pair port-pair-3 port-pair-group-3 $ openstack sfc flow classifier create --source-ip-prefix 1.0.0.0/24 --logical-source-port p1 sfc-flow-class-1 $ openstack sfc port chain create --flow-classifier sfc-flow-class-1 --port-pair-group port-pair-group-1 --port-pair-group port-pair-group-2 --port-pair-group port-pair-group-3 sfc-port-chain-1 |
和以前的代碼有區別, 現在sfc分爲2大模塊: 流分類(flow classifier)和sfc(業務鏈), 2大模塊各自可以配置獨立的驅動, 目前都只是通過ovs實現。按照官方想法, 以後流分類(sg,fw, qos, sfc等模塊均包含流分類)這個功能會抽象出來統一維護。
核心流程代碼流程在/services/sfc/drivers/ovs/driver.py中, 其中最核心的create_port_chain()作用如下: 通過用戶輸入的port-pair, port-group, port-chain信息, 生成path_nodes列表, path_nodes包括: 狀態, 業務鏈中位置nsi (nsh index), 節點類型(src_node, sf_node, dst_node), 下一跳信息(port-pair列表和加權信息, 可以看出這裏能做負載均衡),業務鏈id(分配的nsp id) 等等。
[ { "status": "building", "nsi": 255, "portpair_details": [ "524c85b9-7038-43ac-bb29-7a90e62900fd" ], "node_type": "src_node", "next_hop": "[{\"portpair_id\": \"5197be46-cc98-4ac6-93a6-1759e963b1fe\", \"weight\": 1}]", "next_group_id": 1, "nsp": 1, "fwd_path": true, "id": "fd1b1ba6-01d7-4905-94f4-aa3bd8e6dc6d", "tenant_id": "1af813712bdf42789fc90832dc5ce8be", "portchain_id": "350fb8b7-cc89-49f7-888b-f80f8a0248a6", "project_id": "1af813712bdf42789fc90832dc5ce8be" }, { "status": "building", "nsi": 251, "portpair_details": [], "node_type": "dst_node", "next_hop": null, "next_group_id": null, "nsp": 1, "fwd_path": true, "id": "5e34e41c-9db5-4ab4-8c94-e5e68092773f", "tenant_id": "1af813712bdf42789fc90832dc5ce8be", "portchain_id": "350fb8b7-cc89-49f7-888b-f80f8a0248a6", "project_id": "1af813712bdf42789fc90832dc5ce8be" }, { "status": "building", "nsi": 254, "portpair_details": [ "5197be46-cc98-4ac6-93a6-1759e963b1fe" ], "node_type": "sf_node", "next_hop": "[{\"portpair_id\": \"018c8bf0-b93e-4732-9c50-23be5422bdb9\", \"weight\": 1}]", "next_group_id": 2, "nsp": 1, "fwd_path": true, "id": "d63f0bcf-2743-401f-85bc-41dea3ab7ed6", "tenant_id": "1af813712bdf42789fc90832dc5ce8be", "portchain_id": "350fb8b7-cc89-49f7-888b-f80f8a0248a6", "project_id": "1af813712bdf42789fc90832dc5ce8be" }, { "status": "building", "nsi": 253, "portpair_details": [ "018c8bf0-b93e-4732-9c50-23be5422bdb9" ], "node_type": "sf_node", "next_hop": "[{\"portpair_id\": \"64bb88a2-1390-4c8f-b78f-011e9857e2b4\", \"weight\": 1}]", "next_group_id": 3, "nsp": 1, "fwd_path": true, "id": "6d864f84-bd8b-415b-8b15-0137057cc15a", "tenant_id": "1af813712bdf42789fc90832dc5ce8be", "portchain_id": "350fb8b7-cc89-49f7-888b-f80f8a0248a6", "project_id": "1af813712bdf42789fc90832dc5ce8be" }, { "status": "building", "nsi": 252, "portpair_details": [ "64bb88a2-1390-4c8f-b78f-011e9857e2b4" ], "node_type": "sf_node", "next_hop": null, "next_group_id": null, "nsp": 1, "fwd_path": true, "id": "099da96f-01b0-4f3b-9aab-1281960aeb4f", "tenant_id": "1af813712bdf42789fc90832dc5ce8be", "portchain_id": "350fb8b7-cc89-49f7-888b-f80f8a0248a6", "project_id": "1af813712bdf42789fc90832dc5ce8be" } ] |
如果用戶創建的是非對稱鏈(單向), 那麼就創建出一條path_nodes。如果用戶創建的是對稱鏈(雙向), 那麼就創建出一條fwd_path_nodes和一條rev_path_nodes。
然後根據path_nodes裏的信息, 調用_update_path_nodes()-> _update_path_node_flowrules(), RPC調用ovs-agent上的sfc extension driver方法 :update_flow_rules(), 最終下流表配置到ovs上。
現有的OVS Driver/agent都會被改造和擴展支持service chain。
OVS agent會在br-int和br-tun網橋上下發一些額外流表:
1. 流量分類流表: 會在br-int集成網橋中, 下發流表, 用來做流量分類(即flow-classifier)。
2. Service Function轉發流表: 會在br-tun網橋上下發流表, 使需要走service chain的流量通過tunnel走到下一個port chain 端口。而br-int也會用來終結Service Function Path.
這裏, ovs-agent會通過封裝mpls header來標示chain path。其中, mpls label用來表示chain path id, mpls ttl表示chain hop index(指下一跳, 是該chain上的第幾個port).
MPLS報文頭封裝結構如下:
在這裏mpls用於模擬nsh協議報文:
mpls_label字段定義爲: nsp<<8 | nsi
mpls_ttl定義爲: nsi
nsp即NSH Path ID
nsi即NSH index
511: 即0001 | 11111111 意義爲: 業務鏈ID: 1, 節點位置爲255
510: 即0001 | 11111110 意義爲: 業務鏈ID: 1, 節點位置爲254
509: 即0001 | 11111101 意義爲: 業務鏈ID: 1, 節點位置爲253
port信息:
p1: 997(qvof5ea457f-b7): addr: fa:16:3e:4d:10:37 p2: 998(qvo28855064-04): addr:92:8c:c8:c6:41:22 p3: 999(qvo84b4cdbb-af): addr:fe:ae:86:22:eb:cc p4: 1000(qvo68434f35-f6): addr:0e:d7:5d:60:e2:ad p5: 1001(qvofdaf5c1c-01): addr:16:29:4f:32:40:1b p6: 1002(qvo704baa4f-59): addr:ba:9c:5e:a4:36:4b |
br-int flow信息:
每一跳入口的sfc flow匹配和負載均衡, 在table:0中實現: table=0, priority=30,ip,in_port=997 ,nw_src=1.0.0.0/24 actions=group:1【匹配p1端口和流分類規則, 轉發到group:1,做第1跳的出口負載均衡,並轉發給第1個NFV虛擬機】 table=0, priority=30,ip,in_port=998 ,nw_src=1.0.0.0/24 actions=group:2【匹配p2端口和流分類規則, 轉發到group:2,做第2跳的出口負載均衡,並轉發給第2個NFV虛擬機】 table=0, priority=30,ip,in_port=1000 ,nw_src=1.0.0.0/24 actions=group:3【匹配p4端口和流分類規則, 轉發到group:2,做第3跳的出口負載均衡,並轉發給第3個NFV虛擬機】 table=0, priority=30,ip,in_port=1002 ,nw_src=1.0.0.0/24 actions=NORMAL 【匹配p6(即業務鏈出端口), 然後走正常轉發流程】
每一跳出口的sfc報頭封裝, 在table:5中實現: table=5, priority=0,ip,dl_dst=fa:16:3e:4d:10:37 actions=push_mpls:0x8847,set_field:511->mpls_label,set_mpls_ttl(255),push_vlan:0x8100,set_field:4150->vlan_vid,resubmit(,10) 【匹配發往p1 tap的報文,封裝sfc頭(這裏是mpls),並打上本地vlan, 發往遠端計算節點或發給本地table:10】 table=5, priority=0,ip,dl_dst=fa:16:3e:b5:64:7e actions=push_mpls:0x8847,set_field:510->mpls_label,set_mpls_ttl(254),push_vlan:0x8100,set_field:4128->vlan_vid,resubmit(,10) 【匹配發往p3 tap的報文,封裝sfc頭(這裏是mpls),並打上本地vlan, 發往遠端計算節點或發給本地table:10】 table=5, priority=0,ip,dl_dst=fa:16:3e:51:39:b6 actions=push_mpls:0x8847,set_field:509->mpls_label,set_mpls_ttl(253),push_vlan:0x8100,set_field:4169->vlan_vid,resubmit(,10) 【匹配發往p5 tap的報文,封裝sfc頭(這裏是mpls),並打上本地vlan, 發往遠端計算節點或發給本地table:10】
本地sfc報頭解封裝,併發往nfv虛擬機, 在table:10中實現: table=10, priority=1,mpls,dl_vlan=54,dl_dst=fa:16:3e:4d:10:37,mpls_label=511 actions=pop_vlan,pop_mpls:0x0800,output:997【p1在本地, 匹配目的mac爲p1的業務鏈1報文, mpls解封裝, 併發送p1】 table=10, priority=1,mpls,dl_vlan=32,dl_dst=fa:16:3e:b5:64:7e,mpls_label=510 actions=pop_vlan,pop_mpls:0x0800,output:999【p3在本地, 匹配目的mac爲p1的業務鏈1報文, mpls解封裝, 併發送p3】 table=10, priority=1,mpls,dl_vlan=73,dl_dst=fa:16:3e:51:39:b6,mpls_label=509 actions=pop_vlan,pop_mpls:0x0800,output:1001【p5在本地, 匹配目的mac爲p5的業務鏈1報文, mpls解封裝, 併發送p5】 |
br-int group信息, 這裏主要是用來在每個sf出口處做負載均衡的:
group_id=1,type=select,bucket=actions=set_field:fa:16:3e:4d:10:37->eth_dst,resubmit(,5)【修改目的mac爲p1 tap口,並轉發到table 5】 group_id=2,type=select,bucket=actions=set_field:fa:16:3e:b5:64:7e->eth_dst,resubmit(,5)【修改目的mac爲p3 tap口,並轉發到table 5】 group_id=3,type=select,bucket=actions=set_field:fa:16:3e:51:39:b6->eth_dst,resubmit(,5)【修改目的mac爲p5 tap口,並轉發到table 5】 |
發包驗證:
爲了減少安全組和port-security對實驗的影響, 放開所有NFV虛擬機的安全組, 關閉port-security。 # neutron port-update 68434f35-f6fc-4c64-bbd3-55aca8f7b6ef --no-security-groups # neutron port-update 68434f35-f6fc-4c64-bbd3-55aca8f7b6ef --port_security_enabled=True ...
在目的主機sfc-vm-3上開啓sshd服務: # systemctl start sshd
# arp -s 2.0.0.1 fa:16:3e:ee:ee:ee
在sfc-vm-2上配置路由轉發, 把eth0的流量轉發到eth1上去: # echo 1 > /proc/sys/net/ipv4/ip_forward # ip route add 10.0.0.0/24 dev eth1 # arp -s 4.0.0.8 fa:16:3e:ee:ee:dd
|
在sfc-vm-1上ssh 4.0.0.8(即sfc-vm-3的p6端口):
SFC-VM-1抓圖:
sfc-vm-2中間節點報文抓取截圖:
sfc-vm-2 eth0口:
sfc-vm-2 eth1口:
sfc-vm-3上抓圖, 能夠正確抓到從sfc-vm-2發過來的報文:
計算節點上br-int的流表信息, 可以發現報文確實走的相應流表, 流表統計上
總結如下:
OVS agent在br-int中加了以下流用來實現sfc portchain:
1. table:0 增加sfc流表用來匹配流分類規則, 然後轉發給group表做負載均衡
2. table:5 封裝sfc頭部, 並轉發給下一跳nfv虛擬機, 可能在遠端節點走br-tun或本地節點走table:10
3. table:10做sfc報頭解封裝, 並轉發給nfv虛擬機
2. 限制
1. 目前不支持nsh頭。
networking-sfc項目底層ovs驅動暫時是借用了mpls來實現sfc報文封裝的, 其最終目標會通過使用標準nsh報文頭來封裝sfc。目前底層的openvswitch官方組件還不支持nsh解析、處理, 不過有人fork了ovs的代碼自己實現了nsh標準:
https://github.com/pritesh/ovs/tree/nsh-v8
2. 業務鏈上的logical_source_port必須和整條鏈上第一個port-pair group中的所有port_pair上的ingress port在一個subnet中, 否則會報"Cross-subnetchain not supported"
3. 業務鏈上的相鄰的2個port-pair group中, 前一個port-pair group中的所有port-pair上的egress port必須和後一個port-pair group上的所有port-pair上的ingress port在一個subnet中, 否則會報"Cross-subnetchain not supported"
4. 命令openstack sfc flow classifier create, 中必須指定logicalsource port, 文檔中沒有指出。
5. symmetric鏈中, 必須指定logicaldestination port
6. flow classifier中只能指定ethertype=IPv6或者IPv4,
3. 參考資料
Neutron SFC
Wiki
https://wiki.openstack.org/wiki/Neutron/ServiceInsertionAndChaining
Blueprints
https://blueprints.launchpad.net/neutron/+spec/openstack-service-chain-framework
https://blueprints.launchpad.net/neutron/+spec/intent-based-service-chaining
https://blueprints.launchpad.net/neutron/+spec/neutron-api-extension-for-service-chaining
https://blueprints.launchpad.net/neutron/+spec/common-service-chaining-driver-api
API Spec
https://docs.openstack.org/developer/networking-sfc/
https://github.com/openstack/networking-sfc/blob/master/doc/source/api.rst
https://review.openstack.org/#/c/208663/8/doc/source/portchain-ovs-driver-agent.rst
代碼review
https://review.openstack.org/#/q/topic:networking-sfc,n,z
OpenStack cli
https://review.openstack.org/#/c/344920/
OVS NSH
http://openvswitch.org/support/ovscon2015/16/1040-elzur.pdf