基于 Open vSwitch 的 OpenFlow 实践
2019-03-26 15:12 云物互联 阅读(776) 评论(0) 编辑 收藏 举报目录
文章目录
前文列表
《OpenFlow/SDN 的缘起与发展》
《OpenFlow Switch 1.3 规范》
《OpenvSwitch/OpenFlow 架构解析与实践案例》
Open vSwitch 基本概念
Bridge:在 Linux 的语义中代表一个虚拟的以太网交换机(vSwitch)。
Port:Bridge 的端口,每个 Port 都隶属于一个 Bridge。
Interface:连接到 Port 的网络接口设备(e.g. Tap、eth0)。通常情况下,Port 和 Interface 是一对一关系,为 Port 配置 bond 模式后,Port:Interface 是 1:N 的关系。
Controller:OpenFlow 控制器,OvS 作为 OpenFlow 交换机可以同时接受一个或多个 OpenFlow Controller 的管理。
Datapath:在 OpenFlow 的语义中,Datapath 就是一个 OpenFlow 交换机。负责执行数据交换,把从接收 Port 收到的数据包在流表中进行匹配,并执行匹配到的动作。
Flow Table:流表,Datapath 与流表关联,流表记录了网络包的匹配域、计数器和动作集。OpenFlow Controller 通过设定 OvS 的流表来对 SDN 网络进行 “编程”。
Open vSwitch 与 OpenFlow 的关系
Open vSwitch(简称 OvS)是遵守 OpenFlow Switch Specification 的 OpenFlow 交换机软件实现。OpenFlow 协议是用于管理 OpenFlow Switch 流表的协议,ovs-ofctl 就是 OvS 提供的 OpenFlow 流表配置命令行工具。在没有配置 OpenFlow Controller 的场景中,用户可以直接使用 ovs-ofctl 命令与 OvS 连接 OpenFlow 通道,并以此创建、修改或删除 OvS 中的流表项,同时对 OvS 的运行状况进行动态监控。
在 OvS 中,流表项作为 ovs-ofctl 指令的参数,采用 字段=值
的格式,如果有多个字段,可以是用逗号或者空格分开。e.g.
ovs-ofctl add-flow ovs-switch "table=0, dl_src=01:00:00:00:00:00/01:00:00:00:00:00, actions=drop"
通过 Open vSwitch 实践 OpenFlow
查看 OvS 服务进程:
[root@ovs ~]# ps -ea | grep ovs
1295 ? 00:00:00 ovsdb-server
1310 ? 00:00:00 ovs-vswitchd
查看 OvS 版本:
[root@ovs ~]# ovs-appctl --version
ovs-appctl (Open vSwitch) 2.0.0
Compiled Apr 19 2018 17:57:34
查看 OvS 支持的 OpenFlow 版本:
[root@ovs ~]# ovs-ofctl --version
ovs-ofctl (Open vSwitch) 2.0.0
Compiled Apr 19 2018 17:57:34
OpenFlow versions 0x1:0x4
新建一个 OvS Switch:
[root@ovs ~]# ovs-vsctl add-br ovs-switch
[root@ovs ~]# ovs-vsctl show
367a97be-5b4f-40e2-8630-e64f67172fd2
Bridge ovs-switch
Port ovs-switch
Interface ovs-switch
type: internal
ovs_version: "2.0.0"
[root@ovs ~]# ip a
...
3: ovs-system: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
link/ether 3a:3f:8b:8c:5d:31 brd ff:ff:ff:ff:ff:ff
# Port ovs-switch 的 Interface 虚拟网络设备
4: ovs-switch: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN qlen 1000
link/ether 16:50:76:b2:22:46 brd ff:ff:ff:ff:ff:ff
inet6 fe80::f018:ff:fed7:bf2a/64 scope link
valid_lft forever preferred_lft forever
NOTE:OvS Switch 默认会有一个 “internal” 类型的同名 Port,相当于物理交换机的管理端口。
创建 Port p0,并设置 p0 的 OpenFlow 端口编号为 100。OpenFlow 端口编号常被作为流表项的匹配字段,如果没有显式指定 OpenFlow 端口编号,OvS 会随机指定。
[root@ovs ~]# ovs-vsctl add-port ovs-switch p0 -- set Interface p0 ofport_request=100
[root@ovs ~]# ovs-vsctl show
367a97be-5b4f-40e2-8630-e64f67172fd2
Bridge ovs-switch
Port ovs-switch
Interface ovs-switch
type: internal
Port "p0"
Interface "p0"
ovs_version: "2.0.0"
NOTE:除了在创建 Port 的时候指定虚拟接口设备 p0,也可以指定一个物理接口设备(e.g. eth0)。
设定 p0 的 Interface 类型为 “internal”:
[root@ovs ~]# ovs-vsctl set Interface p0 type=internal
[root@ovs ~]# ip a
...
5: p0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN qlen 1000
link/ether 5a:d7:49:85:d9:da brd ff:ff:ff:ff:ff:ff
inet6 fe80::58d7:49ff:fe85:d9da/64 scope link
valid_lft forever preferred_lft forever
NOTE:Internal 类型是 OvS 内部创建的虚拟网卡接口,每创建一个 Port,OvS 会自动在 Linux 上创建一个同名接口设备挂载到新创建的 Port 上。同时可以为这个虚拟网络设备配置 IP 地址,进行数据监听等。
为了避免 OvS Interface 的 IP 地址与 HostOS 本地的 IP 地址冲突,可以创建一个 Network Namespace ns0,把 p0 的 Interface 移入 ns0 中,并配置 IP 地址为 192.168.1.100。
# 新建 ns0,初始 ns0 只有 lo 设备
[root@ovs ~]# ip netns add ns0
[root@ovs ~]# ip netns exec ns0 ip l
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
# 将 p0 移入 ns0 后,p0 被隔离在 ns0,HostOS 看不见 p0 Interface 设备
[root@ovs ~]# ip link set p0 netns ns0
[root@ovs ~]# ip netns exec ns0 ip l
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
5: p0: <BROADCAST> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
link/ether 5a:d7:49:85:d9:da brd ff:ff:ff:ff:ff:ff
[root@ovs ~]# ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000
link/ether fa:16:3e:c4:f4:7f brd ff:ff:ff:ff:ff:ff
3: ovs-system: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
link/ether 3a:3f:8b:8c:5d:31 brd ff:ff:ff:ff:ff:ff
4: ovs-switch: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT qlen 1000
link/ether 16:50:76:b2:22:46 brd ff:ff:ff:ff:ff:ff
# 为 ns0 内的 p0 设定 IP 地址和开启混杂模式,让所有 MAC 地址的二层帧都能通过
[root@ovs ~]# ip netns exec ns0 ip addr add 192.168.1.100/24 dev p0
[root@ovs ~]# ip netns exec ns0 ifconfig p0 promisc up
[root@ovs ~]# ip netns exec ns0 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
5: p0: <BROADCAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN qlen 1000
link/ether 5a:d7:49:85:d9:da brd ff:ff:ff:ff:ff:ff
inet 192.168.1.100/24 scope global p0
valid_lft forever preferred_lft forever
inet6 fe80::58d7:49ff:fe85:d9da/64 scope link
valid_lft forever preferred_lft forever
使用同样的方法创建 p1、p2,p0-2 的 IP/MAC 地址分别为:
- p0
- Network Namespace: ns0
- IP: 192.168.1.100/24
- MAC: 5a:d7:49:85:d9:da
- OpenFlow Port Number: 100
- p1
- Network Namespace: ns1
- IP: 192.168.1.101/24
- MAC: 62:f0:3e:b6:7d:6f
- OpenFlow Port Number: 101
- p2
- Network Namespace: ns2
- IP: 192.168.1.102/24
- MAC: 3e:3f:34:6b:0a:3d
- OpenFlow Port Number: 102
[root@ovs ~]# ovs-vsctl show
367a97be-5b4f-40e2-8630-e64f67172fd2
Bridge ovs-switch
Port "p2"
Interface "p2"
type: internal
Port ovs-switch
Interface ovs-switch
type: internal
Port "p0"
Interface "p0"
type: internal
Port "p1"
Interface "p1"
type: internal
ovs_version: "2.0.0"
# 查看 OvS Switch 的详细端口信息
[root@ovs ~]# ovs-ofctl show ovs-switch
# OpenFlow Features 响应消息,包含:
# Datapath 的唯一标示 ID
# Datapath 支持的流表数量
# Datapath 可以缓存数据包的最大数量
OFPT_FEATURES_REPLY (xid=0x2): dpid:0000165076b22246
n_tables:254, n_buffers:256
# Datapath 支持的容量,即功能
capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
# Datapath 支持的动作
actions: OUTPUT SET_VLAN_VID SET_VLAN_PCP STRIP_VLAN SET_DL_SRC SET_DL_DST SET_NW_SRC SET_NW_DST SET_NW_TOS SET_TP_SRC SET_TP_DST ENQUEUE
# Datapath 包含的 Ports 及其编号,是一个可变的 ofp_phy_port 结构体数组类型
100(p0): addr:00:00:00:00:00:00
# 配置为 Down,即没有手动配置为 UP
config: PORT_DOWN
# 状态为 Down
state: LINK_DOWN
# 数据传输速率
speed: 0 Mbps now, 0 Mbps max
101(p1): addr:00:00:00:00:00:00
config: PORT_DOWN
state: LINK_DOWN
speed: 0 Mbps now, 0 Mbps max
102(p2): addr:00:00:00:00:00:00
config: PORT_DOWN
state: LINK_DOWN
speed: 0 Mbps now, 0 Mbps max
LOCAL(ovs-switch): addr:f2:18:00:d7:bf:2a
config: PORT_DOWN
state: LINK_DOWN
speed: 0 Mbps now, 0 Mbps max
# GET_CONFIG 答复消息,包含 flags 和 miss_send_len 配置信息
# flags 指定了 OpenFlow 交换机的 IP 碎片处理方法
# miss_send_len 表示在 Table-miss 时和 Packet-In 消息中发送的数据包字节数
OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0
NOTE:执行指令 ovs-ofctl show ovs-switch
就相当于 OpenFlow 控制器向 OpenFlow 交换机发送了询问功能的 Features 请求消息,OpenFlow 交换机响应了 Features 应答消息给 OpenFlow,消息体记录了 OpenFlow 交换机的功能参数。这个过程就是 OpenFlow 的 “握手”。
也可以获取指定 Port 的 OpenFlow 端口编号:
[root@ovs ~]# ovs-vsctl get Interface p0 ofport
100
查看 Datapath 信息:
# Get all
[root@ovs ~]# ovs-dpctl show
system@ovs-system:
lookups: hit:35 missed:21 lost:0
flows: 0
port 0: ovs-system (internal)
port 1: ovs-switch (internal)
port 2: p0 (internal)
port 3: p1 (internal)
port 4: p2 (internal)
# Get one
[root@ovs ~]# ovs-dpctl show system@ovs-system
system@ovs-system:
lookups: hit:35 missed:21 lost:0
flows: 0
port 0: ovs-system (internal)
port 1: ovs-switch (internal)
port 2: p0 (internal)
port 3: p1 (internal)
port 4: p2 (internal)
检查 p0、p1、p2 的互通性:
ip netns exec ns0 ping 192.168.1.101
ip netns exec ns0 ping 192.168.1.102
NOTE:如果想在 ns0 ping 通 p0 Interface 的 IP 地址,那么首先需要把 lo Up 起来,否则执行 ip netns exec ns0 ping 192.168.1.100
ping 不通。这是因为所有只在本机内部流转的数据包都需要经过 lo 设备的处理,而 ping 外部 IP 地址时内核协议栈直接将数据包从 NIC 送出。所以如果 lo DOWN,则无法 ping 通过本地。
[root@ovs ~]# ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
5: p0: <BROADCAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN qlen 1000
link/ether 5a:d7:49:85:d9:da brd ff:ff:ff:ff:ff:ff
inet 192.168.1.100/24 scope global p0
valid_lft forever preferred_lft forever
inet6 fe80::58d7:49ff:fe85:d9da/64 scope link
valid_lft forever preferred_lft forever
[root@ovs ~]# ip netns exec ns0 ifconfig lo up
[root@ovs ~]# ip netns exec ns0 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
5: p0: <BROADCAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN qlen 1000
link/ether 5a:d7:49:85:d9:da brd ff:ff:ff:ff:ff:ff
inet 192.168.1.100/24 scope global p0
valid_lft forever preferred_lft forever
inet6 fe80::58d7:49ff:fe85:d9da/64 scope link
valid_lft forever preferred_lft forever
屏蔽数据包
查看当前 OvS Switch 的所有流表,数量应该就是 OpenFlow 控制器和 OpenFlow 交换机握手时反馈的 254 张:
root@ovs ~]# ovs-ofctl dump-tables ovs-switch
OFPST_TABLE reply (xid=0x2): 254 tables
0: classifier: wild=0x3fffff, max=1000000, active=2
lookup=489, matched=489
1: table1 : wild=0x3fffff, max=1000000, active=0
lookup=0, matched=0
...
查看当前 OvS Switch 的所有流表项:
[root@ovs ~]# ovs-ofctl dump-flows ovs-switch
NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=6469.422s, table=0, n_packets=82, n_bytes=6300, idle_age=2533, priority=0 actions=NORMAL
- cookie:一个 64bit 的整数,OpenFlow 控制器用于标识流表项,相同的 cookie 值可以用来标记是同一批或同一类规则。只对 OpenFlow 控制器有效,OpenFlow 交换机不关心该字段。
- duration:规则创建的时长
- table:流表编号,用来建立流表的层次关系
- n_packets、n_bytes:匹配到这条规则的网络包数、字节数。
- idle_age:该流表项多久没有被匹配过,单位秒。
- hard_age:这条流表项创建、修改了多次时间,单位秒。
- priority:优先级,当 Flow 被同一个流表中的多条流表项匹配时,选择优先级高的。
actions=NORMAL
:正常的 L2/L3 交换机行为。
NOTE:默认情况下 OvS Switch 执行传统 L2/L3 交换机的行为
添加流表项,屏蔽所有进入 OvS 的以太网广播包:
[root@ovs ~]# ovs-ofctl add-flow ovs-switch "table=0, dl_src=01:00:00:00:00:00/01:00:00:00:00:00, actions=drop"
[root@ovs ~]# ovs-ofctl dump-flows ovs-switch
NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=6603.552s, table=0, n_packets=82, n_bytes=6300, idle_age=2667, priority=0 actions=NORMAL
cookie=0x0, duration=1.952s, table=0, n_packets=0, n_bytes=0, idle_age=1, dl_src=01:00:00:00:00:00/01:00:00:00:00:00 actions=drop
- dl_src:以太网数据帧源 MAC 地址
actions=drop
:流表项匹配域匹配之后对 Flow( e.g. 数据包,TCP 连接)执行的动作集,这里表示丢弃数据包
NOTE:dl_src=01:00:00:00:00:00/01:00:00:00:00:00
matches all multicast (including broadcast) Ethernet packets.
添加流表项,屏蔽所有进入 OvS 的 IEEE 802.1D STP 协议广播包:
[root@ovs ~]# ovs-ofctl add-flow ovs-switch "table=0, dl_dst=01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0, actions=drop"
[root@ovs ~]# ovs-ofctl dump-flows ovs-switch
NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=15.302s, table=0, n_packets=0, n_bytes=0, idle_age=15, dl_dst=01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0 actions=drop
cookie=0x0, duration=7051.931s, table=0, n_packets=82, n_bytes=6300, idle_age=3116, priority=0 actions=NORMAL
cookie=0x0, duration=450.331s, table=0, n_packets=0, n_bytes=0, idle_age=450, dl_src=01:00:00:00:00:00/01:00:00:00:00:00 actions=drop
- dl_dst:以太网数据帧目的 MAC 地址
删除流表项:
[root@ovs ~]# ovs-ofctl del-flows ovs-switch "dl_dst=01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0"
[root@ovs ~]# ovs-ofctl del-flows ovs-switch "dl_src=01:00:00:00:00:00/01:00:00:00:00:00"
重定向数据包
添加流表项,重定向所有 ICMP 数据包到 p2:
[root@ovs ~]# ovs-ofctl add-flow ovs-switch "idle_timeout=0, dl_type=0x0800, nw_proto=1, actions=output:102"
[root@ovs ~]# ovs-ofctl dump-flows ovs-switch
NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=14.749s, table=0, n_packets=0, n_bytes=0, idle_age=14, icmp actions=output:102
cookie=0x0, duration=7605.774s, table=0, n_packets=82, n_bytes=6300, idle_age=3670, priority=0 actions=NORMAL
- idle_timeout:流表项空闲超时时间,从上次匹配该流表项开始计时
dl_type=0x0800, nw_proto=1
:ICMP Packetactions=output:102
:匹配该流表项的 Flow 被转发至 OpenFlow 端口编号为 102 的 Port
验证结果,通过 p0 ping p1,但实际是 ICMP echo request 包被转发了到 p2:
# ns0
[root@ovs ~]# ip netns exec ns0 ping 192.168.1.101
PING 192.168.1.101 (192.168.1.101) 56(84) bytes of data.
# ns1
[root@ovs ~]# ip netns exec ns1 tcpdump -i p1 -nntve -p icmp
tcpdump: listening on p1, link-type EN10MB (Ethernet), capture size 262144 bytes
# ns2
[root@ovs ~]# ip netns exec ns2 tcpdump -i p2 -nntve -p icmp
tcpdump: listening on p2, link-type EN10MB (Ethernet), capture size 262144 bytes
5a:d7:49:85:d9:da > 62:f0:3e:b6:7d:6f, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 12532, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.100 > 192.168.1.101: ICMP echo request, id 11772, seq 5, length 64
NOTE:虽然 p2 能够接收到 p0 ping p1 的 ICMP 包,但却不会进行响应,因为 IP 地址对不上。值得注意的是,即便我们从 p0 ping p2,但 p0 依旧无法接收到 ICMP reply。这是因为上述流表包含了所有的 ICMP 类型(e.g. ICMP request、ICMP reply)。e.g.
5a:d7:49:85:d9:da > 3e:3f:34:6b:0a:3d, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 53435, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.100 > 192.168.1.102: ICMP echo request, id 11780, seq 13, length 64
3e:3f:34:6b:0a:3d > 5a:d7:49:85:d9:da, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 6503, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.102 > 192.168.1.100: ICMP echo reply, id 11780, seq 13, length 64
删除流表项:
[root@ovs ~]# ovs-ofctl del-flows ovs-switch "icmp"
修改数据包源 IP 地址
添加流表项,修改从 p0 接收到的数据包的源 IP 地址:
[root@ovs ~]# ovs-ofctl add-flow ovs-switch "priority=1, idle_timeout=0, in_port=100, actions=mod_nw_src:9.181.137.1,normal"
[root@ovs ~]# ovs-ofctl dump-flows ovs-switch
NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=3.594s, table=0, n_packets=0, n_bytes=0, idle_age=3, priority=1,in_port=100 actions=mod_nw_src:9.181.137.1,NORMAL
cookie=0x0, duration=8661.508s, table=0, n_packets=98, n_bytes=6972, idle_age=379, priority=0 actions=NORMAL
- in_port:OvS Switch 的输入端口,表示数据包从哪一个 Switch 端口输入
actions=mod_nw_src:9.181.137.1
:修改与匹配域匹配的数据包的三层数据包源 IP 地址
验证结果,从 p0 ping p1,数据包进入 p0 之后源 IP 地址就会修改为 9.181.137.1 了:
# ns0
[root@ovs ~]# ip netns exec ns0 ping 192.168.1.101
PING 192.168.1.101 (192.168.1.101) 56(84) bytes of data.
# ns1
[root@ovs ~]# ip netns exec ns1 tcpdump -i p1 -nntve
tcpdump: listening on p1, link-type EN10MB (Ethernet), capture size 262144 bytes
5a:d7:49:85:d9:da > 62:f0:3e:b6:7d:6f, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 27038, offset 0, flags [DF], proto ICMP (1), length 84)
9.181.137.1 > 192.168.1.101: ICMP echo request, id 11825, seq 1, length 64
删除流表项:
[root@ovs ~]# ovs-ofctl del-flows ovs-switch "in_port=100"
修改数据包 VLAN tag
在该例子中我们使用 ovs-appctl ofproto/trace
指令来生成测试用的模拟数据包,以此测试 OvS 对数据包的转发。
修改 p1 的 VLAN tag 为 101,是 p1 成为 VLAN tag 101 的 Access 口:
[root@ovs ~]# ovs-vsctl show
367a97be-5b4f-40e2-8630-e64f67172fd2
Bridge ovs-switch
Port "p2"
Interface "p2"
type: internal
Port ovs-switch
Interface ovs-switch
type: internal
Port "p0"
Interface "p0"
type: internal
Port "p1"
Interface "p1"
type: internal
ovs_version: "2.0.0"
[root@ovs ~]# ovs-vsctl set Port p1 tag=101
[root@ovs ~]# ovs-vsctl show
367a97be-5b4f-40e2-8630-e64f67172fd2
Bridge ovs-switch
Port "p2"
Interface "p2"
type: internal
Port ovs-switch
Interface ovs-switch
type: internal
Port "p0"
Interface "p0"
type: internal
Port "p1"
tag: 101
Interface "p1"
type: internal
ovs_version: "2.0.0"
现在 p0 和 p1 被 VLAN 隔离了,它们之间无法进行数据交换。在此前提下,我们尝试使用 ovs-appctl ofproto/trace
生成一个从 p0 发送到 p1 的数据包,这个数据包不包含 VLAN tag:
[root@ovs ~]# ovs-appctl ofproto/trace ovs-switch "in_port=100, dl_src=5a:d7:49:85:d9:da, dl_dst=62:f0:3e:b6:7d:6f" -generate
# 描述模拟的 Flow 信息
Flow: metadata=0,in_port=100,vlan_tci=0x0000,dl_src=5a:d7:49:85:d9:da,dl_dst=62:f0:3e:b6:7d:6f,dl_type=0x0000
# 描述匹配成功的流表项
Rule: table=0 cookie=0 priority=0
# 匹配域匹配的流表项动作
OpenFlow actions=NORMAL
# 目的 MAC 地址没被学习到,进行洪泛
no learned MAC for destination, flooding
# 总结整个处理过程
Final flow: unchanged
Relevant fields: skb_priority=0,in_port=100,vlan_tci=0x0000/0x1fff,dl_src=5a:d7:49:85:d9:da,dl_dst=62:f0:3e:b6:7d:6f,dl_type=0x0000,nw_frag=no
# 数据包被发送到 Datapath 的 1,4 号端口。注:这里的编号与 OpenFlow 端口编号不等同。
Datapath actions: 1,4
添加流表项,从 p0 输入的数据包,如果它不含任何 VLAN tag,则为数据包添加 VLAN tag 101
[root@ovs ~]# ovs-ofctl add-flow ovs-switch "priority=3, in_port=100, dl_vlan=0xffff, actions=mod_vlan_vid:101,normal"
[root@ovs ~]# ovs-ofctl dump-flows ovs-switch
NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=2.448s, table=0, n_packets=0, n_bytes=0, idle_age=2, priority=3,in_port=100,vlan_tci=0x0000 actions=mod_vlan_vid:101,NORMAL
cookie=0x0, duration=11641.822s, table=0, n_packets=101, n_bytes=7098, idle_age=985, priority=0 actions=NORMAL
actions=mod_vlan_vid:101,normal
:打上 VLAN tag 101
再次从尝试使用 ovs-appctl ofproto/trace
生成一个从 p0 到 p1 发送不包含 VLAN tag的数据包,这个数据包在进入 p0 之后会被打上 VLAN 100 的 tag:
[root@ovs ~]# ovs-appctl ofproto/trace ovs-switch "in_port=100, dl_src=5a:d7:49:85:d9:da, dl_dst=62:f0:3e:b6:7d:6f" -generate
Flow: metadata=0,in_port=100,vlan_tci=0x0000,dl_src=5a:d7:49:85:d9:da,dl_dst=62:f0:3e:b6:7d:6f,dl_type=0x0000
Rule: table=0 cookie=0 priority=3,in_port=100,vlan_tci=0x0000
OpenFlow actions=mod_vlan_vid:101,NORMAL
forwarding to learned port
Final flow: metadata=0,in_port=100,dl_vlan=101,dl_vlan_pcp=0,dl_src=5a:d7:49:85:d9:da,dl_dst=62:f0:3e:b6:7d:6f,dl_type=0x0000
Relevant fields: skb_priority=0,in_port=100,vlan_tci=0x0000,dl_src=5a:d7:49:85:d9:da,dl_dst=62:f0:3e:b6:7d:6f,dl_type=0x0000,nw_frag=no
Datapath actions: 3
NOTE:虽然通过流表项令从 p0 进来的数据包都打上 VLAN tag 101,但并非是说 p0 就成为了一个 Access 口,p0 依旧无法和 p1 正常通信,因为 p0 不具有 VLAN access 的完整功能。