基于 Open vSwitch 的 OpenFlow 实践

上一篇中简单介绍了 Open vSwitch,这篇展开讲讲,旨在说明 OVS 配合 OpenFlow 控制器的实际应用。

我的新书《LangChain编程从入门到实践》 已经开售!推荐正在学习AI应用开发的朋友购买阅读!
LangChain编程从入门到实践

Open vSwitch 内部结构

OVS内部结构.png
简单说明:
ovs-vswitchd:OVS 守护进程,和 ovs-ofctl 通信遵从 OpenFlow 协议,与 ovsdb-server 通信使用 OVSDB 协议,和内核模块通过 netlink 通信,支持多个独立的 datapath
ovsdb-server:OVS 轻量级的数据库服务器,用于整个 OVS 的配置信息,包括接口,交换内容,VLAN 等,ovs-vswitchd 根据数据库中的配置信息工作
ovs-dpctl:用来配置 datapath,可以控制转发规则
ovs-vsctl:主要是获取或者更改 ovs-vswitchd 的配置信息,操作的同时会更新 ovsdb
ovs-appctl:a utility that sends commands to running Open vSwitch daemons (ovs-vswitchd)

ovs-ofctl

在没有配置 OpenFlow 控制器的模式下,可以使用 ovs-ofctl(OVS 提供的命令行工具)命令通过 OpenFlow 协议去连接 OVS,创建、修改或删除 OVS 中的流表项,并对 OVS 的运行状况进行动态监控。

Flow 语法说明

Flow 被定义为某个特定的网络流量,例如一个 TCP 连接就是一个 Flow,或者从某个 IP 地址发出来的数据包,都可以被认为是一个 Flow,支持 OpenFlow 协议的交换机应该包括一个或者多个流表,流表中的条目包含:数据包头的信息、匹配成功后要执行的指令和统计信息。
当数据包进入 OVS 后,会将数据包和流表中的流表项进行匹配,如果发现了匹配的流表项,则执行该流表项中的指令集。相反,如果数据包在流表中没有发现任何匹配,OVS 会通过控制通道把数据包发到 OpenFlow 控制器中。

流表常用字段

flowtable-field.png

在 OVS 中,流表项作为 ovs-ofctl 的参数,采用如下的格式:字段=值。如果有多个字段,可以用逗号或者空格分开。一些常用的字段列举如下:
| 字段名称 | 说明 |
| :– | :– |
| in_port=port | 传递数据包的端口的 OpenFlow 端口编号 |
| dl_vlan=vlan | 数据包的 VLAN Tag 值,范围是 0-4095,0xffff 代表不包含 VLAN Tag 的数据包 |
| dl_src=<MAC>
dl_dst=<MAC> | 匹配源或者目标的 MAC 地址
01:00:00:00:00:00/01:00:00:00:00:00 代表广播地址
00:00:00:00:00:00/01:00:00:00:00:00 代表单播地址 |
| dl_type=ethertype | 匹配以太网协议类型,其中:
dl_type=0x0800 代表 IPv4 协议
dl_type=0x086dd 代表 IPv6 协议
dl_type=0x0806 代表 ARP 协议
完整的的类型列表可以参见以太网协议类型列表 |
| nw_src=ip[/netmask] nw_dst=ip[/netmask] | 当 dl_typ=0x0800 时,匹配源或者目标的 IPv4 地址,可以使用 IP 地址或者域名 |
| nw_proto=proto | 和 dl_type 字段协同使用
当 dl_type=0x0800 时匹配 IPv4 协议编号
当 dl_type=0x086dd 时匹配 IPv6 协议编号
完整的 IP 协议编号可以参见 IP 协议编号列表 |
| table=number | 指定要使用的流表的编号,范围是 0-254。在不指定的情况下,默认值为 0。通过使用流表编号,可以创建或者修改多个 Table 中的 Flow |
| reg<idx>=value[/mask] | 交换机中的寄存器的值。当一个数据包进入交换机时,所有的寄存器都被清零,用户可以通过 Action 的指令修改寄存器中的值 |

流表执行的动作

