
简介这是一套面向高校学生与网络安全初学者的SDN课程大作业源码围绕DDoS攻击检测与防御展开适合作为课程设计、期末大作业或毕业设计的参考实现。项目基于软件定义网络架构通过控制器集中采集流量信息、分析异常模式并结合机器学习思路动态调整防御策略涵盖流量监控、攻击识别、策略下发等核心环节帮助读者理解SDN环境下安全应用的完整开发流程。资源包共107个文件约605KB以Java源码为主体配合XML配置、YAML与TXT说明文档另有备份文件与Shell脚本结构清晰便于按模块阅读与二次开发。目前已有183人学习下载。对于需要完成同类课题的读者可直接参考其控制器交互逻辑、攻击检测服务实现与接口设计快速搭建实验环境并理解防御策略的落地方式同时也能借此梳理SDN安全应用的代码组织与调试思路。1. 从课程大作业到真实靶场SDN 里的 DDoS 检测与防御到底在做什么很多人第一次接触SDN是在课程大作业里老师给一个拓扑要求实现一个控制器应用能识别异常流量并下发流表阻断。听起来像玩具但真正动手才会发现DDoS 攻击检测与防御系统在 SDN 环境下的核心矛盾非常真实——控制平面拥有全局视野却受限于 OpenFlow 的流表下发时延数据平面转发快却只能看到局部报文。这个标题对应的不是一份“能跑就行”的作业而是一套完整的闭环流量采集、特征提取、检测判定、策略下发、效果验证。适合谁适合正在做网络方向课程设计的学生、想从传统 iptables 防御转向可编程网络的运维工程师以及需要一套可演示、可扩展的 SDN 安全原型的开发者。源码本身不是目的理解“为什么在 SDN 里做检测比传统设备更灵活、又比传统设备更容易翻车”才是。2. 先跑通最小闭环Ryu 控制器 Mininet 拓扑 攻击流生成2.1 为什么选 Ryu 而不是 ODL 或 ONOS课程大作业场景下控制器选型直接决定调试成本。ODL 和 ONOS 功能全但启动慢、依赖多、日志晦涩一个版本冲突就能耗掉半天。Ryu 是纯 Python 控制器安装轻、代码可读、OpenFlow 1.3 支持完整最适合“检测逻辑要自己写”的需求。源码里如果用的是 Ryu大概率会看到ryu.controller.controller和ryu.app.ofctl_rest这两个模块。前者负责事件循环后者提供 REST API 用于查询流表和下发流表。我一般会先确认 Ryu 版本与 OpenFlow 协议版本的匹配关系常见做法是 Ryu 4.34 配 OpenFlow 1.3Mininet 2.3.0 自带 OVS 2.13 支持良好。2.2 用 Mininet 搭一个可复现的靶场拓扑不要一上来就搞复杂拓扑。最小闭环只需要一台控制器、一台交换机、三个主机一个攻击者、一个受害者、一个正常流量参照。下面这段脚本可以直接保存为topo_ddos.py用sudo python3 topo_ddos.py启动。from mininet.topo import Topo from mininet.net import Mininet from mininet.node import RemoteController, OVSKernelSwitch from mininet.cli import CLI from mininet.log import setLogLevel class DDoSTopo(Topo): def build(self): # 添加一台 OpenFlow 1.3 交换机 s1 self.addSwitch(s1, protocolsOpenFlow13) # h1 为攻击者h2 为受害者h3 为正常流量参照 h1 self.addHost(h1, ip10.0.0.1/24, mac00:00:00:00:00:01) h2 self.addHost(h2, ip10.0.0.2/24, mac00:00:00:00:00:02) h3 self.addHost(h3, ip10.0.0.3/24, mac00:00:00:00:00:03) self.addLink(h1, s1, bw10, delay1ms) self.addLink(h2, s1, bw10, delay1ms) self.addLink(h3, s1, bw10, delay1ms) if __name__ __main__: setLogLevel(info) topo DDoSTopo() # 远程控制器指向本机 6653 端口Ryu 默认监听该端口 net Mininet(topotopo, controllerlambda name: RemoteController(name, ip127.0.0.1, port6653), switchOVSKernelSwitch) net.start() CLI(net) net.stop()逻辑说明protocolsOpenFlow13强制交换机使用 OpenFlow 1.3避免 Ryu 因版本协商失败而无法下发流表。bw10限制链路带宽为 10Mbps这样后续做洪泛攻击时更容易观察到拥塞和丢包。RemoteController的 IP 写127.0.0.1是因为控制器和 Mininet 在同一台机器上运行。参数方面delay1ms模拟局域网时延如果要做跨数据中心场景可以改成10ms以上但检测阈值需要同步调整。2.3 生成攻击流量的两种方式与参数选择课程大作业里常见的攻击流量生成方式有两种hping3和 Python 的scapy。hping3命令简单适合快速验证scapy灵活适合构造特定标志位的报文。下面这条命令在 h1 上执行对 h2 发起 SYN Flood# 在 Mininet CLI 中先切换到 h1 mininet h1 hping3 -S -p 80 --flood --rand-source 10.0.0.2参数解释-S表示发送 SYN 报文-p 80指定目标端口--flood以最高速度发送--rand-source随机化源 IP。这里有一个坑--rand-source会让交换机看到大量不同源 IP 的流如果控制器基于“每源 IP 流表项”做检测流表会瞬间爆炸。常见做法是在检测模块里先做聚合比如按目的 IP 统计 SYN 包速率而不是按源 IP 逐条记录。如果源码里用的是scapy典型写法如下from scapy.all import IP, TCP, send import random target 10.0.0.2 # 发送 1000 个 SYN 包源 IP 随机 for i in range(1000): src 10.0.0.%d % random.randint(10, 250) pkt IP(srcsrc, dsttarget) / TCP(sportrandom.randint(1024, 65535), dport80, flagsS) send(pkt, verbose0)这段代码的逻辑是构造三层 IP 和四层 TCP 的 SYN 报文循环发送。参数上random.randint(10, 250)控制源 IP 范围范围越大越接近真实 DDoS 的反射攻击特征。注意send是三层发送不经过内核 TCP 栈所以不会收到 SYN-ACK 回包适合做单向洪泛。3. 检测模块怎么写从流表统计到阈值判定3.1 利用 OpenFlow 流表统计做特征提取SDN 做 DDoS 检测最大的优势是交换机本身维护了流表计数器。Ryu 可以通过ofctl_rest或直接调用ryu.controller.controller的事件来获取datapath上的流统计信息。核心字段包括packet_count、byte_count、duration_sec、match中的tp_src、tp_dst、nw_proto。我一般会每 2 秒轮询一次所有流表项计算单位时间内的包速率和字节速率。下面是一个简化的统计请求示例from ryu.app.ofctl.api import get_datapath from ryu.controller import ofp_event from ryu.controller.handler import MAIN_DISPATCHER, set_ev_cls from ryu.ofproto import ofproto_v1_3 class DDoSDetector(app_manager.RyuApp): OFP_VERSIONS [ofproto_v1_3.OFP_VERSION] def __init__(self, *args, **kwargs): super(DDoSDetector, self).__init__(*args, **kwargs) self.datapaths {} self.monitor_thread hub.spawn(self._monitor) set_ev_cls(ofp_event.EventOFPStateChange, [MAIN_DISPATCHER]) def _state_change_handler(self, ev): datapath ev.datapath if ev.state MAIN_DISPATCHER: self.datapaths[datapath.id] datapath def _monitor(self): while True: for dp in self.datapaths.values(): self._request_stats(dp) hub.sleep(2) # 每 2 秒采集一次 def _request_stats(self, datapath): parser datapath.ofproto_parser req parser.OFPFlowStatsRequest(datapath) datapath.send_msg(req)逻辑说明_monitor是一个协程每 2 秒遍历所有已连接的 datapath发送OFPFlowStatsRequest。hub.sleep(2)的 2 秒是采集周期太短会增加控制器负担太长会漏掉突发攻击。参数上如果攻击速率极高可以把周期降到 1 秒但要注意 Ryu 的hub是单线程事件循环采集过于频繁会阻塞其他事件处理。3.2 阈值判定为什么固定阈值在课程作业里够用在生产里会翻车拿到packet_count后最简单的检测逻辑是如果某条流在 2 秒内包数超过阈值 T就判定为攻击。T 怎么设在 Mininet 的 10Mbps 链路下正常 TCP 流每秒大约 1000 到 2000 个包SYN Flood 可以轻松打到每秒 10000 个包以上。所以 T 设 5000 是一个经验值。但这里有一个血泪经验固定阈值对背景流量敏感。如果 h3 同时在跑 iperf正常流量也可能冲到 8000 pps导致误报。常见改进是引入滑动窗口和动态基线比如取最近 10 个采集周期的均值加 3 倍标准差作为阈值。下面是一个简化的动态阈值实现import numpy as np class AdaptiveThreshold: def __init__(self, window_size10, k3): self.window [] self.window_size window_size self.k k def update(self, pps): self.window.append(pps) if len(self.window) self.window_size: self.window.pop(0) if len(self.window) self.window_size: return None # 样本不足不判定 mean np.mean(self.window) std np.std(self.window) return mean self.k * std # 动态阈值参数说明window_size10表示用最近 10 个周期的数据k3是标准差倍数。k越大越保守误报少但漏报多k越小越敏感适合攻击特征明显的场景。注意np.std默认是总体标准差如果数据波动大可以改用样本标准差ddof1。3.3 从检测到防御流表下发与超时策略检测到攻击后控制器需要下发流表阻断。常见做法是匹配攻击流的特征比如目的 IP 和目的端口然后执行drop动作。下面这段代码演示如何下发一条阻断流表def block_flow(self, datapath, dst_ip, dst_port): ofproto datapath.ofproto parser datapath.ofproto_parser # 匹配目的 IP 和目的端口 match parser.OFPMatch(eth_type0x0800, ipv4_dstdst_ip, ip_proto6, tcp_dstdst_port) # 空动作列表表示丢弃 actions [] # 设置硬超时 300 秒避免流表永久占用 inst [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)] mod parser.OFPFlowMod(datapathdatapath, priority100, matchmatch, instructionsinst, hard_timeout300) datapath.send_msg(mod)逻辑说明priority100要高于默认流表的优先级否则阻断规则不生效。hard_timeout300表示 300 秒后自动删除这是后悔药——如果误判了5 分钟后自动恢复不用手动清理。参数上ip_proto6表示 TCP如果是 UDP Flood 要改成 17。注意actions[]在 OpenFlow 1.3 里表示丢弃但有些交换机需要显式指定OFPAT_DROPRyu 的parser会自动处理不用手动加。4. 避坑与排查课程大作业里最容易翻车的 5 个点4.1 现象Ryu 启动报错Address already in use原因6653 端口被上一次未正常退出的 Ryu 进程占用或者 Mininet 自带的控制器也在监听。解决先sudo fuser -k 6653/tcp杀掉占用进程再启动 Ryu。如果用的是ryu-manager加--ofp-tcp-listen-port 6653显式指定端口。4.2 现象交换机连上了控制器但流表不下发原因Ryu 应用没有注册EventOFPStateChange或EventOFPSwitchFeatures导致datapath对象没被保存。解决检查_state_change_handler是否在MAIN_DISPATCHER状态下保存了datapath。另一个常见原因是 OpenFlow 版本不匹配Mininet 交换机默认可能是 1.0而 Ryu 应用只声明了 1.3。解决在拓扑脚本里显式写protocolsOpenFlow13。4.3 现象攻击流量发了但检测模块没反应原因hping3的--rand-source导致流表匹配不到固定源 IP而检测逻辑是按源 IP 聚合的。解决改成按目的 IP 聚合或者关闭--rand-source用固定源 IP 先验证逻辑。另一个原因是采集周期太长攻击持续时间短于 2 秒统计请求还没发出去攻击就结束了。解决把hub.sleep(2)改成hub.sleep(0.5)做快速验证。4.4 现象阻断流表下发后正常流量也被断了原因匹配条件太宽比如只匹配了ipv4_dst没匹配tcp_dst导致所有去往受害者的流量都被丢弃。解决尽量精确匹配五元组至少包含目的 IP、目的端口、协议号。如果攻击是混合型可以先用priority较低的流表做限速而不是直接丢弃。4.5 现象Mininet 里ping不通但流表显示有包原因ARP 请求被阻断流表误伤。DDoS 防御流表如果匹配了eth_type0x0806或者优先级过高会把 ARP 也丢掉。解决在阻断规则里加eth_type0x0800只匹配 IPv4或者在控制器里对 ARP 报文放行。这个坑在课程大作业里非常常见因为 ARP 是二层协议不经过 IP 层匹配。5. 进阶技巧用 sFlow 做旁路检测与控制器解耦5.1 为什么要把检测从控制器里拆出来Ryu 控制器单线程处理所有 OpenFlow 事件如果检测逻辑里做了大量计算比如动态阈值、滑动窗口、甚至机器学习推理会阻塞流表下发导致攻击发生时防御动作延迟。常见做法是把流量采集和检测拆到旁路交换机开启 sFlow 或 NetFlow把采样报文发给独立的分析进程分析进程判定后再通过 REST API 通知 Ryu 下发流表。这样控制器只负责执行不负责计算。5.2 用 sFlow 做采样参数与部署要点Mininet 的 OVS 支持 sFlow但需要ovs-vsctl手动配置。下面这条命令在交换机 s1 上开启 sFlow 采样目标指向本机的 6343 端口sudo ovs-vsctl -- --idsflow create sflow agents1 target\127.0.0.1:6343\ \ sampling64 polling10 -- set bridge s1 sflowsflow参数说明sampling64表示每 64 个包采样 1 个采样率越高检测越准但开销越大。polling10表示每 10 秒轮询一次接口计数器。target是 sFlow 收集器的地址可以用sflowtool或 Python 的pysflow接收。注意 Mininet 默认的 OVS 可能没编译 sFlow 模块需要确认ovs-vsctl版本支持。如果报错sflow not supported常见替代方案是用tcpdump在交换机端口抓包但性能差很多。5.3 验证检测效果用混淆矩阵而不是只看准确率课程大作业里经常只报一个“检测准确率 95%”但 DDoS 检测是不平衡分类问题正常流量远多于攻击流量。只看准确率会掩盖漏报。我一般会要求同时看四个指标TP攻击被正确阻断、FP正常流量被误阻断、TN正常流量被放行、FN攻击被漏放。下面是一个简单的统计脚本# 假设 ground_truth 和 prediction 都是 0/1 列表 from sklearn.metrics import confusion_matrix, precision_score, recall_score cm confusion_matrix(ground_truth, prediction) tn, fp, fn, tp cm.ravel() precision precision_score(ground_truth, prediction) recall recall_score(ground_truth, prediction) print(fTP{tp}, FP{fp}, TN{tn}, FN{fn}) print(f精确率{precision:.3f}, 召回率{recall:.3f})逻辑说明precision高说明误报少recall高说明漏报少。DDoS 防御场景下recall比precision更重要因为漏掉一个攻击可能打垮服务而误报一个正常流只是短暂影响。参数上如果recall低于 0.9优先降低阈值如果precision低于 0.8优先提高阈值或增加匹配条件。5.4 一个我踩过的坑sFlow 采样率与攻击速率的关系有一次我把sampling1024结果攻击速率是 5000 pps采样后每秒只有不到 5 个包检测模块完全没反应。后来改成sampling64才正常。血泪经验采样率必须保证攻击特征在采样后仍然可见。粗略公式是采样率 攻击 pps / 检测窗口内最小可判定包数。如果检测窗口是 2 秒最小可判定包数是 100攻击 pps 是 5000那么采样率要小于 100。我一般会先关掉采样做基线测试再逐步调大采样率直到检测延迟可接受。5.5 最后一点源码不是拿来跑的是拿来改的课程大作业的源码通常只实现了最基础的阈值检测防御动作也只有 drop。如果你想拿高分至少做三件事第一把固定阈值改成动态基线用滑动窗口或指数加权移动平均第二把 drop 改成限速加 drop 的组合避免误伤正常突发流量第三加一个可视化面板用 Ryu 的 REST API 把流表统计和阻断事件推到前端。我自己的习惯是每改一个模块就先写一个最小验证脚本确认输入输出符合预期再集成。希望帮到你。本文还有配套的精品资源点击获取