概要
目前天翼云已在部分資源池支持了組播的能力。有需要組播業務上云,可以通過聯系天翼云來開通組播功能。本文基于天翼云的云內組播產品和scapy來學習和實踐IGMP協議。
前提條件
-
需要在天翼云上支持組播的資源池上準備一個VPC。
-
在VPC內創建至少一個linux云主機。
-
云主機的python版本要至少3.7.x
scapy
安裝過程(可通過scapy官網):
例如通過包管理工具安裝
$ pip3 install scapy
scapy使用
使用scapy進入云主機,通過python解釋器,或直接進入scapy解釋器來構造報文,本文介紹構造方法,直接使用scapy解釋器,在linux終端下輸入scapy即可:
# scapy
INFO: Can't import PyX. Won't be able to use psdump() or pdfdump().
WARNING: IPython not available. Using standard Python shell instead.
>>>
在天翼云控制臺創建組播域
天翼云的組播產品,目前一個vpc能夠創建一個組播域,一個組播域下可以有若干組播組(具體配額和聯系天翼云官方查詢)
天翼云的組播域包括云內和云間兩種來源,云間組播域需要業務側拉取專線,并對vpc創建專線實例,云間組播域需要聯系天翼云官方開通。而云內組播域,組播源使用的也是vpc的云主機,若驗證天翼云的組播功能,可以使用域內組播來做實驗。本文通過云內組播即可。
云內組播域包含動態加入和靜態加入,靜態加入不涉及IGMP協議,是通過靜態導入云主機來實現的。本文是來驗證IGMP協議,所以使用動態加入即可。
創建組播域
要創建組播域,可在網絡控制臺找到組播,點擊添加組播域。填寫必要信息后接口創建完成,在這里我們選擇動態加入,默認情況下,會選擇IGMPv2的協議。下文分別對IGMPv2/IGMPv3來進行實踐。
創建動態的組播域后,組播域內不會有任何組播組的,點擊組播域的組播轉發展示可以看到是沒有任何組播組的
需要通過VPC內的云主機通過組播成員關系報文來動態的加組或者離組,我們接下來就使用SCAPY來驗證這個過程
IGMPv2
IGMPv2的報文頭的格式如下,我們通過scapy來構造的報文也要遵從RFC:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Max Resp Time | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Group Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
我們關注報文中的Type和Group Address 兩個字段即可。Max Resp Time 只在查詢報文時有意義,在成員加組離組時,通常配置為0
Type
對于Type,我們只關注IGMPv2,不考慮兼容IGMPv1時,需要了解三種type:
-
0x11 查詢報文類型,通常由組播路由器來觸發,查詢組播成員。在天翼云的動態組播中,也會由特定的組件來發送查詢報文。
-
0x16 成員關系報告,由成員來發送報告來加入組播組。
-
0x17 離組報文,成員通過發送離組報文,離開組播組。
構建成員關系報告報文
scapy在構建IGMPv2的組播時,使用的模塊是scapy.contrib.igmp,我們需要引入此模塊,除了igmp協議外,我們還需要封裝其他網絡層,所以需要引入其他scapy模塊
>>> from scapy.contrib.igmp import *
>>> from scapy.all import *
構造IGMPv2的加組報文,IP層,需要指定目的地址為要加入的組播地址,源地址指定為本機IP地址,此時要換算type,換算成10進制的整數。例如0x16換算后為22
>>> int(0x16)
22
構造后的報文如下:
IP(src="10.4.0.3", dst="224.3.0.1") / IGMP(type=22, gaddr="224.3.0.1")
可通過對報文的show()方法查看報文的簡單格式例如:
>>> p=IP(src="10.4.0.3", dst="224.3.0.1") / IGMP(type=22, gaddr="224.3.0.1")
>>> p.show()
###[ IP ]###
version = 4
ihl = None
tos = 0x0
len = None
id = 1
flags =
frag = 0
ttl = 1
proto = igmp
chksum = None
src = 10.4.0.3
dst = 224.3.0.1
\options \
###[ IGMP ]###
type = Version 2 - Membership Report
mrcode = 20
chksum = None
gaddr = 224.3.0.1
也可以添加以太頭,便于查看整個幀的格式,scapy會自動計算mac地址:
>>> q=Ether()/p
>>> q.show()
###[ Ethernet ]###
dst = 01:00:5e:03:00:01
src = fa:16:3e:3b:38:91
type = IPv4
###[ IP ]###
version = 4
ihl = None
tos = 0x0
len = None
id = 1
flags =
frag = 0
ttl = 1
proto = igmp
chksum = None
src = 10.4.0.3
dst = 224.3.0.1
\options \
###[ IGMP ]###
type = Version 2 - Membership Report
mrcode = 20
chksum = None
gaddr = 224.3.0.1
發送成員關系報告報文并通過tcpdump跟蹤報文過程
通過tcpdump在云主機中持續抓包(也可以抓取后,通過wireshark等工具分析報文)
# tcpdump -nvveeppi eth0 igmp
發送組播關系報告
>>> sendp(q)
.
Sent 1 packets.
抓到的報文如下:
19:31:55.188578 fa:16:3e:3b:38:91 > 01:00:5e:03:00:01, ethertype IPv4 (0x0800), length 42: (tos 0x0, ttl 1, id 1, offset 0, flags [none], proto IGMP (2), length 28)
10.4.0.3 > 224.3.0.1: igmp v2 report 224.3.0.1
0x0000: 0100 5e03 0001 fa16 3e3b 3891 0800 4500 ..^.....>;8...E.
0x0010: 001c 0001 0000 0102 cfd4 0a04 0003 e003 ................
0x0020: 0001 1614 09e7 e003 0001
在IGMPv2類型下,天翼云組播會周期性發送general query。抓包如下:
19:35:04.232587 fa:16:3e:04:00:01 > 01:00:5e:00:00:01, ethertype IPv4 (0x0800), length 46: (tos 0x0, ttl 1, id 0, offset 0, flags [none], proto IGMP (2), length 32, options (RA))
10.4.0.1 > 224.0.0.1: igmp query v2
0x0000: 0100 5e00 0001 fa16 3e04 0001 0800 4600 ..^.....>.....F.
0x0010: 0020 0000 0000 0102 3ad2 0a04 0001 e000 ........:.......
0x0020: 0001 9404 0000 1164 ee9b 0000 0000 .......d......
此時在組播控制臺,點擊組播域的組播轉發展示,可以看到出現了組播組,能夠展示動態組的詳細信息:
-
組播地址
-
組播成員
構建離組報文
構造IGMPv2的離組報文,IP層,需要指定目的地址為224.0.0.2,此地址為所有組播路由器的地址,源地址指定為本機IP地址,此時要換算type,換算成10進制的整數。例如0x17換算后為23
在使用中,天翼云組播對IP層的目的地址并不會強制校驗224.0.0.2,例如使用要離組的組播地址也可以。
>>> int(0x17)
23
構造后的報文如下:
IP(src="10.4.0.3", dst="224.0.0.2") / IGMP(type=23, gaddr="224.3.0.1")
可通過對報文的show()方法查看報文的簡單格式例如:
>>> p=IP(src="10.4.0.3", dst="224.0.0.2") / IGMP(type=23, gaddr="224.3.0.1")
>>> p.show()
###[ IP ]###
version = 4
ihl = None
tos = 0x0
len = None
id = 1
flags =
frag = 0
ttl = 1
proto = igmp
chksum = None
src = 10.4.0.3
dst = 224.0.0.2
\options \
###[ IGMP ]###
type = Leave Group
mrcode = 20
chksum = None
gaddr = 224.3.0.1
也可以添加以太頭,便于查看整個幀的格式,scapy會自動計算mac地址:
>>> q=Ether()/p
>>> q.show()
###[ Ethernet ]###
dst = 01:00:5e:00:00:02
src = fa:16:3e:3b:38:91
type = IPv4
###[ IP ]###
version = 4
ihl = None
tos = 0x0
len = None
id = 1
flags =
frag = 0
ttl = 1
proto = igmp
chksum = None
src = 10.4.0.3
dst = 224.0.0.2
\options \
###[ IGMP ]###
type = Leave Group
mrcode = 20
chksum = None
gaddr = 224.3.0.1
發送離組報文并通過tcpdump跟蹤報文過程
發送組播關系報告
>>> sendp(q)
.
Sent 1 packets.
抓到的報文如下:
19:44:30.610564 fa:16:3e:3b:38:91 > 01:00:5e:00:00:02, ethertype IPv4 (0x0800), length 42: (tos 0x0, ttl 1, id 1, offset 0, flags [none], proto IGMP (2), length 28)
10.4.0.3 > 224.0.0.2: igmp leave 224.3.0.1
0x0000: 0100 5e00 0002 fa16 3e3b 3891 0800 4500 ..^.....>;8...E.
0x0010: 001c 0001 0000 0102 cfd6 0a04 0003 e000 ................
0x0020: 0002 1714 08e7 e003 0001
此時在組播控制臺,點擊組播域的組播轉發展示,可以看到組播組已經自動刪除(由于只有一個組播成員,所以作為最后一個組成員,離組后,整個組就會刪除)。
IGMPv3
對于IGMPv3 報文,消息類型減少到了2個,查詢報文和成員關系報告報文,而在成員關系報告報文中,會攜帶多個組記錄字段,每個組記錄字段單獨配置記錄類型來控制加組和離組,以及更多的功能
Type
-
0x11 查詢報文類型,通常由組播路由器來觸發,查詢組播成員。在天翼云的動態組播中,也會由特定的組件來發送查詢報文。
-
0x22 成員關系報告。
構建成員關系報告報文
報文格式如下,IGMP頭中主要字段下放到了各自的組記錄中:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type = 0x22 | Reserved | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Reserved | Number of Group Records (M) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
. .
. Group Record [1] .
. .
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
. .
. Group Record [2] .
. .
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| . |
. . .
| . |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
. .
. Group Record [M] .
. .
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
scapy在構建IGMPv3的組播時,使用的模塊是scapy.contrib.igmpv3,我們需要引入此模塊,除了igmp協議外,我們還需要封裝其他網絡層,所以需要引入其他scapy模塊
>>> from scapy.contrib.igmpv3 import *
>>> from scapy.all import *
構造IGMPv3的成員關系報告報文,IP層,需要指定目的地址,RFC要求為`224.0.0.22,源地址指定為本機IP地址。構造后的基本的報文如下,其中IGMPv3mr為字段'Number of Group Records',在本文中,只指定一個即可:
IP(src="10.4.0.3", dst="224.0.0.22") / IGMP() / IGMPv3mr(numgrp=1)
接下來看下組記錄的報文格式,IGMPv3為了支持 SSM 模型,在報文中能夠攜帶組播源信息,使組播成員能指定源加入組播組:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Record Type | Aux Data Len | Number of Sources (N) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Multicast Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address [1] |
+- -+
| Source Address [2] |
+- -+
. . .
. . .
. . .
+- -+
| Source Address [N] |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
. .
. Auxiliary Data .
. .
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
而通過記錄類型和指定的源地址,可以組合多種加入,離開組播組的報文格式
Record Type
| 類別 | 記錄類型 | 說明 |
| 當前狀態記錄(Current-State Record):用來響應查詢報文。成員用來通告自己當前的狀態。 |
MODE_IS_INCLUDE |
此組播在過濾器會被指定為INCLUDE模式,且組中的源地址 [i] 字段記錄包含接口可接受的組播源。 如果源列表是空的,不會加組,并會觸發離組 |
| MODE_IS_EXCLUDE |
此組播會在過濾器會被指定為EXCLUDE模式,且組中的源地址 [i] 字段記錄包含接口不可接受的源列表。 如果源列表是空的,說明可通配接受所有的源,這個報文會觸發加組 |
|
| 過濾器模式更改記錄(Filter-Mode-Change Record):成員通過這個類型的報文來修改對應的過濾器模式 | CHANGE_TO_INCLUDE_MODE |
如果原來過濾器中成員對組播組和組播源的對應關系,例如如果 攜帶的源IP和原EXCLUDE的源IP相同,會直接改變源IP和組的對應關系,變成INCLUDE。 如果源列表是空的,說明,不允許任何源IP,會觸發離組 |
| CHANGE_TO_EXCLUDE_MODE |
如果原來過濾器中成員對組播組和組播源的對應關系,例如如果 攜帶的源IP和原INCLUDE的源IP相同,會直接改變源IP和組的對應關系,變成EXCLUDE。 如果源列表是空的,說明,不會過濾任何源IP,會通配所有地址 |
|
| 源-列表-更改記錄(Source-List-Change Record):成員通過這個類型的報文不能修改過濾器的狀態,但如何類型和過濾器模式不同,但是源ip重合時,可以對沖源ip。 | ALLOW_NEW_SOURCES | 匹配的過濾模式為INCLUDE,攜帶的源IP是期待接收到這些源的組播數據包。所以針對INCLUDE的過濾器狀態,增加這些源地址。如果是EXCLUDE的狀態組記錄,則會從列表中刪除這些地址,但是不會改變EXCLUDE的狀態。所以如果最后對沖了所有的源ip,導致源ip為空,等效于通配允許所有的源地址。 |
| BLOCK_OLD_SOURCES | 匹配的過濾模式為EXCLUDE,攜帶的源IP是拒絕接收從這些源發出的組播數據包。所以針對EXCLUDE的過濾器狀態,在列表中增加這些源地址。如果是INCLUDE的狀態組記錄,則會從列表中刪除這些地址,但是不會改變EXCLUDE的狀態。所以如果最后對沖了所有的源ip,導致源ip為空,等效于離組。 |
構造報文
本文僅以MODE_IS_INCLUDE和MODE_IS_EXCLUDE作為示例。
構造單個組記錄
通過scapy封裝的MODE_IS_INCLUDE的報文如下,例如一個組記錄,源IP列表由srcaddrs來指定,組播地址由maddr指定:
IP(src="10.4.0.3", dst="224.0.0.22") / IGMPv3() / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype=1, maddr="238.0.3.1", srcaddrs=["10.4.1.3", "10.4.2.3"])
可通過對報文的show()方法查看報文的簡單格式例如:
>>> q=Ether()/IP(src="10.4.0.3", dst="224.0.0.22") / IGMPv3() / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype=1, maddr="238.0.3.1", srcaddrs=["10.4.1.3", "10.4.2.3"])
>>> q.show()
###[ Ethernet ]###
dst = 01:00:5e:00:00:16
src = fa:16:3e:3b:38:91
type = IPv4
###[ IP ]###
version = 4
ihl = None
tos = 0xc0
len = None
id = 1
flags =
frag = 0
ttl = 1
proto = igmp
chksum = None
src = 10.4.0.3
dst = 224.0.0.22
\options \
###[ IGMPv3 ]###
type = Version 3 Membership Report
mrcode = 0
chksum = None
###[ IGMPv3mr ]###
res2 = 0x0
numgrp = 1
\records \
###[ IGMPv3gr ]###
rtype = Mode Is Include
auxdlen = 0
numsrc = None
maddr = 238.0.3.1
srcaddrs = [10.4.1.3, 10.4.2.3]
?
發送組播關系報告
>>> sendp(q)
.
Sent 1 packets.
抓到的報文如下:
21:28:03.237761 fa:16:3e:3b:38:91 > 01:00:5e:00:00:16, ethertype IPv4 (0x0800), length 58: (tos 0xc0, ttl 1, id 1, offset 0, flags [none], proto IGMP (2), length 44)
10.4.0.3 > 224.0.0.22: igmp v3 report, 1 group record(s) [gaddr 238.0.3.1 is_in { 10.4.1.3 10.4.2.3 }]
0x0000: 0100 5e00 0016 fa16 3e3b 3891 0800 45c0 ..^.....>;8...E.
0x0010: 002c 0001 0000 0102 cef2 0a04 0003 e000 .,..............
0x0020: 0016 2200 d4ec 0000 0001 0100 0002 ee00 ..".............
0x0030: 0301 0a04 0103 0a04 0203 ..........
另天翼云默認的query報文只由general query。不會發送特定query報文,抓包如下:
21:30:04.659276 fa:16:3e:04:00:01 > 01:00:5e:00:00:01, ethertype IPv4 (0x0800), length 50: (tos 0x0, ttl 1, id 0, offset 0, flags [none], proto IGMP (2), length 36, options (RA))
10.4.0.1 > 224.0.0.1: igmp query v3
0x0000: 0100 5e00 0001 fa16 3e04 0001 0800 4600 ..^.....>.....F.
0x0010: 0024 0000 0000 0102 3ace 0a04 0001 e000 .$......:.......
0x0020: 0001 9404 0000 1164 ee9b 0000 0000 0000 .......d........
0x0030: 0000
構造多個組記錄
添加多個組記錄,一個MODE_IS_INCLUDE,一個MODE_IS_EXCLUDE如下:
IP(src="10.4.0.3", dst="224.0.0.22") / IGMPv3() / IGMPv3mr(numgrp=2) / IGMPv3gr(rtype=1, maddr="238.0.3.1", srcaddrs=["10.4.1.3", "10.4.2.3"]) / IGMPv3gr(rtype=2, maddr="238.0.3.2", srcaddrs=["10.4.1.3", "10.4.2.3"])
可通過對報文的show()方法查看報文的簡單格式例如:
>>> q=Ether()/IP(src="10.4.0.3", dst="224.0.0.22") / IGMPv3() / IGMPv3mr(numgrp=2) / IGMPv3gr(rtype=1, maddr="238.0.3.1", srcaddrs=["10.4.1.3", "10.4.2.3"]) / IGMPv3gr(rtype=2, maddr="238.0.3.2", srcaddrs=["10.4.1.3", "10.4.2.3"])
>>> q.show()
###[ Ethernet ]###
dst = 01:00:5e:00:00:16
src = fa:16:3e:3b:38:91
type = IPv4
###[ IP ]###
version = 4
ihl = None
tos = 0xc0
len = None
id = 1
flags =
frag = 0
ttl = 1
proto = igmp
chksum = None
src = 10.4.0.3
dst = 224.0.0.22
\options \
###[ IGMPv3 ]###
type = Version 3 Membership Report
mrcode = 0
chksum = None
###[ IGMPv3mr ]###
res2 = 0x0
numgrp = 2
\records \
###[ IGMPv3gr ]###
rtype = Mode Is Include
auxdlen = 0
numsrc = None
maddr = 238.0.3.1
srcaddrs = [10.4.1.3, 10.4.2.3]
###[ IGMPv3gr ]###
rtype = Mode Is Exclude
auxdlen = 0
numsrc = None
maddr = 238.0.3.2
srcaddrs = [10.4.1.3, 10.4.2.3]
發送組播關系報告
>>> sendp(q)
.
Sent 1 packets.
抓到的報文如下:
21:31:42.853986 fa:16:3e:3b:38:91 > 01:00:5e:00:00:16, ethertype IPv4 (0x0800), length 74: (tos 0xc0, ttl 1, id 1, offset 0, flags [none], proto IGMP (2), length 60)
10.4.0.3 > 224.0.0.22: igmp v3 report, 2 group record(s) [gaddr 238.0.3.1 is_in { 10.4.1.3 10.4.2.3 }] [gaddr 238.0.3.2 is_ex { 10.4.1.3 10.4.2.3 }]
0x0000: 0100 5e00 0016 fa16 3e3b 3891 0800 45c0 ..^.....>;8...E.
0x0010: 003c 0001 0000 0102 cee2 0a04 0003 e000 .<..............
0x0020: 0016 2200 cad8 0000 0002 0100 0002 ee00 ..".............
0x0030: 0301 0a04 0103 0a04 0203 0200 0002 ee00 ................
0x0040: 0302 0a04 0103 0a04 0203
此時在組播控制臺,點擊組播域的組播轉發展示,可以看到出現了組播組,能夠展示動態組的詳細信息:
-
組播地址
-
組播成員
通過構造成INCLUDE的模式,并源列表為空時,會觸發離組
那么由下列幾種方式
-
在任意過濾器模式下,構造MODE_IS_INCLUDE,源列表為空。
-
在任意過濾器模式下,構造CHANGE_TO_INCLUDE_MODE,源列表為空。
-
在INCLUDE過濾器模式下,構造BLOCK_OLD_SOURCES,源列表和當前組對應成員的源列表相同。
在上述幾種方式離組后,此時在組播控制臺,點擊組播域的組播轉發展示,可以看到組播組已經自動刪除(由于只有一個組播成員,所以作為最后一個組成員,離組后,整個組就會刪除)。