对于 add−flow,add−flows 和 mod−flows 这三个命令,还需要指定要执行的动作:actions=[target][,target…]
一个流规则中可能有多个动作,按照指定的先后顺序执行。
常见的操作有:

  • output:port 输出数据包到指定的端口,port 是指端口的 openflow 端口编号
  • group:group_id 输出数据包到 openflow group,group_id 是指openflow group的id
  • enqueue:port:queue 将数据包放到 openflow port 端口的 queue 号队列中
  • mod_vlan_vid:vlan_id 修改数据包中的 vlan tag 为 vlan_id,如果数据包中无 tag,则添加;如果数据包中已经是 vlan_id,同时调整 vlan 优先级为 0
  • strip_vlan: 移除数据包中的 vlan tag
  • mod_dl_src/ mod_dl_dest: 修改源或者目标的 MAC 地址信息
  • mod_nw_src/mod_nw_dst: 修改源或者目标的 IPv4 地址信息
  • mod_tp_src/mod_tp_dst: 将数据包的TCP/UDP/SCTP源或则目的端口
  • drop 将数据包丢弃
  • resubmit:port 替换流表的 in_port 字段,并重新进行匹配
  • load:value−>dst[start..end] 写数据到指定的字段
  • normal: 按照常规L2/L3处理流程处理数据包
  • flood: 将数据包输出到除该数据包输入口外和不可被 flooding 端口外的所有物理端口
  • all: 将数据包输出到除了该数据包的输入口外的所有物理口
  • local: 将数据包输出到与 bridge 同名的端口
  • in_port: 将数据包输出到其输入口
  • controller(key=value): 将数据包以 “packet in” 消息形式发给 openflow 控制器
    • max_len=nbytes: 将数据包的 nbytes 字节数据发给控制器
    • reason=reason: 指明 “packet in” reason; 取值为 action(默认reason)、no_match、invalid_ttl
    • id=controller-id: 指明要发送给的控制器 id
  • mod_nw_tos:tos 修改 ip 头的服务类型 tos 中的高六位(修改数值为[0,255]之间 4 的倍数)
  • mod_nw_ecn:ecn 修改 ip 头的服务类型 tos 中低两位
  • mod_nw_ttl:ttl 修改 TTL,在[0,255]之间
  • set_tunnel:id
  • move:src[start..end]−>dst[start..end] 含义未探究清楚、待定
  • learn(argument[,argument]…) 含义未探究清楚、待定

Flow Table 使用实例

创建网络拓扑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
        +--------------+    +----------------+
| | | |
| ns1 | | ns2 |
| | | |
+--------------+ +----------------+
1.1.1.1/24 |first-if| |second-if 1.1.1.2/24
| | | |
+----+---+ +---+---+
| |
| |
| |
+----+---+ +---+---+
| | | |
|first-br| |second-br
+------------------------------------+
| |
| br0 |
| |
+----------------------+-------------+
|third-br |
| |
+----+----+
|
|
+----+----+
| |
|third-if | 1.1.1.3/24
+--------------+---+
| |
| ns3 |
| |
+------------------+

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ovs-vsctl add-br br0
ip link add first-br type veth peer name first-if
ip link add second-br type veth peer name second-if
ip link add third-br type veth peer name third-if
ovs-vsctl add-port br0 first-br -- set interface first-br ofport-request=1
ovs-vsctl add-port br0 second-br -- set interface second-br ofport-request=2
ovs-vsctl add-port br0 third-br -- set interface third-br ofport-request=3
ip link set first-br up
ip link set second-br up
ip link set third-br up

ip netns add ns1
ip netns add ns2
ip netns add ns3
ip link set first-if netns ns1
ip link set second-if netns ns2
ip link set third-if netns ns3
ip netns exec ns1 ip addr add 1.1.1.1/24 dev first-if
ip netns exec ns1 ifconfig first-if up
ip netns exec ns2 ip addr add 1.1.1.2/24 dev second-if
ip netns exec ns2 ifconfig second-if up
ip netns exec ns3 ip addr add 1.1.1.3/24 dev third-if
ip netns exec ns3 ifconfig third-if up

屏蔽数据包

屏蔽所有进入 OVS 的以太网广播数据包

1
ovs-ofctl add-flow ovs-switch "table=0, dl_src=01:00:00:00:00:00/01:00:00:00:00:00, actions=drop"

