1. OVS 单个交换机连接两台主机
1.1. 网络拓扑
1.2. 实验过程
1.2.1. 创建一个名为 br 的OVS网桥
sudo ovs-vsctl add-br br
1.2.2. 添加端口
- 添加端口 p1
sudo ovs-vsctl add-port br p1 -- set Interface p1 type=internal ofport_request=10
- 添加端口 p2
sudo ovs-vsctl add-port br p2 -- set Interface p2 type=internal ofport_request=11
1.2.3. 创建主机
- 利用 Linux 网络命令空间创建终端主机 h1
sudo ip netns add h1
- 将之前创建好的 p1 端口链接到 h1 上
sudo ip link set p1 netns h1
- 为 h1 设置 ip 信息和端口
sudo ip netns exec h1 ip addr add 192.168.10.10/24 dev p1
-
启用混杂模式
- 混杂模式就是接收所有经过网卡的数据包,包括不是发给本机的包,即不验证MAC地址
sudo ip netns exec h1 ifconfig p1 promisc up
- 按照同样的设置创建终端主机 h2
sudo ip netns del h2
sudo ip netns add h2
sudo ip link set p2 netns h2
sudo ip netns exec h2 ip addr add 192.168.10.11/24 dev p2
sudo ip netns exec h2 ifconfig p2 promisc up
1.2.4. 配置流表转发规则
sudo ovs-ofctl add-flow br in_port=10,actions=output:11
sudo ovs-ofctl add-flow br in_port=11,actions=output:10
1.2.5. 执行测试
# h1 ping h2
sudo ip netns exec h1 ping 192.168.10.11 -c 3
# h2 ping h1
sudo ip netns exec h2 ping 192.168.10.10 -c 3
1.2.6. 测试结果
从测试结果不难看出,主机 h1 和 h2 能够相互 ping 通,ovs 网络拓扑创建成功。
1.2.7. 完整代码
# 如果要创建的网桥已存在则先删除
sudo ovs-vsctl --if-exists del-br br
# 创建一个名为 br 的OVS网桥
sudo ovs-vsctl add-br br
# 添加端口p1
sudo ovs-vsctl add-port br p1 -- set Interface p1 type=internal ofport_request=10
# 添加端口p2
sudo ovs-vsctl add-port br p2 -- set Interface p2 type=internal ofport_request=11
# 利用Linux网络命名空间创建终端主机 h1
sudo ip netns del h1 # 如果h1已经存在则删除
sudo ip netns add h1
# 将之前创建好的p1端口链接到 h1 上
sudo ip link set p1 netns h1
# 为 h1 设置 ip 信息和端口
sudo ip netns exec h1 ip addr add 192.168.10.10/24 dev p1
# 启用混杂模式
# 混杂模式就是接收所有经过网卡的数据包,包括不是发给本机的包,即不验证MAC地址
sudo ip netns exec h1 ifconfig p1 promisc up
# 按照同样的设置创建终端主机 h2
sudo ip netns del h2
sudo ip netns add h2
sudo ip link set p2 netns h2
sudo ip netns exec h2 ip addr add 192.168.10.11/24 dev p2
sudo ip netns exec h2 ifconfig p2 promisc up
# 配置流表转发规则
sudo ovs-ofctl add-flow br in_port=10,actions=output:11
sudo ovs-ofctl add-flow br in_port=11,actions=output:10
# 执行测试
# h1 ping h2
sudo ip netns exec h1 ping 192.168.10.11 -c 3
# 输出换行
echo -e '\n'
# h2 ping h1
sudo ip netns exec h2 ping 192.168.10.10 -c 3
2. OVS 单个交换机连接三台主机
2.1. 网络拓扑
2.2. 创建端口和主机
sudo ovs-vsctl del-br ovs-switch
sudo ovs-vsctl add-br ovs-switch
# 端口p0
sudo ovs-vsctl add-port ovs-switch p0 -- set Interface p0 type=internal ofport_request=100
sudo ip netns del ns0
sudo ip netns add ns0
sudo ip link set p0 netns ns0
sudo ip netns exec ns0 ip addr add 192.168.1.100/24 dev p0
sudo ip netns exec ns0 ifconfig p0 promisc up # 设置网口为混杂模式
# 端口p1
sudo ovs-vsctl add-port ovs-switch p1 -- set Interface p1 type=internal ofport_request=101
sudo ip netns del ns1
sudo ip netns add ns1
sudo ip link set p1 netns ns1
sudo ip netns exec ns1 ip addr add 192.168.1.101/24 dev p1
sudo ip netns exec ns1 ifconfig p1 promisc up
# 端口p2
sudo ovs-vsctl add-port ovs-switch p2 -- set Interface p2 type=internal ofport_request=102
sudo ip netns del ns2
sudo ip netns add ns2
sudo ip link set p2 netns ns2
sudo ip netns exec ns2 ip addr add 192.168.1.102/24 dev p2
sudo ip netns exec ns2 ifconfig p2 promisc up
使用 ovs-ofctl 创建并测试 OpenFlow 命令:
2.3. 查看 OVS 信息
查看 Open vSwitch 中的端口信息。从输出结果中,可以获得交换机对应的 datapath ID (dpid),以及每个端口的 OpenFlow 端口编号,端口名称,当前状态等等。
sudo ovs-ofctl show ovs-switch
显示如下:
OFPT_FEATURES_REPLY (xid=0x2): dpid:0000bedb3ef8c74e
n_tables:254, n_buffers:0
capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst
100(p0): addr:00:00:00:00:00:00
config: PORT_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:be:db:3e:f8:c7:4e
config: PORT_DOWN
state: LINK_DOWN
speed: 0 Mbps now, 0 Mbps max
OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0
这里不知道为什么三个端口(p0、p1、p2)的地址都是0,暂时没找到原因,因为看的 IBM 的那篇教程上三个地址互不相同,并且现在这篇教程已经 404 了。教程地址:https://www.ibm.com/developerworks/cn/cloud/library/1401_zhaoyi_openswitch/index.html
如果想获得网络接口的 OpenFlow 编号,也可以在 OVS 的数据库中查询:
sudo ovs-vsctl get Interface p0 ofport
100
查看 datapath 的信息:
sudo ovs-dpctl show
# 显示结果
system@ovs-system:
lookups: hit:15 missed:27 lost:0
flows: 0
masks: hit:24 total:0 hit/pkt:0.57
port 0: ovs-system (internal)
port 1: ovs-switch (internal)
port 2: p0 (internal)
port 3: p1 (internal)
port 4: p2 (internal)
查看 MAC 地址:
sudo ip netns exec ns0 ping 192.168.1.100
sudo ip netns exec ns0 ping 192.168.1.101
sudo ip netns exec ns0 ping 192.168.1.102
sudo ovs-appctl fdb/show ovs-switch
port VLAN MAC Age
100 0 be:d1:82:9a:82:7e 16
101 0 f2:64:d0:67:62:0d 6
102 0 fe:16:15:2c:5c:19 2
查看交换机所有表:
sudo ovs-ofctl dump-tables ovs-switch
OFPST_TABLE reply (xid=0x2):
table 0 ("classifier"):
active=1, lookup=60, matched=60
max_entries=1000000
matching:
in_port: exact match or wildcard
eth_src: exact match or wildcard
eth_dst: exact match or wildcard
eth_type: exact match or wildcard
vlan_vid: exact match or wildcard
vlan_pcp: exact match or wildcard
ip_src: exact match or wildcard
ip_dst: exact match or wildcard
nw_proto: exact match or wildcard
nw_tos: exact match or wildcard
tcp_src: exact match or wildcard
tcp_dst: exact match or wildcard
table 1 ("table1"):
active=0, lookup=0, matched=0
(same features)
查看交换机中所有的流表项:
sudo ovs?ofctl dump?flows ovs-switch
cookie=0x0, duration=437.874s, table=0, n_packets=60, n_bytes=4712, priority=0 actions=NORMAL
删除编号为 100 的端口上的所有流表项:
sudo ovs-ofctl del-flows ovs-switch "in_port=100"
2.4. 修改数据包
屏蔽所有进入 OVS 的以太网广播数据包:
sudo 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 协议的广播数据包:
sudo ovs-ofctl add-flow ovs-switch "table=0, dl_dst=01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0, actions=drop"
修改数据包,添加新的 OpenFlow 条目,修改从端口 p0 收到的数据包的源地址为 9.181.137.1 。
sudo ovs-ofctl add-flow ovs-switch "priority=1 idle_timeout=0,in_port=100,actions=mod_nw_src:9.181.137.1,normal"
从端口 p0(192.168.1.100)发送测试数据到端口 p1(192.168.1.101),但是没啥相应。
sudo ip netns exec ns0 ping 192.168.1.101
再打开一个ssh终端,登录进去,运行 tcpdump ,发现接收到的数据包的来源已经被修改为 9.181.137.1 。(需要等待几分钟,才能看到响应)
sudo ip netns exec ns1 tcpdump -i p1 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on p1, link-type EN10MB (Ethernet), capture size 262144 bytes
11:13:09.319092 IP 9.181.137.1 > MininetWiFi: ICMP echo request, id 3620, seq 38, length 64
11:13:10.343063 IP 9.181.137.1 > MininetWiFi: ICMP echo request, id 3620, seq 39, length 64
在这里会发现无法ping通,查了一下资料,大概原因是网络中没有控制器,交换机不知道该干什么,需要手动为 OVS 添加流表转发规则,如下:
sudo ovs-ofctl add-flow ovs-switch in_port=100,actions=output:101
sudo ovs-ofctl add-flow ovs-switch in_port=101,actions=output:100
重定向数据包
添加新的 OpenFlow 条目,重定向所有的 ICMP 数据包到端口 p2:
sudo ovs-ofctl add-flow ovs-switch idle_timeout=0,dl_type=0x0800,nw_proto=1,actions=output:102
从端口 p0 (192.168.1.100)发送数据到端口 p1(192.168.1.101)
sudo ip netns exec ns0 ping 192.168.1.101
在端口 p2 上监控数据,发现数据包已被转发到端口 p2(可能要等几分钟)
sudo ip netns exec ns2 tcpdump -i p2 icmp
# 显示部分结果
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on p2, link-type EN10MB (Ethernet), capture size 262144 bytes
11:18:52.935709 IP 192.168.1.100 > 192.168.1.101: ICMP echo request, id 3668, seq 26, length 64
11:18:53.960649 IP 192.168.1.100 > 192.168.1.101: ICMP echo request, id 3668, seq 27, length 64
11:18:54.983723 IP 192.168.1.100 > 192.168.1.101: ICMP echo request, id 3668, seq 28, length 64
2.5. 修改数据包的 VLAN Tag
除了使用 ping 、tcpdump 和 iperf 等 Linux 命令以外,也可以使用 OVS 提供的 ovs-appctl ofproto/trace 工具来测试 OVS 对数据包的转发状况。ovs-appctl ofproto/trace 可以用来生成测试用的模拟数据包,并一步步的展示 OVS 对数据包的流处理过程。在以下的例子中,演示一下如何使用这个命令:
修改端口 p1 的 VLAN tag 为 101,使端口 p1 成为一个隶属于 VLAN 101 的端口:
sudo ovs-vsctl set Port p1 tag=101
现在由于端口 p0 和 p1 属于不同的 VLAN,它们之间无法进行数据交换。使用 ovs-appctl ofproto/trace 生成一个从端口 p0 发送到端口 p1 的数据包,这个数据包不包含任何 VLAN tag,并观察 OVS 的处理过程:
sudo ovs-appctl ofproto/trace ovs-switch in_port=100,dl_src=be:d1:82:9a:82:7e,dl_dst=f2:64:d0:67:62:0d -generate
注意:上面第一个 MAC地址是 p0 的,第二个 MAC 地址是 p1 的。
显示结果:
Flow: in_port=100,vlan_tci=0x0000,dl_src=be:d1:82:9a:82:7e,dl_dst=f2:64:d0:67:62:0d,dl_type=0x0000
bridge("ovs-switch")
--------------------
0. in_port=100, priority 1
mod_nw_src:9.181.137.1
NORMAL
-> no learned MAC for destination, flooding
Final flow: unchanged
Megaflow: recirc_id=0,eth,in_port=100,vlan_tci=0x0000/0x1fff,dl_src=be:d1:82:9a:82:7e,dl_dst=f2:64:d0:67:62:0d,dl_type=0x0000
Datapath actions: 1,4
-
在第一行输出中:Flow: 之后的字段描述了输入的流的信息。由于没有指定太多信息,所以多数字段 (例如 dl_type 和 vlan_tci )被 OVS 设置为空值。 -
bridge("ovs-switch") 下面的第一项描述了匹配成功的流表项。 -
在接下来的一行输出中描述了实际执行的操作。 -
最后一段以 Final flow: 开始的字段是整个处理过程的总结,Datapath actions: 4,1 代表数据包被发送到 datapath 的 4 和 1 号端口。
创建一条新的 Flow:对于从端口 p0 进入交换机的数据包,如果它不包含任何 VLAN tag,则自动为它添加 VLAN tag 101。
sudo ovs-ofctl add-flow ovs-switch "priority=3,in_port=100,dl_vlan=0xffff,actions=mod_vlan_vid:101,normal"
再次尝试从端口 p0 发送一个不包含任何 VLAN tag 的数据包,发现数据包进入端口 p0 之后, 会被加上 VLAN tag101, 同时转发到端口 p1 上。
sudo ovs-appctl ofproto/trace ovs-switch in_port=100,dl_src=be:d1:82:9a:82:7e,dl_dst=f2:64:d0:67:62:0d -generate
输出结果
Flow: in_port=100,vlan_tci=0x0000,dl_src=be:d1:82:9a:82:7e,dl_dst=f2:64:d0:67:62:0d,dl_type=0x0000
bridge("ovs-switch")
--------------------
0. in_port=100,vlan_tci=0x0000, priority 3
mod_vlan_vid:101
NORMAL
-> learned that be:d1:82:9a:82:7e is on port p0 in VLAN 101
-> no learned MAC for destination, flooding
Final flow: in_port=100,dl_vlan=101,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=be:d1:82:9a:82:7e,dl_dst=f2:64:d0:67:62:0d,dl_type=0x0000
Megaflow: recirc_id=0,eth,in_port=100,vlan_tci=0x0000,dl_src=be:d1:82:9a:82:7e,dl_dst=f2:64:d0:67:62:0d,dl_type=0x0000
Datapath actions: push_vlan(vid=101,pcp=0),1,pop_vlan,3,push_vlan(vid=101,pcp=0),4
反过来从端口 p1 发送数据包,由于 p1 现在是带有 VLAN tag 101 的 Access 类型的端口,所以数据包进入端口 p1 之后,会被 OVS 添加 VLAN tag 101 并发送到端口 p0 。
sudo ovs-appctl ofproto/trace ovs-switch in_port=101,dl_dst=f2:64:d0:67:62:0d,dl_src=be:d1:82:9a:82:7e -generate
显示结果:
Flow: in_port=101,vlan_tci=0x0000,dl_src=be:d1:82:9a:82:7e,dl_dst=f2:64:d0:67:62:0d,dl_type=0x0000
bridge("ovs-switch")
--------------------
0. priority 0
NORMAL
-> learned that be:d1:82:9a:82:7e is on port p1 in VLAN 101
-> learned port is input port, dropping
Final flow: unchanged
Megaflow: recirc_id=0,eth,in_port=101,vlan_tci=0x0000/0x1fff,dl_src=be:d1:82:9a:82:7e,dl_dst=f2:64:d0:67:62:0d,dl_type=0x0000
Datapath actions: drop
参考:基于 Open vSwitch 的 OpenFlow 实践
3. OVS 两个交换机
参考《重构网络,SDN架构与实现》这本书。
预期构建的网络拓扑图如下所示。
3.1. 创建 OVS 交换机实例
sudo ovs-vsctl --if-exists del-br s1
sudo ovs-vsctl --if-exists del-br s2
sudo ovs-vsctl add-br s1
sudo ovs-vsctl add-br s2
3.2. 添加端口
在创建 OVS 实例时,会自动创建 LOCAL 端口,名称和交换机一致。为了实现通信,还需要创建其他的数据端口。没有端口汇聚的情况下,port 和 interface 是一一对应的。创建端口之后,需要对端口/接口的端口号等特性进行配置,比如设置 OpenFlow 端口号为 10。将端口设置为 Internal 模式,从而在系统中创建一个虚拟的网络设备。
sudo ovs-vsctl add-port s1 p1 -- set Interface p1 type=internal ofport_request=10
# ethtool -i p1
sudo ovs-vsctl add-port s1 p2 -- set Interface p2 type=internal ofport_request=11
# ethtool -i p2
sudo ovs-vsctl add-port s2 p3 -- set Interface p3 type=internal ofport_request=1
# ethtool -i p3
sudo ovs-vsctl add-port s2 p4 -- set Interface p4 type=internal ofport_request=2
# ethtool -i p4
3.3. 创建命名空间
为了不影响网络中已有地址发生冲突,需要创建 Namespace 作为实验的终端主机。创建主机之后,需要为其设置虚拟 IP,最后将其连接到 OVS 的数据端口,完成主机接入工作。本实验中,创建了 h1 和 h2 两个虚拟主机,设置 IP 分别为 192.168.10.10 和 192.168.10.11,并将这两个主机分别接入到两个 OVS 实例上。
sudo ip netns del h1
sudo ip netns add h1
sudo ip link set p1 netns h1
sudo ip netns exec h1 ip addr add 192.168.10.10/24 dev p1
sudo ip netns exec h1 ifconfig p1 promisc up
同理创建 h2,并设置 IP 为 192.168.10.11 并将其接入到 s2 的 p4 端口上。
sudo ip netns del h2
sudo ip netns add h2
sudo ip link set p4 netns h2
sudo ip netns exec h2 ip addr add 192.168.10.11/24 dev p4
sudo ip netns exec h2 ifconfig p4 promisc up
接下来,建立交换机之间的链路。首先,需要将对应的端口设置为 patch 类型。
sudo ovs-vsctl set interface p2 type=patch
sudo ovs-vsctl set interface p3 type=patch
然后,需要创建 p2 到 p3 的内部链路,命令如下。
sudo ovs-vsctl set interface p2 options:peer=p3
sudo ovs-vsctl set interface p3 options:peer=p2
3.4. 添加流表项
最后,需要向交换机添加对应的流表项,将交换机 s1 从 10 端口进入的数据转发到 11 端口,反向同理,s2 操作同 s1,具体操作如下。
sudo ovs-ofctl add-flow s1 in_port=10,actions=output:11
sudo ovs-ofctl add-flow s1 in_port=11,actions=output:10
sudo ovs-ofctl add-flow s2 in_port=2,actions=output:1
sudo ovs-ofctl add-flow s2 in_port=1,actions=output:2
3.5. 测试结果
创建成功,在 Network Namespace h1 环境下执行 sudo ip netns exec h1 ping 192.168.10.11 -c 3 的操作,结果成功,如下所示。
3.6. 完整代码
sudo ovs-vsctl --if-exists del-br s1
sudo ovs-vsctl --if-exists del-br s2
sudo ovs-vsctl add-br s1
sudo ovs-vsctl add-br s2
sudo ovs-vsctl add-port s1 p1 -- set Interface p1 type=internal ofport_request=10
sudo ovs-vsctl add-port s1 p2 -- set Interface p2 type=internal ofport_request=11
sudo ovs-vsctl add-port s2 p3 -- set Interface p3 type=internal ofport_request=1
sudo ovs-vsctl add-port s2 p4 -- set Interface p4 type=internal ofport_request=2
sudo ip netns del h1
sudo ip netns add h1
sudo ip link set p1 netns h1
sudo ip netns exec h1 ip addr add 192.168.10.10/24 dev p1
sudo ip netns exec h1 ifconfig p1 promisc up
sudo ip netns del h2
sudo ip netns add h2
sudo ip link set p4 netns h2
sudo ip netns exec h2 ip addr add 192.168.10.11/24 dev p4
sudo ip netns exec h2 ifconfig p4 promisc up
sudo ovs-vsctl set interface p2 type=patch
sudo ovs-vsctl set interface p3 type=patch
sudo ovs-vsctl set interface p2 options:peer=p3
sudo ovs-vsctl set interface p3 options:peer=p2
sudo ovs-ofctl add-flow s1 in_port=10,actions=output:11
sudo ovs-ofctl add-flow s1 in_port=11,actions=output:10
sudo ovs-ofctl add-flow s2 in_port=2,actions=output:1
sudo ovs-ofctl add-flow s2 in_port=1,actions=output:2
sudo ip netns exec h1 ping 192.168.10.11 -c 3
do ip link set p4 netns h2
sudo ip netns exec h2 ip addr add 192.168.10.11/24 dev p4
sudo ip netns exec h2 ifconfig p4 promisc up
sudo ovs-vsctl set interface p2 type=patch
sudo ovs-vsctl set interface p3 type=patch
sudo ovs-vsctl set interface p2 options:peer=p3
sudo ovs-vsctl set interface p3 options:peer=p2
sudo ovs-ofctl add-flow s1 in_port=10,actions=output:11
sudo ovs-ofctl add-flow s1 in_port=11,actions=output:10
sudo ovs-ofctl add-flow s2 in_port=2,actions=output:1
sudo ovs-ofctl add-flow s2 in_port=1,actions=output:2
sudo ip netns exec h1 ping 192.168.10.11 -c 3
|