slacr_

Just to record my life and thoughts.
笔记/编程/杂乱/极简

[Mininet]SDN实验

Oct 19, 2023Network8594 words in 57 min

关于

Mininet是一个用于构建虚拟网络环境的开源软件。它允许用户在单个主机上创建一个全功能的网络拓扑,并模拟网络设备(如交换机、路由器和主机)之间的通信。

Mininet基于Linux命名空间和虚拟化技术,可以在一个主机上创建多个虚拟网络节点,并通过虚拟网络设备将这些节点连接起来。用户可以使用Python编写脚本来定义和配置网络拓扑,并使用Mininet提供的命令行界面来管理和控制虚拟网络。

Mininet的主要用途包括网络研究、网络教育和网络应用开发。通过使用Mininet,用户可以在一个简单的环境中快速搭建和测试各种网络场景,而无需真实的硬件设备。

Mininet支持OpenFlow协议,可以与OpenFlow控制器(如POX、RYU等)配合使用,实现对虚拟网络的控制和管理。此外,Mininet还提供了一些实用工具和函数库,用于网络拓扑的创建、节点的配置和网络性能的测量。

安装

官方提供了三种安装方式:

  • MininetVM, 通过一个安装一个内置了Mininet及相关组件的Linux发行版虚拟机
  • Native Installation from Source, 利用源代码安装
  • Installation from Packages, 在Debian11+或Ubuntu发行版(Ubuntu是Debian的一个常见分支)上使用apt包管理工具下载.

官方的不带桌面环境, 因为github限制了release文件最大2G, 不适合发布大文件.

在已有的Ubuntu最近的发行版上

1
2
3
4
5
# lsb_release -a # 查看LSB信息
apt-get update
apt-get install mininet
...
mn --version

测试

1
2
service openvswitch-switch start
mn --switch ovsbr --test pingall

这个只安装了mininet, 下载用来演示的脚本和额外软件还是要用git = =.

1
2
3
4
5
6
7
apt-get install git
git config --global user.name "yourname"
git config --global user.email "xx@xx.com"
git config --global --list

git clone https://www.github.com/mininet/mininet.git
mininet/util/install.sh -fw

如果你https connection time out 的话, 一般是网络的问题, 可以配置 ssh 下载

1
ssh-keygen -t ed25519 -C "your_email@example.com"

然后将生成在 ~/.ssh 下的公钥添加到github上, 这个ed25519 是一个基于AES的 数字签名算法,安全性在 RSA 2048 与 RSA 4096 之间,性能却极高. 用传统的RSA也行

1
2
# 测试
ssh -T git@github.com

直接使用虚拟机

可以在GitHub上下载最新的release https://github.com/mininet/mininet/releases/tag/2.3.0, 学校给的.ova虚拟机镜像太tm的旧了,看了一下版本–Linux version 3.13.0-24-generic (buildd@batsu) (gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1) ) #47-Ubuntu SMP Fri May 2 23:30:00 UTC 2014 – 内核版本号3.13.0-24-generic应用于2014年4月17日发布的 Ubuntu 14.04 LTS,官方推荐使用最新的 Ubuntu 20.04.1 VM image.

解压完得到两个文件, .ovf 虚拟机配置文件 和 .vmdk 虚拟磁盘文件, 用虚拟机软件打开配置文件就行了。

可以改下root密码登录

1
2
3
4
5
6
sudo passwd root
# your_password
su - root
# 测试
service openvswitch-switch start
mn --switch ovsbr --test pingall

一切正常的话:

SDN实验

学校实验课的内容, 按图索骥.

实验一 mininet可视化创建网络拓扑

  1. 用可视化工具 miniedit 创建一个网络拓扑,进行相应配置后,保存为 python 脚本,运行 python 脚本重现拓扑;
  2. 命令行创建拓扑:
    i. 最小的网络拓扑,一个交换机下挂两个主机。
    ii. 每个交换机连接一个主机,交换机间相连接。本例:4 个主机,4 个交换机。
    iii. 每个主机都连接到同一个交换机上。本例:3 个主机,一个交换机。
  3. 交互式界面创建主机、交换机等
  4. 对节点间进行 ping 测试
1
python mininet/examples/miniedit.py

不出意外的话, 会出现意外, _tkinter.TclError: no display name and no $DISPLAY environment variable
这是因为没安装GUI, 官方给的镜像就是不带桌面的, 所以python的tkinter模块启动失败。既然实验要求安装使用GUI工具, 给它安个桌面。

Ubuntu22 换源与磁盘扩容

先换个软件源, 用国内镜像源,默认国外的太慢, 顺便把VM内存参数改大点, 这里用的清华的镜像源。

