- 网络
- 网络安全
【免费下载链接】scapy
Scapy: the Python-based interactive packet manipulation program & library.
导读
Scapy 是一套基于 Python 的交互式数据包操作程序与库。本指南聚焦 Scapy 项目开发侧的完整协作链路:如何参与贡献、如何用 UTScapy 编写和运行回归测试、如何借助 tox 与./test/run_tests快速验证代码,以及维护者如何完成签名发布与打包。读完本文,你将掌握.uts测试语法的每一个控制符、UTScapy 全部命令行参数与配置文件的含义,并能在本仓库中亲自运行一套完整或子集的测试。
项目组织与协作入口
Scapy 的开发使用 Git 版本控制系统,参考仓库位于 secdev/scapy。项目管理基于 GitHub,提供可自由编辑的 Wiki(欢迎贡献),可用于引用 ticket、变更集与项目文件;同时提供 ticket 管理服务,避免补丁或 Bug 被遗忘。
对贡献者而言,官方建议的参与方式包括:
- 发现 Bug 时新建 ticket;
- 改进本文档(文档质量同样属于贡献);
- 编写新的协议层,并通过邮件列表分享或直接提交 pull request;
- 贡献新的回归测试;
- 在 packet samples 页面提交新协议的抓包样本。
这些通道中,回归测试与文档改进都与本仓库内test/目录和 UTscapy 实现 直接相关,下文展开说明。
改进文档:docstring 与用法示例
文档可以从两个层面改进:
- 为源码补充 docstring:Scapy 源码中对函数行为的解释较少。一个包含预期输入输出说明的 docstring,能为协议层开发者和查找高级特性的用户都节省时间。
- 为文档增加使用示例:保持文档与最新版 Scapy 同步,并添加来自官方演示或自有实践的用法示例。
docstring 的推荐写法
文档以scapy.fields.FlagsField类为例给出范本:先写一行类的简短描述,再给出使用说明;如果合理,可以加入采用 doctest 格式的可执行示例;最后按 Sphinx Python 签名规范补充经典签名。原文示例(节选核心结构)如下:
class FlagsField(BitField): """ Handle Flag type field Make sure all your flags have a label Example: >>> from scapy.packet import Packet >>> class FlagsTest(Packet): fields_desc = [FlagsField("flags", 0, 8, ["f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7"])] >>> FlagsTest(flags=9).show2() ###[ FlagsTest ]### flags = f0+f3 >>> FlagsTest(flags=0).show2().strip() ###[ FlagsTest ]### flags = :param name: field's name :param default: default value for the field :param size: number of bits in the field :param names: (list or dict) label for each flag, Least Significant Bit tag's name is written first """写 docstring 的工作通常与编写非回归单元测试成对进行——好文档要能被测试验证,好测试也需要文档解释意图。该FlagsField类在 scapy/fields.py 中实现。
用 UTScapy 做测试:概念与语法
什么是 UTScapy
UTScapy 是一个小型 Python 程序,位于 scapy/tools/UTscapy.py:它读取一个测试战役(campaign)文件,用 Scapy 执行这批测试,并生成报告。报告支持四种格式:text、ansi、HTML、LaTeX(当前源码还额外支持 xUnit 与 live,见下文“输出格式”)。
UTScapy 有三个基本测试容器:
- 单元测试(unit test):一组将交由 Scapy(或其衍生版本)执行的 Python 命令。单元测试中最后一条命令的求值结果决定该单元测试的成败。
- 测试集(test set):若干相关联单元测试的分组。
- 测试战役(campaign):由一个或多个测试集组成。
测试集和单元测试都可以打上关键字(keywords),用于形成逻辑分组;运行战役时可按关键字筛选,从而只执行目标分组的测试。
测试签名:CRC32 与 SHA1
对每个单元测试、测试集和战役,UTScapy 都会计算其CRC32并作为该测试的签名显示。签名足以确认实际运行的测试与预期一致、未被修改;为防止有人恶意篡改文件同时保持 CRC32 不变,UTScapy 还会对整个文件计算一次全局SHA1。
在源码中,compute_campaign_digests 展示了签名的精确计算方式:对单元测试取其剥离空白后的文本t.test.strip()计算 CRC32;测试集把所有成员测试以\0分隔后整体计算;战役则在各测试集前加\0\x01后整体计算;最后再读取整个测试文件计算 SHA1。命令行参数-C可关闭签名计算。
测试战役语法(Syntax Specifiers)
UTScapy 通过行首字符识别语法,语法指示符必须是测试文件每行的第一个字符;其后跟的文本是 UTScapy 解释的参数。没有语法指示符的行只要位于单元测试上下文中就会被当作 Python 命令执行;若出现在错误上下文,UTScapy 会拒绝并发出警告。
| 语法指示符 | 含义 |
|---|---|
% | 给出测试战役的名称 |
+ | 声明一个新的测试集 |
= | 声明一个新的单元测试 |
~ | 为当前单元测试声明关键字 |
* | 表示一条会被包含进报告中的注释 |
# | 测试用例注释,解释器直接丢弃 |
注释的上下文归属规则
写入测试报告的注释带有上下文:每条注释都归属于最近定义的那个测试容器(单元测试、测试集或战役)。同一容器下的多条注释会被拼接起来,紧跟在对应容器的声明之后出现在报告中:
- 整个文件的通用注释应放在声明测试战役之前;
- 与战役关联的注释,必须出现在战役声明之后、任何测试集或单元测试之前;
- 与测试集关联的注释,应放在该测试集第一个单元测试定义之前。
通用战役结构模板
% Test Campaign Name * Comment describing this campaign + Test Set 1 * comments for test set 1 = Unit Test 1 ~ keywords * Comments for unit test 1 # Python statements follow a = 1 print(a) a == 1Python 语句靠“没有已定义的 UTScapy 语法指示符”来识别,被直接送入 Python 解释器,如同在交互式 Scapy shell(interact)中操作。循环、迭代和条件语句都允许,但必须以一个空行结尾。一个测试集可由多个单元测试组成,一个战役可定义多个测试集,甚至一个测试定义文件中可以包含多个测试战役。
关键字(keywords)的使用
关键字允许只测试整个战役的子集。例如开发测试战役期间,可以给尚在开发的测试打上debug关键字;当这些测试稳定通过后,再移除debug。也可以使用regression、limited之类的关键字做逻辑分组。命令行中通过-k(仅保留)和-K(排除)配合关键字进行筛选。
成败判定规则
UTScapy取最后一个 Python 语句的真值作为测试通过与否的指标。最后一行可以包含多个逻辑测试;结果为0或False则测试失败,否则通过。如果需要强制校验中间值,应使用assert()语句——这一点在源码 run_test 中得到印证:res为None或真值时测试判定为passed,且每个单元测试默认超时时间为 5 分钟(见 _run_test_timeout)。
UTScapy 命令行:完整参数表
UTScapy 的调用语法如下(摘自文档并对照 usage() 更新到当前实现):
Usage: UTscapy [-m module] [-f {text|ansi|HTML|LaTeX|xUnit|live}] [-o output_file] [-t testfile] [-T testfile] [-k keywords [-k ...]] [-K keywords [-K ...]] [-l] [-b] [-d|-D] [-F] [-q[q]] [-i] [-P preexecute_python_code] [-c configfile]全部参数均可选。没有参数值的参数可以串在一起使用(如-lqF)。未指定测试文件时,测试定义从 读取;未指定输出文件时输出到 ;默认输出格式为ansi。
| 参数 | 参数值 | 对 UTScapy 的含义 |
|---|---|---|
-t | testfile | 输入定义测试战役的测试文件(默认 = ,可多次使用,支持*通配符展开) |
-T | testfile | 当-t使用*时,移除指定文件(可多次使用) |
-o | output_file | 测试战役结果的输出文件(默认 = ) |
-f | test | 输出报告格式:ansi、HTML、LaTeX、text(默认 = ansi);当前实现还支持xUnit与live |
-l | 本地生成报告关联文件。对 HTML,生成 JavaScript 与样式表(离线可用) | |
-F | 仅展开失败的测试用例(默认在 HTML 输出中展开) | |
-b | 第一个战役失败后不停止(默认在首个失败战役处停止) | |
-d | 执行战役前打印一份简明的战役清单 | |
-D | 打印简明战役清单后停止,不执行战役 | |
-R | 将战役以 reStructuredText 格式导出(当前实现新增,用于生成文档示例) | |
-C | 不计算测试签名(CRC 与 SHA) | |
-c | configfile | 加载.utsc配置文件(JSON),见下文 |
-i | 测试失败时进入 Python 解释器(交互调试) | |
-q | 执行测试时不向屏幕更新进度(quiet) | |
-qq | 静默模式(silent) | |
-n | testnum | 只执行指定编号的测试。测试编号可通过-d/-D获取;支持逗号分隔与区间,如1, 3-7, 12 |
-m | module | 执行前加载额外模块并注入命名空间。适合测试 Scapy 衍生版本;注意:以__main__方式设计的衍生程序不会被 UTScapy 以__main__调用 |
-k | kw1, kw2, ... | 只包含含关键字 kw1 的测试。可指定多个关键字(可多次使用) |
-K | kw1, kw2, ... | 排除含关键字 kw1 的测试。可指定多个关键字(可多次使用) |
-P | python_code | 在运行测试前预执行一段 Python 代码 |
-N | 强制以非 root 模式运行 | |
-x | 使用 pyannotate 收集类型注解信息 |
-t支持通配符展开:源码 resolve_testfiles 会用glob把包含*的参数展开为实际文件列表。-n的参数解析支持区间语法,见 main 中的-n分支。
此外,UTScapy 会自动按环境追加排除关键字:非 root 运行时排除needs_root测试;大端平台排除little_endian_only;libpcap 或 Windows 环境排除not_libpcap;Python 低于 3.8 时排除needs_py38plus;disabled关键字则始终被排除(见 main)。
完整示例:一个包含多个测试集的战役
下面是一个简单的多测试集战役示例,同时演示了关键字的子集筛选。注意第 3、5 个单元测试用assert()检查中间结果;第 2、5 个单元测试故意设计为失败:
% Example Test Campaign # Comment describing this campaign # # To run this campaign, try: # ./UTscapy.py -t example_campaign.txt -f html -o example_campaign.html -F # * This comment is associated with the test campaign and will appear * in the produced output. + Test Set 1 = Unit Test 1 ~ test_set_1 simple a = 1 print(a) = Unit test 2 ~ test_set_1 simple * this test will fail b = 2 a == b = Unit test 3 ~ test_set_1 harder a = 1 b = 2 c = "hello" assert (a != b) c == "hello" + Test Set 2 = Unit Test 4 ~ test_set_2 harder b = 2 d = b d is b = Unit Test 5 ~ test_set_2 harder hardest a = 2 b = 3 d = 4 e = (a * b)**d # The following statement evaluates to False but is not last; continue e == 6 # assert evaluates to False; stop test and fail assert (e == 7) e == 1296 = Unit Test 6 ~ test_set_2 hardest print(e) e == 1296要点回顾:单元测试 2 中a == b是最后一条语句且为False,因此失败;单元测试 5 中虽然e == 6为假,但它不是最后一条语句,真正导致失败的是assert(e == 7);单元测试 6 复用前一个测试留下的变量e(测试之间共享同一个交互式会话命名空间)。
仓库内真实示例
仓库自身的.uts测试文件就是最好的模板。以 test/scapy/crc.uts 为例,它展示了战役头、注释、测试集与单元测试的标准写法,且最后一个表达式p1 == p3 and p2 != p3 and p1 != p2直接作为通过判据。
运行与查看报告
将上面的示例保存为demo_campaign.txt后,可以用以下命令运行并生成 HTML 报告:
./test/run_tests -t demo_campaign.txt -f html -o demo_campaign.html -F -l然后打开生成的demo_campaign.html查看结果。这里的-F让失败用例默认展开,-l把报告所需的 JS/CSS 生成为本地文件(离线可查看)。
用 tox 与 run_tests 测试 Scapy
tox:一键全量测试
tox命令简化了 Scapy 的测试:它会自动创建虚拟环境并安装必需的 Python 模块。例如在全新 Debian 上,下面这条命令无需任何外部依赖即可启动全部单元测试:
tox -- -K vcan_socket -K tcpdump -K tshark -K nmap -K manufdb -K crypto注意:这会在所有可用的 Python 版本上触发单元测试,除非用-e指定版本。从 tox.ini 可以看到默认环境列表为py{37..314}-{linux,bsd,windows}-{non_root,root},每个环境最终都调用python -m scapy.tools.UTscapy -c <平台配置文件>来执行测试。
run_tests:单环境快速回归
为方便普通用户和打包维护者,仓库提供了一个在单个(默认 Python)环境运行的工具:
./test/run_tests不加参数时,test/run_tests 会通过 tox 运行测试,并自动附加-K tcpdump -K wireshark -K tshark -K ci_only -K vcan_socket -K automotive_comm -K imports -K scanner等排除项,确保不依赖外部软件;若带参数,则直接调用 UTScapy(例如./test/run_tests -t tls.uts -F)。它的唯一前置依赖是 Python 3 与 tox。
.utsc 配置文件
tox 中出现的test/configs/linux.utsc等文件是 UTScapy 的 JSON 配置。以 test/configs/linux.utsc 为例,它支持如下字段(与 parse_config_file 对应):
testfiles:测试文件列表,支持*通配符;remove_testfiles:从testfiles中移除的文件;breakfailed:首个失败战役后是否停止;onlyfailed:报告中是否只展开失败用例;extensions:要加载的 Scapy 扩展(如scapy-rpc);preexec:运行前预执行代码的字典,键支持通配符,%name%会被替换为文件名主干(如对test/scapy/layers/tls/*.uts预执行load_layer("tls"),对test/contrib/*.uts预执行load_contrib("%name%"));global_preexec:对所有文件生效的全局预执行代码;kw_ko/kw_ok:默认排除 / 保留的关键字;- 其余字段还包括
verb、dump、docs、crc、outputfile、local、format、num、modules等,语义与命令行参数一致。
配置文件通过-c参数加载,命令行参数仍可叠加生效。
为 .uts 文件配置 VIM 语法高亮
UTScapy 的测试文件使用.uts后缀。仓库在 doc/syntax/vim_uts_syntax/ 提供了 Vim 语法高亮插件,目录结构为:
ftdetect/filetype.vim与ftdetect/uts.vim:文件类型识别;syntax/uts.vim:语法高亮定义。
把ftdetect和syntax两个目录中的文件复制到~/.vim/并保持目录结构即可。如果~/.vim/ftdetect/filetype.vim已存在,可能需要手动合并该文件。可执行命令:
cp -i -v ftdetect/filetype.vim $HOME/.vim/ftdetect/filetype.vim cp -i -v ftdetect/uts.vim $HOME/.vim/ftdetect/uts.vim cp -i -v syntax/uts.vim $HOME/.vim/syntax/uts.vim也可以直接运行仓库内的 install.sh 自动安装(脚本要求当前目录为doc/syntax/vim_uts_syntax,且~/.vim已存在,使用cp -i交互确认避免覆盖已有文件)。
发布 Scapy:签名标签与流程
发布前的检查清单
一个 Scapy 发布在底层体现为一个签名 git tag。维护者在签署提交前必须:
- 确认对应的 CI(Travis / AppVeyor)测试全部通过;
- 在本地运行
./run_scapy; - 运行
tox; - 使用 doc/vagrant_ci/ 提供的 Vagrant 环境在 BSD 上运行单元测试。
签名并发布 tag
以 v2.4.3 为例,签名与发布流程如下:
$ git tag -s v2.4.3 -m "Release 2.4.3" $ git tag v2.4.3 -v $ git push --tags也可以发布候选版本(RC)。例如首个 RC 会打上v2.4.3rc1标签,提交信息为2.4.3 Release Candidate #1。
若尚未配置签名密钥,可配置为 SSH 密钥再注册使用:
$ git config --global gpg.format ssh $ git config --global user.signingkey ~/.ssh/examplekey.pub上传到 PyPI
上传到 PyPI 之前,执行发布的维护者邮箱地址必须已写入 pyproject.toml 的维护者字段。随后构建并上传:
$ pip install --upgrade build $ SCAPY_VERSION=2.6.0rc1 python -m build $ twine check dist/* $ twine upload dist/*警告:上传前务必清理dist/目录中的残留文件,其中应只包含源码包与 wheel 包;同时确认 wheel 文件名以*-py3-none-any.whl结尾。
打包 Scapy:版本一致性与轻量验证
打包 Scapy 时,应在设置SCAPY_VERSION环境变量的前提下执行源码构建,确保版本信息保持一致:
$ SCAPY_VERSION=2.5.0 python3 -m build ... Successfully built scapy-2.5.0.tar.gz and scapy-2.5.0-py3-none-any.whl打包过程中若想顺便测试 Scapy,官方建议直接使用无参数./test/run_tests。它会运行一组不依赖外部软件的子集测试,验证起来更简单,唯一依赖是 tox。
$ ./test/run_tests从 run_tests 的实现在打包时也能看到一致性保障的另一面:tox.ini的twine环境会以SCAPY_VERSION=3.0.0构建并执行twine check --strict,gitarchive环境则验证从 git archive 安装后的版本读取是否正确,这些自动化步骤共同保证了发布版本号与构建产物的严格一致。
总结
从贡献入口、docstring 规范,到 UTScapy 的语法控制符、命令行与配置文件、tox 与 run_tests 的用法、Vim 高亮安装,再到签名发布与版本一致的打包流程,本指南覆盖了 Scapy 开发侧从“写测试”到“发版本”的完整闭环。无论你是准备提交第一个协议层补丁,还是负责维护发布流水线,都可以从 scapy/tools/UTscapy.py 的实现细节与 test/ 目录下的真实用例中获得一手参考。
- 网络
- 网络安全
【免费下载链接】scapy
Scapy: the Python-based interactive packet manipulation program & library.
相关推荐
Scapy 仓库的 AI Agent 协作指南:从 UTScapy 测试到提交规范的完整实践
Scapy 仓库的 AI Agent 协作指南:从 UTScapy 测试到提交规范的完整实践 AGENTS.md 是 Scapy 仓库为 AI Agent(以及
网络网络安全Scapy项目开发指南:从贡献到发布的完整流程
Scapy项目开发指南:从贡献到发布的完整流程 项目概述与组织结构 Scapy作为一个强大的网络数据包操作工具,其开发过程遵循严谨的版本控制和工作流程。项目采用
网络网络安全koro1FileHeader插件打包发布流程:从开发到上架的完整指南
koro1FileHeader插件打包发布流程:从开发到上架的完整指南 koro1FileHeader是VSCode中广受欢迎的文件头部注释生成插件,能够自动为
开发工具代码生成
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考