简介:面向计算机、信息安全、大数据、人工智能等专业的课程设计与期末大作业场景,这份基于SDN的DDoS攻击检测与防御系统源码提供了可运行的Java项目,涵盖攻击检测与防御逻辑,既适合入门进阶,也便于扩展为毕业设计初始方案。压缩包内共88个文件,以71个Java源文件为主,采用Maven工程结构,辅以Spring/MyBatis等XML配置、YAML环境配置、Shell部署脚本、Markdown介绍与TXT说明文档,整体体积仅62KB,代码紧凑便于快速定位与修改。项目已通过功能验证,可根据教学或课设要求调整检测阈值与防御策略,结合SDN控制器实现实时流量监控与联动防护,是理解SDN安全机制的典型实例。截至目前已有750人学习下载,社区实践中验证了其可用性与参考价值,适合需要完成相似课题的同学直接上手研究。
1. 基于SDN的DDoS攻击检测与防御:课程大作业源码到底能帮你做出什么
做计算机相关专业课程设计的同学,只要方向沾到网络安全,十有八九会撞上同一个组合:SDN 和 DDoS。SDN 把控制平面与数据平面拆开,所有未知流量都会以 PacketIn 形式送进控制器,这让原本躲在网络黑匣子里的攻击流量有了一个天然的观测点;DDoS 则是课程大作业和毕设里最常见的攻击类型。这份资源是一套 Java + Maven 工程的「基于 SDN 的 DDoS 攻击检测与防御系统」源码,带自述文件说明,能在 Mininet 仿真环境里完整复现攻击、被控制器识别并触发防御策略,全程可演示、可截图、可跑指标。适合课程设计、期末大作业,也可以直接拿来当毕设初版框架,往里面再加功能不费劲。
2. 从 Maven 结构拆解项目:控制器模块、REST 服务与检测防御闭环
2.1 源码包结构:pom.xml、src/main 与模块边界
拿到压缩包解压后,第一眼看到的是一份典型的 Maven 工程布局:pom.xml、src/main、src/test三个部分。pom.xml是这个项目的构建核心,它声明了所有第三方依赖;src/main放真正的 Java 源码和资源文件;src/test是单元测试目录,一般包含针对检测算法逻辑的 JUnit 用例。
这类基于 SDN 控制器的项目,通常不是独立运行的应用程序,而是以控制器模块的形式存在,常见做法是挂载到 Floodlight 这类 Java 编写的开源控制器上。pom.xml里一般会维护一个控制器的核心依赖,比如:
<dependency> <groupId>org.projectfloodlight</groupId> <artifactId>floodlight</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.8</version> </dependency>第一段依赖引入 Controller 的核心接口和消息监听机制,第二段是为了把检测结果序列化成 JSON,方便通过 REST API 暴露给上层可视化页面。我在拆这类工程时,习惯先看pom.xml里依赖了哪些模块,基本就能判断出作者打算把检测逻辑放在哪个层级,因为依赖决定模块能调用哪些控制器内部服务。
src/main下一般会拆成三个包:检测模块负责监听 OFMessage 并做流量特征统计;防御模块负责构造流表下发指令;REST 模块负责把开关状态和统计信息用 HTTP 接口暴露出来。这种分层的好处是,你答辩时可以说「检测与防御解耦」,老师一听就知道不是纯抄的。
2.2 DDoS 凭什么能被 SDN「看到」:PacketIn 洪泛与流表统计
传统网络里想检测 DDoS,通常要在交换机上做端口镜像,再把流量引到旁路探针上,部署成本很高。SDN 网络天然解决了这个问题:OpenFlow 交换机收到一个数据包,如果在流表里找不到匹配项,会把包封装成 PacketIn 消息上送给控制器。也就是说,控制器能看到所有「新增」流的首包。
这个特性放到 DDoS 场景里非常关键。拿 SYN Flood 举例:攻击者不断发送伪造源 IP 的 SYN 包,目标地址固定,源地址一直在变。每个 SYN 包在交换机里都是一条新流,全部会触发 PacketIn 上送,控制器在单位时间内收到的 PacketIn 数量会瞬间暴涨。我一般会这样设计检测模块的核心数据结构:
public class PacketInMonitor { // key: 交换机 DPID,value: 该交换机最近一个时间窗内的 PacketIn 数量 private ConcurrentHashMap<Long, Long> packetInCount = new ConcurrentHashMap<>(); private ConcurrentHashMap<Long, Long> lastWindowTime = new ConcurrentHashMap<>(); private long windowSizeMs = 1000; // 采样窗口 1 秒 private long packetInThreshold = 5000; // 每秒 5000 条 PacketIn 视为可疑 public void update(long dpid, long currentTimeMs) { Long lastTime = lastWindowTime.get(dpid); if (lastTime == null || currentTimeMs - lastTime >= windowSizeMs) { long count = packetInCount.getOrDefault(dpid, 0L); if (count > packetInThreshold) { // 触发攻击告警,交给防御模块 attackDetected(dpid, count); } packetInCount.put(dpid, 0L); lastWindowTime.put(dpid, currentTimeMs); } } }这里的核心逻辑是分窗口统计,每个 DPID 独立计数,因为一台交换机触发洪泛不代表全网都在被攻击。windowSizeMs控制采样粒度,packetInThreshold决定敏感程度,这两个参数是后面调优的重点。
除了 PacketIn 速率,目的 IP 熵值也是常用指标。正常流量下,目的 IP 的分布比较分散,熵值偏高;当攻击集中打一个目标时,大量流量的目的 IP 收敛到固定几个,熵值会显著下降。所以成熟的检测模块一般会同时算两个指标:PacketIn 速率加目的 IP 熵值,两者都异常才判定为攻击,这样可以减少单指标误报。
2.3 检测到攻击之后:流表下发、丢包限速与告警
检测只是第一步,课程设计能不能拿高分,取决于防御环节做得是否完整。SDN 里做 DDoS 防御有几种常见手段:匹配攻击特征后直接下发 Drop 流表、用 Meter 表做限速、或者把可疑流量重定向到清洗节点。
我看到的这套源码走的是静态流表方案,也就是检测到攻击后,通过控制器向交换机下发高优先级流表,让匹配攻击特征的流量直接丢弃。Floodlight 里对应的服务是IStaticFlowEntryPusher,构造 FlowEntry 时需要指定交换机 DPID、匹配字段、优先级和动作:
public void dropAttackFlow(long dpid, String srcIp, String dstIp) { Map<String, Object> flowEntry = new HashMap<>(); flowEntry.put("switch", "00:00:00:00:00:00:00:01"); flowEntry.put("name", "ddos-drop-" + System.currentTimeMillis()); flowEntry.put("priority", 40000); flowEntry.put("ether-type", "0x0800"); flowEntry.put("src-ip", srcIp); flowEntry.put("dst-ip", dstIp); flowEntry.put("active", "true"); flowEntry.put("actions", "DROP"); try { staticFlowEntryPusher.addFlow(flowEntry); } catch (Exception e) { log.error("下发防御流表失败", e); } }这里的priority必须设置得比正常转发流表高,否则交换机优先匹配正常流表,Drop 规则永远不生效。actions直接写成 DROP,不走正常转发管线。需要注意的是,流表下发到交换机后,已经建立的 TCP 连接不会立刻断开,但对新连接和疑似攻击流量会迅速拦截,效果上「攻击被抑制、正常服务逐渐恢复」。
完整闭环是:PacketIn 洪泛触发检测 → 多指标确认攻击 → 生成防御流表下发到交换机 → 持续观察流量是否回落。有少数项目还会加一个自动恢复逻辑,攻击停止一段时间后自动删除防御流表,避免误伤永久生效。
3. 在 Mininet 里把系统跑起来:拓扑、攻击与防御一条龙
3.1 环境准备:JDK 版本、Mininet 与控制器选型
复现这套系统之前,先把环境版本卡准,版本不匹配是后面所有翻车的根源。控制器按 Floodlight 1.2 来算,Java 必须用 JDK 1.8,用 JDK 11 或 17 编译老项目会遇到一堆类库兼容问题。Mininet 用 2.x 版本就行,Kali 或 Ubuntu 上都可以装,强烈建议在 Ubuntu 16.04 或 18.04 的虚拟机里操作,不要直接用 macOS 或 Windows 原生环境硬折腾。
编译和打包项目的标准流程是:
# 确认 Java 版本为 1.8 java -version # 在项目根目录执行 Maven 构建 mvn clean package -DskipTests-DskipTests在第一次构建时加上,避免单元测试类里写死的路径报错导致打包中断。打包成功后,target目录下会生成一个 JAR 文件,这个 JAR 需要放进控制器的lib目录,或在控制器启动参数里指定加载路径。我一般习惯把 JAR 直接拷贝到 Floodlight 的lib目录,然后修改floodlight.properties,在floodlight.modules里追加检测模块的完整类名。
3.2 启动控制器:模块加载成功的判断标准
模块装配好之后启动控制器,命令很简单:
cd /opt/floodlight java -jar target/floodlight.jar -cf floodlight.properties启动日志里如果看到类似DDoSAttackDetector started的输出,说明模块注册成功了。这时候还需要确认 REST 服务是否可用,在另一个终端执行:
curl http://localhost:8080/wm/core/switches/jsonFloodlight 默认用 8080 端口暴露 REST API,如果返回一串包含交换机 DPID 的 JSON,说明控制器核心服务正常。这里有个关键点:检测模块如果没被加载,REST 端口照样能起,所以不要只用端口通不通判断模块是否生效,必须看启动日志里有没有模块初始化完成的特征输出。
3.3 自定义 Mininet 拓扑:让流量路径可控
直接用sudo mn --topo tree,3也能跑,但自定义 Python 拓扑脚本更可控,你可以明确指定每一台主机的 IP,方便后面攻击时准确填参数。一个标准的连接远程控制器的拓扑脚本长这样:
#!/usr/bin/python from mininet.topo import Topo from mininet.net import Mininet from mininet.cli import CLI from mininet.link import TCLink from mininet.node import RemoteController class DdosTopo(Topo): def build(self): s1 = self.addSwitch('s1') s2 = self.addSwitch('s2') self.addLink(s1, s2) host1 = self.addHost('h1', ip='10.0.0.1/24') host2 = self.addHost('h2', ip='10.0.0.2/24') host3 = self.addHost('h3', ip='10.0.0.3/24') self.addLink(host1, s1) self.addLink(host2, s2) self.addLink(host3, s2) if __name__ == '__main__': net = Mininet(topo=DdosTopo(), controller=RemoteController('c0', ip='127.0.0.1', port=6653), link=TCLink) net.start() CLI(net) net.stop()RemoteController的ip指向控制器所在机器,如果控制器也在本机就写127.0.0.1,如果控制器跑在虚拟机的宿主机里,这里就要填宿主机的 IP,这是后面最常见的踩坑点之一。port=6653是 OpenFlow 协商端口,新版 Floodlight 默认监听 6653,老版本可能还是 6633,端口对不上交换机就会一直显示 FAILED。
启动拓扑后,先在 Mininet 里跑一下连通性测试:
mininet> h1 ping h2能通说明交换机和控制器之间的链路已经建立,此时在控制器日志里能看到 PacketIn 消息在流动。
3.4 模拟 SYN Flood 攻击:hping3 与 Scapy 双方案
连通正常之后,就可以开始打攻击流量了。最省事的方式是直接用 hping3,Kali 系统自带,其他 Linux 发行版可以用 apt 安装:
# 进入 Mininet 的 h1 终端,攻击 h2 mininet> h1 hping3 -S -p 80 --flood 10.0.0.2-S表示发送 SYN 包,-p 80指定目的端口,--flood表示尽可能快地发包,不做任何重传等待,整个过程会把 h1 所在机器的 CPU 用满。如果打出来的效果不明显,比如控制器侧 PacketIn 数量没有明显变化,常见做法是把--flood换成--fast,或者用 Scapy 写一个多进程发包脚本,避免单一进程成为瓶颈。
攻击打起来之后,控制器日志里应该能看到频繁的 PacketIn 输出。此时执行:
# 查看交换机上当前的流表条目 sudo ovs-ofctl dump-flows s1正常情况下,会被新的攻击流疯狂填充,每条流表对应一个不同的源 IP,流表数量迅速增长。这就是 DDoS 在 SDN 视角下最直观的样子:流表资源被耗尽,控制器负载飙升。防御触发的标志是日志里出现告警,同时ovs-ofctl dump-flows中出现优先级为 40000 的 DROP 规则。
4. 参数调优与策略配置:把误报和漏报压到能答辩的程度
4.1 核心参数一览:采样窗口、速率阈值、熵值阈值
拿到源码后第一件事不是急着跑,而是看懂检测模块里那几个参数是干什么的。我把最常见的几个参数整理成了下面的表,后续调优围绕它们展开。
| 参数 | 常见默认值 | 作用 | 调大后果 | 调小后果 |
|---|---|---|---|---|
| windowSizeMs | 1000ms | PacketIn 采样时间窗 | 反应变慢,容易漏掉短时攻击 | 抖动变大,误报率上升 |
| packetInThreshold | 5000 条/秒 | 单交换机 PacketIn 速率阈值 | 漏报,慢速攻击溜过去 | 合法流量闪断被误判 |
| entropyThreshold | 0.6 | 目的 IP 熵值归一化阈值 | 攻击识别延迟 | 正常扫描行为触发防御 |
| synRatioThreshold | 0.8 | SYN 包占比阈值 | 只对纯 SYN Flood 有效 | CC 攻击检测不到 |
这几个参数之间存在联动关系,packetInThreshold和entropyThreshold是典型的「组合判定」关系,源码里一般是用 AND 逻辑,也就是两者都超限才报警。答辩时老师最爱问的问题就是「你这个阈值怎么定的」,下面这个流程可以帮你把这个问题答得滴水不漏。
4.2 阈值设定流程:先建基线,再做攻击对照
正确做法分三步。第一步搭好拓扑后不打任何攻击流量,用h1 ping h2或iperf制造正常流量,持续观察 5 分钟,记录 PacketIn 速率和熵值的正常波动范围。第二步再打 2 分钟 SYN Flood,记录攻击状态下的峰值。第三步,把检测阈值设在正常基线和攻击峰值之间,留出 30% 的冗余区间。
我一般会用一个简单的脚本连续采样控制器侧统计值,这样调参时不用一直盯着日志翻:
for i in $(seq 1 60); do curl -s http://localhost:8080/wm/ddosdetect/stats/json | jq '.packet_in_rate' sleep 1 done注意jq解析依赖 REST API 返回的具体字段名,字段名以源码里定义为准,这里只是示意。把 60 个采样点拉出来做排序,正常情况取 P95 分位数作为阈值基线,攻击情况取 P5 分位数,阈值落在两者之间。这种基于数据定阈值的方式,最能体现出你在认真做课程设计,而不是随手填了两个魔法数字。
4.3 防御策略调优:从永久 DROP 到自动过期
最开始调试防御模块时,我建议先把 Drop 流表设计成带idle_timeout的临时规则,确认效果后再改成永久规则。带超时的流表配置长这样:
curl -X POST -H "Content-Type: application/json" \ -d '{ "switch": "00:00:00:00:00:00:00:01", "name": "ddos-temp-block", "priority": 40000, "idle-timeout": 30, "ether-type": "0x0800", "src-ip": "10.0.0.1", "dst-ip": "10.0.0.2", "actions": "DROP" }' \ http://localhost:8080/wm/staticflowentrypusher/jsonidle-timeout设为 30 秒,表示 30 秒内没有匹配流量就自动淘汰。调试期用这个配置有个好处:如果误判了正常流量,30 秒后自动恢复,不用手动去删流表。确认策略有效之后,再把它改成idle-timeout=0变成永久规则,或者加上自动恢复逻辑,在攻击停止若干时间窗后主动调用deleteFlow清理规则。
限速方案也值得试一下,OpenFlow 1.3 的 Meter 表可以对匹配流量做带宽限制,相比直接 DROP 更精细,但配置复杂度也更高。如果源码里自带 Meter 表实现,答辩时拿它和 DROP 方案做对比,是一个很好的加分点。
5. 避坑与排查:跑通这套系统的五个常见翻车点
5.1 Mininet 交换机连不上控制器:状态一直 FAILED
现象:sudo mn --controller remote,ip=127.0.0.1,port=6653启动后,ovs-vsctl show看到交换机 OpenFlow 端口状态是FAILED,ping h1 h2完全不通。
原因:80% 的情况是控制器监听端口和 Mininet 指定的端口不一致。老版本 Floodlight 默认监听 6633,新版本默认 6653,两个端口混用是最典型的低级错误。还有一种情况是控制器根本没启动,或者虚拟机里 Mininet 和控制器不在同一网络命名空间。
解决:先netstat -an | grep 6633和grep 6653确认控制器实际监听哪个端口,再检查拓扑脚本里RemoteController的port参数是否一致。我习惯在启动脚本里用环境变量统一控制,比如CONTROLLER_PORT=6653,避免在多个文件里维护硬编码端口。
5.2 攻击打过去,控制器侧收不到几条 PacketIn
现象:hping3 已经在--flood模式跑了几十秒,控制器日志里 PacketIn 数量没有暴涨,检测模块纹丝不动。
原因:交换机流表里已经存在从 h1 到 h2 的转发规则,后续所有报文都在数据平面直接转发,根本不会上送控制器。只有第一条流触发 PacketIn,后面全部被流表快速命中,所以检测模块看不到流量。
解决:在发起攻击前清空交换机流表,sudo ovs-ofctl del-flows s1。更稳妥的做法是给检测模块加一个「默认上送」入口流表,把所有未知流量先上报控制器统计完再转发。另外,检查 hping3 目标 IP 是不是 Mininet 内部地址,如果你填的是宿主机网段 IP,流量根本没进拓扑,自然白打。
5.3 JDK 11 编译老项目:直接一堆类找不到
现象:mvn clean package报错,提示找不到javax.xml.bind相关类,或者ClassNotFoundException: com.google.common.base.Function。
原因:JDK 9 开始把 JavaEE 模块拆出了 JDK,旧 Floodlight 依赖的javax.xml.bind等类在新 JDK 里不再默认提供。另外老项目依赖的 Guava 版本太旧,和 JDK 11 的模块化机制冲突。
解决:老老实实装 JDK 1.8,不要硬用高版本。如果机器上没有 1.8,可以用 Docker 跑一个openjdk:8容器,把项目挂载进去构建。这是我反复踩过最多次的坑,之后每次在新机器上复现这个项目,第一件事就是java -version确认 1.8,不在版本上赌运气。
5.4 防御流表下发了,但攻击流量还在跑
现象:日志显示检测模块报警,curl 下发 DROP 流表也返回成功,但ovs-ofctl dump-flows里看不到新的 Drop 规则,或者规则在但流量依旧通。
原因:下发的流表优先级不够高。正常转发流表的优先级通常是 32768 或更低,如果 Drop 规则优先级只有 30000,交换机匹配时优先走转发规则,Drop 永远不会生效。另一个原因是下发目标交换机选错了,拓扑里有 s1 和 s2 两台交换机,攻击穿越了两台设备,只封一台等于白封。
解决:把防御流表的优先级提到 40000 以上,高于所有默认规则,这是行业惯例。同时用ovs-ofctl dump-flows s1和s2分别确认两台交换机上的流表都到位。我在调试时会分别对 s1、s2 下发规则,绝不在程序里只处理「第一个发现异常」的交换机。
5.5 阈值调来调去总是误报:一会儿报警一会儿不报
现象:攻击检测模块时灵时不灵,正常流量偶尔触发告警,攻击流量偶尔又识别不到,参数调整后也没有明显规律。
原因:采样窗口太短导致统计抖动剧烈,或者阈值是拍脑袋填的,没有按环境基线设置。更玄学的是,Mininet 跑在虚拟机里时,宿主机的 CPU 调度会影响 hping3 发包速率,导致攻击流量本身不稳定,看起来就是检测模块间歇性抽风。
解决:先固定一个机制——连续 N 个窗口都超过阈值才判定攻击,通常 N=3,这样能滤掉瞬时抖动。然后把 hping3 从--flood改成控制速率发包,比如hping3 -S -p 80 --rate 2000,让攻击流量速率可预测,再按第 4 章的基线流程重新标定阈值。做完这两步,误报问题基本能消掉八成。
6. 把课程大作业升成毕设:验证指标、可视化扩展与答辩加分项
到现在这套系统能跑通,课程大作业交上去已经没问题了。但如果你想把它发展成毕业设计,或者想在答辩现场让老师多点头,还需要补齐验证指标和展示手段。
指标层面至少要准备三组数据:检测率与误报率、防御前后的网络吞吐量、控制器 CPU 与内存占用对比。检测率可以这样算:发起 10 次攻击,记录检测模块成功告警的次数,正常情况下应该在 9 次以上;误报率则在正常流量场景下观察 10 分钟,看有没有误告警,记录次数。吞吐量对比用 iperf 测:攻击前打一次记录带宽,攻击中再打一次记录带宽,防御触发后再打一次,三次数据画成折线图,这就是最有说服力的防御效果证明。
扩展层面,Floodlight 的 REST API 已经暴露了统计数据,你可以把前面的 curl 命令封装成一个简单的 Web 后端,前端用 ECharts 画实时曲线,把 PacketIn 速率、熵值变化、流表数量三个指标做成大屏。这个工作量不大,但展示效果比让学生看终端日志强太多,答辩时你切到浏览器演示大屏,整个流程一气呵成,气场完全不一样。
我当年交大作业时犯过一个很蠢的错误:全程都在终端里操作,答辩现场老师问「攻击前正常流量是多少、攻击时峰值多少、防御后恢复到了多少」,我一张截图都拿不出来,只能现场重新跑一遍 Mininet,场面一度很被动。从那以后我每次跑完一个实验节点,都会把攻击前基线、攻击中指标、防御后恢复情况三组数据固化在同一张表格里,再截一张ovs-ofctl dump-flows的流表截图一起留档,答辩时直接对着数据讲,比临场演示稳得多。这套流程你提前走一遍,后面会省掉很多麻烦。希望帮到你。
本文还有配套的精品资源,点击获取