searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

通过SCAPY实践天翼云组播

2023-10-13 05:50:37
187
0

概要

目前天翼云已在部分资源池支持了组播的能力。有需要组播业务上云,可以通过联系天翼云来开通组播功能。本文基于天翼云的云内组播产品和scapy来学习和实践IGMP协议。

前提条件

  1. 需要在天翼云上支持组播的资源池上准备一个VPC。

  2. 在VPC内创建至少一个linux云主机。

  3. 云主机的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:

  1. 0x11 查询报文类型,通常由组播路由器来触发,查询组播成员。在天翼云的动态组播中,也会由特定的组件来发送查询报文。

  2. 0x16 成员关系报告,由成员来发送报告来加入组播组。

  3. 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......

此时在组播控制台,点击组播域的组播转发展示,可以看到出现了组播组,能够展示动态组的详细信息:

  1. 组播地址

  2. 组播成员

构建离组报文

构造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

  1. 0x11 查询报文类型,通常由组播路由器来触发,查询组播成员。在天翼云的动态组播中,也会由特定的组件来发送查询报文。

  2. 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  

此时在组播控制台,点击组播域的组播转发展示,可以看到出现了组播组,能够展示动态组的详细信息:

  1. 组播地址

  2. 组播成员

通过构造成INCLUDE的模式,并源列表为空时,会触发离组

那么由下列几种方式

  1. 在任意过滤器模式下,构造MODE_IS_INCLUDE,源列表为空。

  2. 在任意过滤器模式下,构造CHANGE_TO_INCLUDE_MODE,源列表为空。

  3. 在INCLUDE过滤器模式下,构造BLOCK_OLD_SOURCES,源列表和当前组对应成员的源列表相同。

在上述几种方式离组后,此时在组播控制台,点击组播域的组播转发展示,可以看到组播组已经自动删除(由于只有一个组播成员,所以作为最后一个组成员,离组后,整个组就会删除)。

 

 
0条评论
0 / 1000
飞机马耳朵
2文章数
0粉丝数
飞机马耳朵
2 文章 | 0 粉丝
飞机马耳朵
2文章数
0粉丝数
飞机马耳朵
2 文章 | 0 粉丝
原创

通过SCAPY实践天翼云组播

2023-10-13 05:50:37
187
0

概要

目前天翼云已在部分资源池支持了组播的能力。有需要组播业务上云,可以通过联系天翼云来开通组播功能。本文基于天翼云的云内组播产品和scapy来学习和实践IGMP协议。

前提条件

  1. 需要在天翼云上支持组播的资源池上准备一个VPC。

  2. 在VPC内创建至少一个linux云主机。

  3. 云主机的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:

  1. 0x11 查询报文类型,通常由组播路由器来触发,查询组播成员。在天翼云的动态组播中,也会由特定的组件来发送查询报文。

  2. 0x16 成员关系报告,由成员来发送报告来加入组播组。

  3. 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......

此时在组播控制台,点击组播域的组播转发展示,可以看到出现了组播组,能够展示动态组的详细信息:

  1. 组播地址

  2. 组播成员

构建离组报文

构造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

  1. 0x11 查询报文类型,通常由组播路由器来触发,查询组播成员。在天翼云的动态组播中,也会由特定的组件来发送查询报文。

  2. 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  

此时在组播控制台,点击组播域的组播转发展示,可以看到出现了组播组,能够展示动态组的详细信息:

  1. 组播地址

  2. 组播成员

通过构造成INCLUDE的模式,并源列表为空时,会触发离组

那么由下列几种方式

  1. 在任意过滤器模式下,构造MODE_IS_INCLUDE,源列表为空。

  2. 在任意过滤器模式下,构造CHANGE_TO_INCLUDE_MODE,源列表为空。

  3. 在INCLUDE过滤器模式下,构造BLOCK_OLD_SOURCES,源列表和当前组对应成员的源列表相同。

在上述几种方式离组后,此时在组播控制台,点击组播域的组播转发展示,可以看到组播组已经自动删除(由于只有一个组播成员,所以作为最后一个组成员,离组后,整个组就会删除)。

 

 
文章来自个人专栏
云网络测试与实践
2 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
1