磁盘分区扩容, 这里把VM磁盘空间设置扩展成20G, 之后还要在系统中重新分区与创建FS与挂载

1
2
3
4
5
6
7
8
9
df -Th
# sda 的格式是MBR分区 要扩展逻辑分区sda5 要先扩展扩展分区sda2
apt install cloud-guest-utils
growpart /dev/sda 2
growpart /dev/sda 5
partprobe
# 扩展文件系统, xfs用 xfs_growfs, 这里是ext4 用resize2fs
resize2fs /dev/sda5
df -h

如果没挂梯子, 建议换下源.

1
2
cp /etc/apt/sources.list /etc/apt/sources.list.backup
vi /etc/apt/sources.list

删除原本的内容,这样粘贴, vim 在insert模式, 不要带中文的内容, 不然粘贴不了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse

deb http://security.ubuntu.com/ubuntu/ jammy-security main restricted universe multiverse
# deb-src http://security.ubuntu.com/ubuntu/ jammy-security main restricted universe multiverse

# 预发布软件源,不建议启用
# deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-proposed main restricted universe multiverse
# # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-proposed main restricted universe multiverse

这样执行会提示 Certification vertification failed 证书验证失败, 解决方法:
重新安装两个证书相关组件

1
apt-get reinstall apt-transport-https ca-certificates

然后执行apt update 就没问题了, 接下来正式安装桌面环境。

安装桌面环境

1
apt update && apt upgrade

下载登录管理器, 原本默认的是GDM3, 下载SDDM,SLiM或LightDM

1
2
3
apt install lightdm
apt install tasksel
tasksel

tasksel是Debian下提供的一个简单的可以快速安装桌面环境和web服务的工具
方向键移动, 空格选中/取消, 回车确认, tab 移动

如果出现 tasksel: apt-get failed(100), 是firefox snap 安装出现问题

1
2
snap install firefox
apt --fix-broken install

如果出现 340.439687 proc: bad value for 'hideid ’

1
apt-get install --reinstall xserver-xorg

切换登录管理器

1
2
dpkg-reconfigure [lightdm/slim/GDM3]
service [lightdm/slim/GDM3] restart

打开终端 alt+f2, 输入uxterm 打开终端, 不知道为什么 gnome-terminal 打不开(之后把系统环境改成中文又能打开了, ctrl alt t)

这时候再执行python mininet/examples/miniedit.py

如果出现 $DIPLAY 变量错误是因为还没设置tkinter的输出目标, 将默认的X11显示器设置为输出对象.

1
2
export DISPLAY=:0
sudo python2.7 mininet/examples/miniedit.py

virtualenv 使用python虚拟环境

virtualenv 是一个用于创建隔离的 Python 环境的工具。它可以帮助你在不同的项目中使用不同版本的 Python 和依赖库,从而避免冲突。

1
2
3
4
5
6
7
8
9
10
11
12
13
## 下载 virtualenv
apt-get install virtualenv

## 添加虚拟环境
virtualenv --python=/usr/bin/python2.7 <path/to/new/virtualenv>
# virtualenv ./sdn_env -p python2.7

# 激活并进入虚拟环境
source <path/to/new/virtualenv>/bin/activate
# 退出虚拟环境
deactive
# 删除虚拟环境
rm -rf <path/to/virtualenv>

解决 右上角网络图标不显示的问题

在网上找了一圈, 大部分答案都是让你重装NetworkManager或者让你修改NetworkManager的配置文件, 结果完全没用. 正确的做法是切换Netplan中的renderer, 原本是networkd, 一个系统级别的网络配置和管理工具, 要配置成NetworkManager才能显示在GUI中.https://askubuntu.com/a/1178044/1743772

1
vi /etc/netplan/xxx.yaml

1
netplan apply

实验

s1
1
sudo python2.7  mininet/examples/miniedit.py

s2

创建网络拓扑

1
2
3
sudo mn --topo single,2
sudo mn --topo linear,4
sudo mn --topo single,3

s3

s4

实验二 python 脚本构建网络拓扑

mininet api

mininet 的 python api
https://mininet.org/api/annotated.html

mn 部分交互式命令

help 查看帮助
help <arg> 查看对应命令帮助
net 显示网络拓扑
nodes 显示所有节点
dump 显示节点配置
iperf 网络性能测试
py 执行python表达式
sh 执行shell命令
exit/quit 退出
dpctl 交换机交互
节点操作
h1 ping h2 ping
h1 ifconfig 网卡信息
gterm/xterm <node> 打开xterm终端

