上一节的试验中讲到了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
安装ryu
cd ryu
sudo pip3 install -r tools/pip-requires
sudo python3 setup.py install
二、流表的匹配项和动作(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:2ps: 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:2ps: 常用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作为一个测试主机,具体的设置如下图的拓扑所示。(上一节详见:)

(1)实验一:使用流表规则,使得ns1和ns2之间不可以ping通,但是ns1可以用http协议访问ns2
首先,在物理机终端中使用命令ovs-ofctl dump-flows vswitch1-2,查看vswitch1-2中的流表,如下图所示。此时,ns1和ns2是可以相互通信的,无论是ping还是http访问。

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,增加流表,如下图所示。

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

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

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

根据上述试验结果,看见已经达成了实验结果。
(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。

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

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

然后,进入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(试一下),如下图所示。

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

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

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

(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报文。

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


















