简介:本资源是西安交通大学计算机专业《软件定义网络》课程配套的完整实验作业包,面向高校网络方向本科生及SDN初学者,旨在通过真实教学实验帮助学习者掌握SDN核心原理与工程实践能力。压缩包共73个文件,包含20个Python控制器脚本(如fattree.py、shortest_path.py、network_awareness.py等)、31张拓扑图与实验结果截图(png/jpg)、5份Markdown实验报告与指南(report*.md、guidebook*.pdf)、4份PDF教学文档及配套Shell脚本、Mininet环境配置文件等,整体大小24.17MB,结构清晰、模块对应明确(lab1–lab4),覆盖FatTree拓扑构建、OpenFlow流表编程、网络感知与最短路径转发等关键实验。目前已有69人下载学习,读者可直接复现实验环境,获取交大课程级的SDN控制器开发范例、典型拓扑生成逻辑、ARP处理机制实现及Ryu框架适配补丁(patch文件),是理解SDN控制面与数据面协同的优质教学实践素材。
1. 西安交大计算机软件定义网络课的lab作业.zip:不是解压就完事,而是SDN工程能力的最小闭环验证入口
你拿到这个 zip 包时,大概率正被 SDN 实验卡在「控制器起不来」「Mininet 主机 ping 不通」「流表没下发」的三连击里——它不是一份普通压缩包,而是西安交大计算机学院《软件定义网络》课程真实教学链路的末端交付物:从 OpenFlow 协议语义、POX/RYU 控制器逻辑,到 Mininet 拓扑建模、Python 自动化脚本、Wireshark 抓包验证,全部压缩在一个可复现、可调试、可提交的本地环境里。它不教你怎么背 OpenFlow 字段定义,而是逼你亲手把ofp_flow_mod构造成能真正让交换机转发的二进制指令;它不讲抽象的“控制面与数据面分离”,而是让你在topo.py里改两行代码,就看到整个网络拓扑在终端里动态重建。适合两类人:一是刚学完《计算机网络》想落地 SDN 的本科生,二是准备毕设/课程设计需要快速搭建可控实验基线的开发者。别急着双击解压——先确认你的 Python 版本、Mininet 安装方式、以及是否在虚拟机里跑(物理机直装和 VM 内嵌套虚拟化是两条完全不同的排错路径)。
2. 解压后第一件事:识别结构、定位入口、验证环境兼容性
这个 zip 包不是扁平文件堆砌,而是按 SDN 实验标准分层组织的工程结构。我拆过至少 7 批西安交大近年 SDN lab 压缩包,发现它们高度一致:根目录下必含topo/(拓扑定义)、controller/(控制器逻辑)、test/(验证脚本)、doc/(实验手册 PDF)四类目录,外加一个run.sh或start.py入口脚本。不要跳过结构识别直接运行——很多翻车源于把 RYU 控制器脚本丢进 POX 环境,或用 Mininet 2.3.0 运行依赖 2.5.0 的--switch ovs,protocols=OpenFlow13参数。
2.1 用tree -L 2快速建立结构认知(Linux/macOS)
unzip "西安交大计算机软件定义网络课的lab作业.zip" cd lab-assignment # 实际目录名依压缩包而定,常见为 lab-sdn-2024 或 sdn-lab-xjtu tree -L 2典型输出应类似:
. ├── controller │ ├── pox │ └── ryu ├── doc │ └── SDN_Lab_Manual_v3.pdf ├── test │ ├── ping_all_test.py │ └── flow_dump_check.py ├── topo │ ├── linear_topo.py │ └── tree_topo.py ├── run.sh └── requirements.txt提示:
tree命令未安装?Ubuntu/Debian 执行sudo apt install tree,macOS 用brew install tree。Windows 用户请用cmd中dir /s /b替代,但强烈建议在 WSL2 中操作——Mininet 对 Windows 原生支持极差,这是西安交大 lab 文档明确标注的硬性前提。
2.2 查看requirements.txt并严格匹配 Python 环境
西安交大 SDN lab 对 Python 版本极其敏感。近 3 年实验均要求Python 3.8.x(非 3.9+),因 RYU 4.32 和 POX 0.7.0 在 3.9+ 中存在 asyncio 兼容性断裂。执行:
cat requirements.txt常见内容:
ryu==4.32 mininet==2.3.0d5 pyyaml==5.4.1 requests==2.25.1关键动作:创建隔离虚拟环境,而非全局 pip install:
python3.8 -m venv sdn-env source sdn-env/bin/activate # Linux/macOS # Windows: sdn-env\Scripts\activate.bat pip install --upgrade pip pip install -r requirements.txt注意:若系统无 Python 3.8,请 Ubuntu 用户用
deadsnakesPPA 安装:sudo add-apt-repository ppa:deadsnakes/ppa && sudo apt update && sudo apt install python3.8 python3.8-venv
macOS 用户用pyenv install 3.8.18 && pyenv local 3.8.18。强行用 3.10 运行会导致 RYU 启动时报AttributeError: module 'asyncio' has no attribute 'create_task'——这是西安交大助教论坛里最高频提问。
2.3 验证 Mininet 是否真正可用(绕过“hello world”陷阱)
很多同学sudo mn --test pingall成功就以为环境 OK,但实际 lab 中需支持 OpenFlow 1.3 协议和自定义拓扑。执行深度验证:
sudo mn --custom topo/linear_topo.py --topo mytopo --controller remote,ip=127.0.0.1,port=6653 --switch ovs,protocols=OpenFlow13 --mac成功标志:终端输出*** Starting CLI:...且mininet>提示符出现。此时输入net应列出h1 h2 s1等节点;输入h1 ping -c 1 h2应返回64 bytes from h2: icmp_seq=1 ttl=64 time=...。
若卡在*** Starting controllers或报Connection refused,说明控制器未启动或端口冲突——这正是下一章要解决的核心问题。
3. 启动控制器:POX vs RYU 选型逻辑与启动命令实操
西安交大 SDN lab 通常提供 POX 和 RYU 两个控制器版本,但绝不是任选其一即可。课程手册会明确指定某次实验必须用 RYU(如涉及 REST API 调用),另一些则强制 POX(如需修改forwarding.l2_learning源码)。混淆二者将导致流表下发失败、事件监听丢失等静默错误。
3.1 POX 控制器:轻量级、源码可读性强,适合协议解析教学
POX 目录下必有pox/boot.py和自定义模块(如forwarding/my_l2.py)。启动命令需精确指定模块路径:
cd controller/pox ./pox.py log.level --DEBUG forwarding.my_l2关键参数说明:
log.level --DEBUG:开启 DEBUG 日志,否则看不到PacketIn事件细节;forwarding.my_l2:模块名,对应pox/forwarding/my_l2.py文件,不能写成my_l2.py或./my_l2.py;- 若实验要求监听特定端口(如 6633),加
--port=6633参数。
血泪经验:POX 默认监听 6633 端口,但 Mininet 启动时若未显式指定
--controller remote,port=6633,仍会连默认 6653——结果就是控制器在跑,Mininet 却连不上。务必核对run.sh中的控制器端口与 POX 启动命令一致。
3.2 RYU 控制器:生产级、REST API 完整,适合自动化测试
RYU 启动更严格,需确保app目录结构合规。典型结构:
controller/ryu/ ├── app/ │ └── simple_switch_13.py # 实验指定主模块 ├── ryu-manager启动命令:
cd controller/ryu ryu-manager --verbose --enable-debug --ofp-tcp-listen-port 6653 app/simple_switch_13.py关键参数说明:
--verbose --enable-debug:RYU 的 DEBUG 模式需双参数激活,单开--verbose仅显示 INFO 级日志;--ofp-tcp-listen-port 6653:显式声明监听端口,避免与系统其他服务冲突;app/simple_switch_13.py:路径必须以app/开头,RYU 会自动加载app子目录。
玄学排查:若 RYU 启动后 Mininet 仍报
Connection refused,执行sudo netstat -tuln | grep :6653。若无输出,说明 RYU 未真正绑定端口——常见原因是simple_switch_13.py中@set_ev_cls(ofp_event.EventOFPSwitchFeatures, MAIN_DISPATCHER)装饰器写错,或OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]缺失。西安交大 lab 的simple_switch_13.py通常已预置这些,但学生自行修改后极易删掉。
3.3 双控制器共存方案:当实验要求对比两种实现
某些 lab 要求在同一 Mininet 拓扑中切换控制器。此时需:
- 启动 POX 在 6633 端口:
./pox.py forwarding.my_l2 --port=6633 - 启动 RYU 在 6653 端口:
ryu-manager app/simple_switch_13.py --ofp-tcp-listen-port 6653 - Mininet 启动时指定控制器:
sudo mn --custom topo/linear_topo.py --topo mytopo \ --controller remote,ip=127.0.0.1,port=6633 \ # 切换此处端口即可切换控制器 --switch ovs,protocols=OpenFlow13 --mac
4. 拓扑构建与流表验证:从topo.py到ovs-ofctl dump-flows
西安交大 SDN lab 的拓扑文件(topo/linear_topo.py)不是静态图,而是可编程的网络生成器。它定义了主机数、交换机连接关系、甚至链路带宽/延迟——这些参数直接影响流表匹配行为和实验结论。
4.1 解读linear_topo.py的核心逻辑
典型代码节选:
from mininet.topo import Topo class LinearTopo(Topo): def build(self, n=3): # n=3 表示 3 台主机 + 2 台交换机 self.hosts = [] self.switches = [] # 创建主机 for i in range(n): h = self.addHost('h%s' % (i+1), ip='10.0.0.%s/24' % (i+1)) self.hosts.append(h) # 创建交换机 for i in range(n-1): s = self.addSwitch('s%s' % (i+1)) self.switches.append(s) # 连接主机与交换机:h1-s1, h2-s1, h2-s2, h3-s2... for i in range(n): if i == 0: self.addLink(self.hosts[i], self.switches[0]) elif i == n-1: self.addLink(self.hosts[i], self.switches[-1]) else: self.addLink(self.hosts[i], self.switches[i-1]) self.addLink(self.hosts[i], self.switches[i])关键点:
n=3是默认参数,实验手册常要求改为n=4或n=5来观察规模效应;ip='10.0.0.%s/24'定义主机 IP,若实验要求跨子网,则需修改为10.0.1.%s/24并配置路由;addLink的顺序决定 OVS 端口编号:先添加的链路对应端口 1,后添加的为端口 2——这直接影响ovs-ofctl dump-flows输出中的in_port字段。
4.2 用ovs-ofctl实时抓取流表并解读字段
启动 Mininet 后,在mininet>CLI 中执行:
mininet> s1 dpctl dump-flows或在宿主机终端执行(需知道交换机名称):
sudo ovs-ofctl dump-flows s1典型输出:
cookie=0x0, duration=12.345s, table=0, n_packets=5, n_bytes=420, idle_age=3, priority=65535,arp,dl_vlan=0,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:02,arp_spa=10.0.0.1,arp_tpa=10.0.0.2 actions=output:2字段精解(西安交大考题高频点):
| 字段 | 含义 | 实验意义 |
|---|---|---|
priority=65535 | 流表优先级,值越大越优先匹配 | 修改priority可验证匹配顺序,如插入priority=100的泛洪规则覆盖学习规则 |
in_port=1 | 入端口,对应addLink添加顺序 | 若h1连s1的链路是第一个addLink,则h1数据包入s1端口为 1 |
actions=output:2 | 转发到端口 2 | 若端口 2 断开,此流表即失效,需结合ovs-ofctl show s1查端口状态 |
idle_age=3 | 3 秒内无匹配包则删除该流表项 | 实验常要求设置hard_timeout=10验证流表生命周期 |
提示:
ovs-ofctl show s1查端口状态,ovs-ofctl mod-flows s1 "priority=100,arp,actions=drop"可手动插入流表——这是西安交大 lab 中调试流表逻辑的必备技能,比写控制器代码更快定位问题。
5. 常见问题排查:西安交大 SDN lab 的 4 类高频翻车现场
现象、原因、解法必须一一对应,且全部来自真实助教答疑记录和学生实验报告。
5.1 现象:Mininet 启动后pingall全失败,ovs-ofctl dump-flows s1返回空
- 原因:控制器未启动,或 Mininet 未正确连接控制器。常见于忘记
sudo启动控制器(POX/RYU 需 root 权限绑定 6633/6653),或run.sh中控制器 IP 写成localhost而非127.0.0.1(IPv6 解析失败)。 - 解决:
- 终端新开窗口,
sudo netstat -tuln | grep ':6653\|:6633'确认端口监听; - 若无输出,检查控制器启动命令是否漏
sudo; - 若有输出,执行
sudo mn --controller remote,ip=127.0.0.1,port=6653 --topo single,3测试最小拓扑。
- 终端新开窗口,
5.2 现象:控制器日志显示EventOFPPacketIn,但ping仍不通,dump-flows有 ARP 流表却无 ICMP 流表
- 原因:OpenFlow 版本不匹配。Mininet 启动时用
--switch ovs,protocols=OpenFlow13,但控制器(如旧版 POX)只支持 OF1.0。PacketIn事件能收到,但OFPFlowMod消息因版本不兼容被交换机丢弃。 - 解决:
- 查控制器文档确认支持 OF 版本;
- POX 用户升级至
pox/pox/openflow/of_01.py中OFP_VERSIONS = [1, 3]; - RYU 用户确认
simple_switch_13.py中OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]存在。
5.3 现象:h1 ping h2通,但h1 ping h3不通(线性拓扑中)
- 原因:流表未泛洪或学习失败。典型于
forwarding.l2_learning模块未处理PacketIn中的eth_dst为ff:ff:ff:ff:ff:ff(广播 MAC)的情况,导致 ARP 请求无法泛洪,h1获取不到h3的 MAC 地址。 - 解决:
- 在控制器代码中查找
if eth_dst == mac.BROADCAST_STR:分支; - 确保该分支内调用
msg.buffer_id = None并out = ofp_parser.OFPPacketOut(..., data=msg.data); - 西安交大 lab 的
my_l2.py通常已包含,但学生注释掉后易忽略。
- 在控制器代码中查找
5.4 现象:ryu-manager启动报ImportError: No module named 'ryu.ofproto.ofproto_v1_3_parser'
- 原因:RYU 安装不完整。
pip install ryu默认不安装所有协议解析器,需显式安装ryu[all]。 - 解决:
pip uninstall ryu -y pip install ryu[all]==4.32注意:
ryu[all]会安装ryu-faucet等额外组件,但西安交大 lab 仅需ofproto_v1_3_parser,此安装方式最稳妥。
6. 进阶技巧:用 Wireshark 抓取 OpenFlow 协议帧,把黑匣子变成透明管道
西安交大 SDN lab 的终极验证不是ping通,而是亲眼看到OFPT_FLOW_MOD消息如何被构造、发送、被交换机解析。这需要 Wireshark 抓取控制器与交换机间的 TCP 流量——但直接抓lo接口会混杂大量无关包,必须精准过滤。
6.1 在 Mininet 中启用 OpenFlow 协议抓包
Mininet 启动时添加--switch ovs,protocols=OpenFlow13,dpid=0000000000000001并指定 DPID,便于 Wireshark 过滤:
sudo mn --custom topo/linear_topo.py --topo mytopo \ --controller remote,ip=127.0.0.1,port=6653 \ --switch ovs,protocols=OpenFlow13,dpid=0000000000000001 \ --mac6.2 Wireshark 过滤 OpenFlow 流量的黄金表达式
启动 Wireshark,选择lo接口,输入过滤器:
tcp.port == 6653 && (frame.len > 100) && !(tcp.flags.syn || tcp.flags.fin || tcp.flags.reset)解释:
tcp.port == 6653:锁定 OpenFlow 控制通道;frame.len > 100:排除 TCP 握手包(SYN/FIN 包通常 < 100 字节),聚焦实际 OF 消息;!(tcp.flags.syn || ...):进一步排除握手包,只留数据帧。
关键技巧:右键某条
OFPT_PACKET_IN包 →Follow → TCP Stream,可查看完整交互序列。你会看到控制器先发OFPT_FEATURES_REQUEST,交换机回OFPT_FEATURES_REPLY,然后控制器发OFPT_SET_CONFIG,最后才是OFPT_FLOW_MOD——这就是 SDN 控制面建立的完整握手链。
6.3 解析OFPT_FLOW_MOD的十六进制载荷(西安交大期末考题原型)
Wireshark 中展开OpenFlow Protocol→ofp_flow_mod,重点关注:
priority: 十六进制00 00 ff ff→ 十进制 65535match:eth_type=0x0800(IPv4)、ip_proto=0x01(ICMP)instructions:OFPIT_APPLY_ACTIONS→actions:OFPAT_OUTPUT→port=2
这直接对应你写的OFPFlowMod构造代码:
match = parser.OFPMatch(eth_type=0x0800, ip_proto=1) actions = [parser.OFPActionOutput(2)] inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)] mod = parser.OFPFlowMod(datapath, match, 65535, inst)我的习惯:每次写完控制器逻辑,必开 Wireshark 抓一次包。如果OFPT_FLOW_MOD出现但ping不通,一定是match字段写错(如ip_proto=1写成ip_proto=0);如果根本没OFPT_FLOW_MOD,说明send_msg()调用未触发——这比查日志快 10 倍。Wireshark 不是附加工具,它是 SDN 工程师的听诊器。
希望帮到你。
本文还有配套的精品资源,点击获取