上一节的试验中讲到了ovs的控制管理类和流表类的简单试验(详见:)。

这一节,继续根据上节内容对ovs的匹配项和指令动作进行讲解,并讲解控制器ryu的安装和连接。

一、ryu控制器的安装

(1)安装python套件

apt install python3-eventlet
apt install python3-routes
apt install python3-webob
apt install python3-paramiko
apt install python3-pip

(2)获取ryu并安装

获取ryu源码

git clone https:///osrg/ryu.git

Android修改原生Switch 滑块大小_IP

安装ryu

cd ryu
sudo pip3 install -r tools/pip-requires
sudo python3 setup.py install

Android修改原生Switch 滑块大小_ovs_02

二、流表的匹配项和动作(match、actions)命令

(1)匹配项

  • 匹配vlan tag,范围为0-4095
ovs-ofctl add-flow br0 priority=401,in_port=1,dl_vlan=777,actions=output:2
  • 匹配vlan pcp,范围为0-7
ovs-ofctl add-flow br0 priority=401,in_port=1,dl_vlan_pcp=7,actions=output:2
  • 匹配源/目的MAC
ovs-ofctl add-flow br0 in_port=1,dl_src=00:00:00:00:00:01/00:00:00:00:00:01,actions=output:2
ovs-ofctl add-flow br0 in_port=1,dl_dst=00:00:00:00:00:01/00:00:00:00:00:01,actions=output:2
  • 匹配以太网类型,范围为0-65535
ovs-ofctl add-flow br0 in_port=1,dl_type=0x0806,actions=output:2
  • 匹配源/目的IP

条件:指定dl_type=0x0800,或者ip/tcp

ovs-ofctl add-flow br0 ip,in_port=1,nw_src=10.10.0.0/16,actions=output:2
ovs-ofctl add-flow br0 ip,in_port=1,nw_dst=10.20.0.0/16,actions=output:2
  • 匹配协议号,范围为0-255

条件:指定dl_type=0x0800或者ip

# ICMP
ovs-ofctl add-flow br0 ip,in_port=1,nw_proto=1,actions=output:2
  • 匹配IP ToS/DSCP,tos范围为0-255,DSCP范围为0-63

条件:指定dl_type=0x0800/0x86dd,并且ToS低2位会被忽略(DSCP值为ToS的高6位,并且低2位为预留位)

ovs-ofctl add-flow br0 ip,in_port=1,nw_tos=68,actions=output:2
ovs-ofctl add-flow br0 ip,in_port=1,ip_dscp=62,actions=output:2
  • 匹配IP ecn位,范围为0-3

条件:指定dl_type=0x0800/0x86dd

ovs-ofctl add-flow br0 ip,in_port=1,ip_ecn=2,actions=output:2
  • 匹配IP TTL,范围为0-255
ovs-ofctl add-flow br0 ip,in_port=1,nw_ttl=128,actions=output:2
  • 匹配tcp/udp,源/目的端口,范围为0-65535
# 匹配源tcp端口179
ovs-ofctl add-flow br0 tcp,tcp_src=179/0xfff0,actions=output:2
# 匹配目的tcp端口179
ovs-ofctl add-flow br0 tcp,tcp_dst=179/0xfff0,actions=output:2
# 匹配源udp端口1234
ovs-ofctl add-flow br0 udp,udp_src=1234/0xfff0,actions=output:2
# 匹配目的udp端口1234
ovs-ofctl add-flow br0 udp,udp_dst=1234/0xfff0,actions=output:2
  • 匹配tcp flags

tcp flags=fin,syn,rst,psh,ack,urg,ece,cwr,ns

ovs-ofctl add-flow br0 tcp,tcp_flags=ack,actions=output:2
  • 匹配icmp code,范围为0-255

条件:指定icmp

ovs-ofctl add-flow br0 icmp,icmp_code=2,actions=output:2
  • 匹配vlan TCI

TCI低12位为vlan id,高3位为priority,例如tci=0xf123则vlan_id为0x123和vlan_pcp=7

ovs-ofctl add-flow br0 in_port=1,vlan_tci=0xf123,actions=output:2
  • 匹配mpls label

条件:指定dl_type=0x8847/0x8848

ovs-ofctl add-flow br0 mpls,in_port=1,mpls_label=7,actions=output:2
  • 匹配mpls tc,范围为0-7

条件:指定dl_type=0x8847/0x8848

ovs-ofctl add-flow br0 mpls,in_port=1,mpls_tc=7,actions=output:2
  • 匹配tunnel id,源/目的IP
