Mininet 上启用 DCTCP 和 ECN 进行拥塞控制、使用 HTB 和 RED 队列进行流量控制

本文详细介绍了如何在Mininet环境中配置DCTCP和ECN,包括主机和交换机的设置步骤。首先,主机启用DCTCP和ECN功能,然后通过清理旧的QoS规则和进程,对交换机端口应用HTB+RED+ECN的qdisc配置。通过示例代码展示了如何构建网络拓扑并进行配置,最后进行了简单的测试以验证ECN标记的有效性。测试结果显示,ECN在队列过长时能够成功标记数据包,实现流量控制。
摘要由CSDN通过智能技术生成

关于 TC 流量控制工具详解可看这篇博客

开启 DCTCP,使用 ECN 进行拥塞控制,需要主机和交换机的配合。

主机要开启 ECN 功能,能处理 ECN 信号;交换机要能在拥塞队列过长时进行 ECN 标记。部署前还需要先清空系统中可能存在的残留qos规则、进程等。

主机可直接调用内核命令启动 DCTCP和 ECN,交换机网口上使用 HTB 队列进行限速,RED 队列进行 ECN标记。

部署流程

可在构建 Mininet 拓扑时进行部署,即对交换机、主机的配置都写在 Mininet 拓扑文件中。

网络拓扑启动前,开启主机上的dctcp,随后对于在该主机上运行的所有主机都会默认启动。Linux 内核从 4.1 版本开始支持DCTCP,建议使用4.4或更高版本。

os.system("sysctl -w net.ipv4.tcp_congestion_control=dctcp")

在进行交换机配置之前,考虑先清空原有的配置和进程。

def clean_all():
    os.system("sudo ovs-vsctl --all destroy QoS")
    os.system("sudo ovs-vsctl --all destroy Queue")
    os.system("sudo killall iperf3")
    os.system("sudo killall iperf")
    os.system("sudo killall sockperf")
    
def clean_qdisc(eth):
    os.system("sudo tc qdisc del dev %s root handle 1:" % eth)

对所有交换机端口进行qdisc配置:

def set_qdisc(eth):
    os.system("tc qdisc add dev %s root handle 1: htb default 1" % eth)
    os.system("tc class add dev %s parent 1: classid 1:1 htb rate 100mbit burst 150kb" % eth)
    os.system("tc qdisc add dev %s parent 1:1 handle 2: red limit 400000 min 60000 max 90000 avpkt 1000 burst 70 ecn probability 0.2 bandwidth 100Mbit" % eth)
    os.system("tc filter add dev %s protocol ip parent 1: prio 0 u32 match ip src 0.0.0.0/0 flowid 1:1" % eth)

在网络构建结束后即可对交换机和主机进行配置。

# set switch
clean_all()
for switch in net.switches:   # 对所有 switch
  for intf in switch.intfList():    # 按端口号排序的网口列表。
    inter_face = str(intf)
    if inter_face != "lo":   # 除本地网口外
      print(inter_face)
      # clean_qdisc(inter_face)
      set_qdisc(inter_face)

如果由于其他故障没有启动DCTCP,又要使用 ECN,则需要对所有主机开启 ECN 功能。

# set host start ecn without dctcp            
for host in net.hosts:
   conmand = host.popen("sysctl net.ipv4.tcp_ecn=1")

示例代码

from mininet.net import Mininet
from mininet.node import Controller, RemoteController, OVSController
from mininet.node import CPULimitedHost, Host, Node
from mininet.node import OVSKernelSwitch, UserSwitch
from mininet.node import IVSSwitch
from mininet.cli import CLI
from mininet.log import setLogLevel, info
from mininet.link import TCLink, Intf
from subprocess import call
import os

