作者简介:黄志文,福州大学数计学院2014级计算机科学与技术(实验班)本科生,研究侧重于数据平面可编程化。
一、背景
Floodlight是Apache授权并且基于JAVA开发的企业级OpenFlow控制器,它的稳定性、易用性已经得到SDN专业人士以及爱好者们的一致好评,并因其完全开源,这让SDN网络世界变得更加有活力。控制器作为SDN网络中的重要组成部分,能集中地灵活控制SDN网络,为核心网络及应用创新提供了良好的扩展平台。
Floodlight提供的API接口主要有:ACL、防火墙、静态流表、虚拟网。
二、环境
- Ubuntu14.04LTS in VM
- Mininet:安装见SDNLAB网站《Mininet使用源码安装》;
- Floodlight:安装见SDNLAB网站《常用SDN控制器安装部署之Floodlight篇》。
三、访问控制(ACL)
Floodlight包含以响应方式执行ACL的防火墙应用程序。控制器通过监视Packet-in消息来响应性地执行ACL规则,然后推送流表。然而,通过主动添加ACL规则方式,控制器执行ACL规则而不用监视Packet-in消息,从而避免额外的延迟。 ACL应用程序解析用户的REST更新ACL请求,并以主动的方式通过静态流表强制执行,而无需监视Packet-in消息。删除相关的ACL规则时,还可以及时删除生成的ACL流表。
增加ACL规则:
1 |
curl -X POST -d '{"src-ip":"10.0.0.1/32","dst-ip":"10.0.0.2/32","action":"deny"}' http://controller_ip:8080/wm/acl/rules/json |
显示所有ACL规则:
1 |
curl http://controller_ip:8080/wm/acl/rules/json | python -mjson.tool |
删除ACL规则:
1 |
curl -X DELETE -d '{"ruleid":"1" }' http://controller_ip:8080/wm/acl/rules/json |
删除所有ACL规则:
1 |
curl http://controller_ip:8080/wm/acl/clear/json |
ACL规则的属性:
属性 | 类型 | 备注 |
nw-protp | string | TCP或UDP或ICMP |
src-ip | ipv4地址[/子网掩码] | 必填 |
dst-ip | ipv4地址[/子网掩码] | 必填 |
tp-dst | number | nw-proto ==“TCP”或“UDP”时有效。 |
action | string | "DENY" 或者 "ALLOW",没有指定默认"DENY" |
注意点:
- 较早添加的ACL规则有较高的优先级
- ACL由静态流表组成
- Src-ip Dst-ip必填
四、静态流表(Static Entry Pusher)
用户通过REST API公开的Floodlight模块,允许用户手动将流表和组下发到OpenFlow网络。
OpenFlow支持两种流下发方式:主动和被动。当数据包到达没有匹配流的OpenFlow交换机时,就会发生响应流表下发。数据包被发送到控制器,它对其进行分析,添加适当的流表,并让交换机继续转发。或者,在数据包到达之前,可以通过控制器在交换机中主动下发流表。Floodlight支持两种机制。Static Entry Pusher通常用于响应式。
请注意,默认情况下,Floodlight会加载响应流表下发的Forwading模块。可以从floodlight.properties文件中删除转发。
下发流表:
1 |
curl -X POST -d '{"switch":"00:00:00:00:00:00:00:01", "name":"flow-mod-1", "cookie":"0", "priority":"32768", "in_port":"1","active":"true", "actions":"output=2"}' http://controller_ip:8080/wm/staticentrypusher/json |
查看流表:
1 |
curl http://controller_ip:8080/wm/staticentrypusher/list/00:00:00:00:00:00:00:01/json |
查看所有流表:
1 |
curl http://controller_ip:8080/wm/staticentrypusher/list/all/json |
清空某一交换机上的所有流表:
1 |
curl http://controller_ip:8080/wm/staticentrypusher/clear/00:00:00:00:00:00:00:01/json |
清空所有流表:
1 |
curl http://controller_ip:8080/wm/staticentrypusher/clear/all/json |
删除流表:
1 |
curl -X DELETE -d '{"name":"flow-mod-1"}' http://controller_ip:8080/wm/staticentrypusher/json |
必须输入的属性:
属性 |
类型 |
备注 |
name |
string |
唯一,作为流表的主键 |
switch |
switch DPIP |
格式:xx:xx:xx:xx:xx:xx:xx:xx |
entry_type |
string |
“flow” 或者 “group”,未填默认flow |
保留端口关键字:
保留端口 |
备注 |
all |
所有的端口 |
flood |
所有的端口,除了入端口和禁止flood的端口 |
in_port |
数据包入端口 |
local |
本地网络栈交换机端口 |
normal |
交换机L2/L3 pipeline |
any |
任一交换机端口 |
controller |
控制器端口 |
可选动作:
属性 |
类型 |
必要条件 |
支持OF版本 |
备注 |
output |
number/保留端口 |
|
all |
没有丢包选项 |
set_vlan_vid |
number |
eth_vlan_vid = something |
1.0 |
十六进制(0x开头)或十进制 |
set_eth_src |
MAC地址 |
|
1.0-1.1 |
xx:xx:xx:xx:xx:xx |
set_eth_dst |
MAC地址 |
|
1.0-1.1 |
xx:xx:xx:xx:xx:xx |
set_ipv4_src |
IPv4 [/mask] |
eth_type = 0x0800 |
1.0-1.1 |
xx.xx.xx.xx |
set_ipv4_dst |
IPv4 [/mask] |
eth_type = 0x0800 |
1.0-1.1 |
xx.xx.xx.xx |
group |
number |
|
1.1+ |
十六进制(0x开头)或十进制 |
enqueue |
number:number |
|
1.0 |
第一个数字是端口号,第二个则是队列号。十六进制(0x开头)或十进制 |
set_queue |
number |
|
1.1_ |
十六进制(0x开头)或十进制 |
strip_vlan |
|
eth_vlan_vid = something |
1.0 |
|
set_vlan_pcp |
number |
eth_vlan_vid = something |
1.0 |
|
set_vlan_pcp |
number |
eth_vlan_vid = something |
all |
十六进制(0x开头)或十进制 |
push_vlan |
eth-type-number |
|
1.1+ |
十六进制(0x开头)或十进制。必须单独执行来设置VLAN ID |
pop_vlan |
|
eth_vlan_vid = something |
1.1+ |
|
set_ip_tos |
number |
eth_type = 0x0800 || 0x86dd |
1.0 |
十六进制(0x开头)或十进制 |
set_ip_ecn |
number |
eth_type = 0x0800 || 0x86dd |
1.0 |
十六进制(0x开头)或十进制 |
set_ip_ttl |
number |
eth_type = 0x0800 || 0x86dd |
1.1+ |
十六进制(0x开头)或十进制 |
dec_ip_ttl |
|
eth_type = 0x0800 || 0x86dd |
1.1+ |
十六进制(0x开头)或十进制 |
copy_ip_ttl_in |
|
eth_type = 0x0800 || 0x86dd |
1.1+ |
|
copy_ip_ttl_out |
|
eth_type = 0x0800 || 0x86dd |
1.1+ |
|
set_mpls_label |
number |
eth_type = 0x8847 || 0x8848 |
1.1+ |
十六进制(0x开头)或十进制 |
set_mpls_tc |
number |
eth_type = 0x8847 || 0x8848 |
1.1+ |
十六进制(0x开头)或十进制 |
set_mpls_ttl |
number |
eth_type = 0x8847 || 0x8848 |
1.1+ |
十六进制(0x开头)或十进制 |
dec_mpls_ttl |
|
eth_type = 0x8847 || 0x8848 |
1.1+ |
|
push_mpls |
number |
|
1.1+ |
十六进制(0x开头)或十进制 |
pop_mpls |
number |
eth_type = 0x8847 || 0x8848 |
1.1+ |
十六进制(0x开头)或十进制 |
push_pbb |
number |
|
1.1+ |
十六进制(0x开头)或十进制 |
pop_pbb |
|
eth_type = 0x88e7 |
1.1+ |
|
set_tp_src |
number |
ip_proto = 0x06 || 0x11 || 0x84 |
1.0 |
十六进制(0x开头)或十进制 |
set_tp_dst |
number |
ip_proto = 0x06 || 0x11 || 0x84 |
1.0 |
十六进制(0x开头)或十进制 |
set_field |
OXM-value |
See match table above. |
1.2+ |
例 "set_field=eth_src-00:11:22:33:44:55" |
meter |
number |
|
1.5+ |
替换"instruction_goto_meter" |
copy_field |
copy field |
|
1.5+ |
JSON object.: |
set_tp_src |
number |
ip_proto = 0x06 || 0x11 || 0x84 |
1.0 |
十六进制(0x开头)或十进制 |
可选匹配项:
属性 |
类型 |
必要条件 |
支持OF版本 |
备注 |
||
in_port |
number/保留端口 |
|
all |
|
||
eth_type |
number |
|
all |
十六进制(0x开头)或十进制 |
||
eth_src |
MAC地址 |
|
all |
xx:xx:xx:xx:xx:xx |
||
eth_dst |
MAC地址 |
|
all |
xx:xx:xx:xx:xx:xx |
||
ip_proto |
number |
eth_type = 0x0800 || 0x86dd |
all |
十六进制(0x开头)或十进制 |
||
ipv4_src |
IPv4 [/mask] |
eth_type = 0x0800 |
all |
xx.xx.xx.xx[/xx] |
||
ipv4_dst |
IPv4 [/mask] |
eth_type = 0x0800 |
all |
xx.xx.xx.xx[/xx] |
||
tp_src |
number |
ip_proto = 0x06 || 0x11 || 0x84 |
|
UDP,TCP,SCTP |
||
tp_dst |
number |
ip_proto = 0x06 || 0x11 || 0x84 |
|
UDP,TCP,SCTP |
||
eth_vlan_vid |
number |
|
all |
必须在匹配值中包含当前位(bit 12),例:要匹配标记0x0064,请使用0x1064 |
||
eth_vlan_pcp |
number |
eth_vlan_vid = something |
all |
十六进制(0X开头)或十进制. |
||
ipv6_src |
IPv6 [/mask] |
eth_type = 0x86dd |
1.2+ |
xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx[/xx] |
||
ipv6_dst |
IPv6 [/mask] |
eth_type = 0x86dd |
1.2+ |
xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx[/xx] |
||
ipv6_label |
number |
eth_type = 0x86dd |
1.2+ |
十六进制(0X开头)或十进制. |
||
ip_tos |
number |
eth_type = 0x0800 || 0x86dd |
all |
十六进制(0X开头)或十进制. |
||
ip_ecn |
number |
eth_type = 0x0800 || 0x86dd |
all |
十六进制(0X开头)或十进制. |
||
ip_dscp |
number |
eth_type = 0x0800 || 0x86dd |
all |
十六进制(0X开头)或十进制. |
||
udp_src |
number |
ip_proto = 0x11 |
all |
UDP |
||
udp_dst |
number |
ip_proto = 0x11 |
all |
UDP |
||
tcp_src |
number |
ip_proto = 0x06 |
all |
TCP |
||
tcp_dst |
number |
ip_proto = 0x06 |
all |
TCP |
||
sctp_src |
number |
ip_proto = 0x84 |
all |
SCTP |
||
sctp_dst |
number |
ip_proto = 0x84 |
all |
SCTP |
||
icmpv4_type |
number |
ip_proto = 0x01 |
1.2+ |
十六进制(0X开头)或十进制. |
||
icmpv4_code |
number |
ip_proto = 0x01 |
1.2+ |
十六进制(0X开头)或十进制. |
||
icmpv6_type |
number |
ip_proto = 0x3a |
1.2+ |
十六进制(0X开头)或十进制. |
||
icmpv6_code |
number |
ip_proto = 0x3a |
1.2+ |
十六进制(0X开头)或十进制. |
||
ipv6_nd_sll |
MAC |
icmpv6_type = 0x87 |
1.2+ |
xx:xx:xx:xx:xx:xx |
||
ipv6_nd_tll |
MAC |
icmpv6_type = 0x88 |
1.2+ |
xx:xx:xx:xx:xx:xx |
||
ipv6_nd_target |
IPv6 |
icmpv6_type = 0x87 || 0x88 |
1.2+ |
xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx |
||
ipv6_exthdr |
number |
eth_type = 0x86dd |
1.3+ |
十六进制(0X开头)或十进制. |
||
arp_opcode |
number |
eth_type = 0x0806 |
1.2+ |
十六进制(0X开头)或十进制. |
||
arp_sha |
MAC |
eth_type = 0x0806 |
1.2+ |
xx:xx:xx:xx:xx:xx |
||
arp_tha |
MAC |
eth_type = 0x0806 |
1.2+ |
xx:xx:xx:xx:xx:xx |
||
arp_spa |
IPv4 |
eth_type = 0x0806 |
1.2+ |
xx.xx.xx.xx |
||
arp_tpa |
IPv4 |
eth_type = 0x0806 |
1.2+ |
xx.xx.xx.xx |
||
mpls_label |
number |
eth_type = 0x8847 || 0x8848 |
1.2+ |
十六进制(0X开头)或十进制. |
||
mpls_tc |
number |
eth_type = 0x8847 || 0x8848 |
1.2+ |
十六进制(0X开头)或十进制. |
||
mpls_bos |
boolean |
eth_type = 0x8847 || 0x8848 |
1.3+ |
"true" or "false" |
||
pbb_uca |
boolean |
|
1.3+ |
"true" or "false" |
||
tunnel_id |
number |
|
1.3+ |
十六进制(0X开头)或十进制. |
||
tunnel_ipv4_src |
IPv4 |
|
1.3+ |
xx.xx.xx.xx |
||
tunnel_ipv4_dst |
IPv4 |
|
1.3+ |
xx.xx.xx.xx |
||
metadata |
number |
|
1.2+ |
十六进制(0X开头)或十进制. |
||
tcp_flags |
number |
ip_proto = 0x06 |
1.5+ |
十六进制(0X开头)或十进制. |
||
actset_output |
number |
|
1.5+ |
十六进制(0X开头)或十进制. |
||
packet_type |
number/number |
|
1.5+ |
Must take form ns/ns_type, where both ns and ns_type are numbers. |
||
sctp_dst |
number |
ip_proto = 0x84 |
all |
SCTP only. |
||
icmpv4_type |
number |
ip_proto = 0x01 |
1.2+ |
十六进制(0X开头)或十进制. |
||
icmpv4_code |
number |
ip_proto = 0x01 |
1.2+ |
十六进制(0X开头)或十进制. |
||
icmpv6_type |
number |
ip_proto = 0x3a |
1.2+ |
十六进制(0X开头)或十进制. |
||
icmpv6_code |
number |
ip_proto = 0x3a |
1.2+ |
十六进制(0X开头)或十进制. |
||
ipv6_nd_sll |
MAC |
icmpv6_type = 0x87 |
1.2+ |
xx:xx:xx:xx:xx:xx |
||
ipv6_nd_tll |
MAC |
icmpv6_type = 0x88 |
1.2+ |
xx:xx:xx:xx:xx:xx |
||
ipv6_nd_target |
IPv6 |
icmpv6_type = 0x87 || 0x88 |
1.2+ |
xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx |
||
ipv6_exthdr |
number |
eth_type = 0x86dd |
1.3+ |
十六进制(0X开头)或十进制. |
||
arp_opcode |
number |
eth_type = 0x0806 |
1.2+ |
十六进制(0X开头)或十进制. |
||
arp_sha |
MAC |
eth_type = 0x0806 |
1.2+ |
xx:xx:xx:xx:xx:xx |
||
arp_tha |
MAC |
eth_type = 0x0806 |
1.2+ |
xx:xx:xx:xx:xx:xx |
||
arp_spa |
IPv4 |
eth_type = 0x0806 |
1.2+ |
xx.xx.xx.xx |
||
arp_tpa |
IPv4 |
eth_type = 0x0806 |
1.2+ |
xx.xx.xx.xx |
||
mpls_label |
number |
eth_type = 0x8847 || 0x8848 |
1.2+ |
十六进制(0X开头)或十进制. |
||
mpls_tc |
number |
eth_type = 0x8847 || 0x8848 |
1.2+ |
十六进制(0X开头)或十进制. |
||
mpls_bos |
boolean |
eth_type = 0x8847 || 0x8848 |
1.3+ |
"true" or "false" |
||
pbb_uca |
boolean |
|
1.3+ |
"true" or "false" |
||
tunnel_id |
number |
|
1.3+ |
十六进制(0X开头)或十进制. |
||
tunnel_ipv4_src |
IPv4 |
|
1.3+ |
xx.xx.xx.xx |
||
tunnel_ipv4_dst |
IPv4 |
|
1.3+ |
xx.xx.xx.xx |
||
metadata |
number |
|
1.2+ |
十六进制(0X开头)或十进制. |
||
tcp_flags |
number |
ip_proto = 0x06 |
1.5+ |
十六进制(0X开头)或十进制. |
||
actset_output |
number |
|
1.5+ |
十六进制(0X开头)或十进制. |
||
packet_type |
number/number |
|
1.5+ |
十六进制(0X开头)或十进制. |
||
arp_spa |
IPv4 |
eth_type = 0x0806 |
1.2+ |
xx.xx.xx.xx |
||
arp_tpa |
IPv4 |
eth_type = 0x0806 |
1.2+ |
xx.xx.xx.xx |
||
mpls_label |
number |
eth_type = 0x8847 || 0x8848 |
1.2+ |
十六进制(0X开头)或十进制. |
||
mpls_tc |
number |
eth_type = 0x8847 || 0x8848 |
1.2+ |
十六进制(0X开头)或十进制. |
||
mpls_bos |
boolean |
eth_type = 0x8847 || 0x8848 |
1.3+ |
"true" or "false" |
五、防火墙(Firewall)
显示防火墙是启用还是禁用。
1 |
curl http:// controller_ip:8080/wm/firewall/module/status/json |
开启防火墙,默认拒绝所有的通信除非增加ALLOW规则:
1 |
curl http:// controller_ip:8080/wm/firewall/module/enable/json -X PUT -d '' |
为IP主机10.0.0.3和主机10.0.1.5之间的所有流添加ALLOW规则。不指定操作意味着允许规则:
1 2 |
curl -X POST -d '{"src-ip": "10.0.0.3/32", "dst-ip": "10.0.0.7/32"}' http:// controller_ip:8080/wm/firewall/rules/json curl -X POST -d '{"src-ip": "10.0.0.7/32", "dst-ip": "10.0.0.3/32"}' http:// controller_ip:8080/wm/firewall/rules/json |
六、虚拟网(Virtual Network Filter)
创建名为“VirtualNetwork1”的虚拟网络,ID为“NetworkId1”,网关为“10.0.0.7”,租户为“默认”(目前被忽略):
1 |
curl -X PUT -d '{ "network": { "gateway": "10.0.0.7", "name": "virtualNetwork1" } }' http://localhost:8080/networkService/v1.1/tenants/default/networks/NetworkId1 |
将主机添加到MAC地址为“00:00:00:00:00:08”的VirtualNetwork1和端口“port1”:
1 |
curl -X PUT -d '{"attachment": {"id": "NetworkId1", "mac": "00:00:00:00:00:08"}}' http://localhost:8080/networkService/v1.1/tenants/default/networks/NetworkId1/ports/port1/attachment |
七、应用场景
如图所示的网络拓扑环境:
实现可添加/删除 TCP/UDP 数据报控制项,每一控制项可设定以下 3 个选项:协议类型TCP或 UDP),源 IP,目的端口。实现匹配控制项的网络数据阻断(从 h1 或 h2 发送至 server 服务器的数据报)。(注意:目的 IP 不是 server 服务器 IP 的数据报不受影响)
举例说明:假设 h1 的 IP 为 192.168.10.1, 可在程序界面增加以下控制项:{TCP,192.168.10.1, 22},从而使得所有来自 h1 的、目的 IP 为 server 服务器且目的端口为 22 的 TCP 数据报均被拦截。
解决方案:
编写简单的shell脚本调用floodlight API来实现所需功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
!/bin/sh opr=$1 proto=$3 ip=$4 port=$5 name=$2 if [ "$opr" = "add" ]&&[ "$proto" = "tcp" ]; then curl -X POST -d '{"switch":"00:00:00:00:00:00:00:01", "name":"'${name}'", "cookie":"0", "priority":"32768","eth_type":"0x0800","ip_proto":"0x06","tcp_dst":"'${port}'","ipv4_dst":"10.0.0.3","ipv4_src":"'${ip}'","active":"true", "actions":"output=4"}' http://10.0.2.15:8080/wm/staticentrypusher/json curl http://10.0.2.15:8080/wm/staticentrypusher/list/00:00:00:00:00:00:00:01/json elif [ "$opr" = "add" ]&&[ "$proto" = "udp" ]; then curl -X POST -d '{"switch":"00:00:00:00:00:00:00:01", "name":"'${name}'", "cookie":"0", "priority":"32768","eth_type":"0x0800","ip_proto":"0x11","udp_dst":"'${port}'","ipv4_dst":"10.0.0.3","ipv4_src":"'${ip}'","active":"true", "actions":"output=4"}' http://10.0.2.15:8080/wm/staticentrypusher/json curl http://10.0.2.15:8080/wm/staticentrypusher/list/00:00:00:00:00:00:00:01/json else [ "$opr" = "del" ] curl -X DELETE -d '{"name":"'${name}'"}' http://10.0.2.15:8080/wm/staticentrypusher/json curl http://10.0.2.15:8080/wm/staticentrypusher/list/00:00:00:00:00:00:00:01/json fi |
在/mininet/custuom/文件夹下写一个生成自定义网络拓扑的Python脚本3h-1s.py来实现的拓扑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
from mininet.topo import Topo from mininet.cli import CLI from mininet.log import lg, info class MyTopo( Topo ): "Simple topology example." def __init__( self ): "Create custom topo." # Initialize topology Topo.__init__( self ) # Add hosts and switches h1 = self.addHost( 'h1' ) h2 = self.addHost( 'h2' ) server = self.addHost( 'server' ) s1 = self.addSwitch( 's1' ) # Add links self.addLink( s1, h1 ) self.addLink( s1, h2 ) self.addLink( s1, server ) topos = { 'mytopo': ( lambda: MyTopo() ) } |
打开终端,创建网络拓扑,同时与控制器建立连接,如图所示:
通过xterm命名分别打开server主机和h1主机,在server下发命令。
再用iperf将主机server设为server,将主机h1设为client。
由图可知,在流表下发之后,server和h1无法在端口22建立TCP连接,证明实现了匹配控制项的网络数据的阻断。
通过shell脚本调用Floodlight北向API 删除静态流表。
用iperf验证流表是否删除。
由图可知,在删除流表之后,server和h1在端口22建立TCP连接,证明实现了匹配流表的删除。