实验

  1. 编写 Python 脚本,定义如下拓扑:
    i. topo linear,4
    ii. topo single,3
    iii. topo tree,depth=2,fanout=2
    iv. 1 个交换机,2 个主机,并且赋予主机 IP 地址。

  2. 编写 Python 脚本,对网络性能进行限制。观察下面给出的脚本文件,addHost()语法可以对主机 cpu 进行设置,以百分数的形式;addLink()语法可以设置带宽 bw、延迟 delay、最大队列的大小 max_queue_size、损耗率 loss。

s1
  • s1.1
1
2
3
4
5
6
7
8
9
10
11
12
13
from mininet.net import Mininet
from mininet.topo import LinearTopo
from mininet.cli import CLI

def create_linear_topology():
topo = LinearTopo(k=4) # 创建线性拓扑,k=4表示有4个主机
net = Mininet(topo) # 创建Mininet网络
net.start() # 启动网络
CLI(net) # 打开Mininet命令行界面
net.stop() # 停止网络

if __name__ == "__main__":
create_linear_topology()

四个交换机线性连接, 并各自连接一台主机.

  • s1.2
1
2
3
4
5
6
7
8
9
10
11
12
13
from mininet.net import Mininet
from mininet.topo import SingleSwitchTopo
from mininet.cli import CLI

def create_single_topology():
topo = SingleSwitchTopo(k=3) # 创建单一拓扑,k=3表示有3个主机
net = Mininet(topo) # 创建Mininet网络
net.start() # 启动网络
CLI(net) # 打开Mininet命令行界面
net.stop() # 停止网络

if __name__ == "__main__":
create_single_topology()

一台交换机连接三台主机

  • s1.3
1
2
3
4
5
6
7
8
9
10
11
12
13
from mininet.net import Mininet
from mininet.topolib import TreeTopo
from mininet.cli import CLI

def create_tree_topology():
topo = TreeTopo(depth=2, fanout=2) # 创建树状拓扑,深度为2,扇出为2
net = Mininet(topo) # 创建Mininet网络
net.start() # 启动网络
CLI(net) # 打开Mininet命令行界面
net.stop() # 停止网络

if __name__ == "__main__":
create_tree_topology()

树形拓扑, 出度为2.

s2
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
from mininet.net import Mininet
from mininet.node import CPULimitedHost
from mininet.node import TCIntf
from mininet.link import TCLink
from mininet.cli import CLI

def create_network_topology():
net = Mininet()

# 添加主机,并设置CPU限制为20%
h1 = net.addHost('h1', cls=CPULimitedHost, cpu=0.2)
h2 = net.addHost('h2', cls=CPULimitedHost, cpu=0.2)

# 添加交换机
s1 = net.addSwitch('s1', cls=OVSSwitch, failMode='standalone')

# 添加链路,并设置带宽、延迟、最大队列大小和损耗率
net.addLink(h1, s1, bw=10, delay='10ms', max_queue_size=100, loss=10)
net.addLink(h2, s1, bw=10, delay='10ms', max_queue_size=100, loss=10)

net.start()
CLI(net)
net.stop()

if __name__ == "__main__":
create_network_topology()

会报错 cgroups not mounted on /sys/fs/cgrops, cgroups(Control Groups)是Linux内核中的一个功能,用于对进程组进行资源管理和限制.

参看原码会发现执行了grep cgrop /proc/mount 并做判断是否装载了cgroups文件系统

这里显示的是 cgroup v2, 目前mininet并没有提供对 cgroup v2的支持, 而在Ubuntu 21.10以上和众多发行版中已开始默认使用 cgroup v2https://github.com/opencontainers/runc/blob/main/docs/cgroup-v2.md#cgroup-v2, 所以需要切换cgroups版本.

1
vi /etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULT 或 GRUB_CMDLINE_LINUX中填入
systemd.unified_cgroup_hierarchy=no, 表示允许使用cgroup-v1

1
2
3
4
# 更新grub引导文件
sudo update-grub
# 确认新生成的文件
vi /boot/grub/grub.cfg

重启生效

1
grep cgroup /proc/mounts

cgroup-v1 和 v2 并存

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
34
35
36
37
38
from mininet.net import Mininet
from mininet.link import TCLink
from mininet.cli import CLI
from mininet.node import *

def create_network_topology():
net = Mininet()

# 添加交换机
s1 = net.addSwitch('s1', cls=OVSSwitch, failMode='standalone')

# 添加主机,并设置CPU限制为20%
h1 = net.addHost('h1', cls=CPULimitedHost, cpu=0.2, cores=1)
h2 = net.addHost('h2', cls=CPULimitedHost, cpu=0.2, cores=1)

# 添加链路,并设置带宽、延迟、最大队列大小和损耗率
net.addLink(h1, s1, cls=TCLink, bw=10, delay='10ms', max_queue_size=100, loss=20)
net.addLink(h2, s1, cls=TCLink, bw=10, delay='10ms', max_queue_size=100, loss=20)