def myNetwork():
    # start dctcp
    os.system("sysctl -w net.ipv4.tcp_congestion_control=dctcp")

    net = Mininet( topo=None,
                   build=False,
                   ipBase='10.0.0.0/8')

    info( '*** Adding controller\n' )
    c0=net.addController(name='c0',
                      controller=RemoteController,
                      ip='127.0.0.1',
                      protocol='tcp',
                      port=6633)

    info( '*** Add switches\n')
    s1 = net.addSwitch('s1', cls=OVSKernelSwitch, mac='00:00:00:00:00:11', dpid='0000000000000001')
    s2 = net.addSwitch('s2', cls=OVSKernelSwitch, mac='00:00:00:00:00:12', dpid='0000000000000002')
    s3 = net.addSwitch('s3', cls=OVSKernelSwitch, mac='00:00:00:00:00:13', dpid='0000000000000003')
    s4 = net.addSwitch('s4', cls=OVSKernelSwitch, mac='00:00:00:00:00:14', dpid='0000000000000004')
    s5 = net.addSwitch('s5', cls=OVSKernelSwitch, mac='00:00:00:00:00:15', dpid='0000000000000005')

    info( '*** Add hosts\n')
    h1 = net.addHost('h1', cls=Host, ip='10.0.0.1/24', mac='00:00:00:00:00:01', defaultRoute=None)
    h2 = net.addHost('h2', cls=Host, ip='10.0.0.2/24', mac='00:00:00:00:00:02', defaultRoute=None)
    h3 = net.addHost('h3', cls=Host, ip='10.0.0.3/24', mac='00:00:00:00:00:03', defaultRoute=None)
    h4 = net.addHost('h4', cls=Host, ip='10.0.0.4/24', mac='00:00:00:00:00:04', defaultRoute=None)
    h5 = net.addHost('h5', cls=Host, ip='10.0.0.5/24', mac='00:00:00:00:00:05', defaultRoute=None)

    info( '*** Add links\n')
    net.addLink(s1, h1)
    net.addLink(s1, h2)
    net.addLink(s2, h3)
    net.addLink(s3, h4)
    net.addLink(s5, h5)
    net.addLink(s1, s2)
    net.addLink(s2, s4)
    net.addLink(s3, s4)
    net.addLink(s4, s5)

    info( '*** Starting network\n')
    net.build()
    info( '*** Starting controllers\n')
    for controller in net.controllers:
        controller.start()

    info( '*** Starting switches\n')
    net.get('s1').start([c0])
    net.get('s2').start([c0])
    net.get('s3').start([c0])
    net.get('s4').start([c0])
    net.get('s5').start([c0])

    info( '*** Post configure switches and hosts\n')
    # set switch
    clean_all()
    for switch in net.switches:
        for intf in switch.intfList():
            inter_face = str(intf)
            if inter_face != "lo":
                # print(inter_face)
                # clean_qdisc(inter_face)
                set_qdisc(inter_face)
    '''
    # set host start ecn if without dctcp            
    for host in net.hosts:
        conmand = host.popen("sysctl net.ipv4.tcp_ecn=1")
    '''    

    CLI(net)
    net.stop()
    
def set_qdisc(eth):
    os.system("tc qdisc add dev %s root handle 1: htb default 1" % eth)
    os.system("tc class add dev %s parent 1: classid 1:1 htb rate 500mbit burst 150kb" % eth)
    os.system("tc qdisc add dev %s parent 1:1 handle 2: red limit 400000 min 60000 max 90000 avpkt 1000 burst 70 ecn probability 0.2 bandwidth 500Mbit" % eth)
    os.system("tc filter add dev %s protocol ip parent 1: prio 0 u32 match ip src 0.0.0.0/0 flowid 1:1" % eth)

def clean_qdisc(eth):
    os.system("sudo tc qdisc del dev %s root handle 1:" % eth)
    
def clean_all():
    os.system("sudo ovs-vsctl --all destroy QoS")
    os.system("sudo ovs-vsctl --all destroy Queue")
    os.system("sudo killall iperf3")
    os.system("sudo killall iperf")
    os.system("sudo killall sockperf")
    