# 匹配tunnel id
ovs-ofctl add-flow br0 in_port=1,tun_id=0x7/0xf,actions=output:2
# 匹配tunnel源IP
ovs-ofctl add-flow br0 in_port=1,tun_src=192.168.1.0/255.255.255.0,actions=output:2
# 匹配tunnel目的IP
ovs-ofctl add-flow br0 in_port=1,tun_dst=192.168.1.0/255.255.255.0,actions=output:2

一些匹配项的速记符

速记符

匹配项

ip

dl_type=0x800

ipv6

dl_type=0x86dd

icmp

dl_type=0x0800,nw_proto=1

icmp6

dl_type=0x86dd,nw_proto=58

tcp

dl_type=0x0800,nw_proto=6

tcp6

dl_type=0x86dd,nw_proto=6

udp

dl_type=0x0800,nw_proto=17

udp6

dl_type=0x86dd,nw_proto=17

sctp

dl_type=0x0800,nw_proto=132

sctp6

dl_type=0x86dd,nw_proto=132

arp

dl_type=0x0806

rarp

dl_type=0x8035

mpls

dl_type=0x8847

mplsm

dl_type=0x8848

(2)指令动作

  • 动作为出接口

从指定接口转发出去

ovs-ofctl add-flow br0 in_port=1,actions=output:2
  • 动作为指定group

group id为已创建的group table

ovs-ofctl add-flow br0 in_port=1,actions=group:666
  • 动作为normal

转为L2/L3处理流程

ovs-ofctl add-flow br0 in_port=1,actions=normal
  • 动作为flood

从所有物理接口转发出去,除了入接口和已关闭flooding的接口

ovs-ofctl add-flow br0 in_port=1,actions=flood
  • 动作为all

从所有物理接口转发出去,除了入接口

ovs-ofctl add-flow br0 in_port=1,actions=all
  • 动作为local

一般是转发给本地网桥

ovs-ofctl add-flow br0 in_port=1,actions=local
  • 动作为in_port

从入接口转发回去

ovs-ofctl add-flow br0 in_port=1,actions=in_port
  • 动作为controller

以packet-in消息上送给控制器

ovs-ofctl add-flow br0 in_port=1,actions=controller
  • 动作为drop

丢弃数据包操作

ovs-ofctl add-flow br0 in_port=1,actions=drop
  • 动作为mod_vlan_vid

修改报文的vlan id,该选项会使vlan_pcp置为0

ovs-ofctl add-flow br0 in_port=1,actions=mod_vlan_vid:8,output:2
  • 动作为mod_vlan_pcp

修改报文的vlan优先级,该选项会使vlan_id置为0

ovs-ofctl add-flow br0 in_port=1,actions=mod_vlan_pcp:7,output:2
  • 动作为strip_vlan

剥掉报文内外层vlan tag

ovs-ofctl add-flow br0 in_port=1,actions=strip_vlan,output:2
  • 动作为push_vlan

在报文外层压入一层vlan tag,需要使用openflow1.1以上版本兼容

ovs-ofctl add-flow -O OpenFlow13 br0 in_port=1,actions=push_vlan:0x8100,set_field:4097-\>vlan_vid,output:2

ps: set field值为4096+vlan_id,并且vlan优先级为0,即4096-8191,对应的vlan_id为0-4095

  • 动作为push_mpls

修改报文的ethertype,并且压入一个MPLS LSE

ovs-ofctl add-flow br0 in_port=1,actions=push_mpls:0x8847,set_field:10-\>mpls_label,output:2
  • 动作为pop_mpls

剥掉最外层mpls标签,并且修改ethertype为非mpls类型

ovs-ofctl add-flow br0 mpls,in_port=1,mpls_label=20,actions=pop_mpls:0x0800,output:2
  • 动作为修改源/目的MAC,修改源/目的IP
# 修改源MAC
ovs-ofctl add-flow br0 in_port=1,actions=mod_dl_src:00:00:00:00:00:01,output:2
# 修改目的MAC
ovs-ofctl add-flow br0 in_port=1,actions=mod_dl_dst:00:00:00:00:00:01,output:2
# 修改源IP
ovs-ofctl add-flow br0 in_port=1,actions=mod_nw_src:192.168.1.1,output:2
# 修改目的IP
ovs-ofctl add-flow br0 in_port=1,actions=mod_nw_dst:192.168.1.1,output:2
  • 动作为修改TCP/UDP/SCTP源目的端口