net.start()

# 设置网卡参数
intf1 = s1.intf('s1-eth1')
intf1.setIP('10.0.0.11/24')
intf2 = s1.intf('s1-eth2')
intf2.setIP('10.0.0.22/24')

intf_h1 = h1.intf('h1-eth0')
intf_h1.setIP('10.0.0.1/24')
intf_h2 = h2.intf('h2-eth0')
intf_h2.setIP('10.0.0.2/24')

CLI(net)
net.stop()

if __name__ == "__main__":
create_network_topology()

限制了带宽10M, 对丢包率每条链路20%, h1到h2 应该是36%, 发了20个包丢一半.

实验三 Open vSwitch应用实践

Open vSwitch

Open vSwitch(下面简称为 OVS)是由 Nicira Networks 主导的,运行在虚拟化平台(例如 KVM,Xen)上的虚拟交换机。在虚拟化平台上,OVS 可以为动态变化的端点提供 2 层交换功能,很好的控制虚拟网络中的访问策略、网络隔离、流量监控等等。
OVS 遵循Apache 2.0 许可证, 能同时支持多种标准的管理接口和协议。OVS 也提供了对OpenFlow 协议的支持,用户可以使用任何支持 OpenFlow 协议的控制器对 OVS 进行远程管理控制。

floodlight安装

安装配置jdk8

下载安装 Oracle-jdk8

1
2
3
4
5
6
7
8
9
10
tar -xzf /xxx/jdk-8u391-linux-x64.tar.gz -C /home/dev

vi /etc/profile.d/java.sh

export JAVA_HOME=/home/dev/jdk-8u391-linux-x64
export PATH= JAVA_HOME/bin: PATH

source /etc/profile.d/java.sh

java -version

编译安装
1
2
3
4
5
6
7
sudo apt-get install ant build-essential maven
git clone git@github.com:floodlight/floodlight.git -b v1.2
cd floodlight
ant

# 启动
java -jar target/floodlight.jar

实验

  1. 交换机创建和端口 p0 的配置
    创建一个新的 OVS 交换机:
    $ ovs-vsctl add-br [名称]
    如:sudo ovs-vsctl add-br ovs-switch
    创建一端口 p0,设置端口 p0 的 OpenFlow 端口编号为 100,如果在创建端口的时候没
    有指定 OpenFlow 端口编号,OVS 会自动生成一个。
    $ sudo ovs-vsctl add-port ovs-switch p0
    set Interface p0 of port_request=100
    设置网络接口设备的类型为“internal”。对于 internal 类型的的网络接口,OVS 会同时
    在 Linux 系统中创建一个可以用来收发数据的模拟网络设备。我们可以为这个网络设备
    配置 IP 地址、进行数据监听等等。查看结果。
    $ sudo ovs-vsctl set Interface p0 type=internal
    $ sudo ethtool -i p0

  2. 虚拟网络空间的配置
    为了避免网络接口上的地址和本机已有网络地址冲突,我们可以创建一个虚拟网络空间
    ns0,把 p0 接口移入网络空间 ns0,并配置 IP 地址为 192.168.1.100。
    $ 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

  3. 查看 OVS 交换机
    通过 sudo ovs-vsctl show 命令查看 OVS 交换机的信息。

  4. 使用 ovs-ofctl 创建并测试 OpenFlow 命令
    查看 Open vSwitch 中的端口信息。从输出结果中,可以获得交换机对应的 datapath ID
    (dpid),以及每个端口的 OpenFlow 端口编号,端口名称,当前状态。
    ①查看交换机中的所有 Table: $ sudo ovs-ofctl dump-tables ovs-switch
    ②查看交换机中的所有流表项:$sudo ovs-ofctl dump-flows ovs-switch
    ③查看交换机上的端口信息:$ sudo ovs-ofctl show ovs-switch
    ④两个端口相互测试:$ sudo ip netns exec ns0 ping 192.168.1.101

  • 进阶技能
  1. 任务与要求通过创建拓扑连接 floodlight 控制,通过控制器的图像化界面,编辑交换机流
    表规则来控制数据转发。自主完成以下任务。
  1. 在虚拟机环境下,打开终端运行 Floodlight;提示:
    cd floodlight // 进入 floodlight 文件夹
    java -jar target/floodlight.jar //运行 floodlight 控制器
  2. 创建 1 个交换机连接 4 台主机的拓扑,并连接控制器。提示:
    sudo mn --topo single,4 --controller remote,ip=127.0.0.1,port=6653
  3. 打开 floodlight 图像化界面并查看网页中的 topo。提示:
    localhost:8080/ui/pages/index.html //在虚拟机的浏览器中访问控制器的 web UI
  4. 在 mininet 中执行 pingall 命令,在网页中查看交换机 s1 的流表。
  5. 在网页中给交换机中添加一条流表项,使得 h1 不能访问其他所有主机。并在
    mininet 中进行 pingall 测试。注意:表的序号是从 0 开始的。
  6. 删除之前添加的流表项,在 mininet 中进行 pingall 测试。
