
简介面向计算机科学、信息安全、数据科学与大数据技术、人工智能、通信、物联网等专业的学生与教师是一套围绕SDN环境下的DDoS攻击检测与防御的完整课程大作业/毕设源码。项目基于Maven构建共88个文件其中Java源码71个另有XML配置、YAML配置、Shell脚本、Markdown说明等压缩包仅62KB整体结构清晰。XML与YAML主要用于依赖管理和运行参数配置Shell脚本可辅助环境初始化Markdown文档则提供项目介绍便于快速上手。已有750人学习浏览可作为课程设计、期末大作业、毕业设计或初期项目立项演示使用。从源码中可以学习DDoS流量特征识别、攻击检测逻辑、SDN控制器下发流表进行防御等关键思路结合配置文件和启动脚本可快速搭建实验环境进一步扩展检测算法或对接实际SDN网络适合作为安全方向入门的实践参考。1. 课程大作业为什么绕不开SDN架构控制器就是最便宜的DDoS观测点拿到一份「课程大作业基于SDN的ddos攻击检测与防御系统源码.zip」先别急着解压跑代码得想明白一个问题为什么这门课的大作业偏偏选 SDN 来做 DDoS 攻防而不是直接在路由器上写 ACL 或者上防火墙答案其实很反直觉——SDN 控制器本身就是一个天然的流量观测点你不用在链路上串接任何探针就能拿到全网每个交换机的流表统计、端口计数和包级样本。这意味着检测 DDoS 不再依赖旁路抓包设备只要控制器轮询交换机的 flow stats就能算出流量特征的突变。这个特性正是课程大作业设计成 SDN 方案的根本原因。这套系统的典型架构非常固定数据平面用 Open vSwitch 或 Mininet 模拟的网络交换机控制平面用 RYU、Floodlight 或 ONOS 做中央控制器检测模块读流表统计防御模块下发 OpenFlow 流表完成丢包或限速。整份源码 zip 拆开之后核心也就这三块——拓扑脚本、检测算法、防御动作。本文要解决的就是环境怎么搭、检测模块怎么写、防御模块怎么下流表、哪些参数坑会让你的演示现场翻车以及最后怎么用一套脚本向老师证明系统真的生效了。2. 先把实验环境跑通Mininet、RYU 和 OVS 的最小组合课程作业最忌讳的事是代码写完但环境跑不起来。所以环境搭建必须用最小组合能少装一个包就少装一个。我一般建议 Ubuntu 22.04 或 20.04 上用 Mininet 模拟网络RYU 做控制器OVS 做交换机攻击流量用 hping3 或 Scapy 构造 SYN Flood。这套组合的好处是全部走 pip 和 apt不需要编译源码而且 RYU 的代码量比 ONOS 的 Java 工程小一个量级课设周期内能改得动。2.1 环境安装四条命令装完不用碰源码编译先确认系统里有没有 Mininet没有就 apt 装。然后装 RYU注意要用 pip 指定版本因为最新版的依赖偶尔会和系统 Python 冲突。Open vSwitch 在 Mininet 里通常自带但如果你是想物理机做实验而不是纯模拟再单独装 ovs-switchd。sudo apt update sudo apt install -y mininet openvswitch-switch pip install ryu4.34 sudo ovs-vsctl show这段逻辑不复杂前两条装的是数据平面的基础ryu4.34是目前课程作业里用得最多的版本支持 OpenFlow 1.3API 稳定最后一条ovs-vsctl show是验证 OVS 服务有没有起来如果提示 socket 不存在先sudo systemctl start openvswitch-switch没启动的话 Mininet 里--switch ovs会直接失败。很多同学在这里卡住其实是 ovsdb-server 没拉起而不是 Mininet 的问题。装完验证一下 RYU 能不能正常监听端口。RYU 默认监听 6633但新版 OVS 的默认控制器端口是 6653所以启动 RYU 时最好显式指定ryu-manager --ofp-tcp-listen-port 6653 ryu.app.simple_switch_13看到loading app ryu.app.simple_switch_13和installing 0 of 1之类的日志就说明监听成功。这里有第一个容易忽略的点Mininet 里的 remote controller 也建议写成 6653而不是默认 6633否则会出现交换机一直在WAITING_CONNECTION状态看起来像代码 bug实际是端口没对上。2.2 拓扑脚本一台交换机三条主机攻击者单独挂一个端口课程演示场景不用复杂拓扑一个交换机、三台正常主机、一台攻击主机就够。攻击者单独挂一个端口的意义在于防御时你可以精确匹配in_port不做全网丢包这样演示更有说服力——只断攻击者的流量不影响正常通信。from mininet.topo import Topo class MyTopo(Topo): def build(self): s1 self.addSwitch(s1, protocolsOpenFlow13) h1 self.addHost(h1, ip10.0.0.1/24) h2 self.addHost(h2, ip10.0.0.2/24) h3 self.addHost(h3, ip10.0.0.3/24) attacker self.addHost(h4, ip10.0.0.100/24) self.addLink(h1, s1) self.addLink(h2, s1) self.addLink(h3, s1) self.addLink(attacker, s1) topos {mytopo: MyTopo}脚本里的关键设计是攻击者 IP 故意用10.0.0.100和正常主机网段一致但地址偏离主段方便你在检测日志里一眼认出来。protocolsOpenFlow13是必须写的否则 Mininet 默认协商 OpenFlow10RYU 的简单交换机虽然兼容但高级流表特性在 OF1.0 里表现不一样。启动时用--controller remote指到本地 RYUsudo mn --custom mytopo.py --topo mytopo \ --controller remote,ip127.0.0.1,port6653 \ --switch ovs,protocolsOpenFlow13启动后别急着测先sudo ovs-ofctl -O OpenFlow13 dump-flows s1看一眼有没有流表。如果有table0, priority0, actionsCONTROLLER这类默认表项说明交换机已经连上控制器了。这个检查步骤花十秒能帮你把“网络没通”和“控制器没连上”两类问题分开。3. 检测模块怎么写用流表统计做熵值突变不靠抓包检测是整个系统的灵魂。课程作业最常见的错误是检测模块去抓交换机端口镜像或者监听网卡这等于抛弃了 SDN 的核心优势。正确做法是让 RYU 定期向交换机发OFPFlowStatsRequest拿回每个流表项的packet_count和byte_count然后聚合成目的 IP 维度上的流量分布再算信息熵。DDoS 的特征是大量攻击流量涌向一个或少数几个目的 IP目的 IP 分布的熵值会骤降这个突变就是检测信号。3.1 熵值计算香农熵为什么对 SYN Flood 敏感先用一句话说原理正常情况下流量分散到多台主机目的 IP 概率分布比较均匀熵值高SYN Flood 把绝大部分包打向同一个 IP概率分布集中熵值断崖式下跌。这个特性比单纯看带宽阈值靠谱因为它不依赖“多大流量算攻击”的绝对值而是看分布形态的变化对低速慢速 DDoS 也能有反应。计算熵值的代码只有十几行import math from collections import Counter def calc_entropy(ip_counter): ip_counter: {dst_ip: packet_count} total sum(ip_counter.values()) if total 0: return 0 entropy 0.0 for cnt in ip_counter.values(): p cnt / total if p 0: entropy - p * math.log2(p) return entropy这段的逻辑是先统计每个目的 IP 的包数占总包数的比例然后按香农公式累加-p * log2(p)。注意分母用的是总包数而不是 IP 数所以只发一个 IP 的极端情况熵值是 0流量均匀分布到 4 个 IP 时熵值接近 2。参数上你需要关心的只有一件事统计窗口取多大。窗口太小正常流量波动就会引起误报窗口太大攻击开始后要几十秒才能反映到熵值上。我一般取 5000 到 10000 个包为一个窗口配合 2 秒轮询间隔检测延迟控制在 3 秒内。3.2 RYU 定时轮询请求流表统计的完整代码RYU 里做这件事要用到ryu.controller.handler的set_ev_cls装饰器监听EventOFPFlowStatsReply同时自己定时发请求。注意 RYU 的packet_count是累计值不会自己清零所以代码里必须缓存上一次的数值做差否则同一批流表项会反复触发告警。from ryu.base import app_manager from ryu.controller import ofp_event from ryu.controller.handler import MAIN_DISPATCHER, set_ev_cls from ryu.ofproto import ofproto_v1_3 from ryu.lib import hub from collections import Counter class DDoSDetector(app_manager.RyuApp): OFP_VERSIONS [ofproto_v1_3.OFP_VERSION] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.datapaths {} self.prev_counts {} # 缓存上次的 packet_count self.monitor_thread hub.spawn(self._monitor) self.BASELINE_ENTROPY 1.8 # 正常时的熵值基线 def _monitor(self): while True: for dp in self.datapaths.values(): self._request_flow_stats(dp) hub.sleep(2) def _request_flow_stats(self, dp): ofp dp.ofproto parser dp.ofproto_parser req parser.OFPFlowStatsRequest(dp) dp.send_msg(req) set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER) def _flow_stats_handler(self, ev): dst_counter Counter() for stat in ev.msg.body: key (stat.match.get(ipv4_dst), stat.priority) cur_cnt stat.packet_count prev self.prev_counts.get(key, 0) delta cur_cnt - prev self.prev_counts[key] cur_cnt if delta 0 and stat.match.get(ipv4_dst): dst_counter[stat.match[ipv4_dst]] delta entropy calc_entropy(dst_counter) self.logger.info(entropy%.3f total_pkts%d, entropy, sum(dst_counter.values())) if entropy self.BASELINE_ENTROPY * 0.6: self.logger.warning(possible DDoS detected, entropy dropped to %.3f, entropy)代码里的关键点有三个。第一self.prev_counts用(dst_ip, priority)做键因为同一目的 IP 可能有不同优先级的表项混在一起会让计数错乱。第二delta cur_cnt - prev解决累计值问题R 不是 RYU 的 packet_count 不重置不差分会把历史总量全算进当前窗口。第三阈值BASELINE_ENTROPY * 0.6是经验值意思是熵值跌到基线的 60% 就认定攻击。你可以先跑 10 分钟正常流量记录基线再把这个值写死这是课设答辩时能讲清楚调参过程的素材。4. 防御模块怎么写高优先级 Drop 流表先保住交换机端口检测到攻击之后防御动作最常用的是两条路下发精确匹配的 drop 流表或者用 OpenFlow meter 做限速。课程作业里我建议先用 drop 流表因为 meter 表在 OVS 里的行为受内核模块版本影响现场演示容易翻车。Drop 流表的核心逻辑是在交换机里插一条优先级高于正常转发规则的表项匹配攻击流量action 为空即丢弃然后给控制器留一条逃生通道确保管理流量不被一起丢。4.1 匹配什么字段源 IP、目的 IP 还是 in_port防御的匹配精度直接决定演示效果。只匹配目的 IP 会把所有去往受害主机的流量全丢了包括正常用户的请求这不叫防御是叫自损。只匹配 in_port 的问题是攻击者换端口就绕过。课程作业不用搞那么复杂但至少要做到“源 IP 目的 IP 协议类型”三元组匹配这样正常用户不受影响。常见做法是先检测攻击目的 IP然后查流表找到从哪个端口进来再下发带in_port和ipv4_src的 drop 规则。展示效果上只丢攻击者h2 上的正常 ping 还能通这比一刀切更让答辩老师信服。4.2 下发防御流表的 RYU 实现与参数说明from ryu.ofproto import ofproto_v1_3 from ryu.ofproto import ether from ryu.lib.packet import ethernet, ipv4, tcp def block_attacker(self, datapath, src_ip, dst_ip, in_port): ofp datapath.ofproto parser datapath.ofproto_parser match parser.OFPMatch( in_portin_port, eth_typeether.ETH_TYPE_IP, ipv4_srcsrc_ip, ipv4_dstdst_ip, ip_proto6 ) instructions [parser.OFPInstructionActions(ofp.OFPIT_CLEAR_ACTIONS, [])] mod parser.OFPFlowMod( datapathdatapath, priority100, matchmatch, instructionsinstructions, hard_timeout60, buffer_idofp.OFP_NO_BUFFER ) datapath.send_msg(mod)这段代码里最有讲究的是priority100和OFPIT_CLEAR_ACTIONS。OVS 默认转发规则优先级是 0你下发的防御规则必须在数值上远大于 0否则会被默认规则覆盖。OFPIT_CLEAR_ACTIONS表示清空所有动作再执行相当于强制丢包。hard_timeout60是自动过期时间这是课设演示的一步妙棋攻击结束后 60 秒规则自动消失避免把交换机流表永久写死也方便做二次攻击测试。ip_proto6是 TCPSYN Flood 用这个如果是 UDP Flood 改成 17 即可。我习惯在检测到阈值下降后的第一次告警里延迟 2 秒再下发防御流表并把这两秒内的日志打出来。这个“检测到动作”之间的间隔就是系统的响应时间答辩时直接甩日志给老师看entropy dropped at 12:01:03, block rule installed at 12:01:05响应时间 2 秒这就是可量化的成果。5. 避坑OVS 优先级、累计计数器和控制器断连的三类翻车现场这个系统的坑比想象的深而且很多坑不跑到演示前一刻根本发现不了。我把课程作业里最常翻车的三类问题拆开讲每条按“现象 → 原因 → 解决”写你可以对照自己的源码逐一排查。坑一流表下发了但攻击流量还在跑。现象是 hping3 一直刷包ovs-ofctl dump-flows也能看到 drop 表项但 h2 还是被打挂。原因大概率是优先级不够或匹配字段写错。OVS 里如果同时存在两条匹配同一流量但优先级不同的规则低优先级的会被隐藏而不是被删除。很多课程源码里防御规则的 priority 填 0和默认转发规则同级OVS 倾向选择先安装的结果就是 drop 规则永远不生效。解决方法是防御规则 priority 显式设成 100 以上并且在代码里确认OFPIT_CLEAR_ACTIONS而不是OFPIT_GOTO_TABLE。坑二检测模块每隔几秒误报一次 DDoS。现象是没攻击的时候日志里也反复出现 entropy 下降告警。原因是packet_count是累计值而 RYU 的 EventOFPFlowStatsReply 拿到的 body 里每次都是全部表项。如果你用stat.packet_count直接参与熵值计算等于把历史流量全部加进了当前窗口正常流量波动就会被放大成熵值突变。解决方法是代码里维护self.prev_counts缓存每次只取增量delta cur_cnt - prev_cnt。这个坑在 3.2 节已经埋了伏笔但值得单独拎出来重复一次因为它是所有课设里出现频率最高的 bug。坑三Mininet 启动后交换机一直显示WAITING_CONNECTION控制器什么日志都没有。现象是 RYU 控制台空白ovs-vsctl show里is_connected: false。原因不是代码问题是端口或协议版本没对齐。Mininet 里--controller remote,port6653和 RYU 的--ofp-tcp-listen-port必须一致一个用 6633 一个默认 6653就会互相等。另一个隐藏原因是 Mininet 创建 OVS 时默认用 OpenFlow10而 RYU 的 app 写了OFP_VERSIONS [ofproto_v1_3.OFP_VERSION]协商失败直接不握手。解决方法是拓扑脚本里交换机指定protocolsOpenFlow13RYU 启动参数也显式写--ofp-tcp-listen-port 6653。第四个常踩的坑是基线阈值不科学。很多源码直接把BASELINE_ENTROPY写死成 1.8但不同拓扑、不同主机数量下正常熵值不一样四台主机打流量和八台主机的熵基线差了快一倍。我的做法是检测模块启动前先跑 30 秒学习模式统计正常熵值均值再乘以 0.6 作为告警阈值。这个细节答辩时提出来很加分说明你理解熵值的相对性而不是背公式。最后一个坑必须讲手动执行ryu-manager调试没问题但写进课程报告里的启动命令别忘加--observe-links。不加这个参数RYU 拿不到链路发现信息部分拓扑相关的 API 会静默失败。如果你发现检测模块只能处理单交换机拓扑多交换机的时候流表统计对不上先检查这个参数。6. 用一套脚本验证攻防生效从熵值曲线到流表下发日志验证不是“点一下按钮看有没有告警”而是要做成一条可复现的演示链路记录正常基线 → 发起攻击 → 观察熵值骤降 → 观察防御流表下发 → 验证正常流量没有中断。我会把整个过程压缩成一个脚本每步留日志这样老师看日志就能确认每个环节不是吹的。# 步骤1启动 RYU 检测应用后台记录日志 ryu-manager --ofp-tcp-listen-port 6653 ddos_detector.py ryu.log 21 # 步骤2发起持续 30 秒的 SYN Flood源地址随机 sudo hping3 -S -p 80 --flood --rand-source 10.0.0.2 # 步骤3观察防御流表 sudo ovs-ofctl -O OpenFlow13 dump-flows s1 | grep priority100脚本的思路是让攻击持续 30 秒然后手动中断再从ryu.log里提取熵值序列。如果你嫌 grep 日志不够直观可以写个 20 行 Python 脚本把熵值按时间戳画出来攻击开始前一条直线、攻击开始后断崖下跌、防御下发后回弹这三段曲线就是整套系统最好的说明书。画图代码不复杂用 matplotlib 读日志文件里的entropy字段就行这里不展开。关于响应时间我习惯给_flow_stats_handler里加一行time.time()记录攻击脚本启动时也打一个时间戳两个时间戳之差就是检测延迟。课程作业报告里写“平均检测延迟 1.5 秒、防御规则生效延迟 0.8 秒”这种量化数据比任何文字描述都有说服力。想压更低延迟就把轮询间隔从 2 秒改成 0.5 秒但要注意 RYU 处理 FlowStatsReply 的 CPU 占用会上升Mininet 模拟环境下太频繁反而可能丢事件这个参数要实测。最后一件事演示完把 Mininet 清干净再走。不清理的话 OVS 里的旧流表会带着上一次实验的防御规则下次启动拓扑时 h4 可能直接从 switch 里被 “静默丢包”你误以为代码有 bug其实是自己上次没擦干净。用sudo mn -c清一次再重新跑拓扑这个习惯我保持了很久也建议你写进实验笔记里。希望帮到你——课件里那句 “基于 SDN 的 DDoS 检测与防御” 听起来像个大词拆开之后其实就是“控制器里读计数器、算熵值、下流表”三件事把这三件事做扎实离一个能演示、能答辩、能讲清楚原理的系统就不远了。本文还有配套的精品资源点击获取