if __name__ == '__main__':
    setLogLevel( 'info' )
    myNetwork()

此代码构建的拓扑连接如下:

mininet> links
s1-eth1<->h1-eth0 (OK OK)
s1-eth2<->h2-eth0 (OK OK)
s2-eth1<->h3-eth0 (OK OK)
s3-eth1<->h4-eth0 (OK OK)
s5-eth1<->h5-eth0 (OK OK)
s1-eth3<->s2-eth2 (OK OK)
s2-eth3<->s4-eth1 (OK OK)
s3-eth2<->s4-eth2 (OK OK)
s4-eth3<->s5-eth2 (OK OK)

拓扑图如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oU7b6Abt-1649133887139)(https://secure2.wostatic.cn/static/w9n3EJb13ZFZcdb9NruV2w/topotest.png)]

简单测试

使用xterm,h5作为服务端。

iperf -s -p 5566 -i 1

h1、h2、h3、h4作为客户端向h5打流。

iperf -c 10.0.0.5 -p 5566 -i 1 -t 20

wireshark 可抓到 ECN 标记的包。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iDkaN0A7-1649133887141)(https://secure2.wostatic.cn/static/2HQu8Fn6nq2v8hDajnDGm/image.png)]

终端使用命令查看所有网口qdisc详情:

tc -s -d qdisc show
  • 点击展开RED qdisc详情
qdisc red 2: dev s1-eth1 parent 1:1 limit 400000b min 60000b max 90000b ecn ewma 4 probability 0.2 Scell_log 11
 Sent 402722 bytes 5980 pkt (dropped 0, overlimits 0 requeues 0) 
 backlog 0b 0p requeues 0
  marked 0 early 0 pdrop 0 other 0 

qdisc red 2: dev s1-eth2 parent 1:1 limit 400000b min 60000b max 90000b ecn ewma 4 probability 0.2 Scell_log 11
 Sent 344648 bytes 5103 pkt (dropped 0, overlimits 0 requeues 0) 
 backlog 0b 0p requeues 0
  marked 0 early 0 pdrop 0 other 0 

qdisc red 2: dev s2-eth1 parent 1:1 limit 400000b min 60000b max 90000b ecn ewma 4 probability 0.2 Scell_log 11
 Sent 408978 bytes 6078 pkt (dropped 0, overlimits 0 requeues 0) 
 backlog 0b 0p requeues 0
  marked 0 early 0 pdrop 0 other 0 

qdisc red 2: dev s3-eth1 parent 1:1 limit 400000b min 60000b max 90000b ecn ewma 4 probability 0.2 Scell_log 11
 Sent 396012 bytes 5881 pkt (dropped 0, overlimits 0 requeues 0) 
 backlog 0b 0p requeues 0
  marked 0 early 0 pdrop 0 other 0 

qdisc red 2: dev s5-eth1 parent 1:1 limit 400000b min 60000b max 90000b ecn ewma 4 probability 0.2 Scell_log 11
 Sent 204709048 bytes 135409 pkt (dropped 0, overlimits 2 requeues 0) 
 backlog 0b 0p requeues 0
  marked 2 early 0 pdrop 0 other 0 

qdisc red 2: dev s2-eth2 parent 1:1 limit 400000b min 60000b max 90000b ecn ewma 4 probability 0.2 Scell_log 11
 Sent 722564 bytes 10850 pkt (dropped 0, overlimits 0 requeues 0) 
 backlog 0b 0p requeues 0
  marked 0 early 0 pdrop 0 other 0 

qdisc red 2: dev s1-eth3 parent 1:1 limit 400000b min 60000b max 90000b ecn ewma 4 probability 0.2 Scell_log 11
 Sent 82173492 bytes 54333 pkt (dropped 0, overlimits 16 requeues 0) 
 backlog 0b 0p requeues 0
  marked 16 early 0 pdrop 0 other 0 

qdisc red 2: dev s4-eth1 parent 1:1 limit 400000b min 60000b max 90000b ecn ewma 4 probability 0.2 Scell_log 11
 Sent 1106740 bytes 16695 pkt (dropped 0, overlimits 0 requeues 0) 
 backlog 0b 0p requeues 0
  marked 0 early 0 pdrop 0 other 0 

qdisc red 2: dev s2-eth3 parent 1:1 limit 400000b min 60000b max 90000b ecn ewma 4 probability 0.2 Scell_log 11
 Sent 181673872 bytes 120098 pkt (dropped 0, overlimits 0 requeues 0) 
 backlog 0b 0p requeues 0
  marked 0 early 0 pdrop 0 other 0 

qdisc red 2: dev s4-eth2 parent 1:1 limit 400000b min 60000b max 90000b ecn ewma 4 probability 0.2 Scell_log 11
 Sent 392674 bytes 5853 pkt (dropped 0, overlimits 0 requeues 0) 
 backlog 0b 0p requeues 0
  marked 0 early 0 pdrop 0 other 0 

qdisc red 2: dev s3-eth2 parent 1:1 limit 400000b min 60000b max 90000b ecn ewma 4 probability 0.2 Scell_log 11
 Sent 23028434 bytes 15254 pkt (dropped 0, overlimits 0 requeues 0) 
 backlog 0b 0p requeues 0
  marked 0 early 0 pdrop 0 other 0 

qdisc red 2: dev s5-eth2 parent 1:1 limit 400000b min 60000b max 90000b ecn ewma 4 probability 0.2 Scell_log 11
 Sent 1474440 bytes 22313 pkt (dropped 0, overlimits 0 requeues 0) 
 backlog 0b 0p requeues 0
  marked 0 early 0 pdrop 0 other 0 

qdisc red 2: dev s4-eth3 parent 1:1 limit 400000b min 60000b max 90000b ecn ewma 4 probability 0.2 Scell_log 11
 Sent 204705710 bytes 135381 pkt (dropped 0, overlimits 173 requeues 0) 
 backlog 0b 0p requeues 0
  marked 173 early 0 pdrop 0 other 0 

统计发生ECN标记的网口:

网口ECN标记数
s1-eth316
s4-eth3173
s5-eth12

可以看到在瓶颈链路上由于队列过长,ECN标记生效。

实现细节

主机开启 DCTCP/ECN 功能

sudo sysctl net.ipv4.tcp_congestion_control=dctcp
sudo sysctl net.ipv4.tcp_ecn=1
sudo sysctl net.ipv4.tcp_ecn_fallback=0

  • tcp_congestion_control=dctcp:将Linux默认拥塞控制改为DCTCP。实际上只要开启了DCTCP,就会自动启动 ECN,无需再使用第二条命令。注意低版本的Linux 内核可能没有 dctcp 功能。
  • tcp_ecn:0表示关闭ECN功能,既不会初始化也不会接受ECN,1表示主动连接和被动连接时候都会尝试使能ECN,2表示主动连接时候不会使能ECN,被动连接的时候会尝试使能ECN
  • tcp_ecn_fallback:可选,这个参数设置为非0时,如果内核侦测到ECN的错误行为,就会关闭ECN功能。 这个参数实际上是控制后向兼容的一个参数,TCP建立连接的时候需要进行ECN协商过程,SYN报文中需要同时设置CWR和ECE标志位,如果tcp_ecn_fallback设置为非0,那么重传SYN报文的时候就会取消CWR和ECE标志的设置。

如果是在一台Ubuntu 主机使用 Mininet 创建拓扑,则只需原主机开启了 DCTCP,Mininet 中的所有虚拟主机就能与原主机保持一致。

Linux 内核从 4.1 版本开始支持DCTCP,建议使用4.4或更高版本。

如果不开启DCTCP,或者由于某些故障开启失败,可以仅在主机开启 ECN功能,这需要对每个虚拟主机运行第二条命令。

交换机队列 qdisc 配置:HTB+RED+ECN

此处使用Linux 流量控制工具 TC 进行 qdisc(队列规则)配置。其中 s1-eth2 为交换机网口,根据实际替换名称。

tc qdisc add dev s1-eth2 root handle 1: htb
tc class add dev s1-eth2 parent 1: classid 1:1 htb rate 100mbit ceil 100mbit burst 150kb
tc qdisc add dev s1-eth2 parent 1:1 handle 2: red limit 400000 min 60000 max 90000 avpkt 1000 burst 70 ecn probability 0.5 bandwidth 100Mbit
tc filter add dev s1-eth2 protocol ip parent 1: prio 0 u32 match ip src 0.0.0.0/0 flowid 1:1

第一行建立根队列,句柄为 1,使用 HTB 队列。

第二行建立根的子类,句柄为 1:1rate 为保证带宽,ceil 为最大带宽,这两个相等即限制网口带宽。burst 为rate 桶大小,即最大突发流量。

第三行在 1:1 类下变更默认 qdisc 为 RED 队列。limit 为真实队列长度硬性限制,超过即丢包。ecn 参数表示将随机丢包改为随机标记。队列长度在 minmax 之间,即开始随机标记,标记概率线性增加,到 max 为最大值 probabilityavpkt 为计算时用到的时间常数,建议值 1000burst 即允许的突发流量大小,经验值 (min+min+max)/(3*avpkt)bandwidth 设置为网口的带宽。

第四行添加过滤器,使用 u32 分类器让所有来源的 IP 数据包都走1:1类。

RED 详细参数

tc  qdisc ... red  limit BYTES [min BYTES] [max BYTES] avpkt BYTES [burst PACKETS] [probability PROBABILITY] [bandwidth KBPS] [ecn] [adaptive] [harddrop]

参数:

  • limit:真实队列的硬性限制,单位为字节,与 RED 算法无关。应当设置为大于最大队列长度与突发报文之和max + burst,超过此值后,接收到的报文都将被丢弃。RED正常工作的情况下,队列长度不会达到此值。
  • min:单位为字节。开始进行标记的平均队列大小,默认为 max/3
  • max:单位为字节。平均队列大小达到该值后,标记的概率达到最大(probability)。为了避免同步重传,应该至少是min的两倍,且大于最小的min值,默认为limit/4
  • Avpkt:单位是字节,与burst一起确定平均队列长度avg的时间常数。建议值1000(MTU为1500时)。
  • burst:桶大小,决定了计算平均队列长度avg在多大程度上受到实际队列长度的影响。较大的值会减慢avg更新的速度,从而允许在标记开始之前出现更多的突发流量。实际经验遵循(min+min+max)/(3*avpkt)
  • probability:标记的概率的最大值(队列长达到max时),为0.0到1.0之间的浮点数,默认为0.02
  • bandwidth:该速率用于在一段空闲时间之后计算平均队列的大小,设置为接口的带宽,但不会进行整流。默认值为10Mbit
  • ecn:允许RED对支持ECN的主机以报文标记的形式通知拥塞(除非队列达到limit的字节数),推荐使用。
  • harddrop: 如果平均队列长度大于max字节数,则强制丢弃报文,而不是进行ECN标记。
  • adaptive:Adaptive-RED算法,旨在通过动态调整probability的值(范围:[0.01,0.50]),将队列长度维持在 (max-min)/2
参数设置

通过计算最高可接受的基本排队延迟来设置min,并将其乘以带宽。例如,在 64kbit/s ISDN 链路上,想要 200 毫秒的基本排队延迟,因此将min设置为 1600 字节:

64 × 200 / 8 = 1600 64×200/8=1600 64×200/8=1600)。

设置 min 太小会降低吞吐量,太大会降低延迟。设置较小的最小值并不能替代降低慢速链路上的 MTU 以改善交互响应。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值