s1

交换机创建和端口 eth0 的配置

1
2
3
4
5
6
7
8
# 创建ovs交换机
ovs-vsctl add-br ovs-switch
# 创建端口并设置端口编号
sudo ovs-vsctl add-port ovs-switch eth0 -- set Interface eth0 ofport_request=100
# 设置网络接口类型 internal
ovs-vsctl set Interface eth0 type=internal
# 查看网口信息
ethtool -i eth0

s2

虚拟网络空间的配置

1
2
3
4
sudo ip netns add ns0 
sudo ip link set eth0 netns ns0
sudo ip netns exec ns0 ip addr add 192.168.128.100/24 dev eth0
sudo ip netns exec ns0 ifconfig eth0 promisc up
s3

查看 OVS 交换机

1
ovs-vsctl show

s4

使用 ovs-ofctl 创建并测试 OpenFlow 命令

1
2
3
4
5
6
7
8
# 查看交换机中的所有 Table
sudo ovs-ofctl dump-tables ovs-switch
# 查看交换机中的所有流表项
sudo ovs-ofctl dump-tables ovs-switch
# 查看交换机上的端口信息
sudo ovs-ofctl dump-tables ovs-switch
# 两个端口相互测试
sudo ip netns exec ns0 ping 192.168.128.1

进阶
  • j1

  • j2

  • j3
    一开始会错误

发现OF协议版本不兼容
floodlight 只支持到1.4, ovs用的是1.6

指定OFv1.0版本

1
sudo mn --topo single,5 --mac --controller remote --switch ovs,protocols=OpenFlow10

进入web面板, http://localhost:8080/ui/index.html

  • j4
    使用floodlight 控制器 北向REST API添加静态流表
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
import requests
import json

controller_ip = '127.0.0.1'
switch_dpid = '00:00:00:00:00:00:00:01'
h1_mac_address = '00:00:00:00:00:01'
blocking_port = '1'

url = f'http://{controller_ip}:8080/wm/staticflowpusher/json'

headers = {
'Content-Type': 'application/json'
}

data = {
'switch': switch_dpid,
'name': 'block-h1',
'priority': '100',
'in_port': blocking_port,
'eth-src': h1_mac_address,
'active': 'true',
'actions': 'drop' # 阻断动作:丢弃数据包
}

response = requests.post(url, headers=headers, data=json.dumps(data))

if response.status_code == 200:
print('流表项成功添加,h1 发出的数据包将被阻断.')
else:
print('无法添加流表项.')
print(response.text)

  • j5
1
curl -X DELETE http://127.0.0.1:8080/wm/staticflowpusher/json -d '{"name":"block-h1", "switch":"00:00:00:00:00:00:00:01"}'

h1通信恢复

实验四 OpenFlow协议分析

实验

  1. 可视化界面搭建网络拓扑
  2. 配置 PC 的 IP 信息
  3. Wireshark 抓包并分析
  • 进阶技能
    以前面的基本技能训练为基础, 通过配置远程控制器要求完成一下任务。
  1. 对基础技能创建的 lab_3.py 进行修改,将控制器设置为“RemoteController”,
    端口号设置为“6653”;
  2. 启动抓包软件 wireshark;
  3. 启动 RYU 控制器;
  4. 运行 lab_3.py;
  5. 在 wireshark 中查看控制器与交换机建立连接的过程,画出通信流程图,并对通
    信过程中的具体消息进行分析;
  6. 在终端查看交换机 s1 的流表,对两台主机相互 ping 测试, 在终端查看交换机
    s1 的流表变化并分析;
s1

可视化界面搭建网络拓扑

s2

配置 PC 的 IP 信息

s3

wireshark过滤对应版本的OF协议数据报

可以看到控制器和交换机之间的通信流程:

一开始控制器发送OFPT_HELLO报文, 这是OpenFlow协议的起始消息,用于建立控制器和交换机之间的连接。控制器发送此消息以确认其支持的OpenFlow版本,并与交换机进行握手。

然后OFPT_FEATURES_REQUEST, 控制器向交换机发送的请求消息,用于获取交换机的功能信息。控制器通过此消息了解交换机的特性和能力.

OFPT_FLOW_MOD, 控制器向交换机发送的消息,用于配置交换机的流表项(Flow Entry)。