屏蔽 STP 协议的广播数据包

1
ovs-ofctl add-flow ovs-switch "table=0, dl_dst=01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0, actions=drop"

修改数据包

修改从端口 first-br 收到的数据包的源地址为 1.1.1.4

1
2
3
4
5
6
7
8
$ ovs-ofctl add-flow br0 "priority=1 idle_timeout=0,in_port=1,actions=mod_nw_src:1.1.1.4,normal"
$ ip netns exec ns1 ping -c3 1.1.1.2
# 在接收端口 second-if 监控数据,发现接收到的数据包的来源已经被修改为 1.1.1.4
$ ip netns exec ns2 tcpdump -i second-if
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on second-if, link-type EN10MB (Ethernet), capture size 262144 bytes
21:12:13.703919 IP 1.1.1.4 > 1.1.1.2: ICMP echo request, id 16751, seq 1, length 64
...

重定向数据包

重定向所有的 ICMP 数据包到端口 third-br

1
2
3
4
5
6
7
$ ovs-ofctl add-flow br0 "idle_timeout=0,dl_type=0x0800,nw_proto=1,actions=output:3"
$ ip netns exec ns3 tcpdump -i third-if
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on third-if, link-type EN10MB (Ethernet), capture size 262144 bytes
21:21:57.811993 IP 1.1.1.1 > 1.1.1.2: ICMP echo request, id 20299, seq 1, length 64
21:21:58.837772 IP 1.1.1.1 > 1.1.1.2: ICMP echo request, id 20299, seq 2, length 64
...

修改数据包的 VLAN Tag

OVS 提供的 ovs-appctl ofproto/trace 工具可以生成测试用的模拟数据包,并一步步的展示 OVS 对数据包的流处理过程,来跟踪转发状况。

1
2
3
4
# 使端口 second-br 成为一个隶属于 VLAN 102 的端口
ovs-vsctl set port second-br tag=102
# 对于从端口 first-br 进入交换机的数据包,如果它不包含任何 VLAN tag,则自动为它添加 VLAN tag 102
ovs-ofctl add-flow br0 "priority=3,in_port=1,dl_vlan=0xffff,actions=mod_vlan_vid:102,normal"

使用 ofproto/trace 生成一个从端口 first-br 发送到端口 second-br 的数据包,发现数据包进入端口 first-br 之后,会被加上 VLAN tag 102,同时转发到端口 second-br 上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ ovs-appctl ofproto/trace br0 in_port=1,dl_src=2e:07:86:10:44:29,dl_dst=22:de:bf:73:db:b8 

Flow: in_port=1,vlan_tci=0x0000,dl_src=2e:07:86:10:44:29,dl_dst=22:de:bf:73:db:b8,dl_type=0x0000

bridge("br0")
-------------
0. in_port=1,vlan_tci=0x0000, priority 3
mod_vlan_vid:102
NORMAL
-> no learned MAC for destination, flooding

Final flow: in_port=1,dl_vlan=102,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=2e:07:86:10:44:29,dl_dst=22:de:bf:73:db:b8,dl_type=0x0000
Megaflow: recirc_id=0,eth,in_port=1,vlan_tci=0x0000,dl_src=2e:07:86:10:44:29,dl_dst=22:de:bf:73:db:b8,dl_type=0x0000
Datapath actions: push_vlan(vid=102,pcp=0),1,pop_vlan,3,push_vlan(vid=102,pcp=0),4

“Flow: ” 之后的字段描述了输入的流的信息。由于没有指定太多信息,所以多数字段 (例如 dl_type 和 vlan_tci)被 OVS 设置为空值
”Final flow: ” 之后的字段是整个处理过程的总结
“Datapath actions: ” 代表数据包在 datapath 的 1,4号端口添加 tag,在 3 号端口删除 tag

清除实验环境

1
2
3
4
5
6
7
8
ovs-vsctl del-br br0
ip netns del ns1
ip netns del ns2
ip netns del ns3

ip link del first-br
ip link del second-br
ip link del third-br

参考链接

openvSwitch flow

基于 Open vSwitch 的 OpenFlow 实践

https://liduos.com/ovs-openflow.html

作者

莫尔索

发布于

2020-05-26

更新于

2024-05-19

许可协议

评论