# 修改TCP源端口
ovs-ofctl add-flow br0 tcp,in_port=1,actions=mod_tp_src:67,output:2
# 修改TCP目的端口
ovs-ofctl add-flow br0 tcp,in_port=1,actions=mod_tp_dst:68,output:2
# 修改UDP源端口
ovs-ofctl add-flow br0 udp,in_port=1,actions=mod_tp_src:67,output:2
# 修改UDP目的端口
ovs-ofctl add-flow br0 udp,in_port=1,actions=mod_tp_dst:68,output:2
  • 动作为mod_nw_tos

条件:指定dl_type=0x0800

修改ToS字段的高6位,范围为0-255,值必须为4的倍数,并且不会去修改ToS低2位ecn值

ovs-ofctl add-flow br0 ip,in_port=1,actions=mod_nw_tos:68,output:2
  • 动作为mod_nw_ecn

条件:指定dl_type=0x0800,需要使用openflow1.1以上版本兼容

修改ToS字段的低2位,范围为0-3,并且不会去修改ToS高6位的DSCP值

ovs-ofctl add-flow br0 ip,in_port=1,actions=mod_nw_ecn:2,output:2
  • 动作为mod_nw_ttl

修改IP报文ttl值,需要使用openflow1.1以上版本兼容

ovs-ofctl add-flow -O OpenFlow13 br0 in_port=1,actions=mod_nw_ttl:6,output:2
  • 动作为dec_ttl

对IP报文进行ttl自减操作

ovs-ofctl add-flow br0 in_port=1,actions=dec_ttl,output:2
  • 动作为set_mpls_label

对报文最外层mpls标签进行修改,范围为20bit值

ovs-ofctl add-flow br0 in_port=1,actions=set_mpls_label:666,output:2
  • 动作为set_mpls_tc

对报文最外层mpls tc进行修改,范围为0-7

ovs-ofctl add-flow br0 in_port=1,actions=set_mpls_tc:7,output:2
  • 动作为set_mpls_ttl

对报文最外层mpls ttl进行修改,范围为0-255

ovs-ofctl add-flow br0 in_port=1,actions=set_mpls_ttl:255,output:2
  • 动作为dec_mpls_ttl

对报文最外层mpls ttl进行自减操作

ovs-ofctl add-flow br0 in_port=1,actions=dec_mpls_ttl,output:2
  • 动作为move NXM字段

使用move参数对NXM字段进行操作

# 将报文源MAC复制到目的MAC字段,并且将源MAC改为00:00:00:00:00:01
ovs-ofctl add-flow br0 in_port=1,actions=move:NXM_OF_ETH_SRC[]-\>NXM_OF_ETH_DST[],mod_dl_src:00:00:00:00:00:01,output:2

ps: 常用NXM字段参照表

NXM字段

报文字段

NXM_OF_ETH_SRC

源MAC

NXM_OF_ETH_DST

目的MAC

NXM_OF_ETH_TYPE

以太网类型

NXM_OF_VLAN_TCI

vid

NXM_OF_IP_PROTO

IP协议号

NXM_OF_IP_TOS

IP ToS值

NXM_NX_IP_ECN

IP ToS ECN

NXM_OF_IP_SRC

源IP

NXM_OF_IP_DST

目的IP

NXM_OF_TCP_SRC

TCP源端口

NXM_OF_TCP_DST

TCP目的端口

NXM_OF_UDP_SRC

UDP源端口

NXM_OF_UDP_DST

UDP目的端口

NXM_OF_SCTP_SRC

SCTP源端口

NXM_OF_SCTP_DST

SCTP目的端口

  • 动作为load NXM字段

使用load参数对NXM字段进行赋值操作

# push mpls label,并且把10(0xa)赋值给mpls label
ovs-ofctl add-flow br0 in_port=1,actions=push_mpls:0x8847,load:0xa-\>OXM_OF_MPLS_LABEL[],output:2
# 对目的MAC进行赋值
ovs-ofctl add-flow br0 in_port=1,actions=load:0x001122334455-\>OXM_OF_ETH_DST[],output:2
  • 动作为pop_vlan

弹出报文最外层vlan tag

ovs-ofctl add-flow br0 in_port=1,dl_type=0x8100,dl_vlan=777,actions=pop_vlan,output:2

三、数据流转发试验

在上一个试验的基础之上,再新建一个ns3作为一个测试主机,具体的设置如下图的拓扑所示。(上一节详见:)

Android修改原生Switch 滑块大小_IP_03

(1)实验一:使用流表规则,使得ns1和ns2之间不可以ping通,但是ns1可以用http协议访问ns2

首先,在物理机终端中使用命令ovs-ofctl dump-flows vswitch1-2,查看vswitch1-2中的流表,如下图所示。此时,ns1和ns2是可以相互通信的,无论是ping还是http访问。

Android修改原生Switch 滑块大小_Linux_04