OFPT_PORT_STATUTS, 交换机向控制器发送的消息,用于通知控制器有关交换机端口状态的变化。

OFPT_FEATURES_REPLY, 这是交换机向控制器发送的响应消息,用于回复控制器的FEATURES_REQUEST消息。此消息中包含了交换机的功能信息,如支持的OpenFlow版本、端口数量、支持的特性等。

周期性发送 OFPT_ECHO_REPLY和OFPT_ECHO_REQUEST, 用于检测控制器和交换机之间连通性

  • 执行h1 ping h3

会看到OFPT_PACKET_IN, OFPT_FLOW_MOD, OFPT_PACKET_OUT 消息. 一开始没有流表项, h1 发送给h3 的ICMP报文经过s1 无法匹配转发规则, s1 向控制器发送OFPT_PACKET_IN 请求下发流表; 控制器向交换机发送OFPT_PACKET_OUT消息, 将数据包发送给交换机进行转发, 并发送OFPT_FLOW_MOD, 用于添加或修改流表项.

进阶

安装Ryu

1
2
3
git clone https://github.com/faucetsdn/ryu.git
cd ryu
pip3 install .

运行 ryu-manager 会报错, 因为python版本的问题 #issue169

1
2
3
4
5
6
7
8
9
10
11
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt-get install virtualenv python3.9 python3.9-distutils

#create the virtualenv
virtualenv -p /usr/bin/python3.9 ryu-python3.9-venv
source ryu-python3.9-venv/bin/activate
echo $VIRTUAL_ENV #check if we are indeed in the virtual environment
pip install ryu
pip uninstall eventlet
pip install eventlet==0.30.2
ryu-manager --help

启动ryu控制器

wireshark抓取并查看对应的OF消息

和前面使用OVS-controller通信流程差不多, 多了OFPT_MULTI_PART_REQUEST, OFPMP_PORT_DESC,OFPT_MULTI_PART_REPLY消息

流程图

实验五 OpenFlow流表操作

启动远程控制器Ryu,并在图 2 所示的网络拓扑中,完成相关 IP 配置,随后进行流表的添加与删除等操作,查看流表变化及动作结果,并给出合理解释。

实验

先清除mininet缓存, 防止先前流表干扰

1
sudo mn -c 

网络拓扑构建

查看节点和连接信息

查看流表

执行pingall

因为默认actions为空, 故丢弃流经交换机数据包

添加流表项测试

一开始添加了单向规则, ICMP报文无法响应, 故任意两个节点之间都ping不通, 添加了双向规则后, h2和h3 之间可正常连接.

删除流表

先用控制器生成所有流表

1
dpctl add-flows in_port=*,actions=output:controller

添加丢弃数据包的流表, 所有交换机的2号端口全部丢弃

命令顺序测试

实验六 POX 控制器编程

  1. 使用POX 控制器验证Hub 模块操作
  2. 使用POX 控制器验证Switch 模块
    进阶:完成二层交换机对于一定MAC地址发送的数据包的过滤

实验

s1

建立拓扑

1
sudo mn --topo=single,3 --mac --controller=remote,ip=127.0.0.1,port=6633 --switch ovsk,protocols=OpenFlow10

debug模式运行hub模块

1
./pox.py log.level --DEBUG forwarding.hub

xterm h1 h2 h3, 在h2, h3 上使用tcpdump抓包

1
tcpdump -nn -i h3-eth0

POX控制器模拟实现了HUB的功能, 在集线器上收到数据包将广播到所有端口.

s2
1
./pox.py log.level --DEBUG forowarding.l2_learning

POX控制器模拟实现了L2交换机的功能, 经过交换机的数据包基于ARP表转发.

进阶

实现防火墙功能

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
34
from pox.core import core
import pox.openflow.libopenflow_01 as of

log = core.getLogger()

class SimpleFirewall(object):
def __init__(self):
self.blocked_mac = '00:00:00:00:00:01'
core.openflow.addListeners(self)

def _handle_ConnectionUp(self, event):
log.info("Switch %s connected", event.connection)

def _handle_PacketIn(self, event):
packet = event.parsed

# 检查源MAC地址是否在要阻止的列表中
if packet.src == self.blocked_mac:
log.warning("Blocked packet from MAC %s", packet.src)
# 使用 OFPP_NONE 阻止数据包
msg = of.ofp_packet_out()
msg.data = event.ofp
msg.actions.append(of.ofp_action_output(port=of.OFPP_NONE))
event.connection.send(msg)
else:
# 源MAC地址不在列表中,则转发数据包到所有其他端口
msg = of.ofp_packet_out()
msg.data = event.ofp
msg.actions.append(of.ofp_action_output(port=of.OFPP_ALL))
event.connection.send(msg)

def launch():
core.registerNew(SimpleFirewall)
log.info("SimpleFirewall is running.")

放到pox目录中

1
./pox.py log.level --DEBUG forowarding.l2_learning fw

实现通过mac地址对h1 数据包的过滤

思考题
  1. 在进阶技能中对固定MAC 地址主机发出的数据包进行了限制,那么能否对固定
    MAC 位置的固定端口进行数据包的限制。

可以, 记录mac和端口的映射关系, 对同一设备的不同端口进行过滤.

实验七-八 RYU 控制器编程实践

实验

Ryu实现集线器Hub
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_3
from ryu.lib.packet import packet, ethernet

class SimpleHub(app_manager.RyuApp):
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]

def __init__(self, *args, **kwargs):
super(SimpleHub, self).__init__(*args, **kwargs)

@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_features_handler(self, ev):
datapath = ev.msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser

# 安装流表项,将所有数据包输出到所有端口
match = parser.OFPMatch()
actions = [parser.OFPActionOutput(ofproto.OFPP_FLOOD)]
self.add_flow(datapath, 0, match, actions)

def add_flow(self, datapath, priority, match, actions, buffer_id=None):
ofproto = datapath.ofproto
parser = datapath.ofproto_parser

inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)]
if buffer_id:
mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id,
priority=priority, match=match,
instructions=inst)
else:
mod = parser.OFPFlowMod(datapath=datapath, priority=priority,
match=match, instructions=inst)
datapath.send_msg(mod)

@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def packet_in_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser

# 解析数据包
pkt = packet.Packet(msg.data)
eth_pkt = pkt.get_protocol(ethernet.ethernet)

# 处理以太网帧
if eth_pkt.ethertype == ether_types.ETH_TYPE_IP:
pass

# 发送到控制器以便进一步处理
self.send_packet_out(datapath, msg)

def send_packet_out(self, datapath, msg):
ofproto = datapath.ofproto
parser = datapath.ofproto_parser

actions = [parser.OFPActionOutput(ofproto.OFPP_FLOOD)]
out = parser.OFPPacketOut(
datapath=datapath, buffer_id=msg.buffer_id, in_port=msg.in_port,
actions=actions, data=msg.data
)
datapath.send_msg(out)

if __name__ == "__main__":
app_manager.main()

放置ryu/ryu/app/下

1
2
ryu-manager ./hub.py --verbose
sudo mn --topo single,3 --controller remote,ip=127.0.0.1,port=6633 --switch ovsk

实现了集线器的功能

进阶 Ryu实现Switch
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
from ryu.lib.mac import haddr_to_bin
from ryu.base import app_manager
from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_3
from ryu.lib.packet import packet, ethernet, ether_types
from ryu.controller import ofp_event

class SimpleSwitch(app_manager.RyuApp):
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]

def __init__(self, *args, **kwargs):
super(SimpleSwitch, self).__init__(*args, **kwargs)
self.mac_to_port = {}

@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_features_handler(self, ev):
datapath = ev.msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser

# 安装默认流表项,将未知目的MAC地址的数据包输出到控制器
match = parser.OFPMatch()
actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)]
self.add_flow(datapath, 0, match, actions)

def add_flow(self, datapath, priority, match, actions, buffer_id=None):
ofproto = datapath.ofproto
parser = datapath.ofproto_parser

inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)]
if buffer_id:
mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id,
priority=priority, match=match,
instructions=inst)
else:
mod = parser.OFPFlowMod(datapath=datapath, priority=priority,
match=match, instructions=inst)
datapath.send_msg(mod)

@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def packet_in_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser

pkt = packet.Packet(msg.data)
eth_pkt = pkt.get_protocol(ethernet.ethernet)

# 只处理以太网帧
if eth_pkt.ethertype == ether_types.ETH_TYPE_IP:
pass

dst = eth_pkt.dst
src = eth_pkt.src
in_port = msg.match['in_port']

# 学习源MAC地址和输入端口的映射关系
self.mac_to_port.setdefault(datapath.id, {})
self.mac_to_port[datapath.id][src] = in_port

# 查找目的MAC地址对应的端口号
if dst in self.mac_to_port[datapath.id]:
out_port = self.mac_to_port[datapath.id][dst]
else:
out_port = ofproto.OFPP_FLOOD

# 安装流表项,将数据包发送到目的端口
actions = [parser.OFPActionOutput(out_port)]
match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
self.add_flow(datapath, 1, match, actions, msg.buffer_id)

# 发送数据包到目的端口
self.send_packet_out(datapath, msg, out_port)

def send_packet_out(self, datapath, msg, out_port):
ofproto = datapath.ofproto
parser = datapath.ofproto_parser