ping使用的是icmp协议,http访问使用的http协议,所以如果想要实现试验目的,就需要对icmp协议进行drop。

使用命令添加流表,对ns1发送的icmp数据包丢弃,即actions=drop,匹配项的协议为icmp,即dl_type=0x0800,nw_proto=1 或者 icmp 对应icmp协议。所以使用命令ovs-ofctl add-flow vswitch1-2 icmp,priority=3,in_port=1,actions=drop,增加流表,如下图所示。

Android修改原生Switch 滑块大小_SDN_05

此时,在ns1中ping ns2,如下图所示,发现此时已经无法ping通。

Android修改原生Switch 滑块大小_ryu_06

为了验证http协议是否能够正常访问,进入ns2的网络空间下,开启ns2的http服务,使用命令python3 -m http.server 8000,在ns2中开启http服务,服务端口为8000,如下图所示。

Android修改原生Switch 滑块大小_ovs_07

接下来,在ns1空间下,使用命令curl http://192.168.101.11:8000,对ns2进行访问,如下图所示。ns2对ns1的访问有回应。

Android修改原生Switch 滑块大小_Linux_08

根据上述试验结果,看见已经达成了实验结果。

(2)实验二:ovs连接ryu控制器

由于本系列中又加入了一个主机,此时要想实现3台主机的互相通信需要手动构建6个流表转发规则,所以如果主机数量进一步增加,流规则的下发比较麻烦,这个时候就可以使用控制器实现的自学习交换机功能,自动完成流表的自学习,具体的SDN网络拓扑的原理这里不再详述,主要讲述ovs如何连接远程的ryu控制器,并实现自学习交换机自动写入转发流表。

前面已经讲解了ryu控制器的安装步骤,ryu控制器安装在另一台Linux系统中,所以ovs连接ryu控制器时需要指定ryu控制器所在主机的IP地址。

首先,进入ryu控制器所在的主机(controller_01),输入命令ip a,查看主机的IP地址,如下图所示。enp0s8网卡是我用来作为两台Linux物理机通信的网卡,其IP地址为192.168.56.107。

Android修改原生Switch 滑块大小_SDN_09

在ovs所在的主机(datapath_01)中执行命令ping 192.168.56.107,如下图所示,可以看出两台主机可以相互通信。

Android修改原生Switch 滑块大小_ovs_10

接下来,在controller_01中启动ryu控制器,如下图所示,先cd到ryu/ryu/app目录下,执行simple_switch_13.py程序(遵循openflow1.3协议的自学习交换机模块)。

Android修改原生Switch 滑块大小_IP_11

然后,进入datapath_01主机中,执行命令ovs-ofctl del-flows vswitch1-2,清除ovs中的所有流表。

接下来,连接控制器,在datapath_01中执行命令ovs-vsctl set-controller vswitch1-2 tcp:192.168.56.107:6633,连接ryu控制器,默认端口为6633或者6653(试一下),如下图所示。

Android修改原生Switch 滑块大小_ryu_12

此时,查看ovs的流表,如下图所示,可以看出存在一个默认流表,动作执行指向控制器。

Android修改原生Switch 滑块大小_ovs_13

然后,ns1、ns2、ns3之间就可以相互ping通了,如下图所示。

Android修改原生Switch 滑块大小_SDN_14

查看ovs的流表如下图所示,可以发现流表已经自动写入了。

Android修改原生Switch 滑块大小_SDN_15

(3)试验三:ns1无法ping ns2,但是可以ping ns3

这一个试验,是上一步试验的完善,在(1)中actions直接对端口1的所有icmp报文都丢弃,所以(1)中ns1无法ping通ns2,也无法ping通ns3。

所以,如果想实现本次试验的目的,必须对流表加以改进,让ovs只将源为ns1,目的为ns2的icmp报文丢弃。也就是,可以加入更多的匹配项,如源IP、目的IP,源mac地址、目的mac地址,本次试验,以源IP和目的IP作为匹配项进行试验。

首先,需要向ovs中添加流表,使用命令ovs-ofctl add-flow vswitch1-2 icmp,priority=100,in_port=1,nw_src=192.168.101.1,nw_dst=192.168.101.11,actions=drop,丢弃掉ns1到ns2的icmp报文。

Android修改原生Switch 滑块大小_ryu_16

此时,进入ns1的空间下,分别ping ns2和ns3,如下图所示,可以看出可以ping通ns3,但是不可以ping通ns2,且ns1对ns2的http访问仍然可以正常进行。

Android修改原生Switch 滑块大小_Linux_17

Android修改原生Switch 滑块大小_SDN_18