actions = [parser.OFPActionOutput(out_port)]
out = parser.OFPPacketOut(
datapath=datapath, buffer_id=msg.buffer_id, in_port=msg.match['in_port'],
actions=actions, data=msg.data
)
datapath.send_msg(out)

实现了交换机的功能

其他

Ubuntu 部署Samba 文件服务

本来想用VMware选项中自带的共享文件夹功能, 结果发现 win11 不支持. https://docs.vmware.com/cn/VMware-Workstation-Pro/17/com.vmware.ws.using.doc/GUID-7DAD8EC0-0403-4152-86ED-99700F6C02B8.html 也可以用ssh 工具连接虚拟机实现复制粘贴. VMwere对不同的虚拟机镜像支持程度不同, 大多数的linux 的Desktop版安装了VMtools 是可以无缝实现主机与虚拟机的复制粘贴的, 而server版本则完全不行.

部署SMB服务实现在本地局域网文件共享.

1
2
3
4
5
apt-get install samba samba-common
mkdir /vmshare
chmod -R 777 /vmshare

vi /etc/samba/smb.conf

shift G 跳到最后

1
2
3
4
5
6
7
8
9
10
11
12
[share]
comment = share folder
browseable = yes
path = /vmshare
create mask = 0755
directory mask = 0755
valid users = slacr
force user = slacr
force group = slacr
public = yes
available = yes
writable = yes

添加用户, 账号和密码应该为系统中存在的用户

1
2
3
4
5
# 创建用户
adduser slacr --home /home/slacr
# 设置smb 用户
smbpasswd -a slacr
systemctl enble --now smbd

MS Word 从超链导入图片

每次传完图床就懒本地保存了, 下载超链的图片一般直接用浏览器或者用curl 或 wget, 比如:

1
2
3
4
5
wget -o test1.png https://cdn.jsdelivr.net/gh/s1acr/PicGoLib@main/2023-9/202311052023840.png

# or

curl https://cdn.jsdelivr.net/gh/s1acr/PicGoLib@main/2023-9/202311052023840.png -o test2.png

MSWord 可以直接从超链导入图片, 不用每次下载再插入.

将文件名填成URI

再点插入会自动导入云端图片, 可惜垃圾 WPS 不支持, MSOffice是可以的. 我用的office365, 之前下Viso时候一起安装了KMS破解.

关于VMware 虚拟机快照

VMware 能创建类似行为树的快照结构, 点击转到会删除当前位置的快照并创建该节点新的分支并不会删除其他子树.

U2FsdGVkX1+r0mmkT4aOMcM7ElMzLjaPrtmEBPXUIxWuAJc8ixW61Jn8k0qvNmj4BFiPYIIKhuaMc3wI5qpKAFSSWPXsQk2fNRh7OjKTkOIWSw0SVQS9uK03Vi35kLmFixBW1jwR7QiaXtSc6GePxHbr5tW+8zluTEUX++6INaWhpA4THIfY8WyPf1PeI+ENFJk4uQNoitiyh1SXnGAVGHS7v3UG3o4fzN5L0wUSW7MorSX7cv99WbNm8/1d1EWPG81gCPAR553mOnHm52jJbbTUCg9gR9KTmo/vvE287T/foa93mWVFY5wuZJFXRL3gCRDiEgQoX2FQS9injb27NUkHy7m/igskgH8ajglNcrq2jEWq2dT/I0SzG7FTdl6Id+zIm4vjLJbog/tRIMgfy0bshhJOBIN/o0/JEC0xF6DjsG6T1u+yiAgeyOIohIqRy19K5k1WrW/TErmlMUV7nBXWcA8888CG9PhXD9O4vt+tmJVqm2VxOj4eX9klKYn042yTiVVsUsyewRh480DaTqu/HHM2u7Rz9phUXY2IBNhu6MlG64oqfl0t1YYtO9QVRs/bn658w2dRhtgbqbyhHQ==

参考

  1. Download/Get Started With Mininet
  2. Mininet VM Setup Notes
  3. 获取Debian
  4. 清华大学开源镜像站
  5. ubuntu换清华源后错误解决 Certificate verification failed: The certificate is NOT trusted.
  6. Ubuntu安装桌面
  7. Ubuntu部署Samba文件服务
  8. debian开启cgroup memory子系统
  9. 在新 Linux 发行版上切换 cgroups 版本
  10. 「Floodlight x Mininet」从0开始搭建环境
  11. floodlight-guide
  12. floodlight-REST API
  13. OpenFlow switch v1.5.1 Specification
  • Author:

    slacr_

  • Copyright:

  • Published:

    October 19, 2023

  • Updated:

    October 19, 2023

Buy me a cup of coffee ☕.

1000000