尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

UDP协议实验:用nc抓包与Wireshark逐字段分析

UDP协议实验:用nc抓包与Wireshark逐字段分析 简介这份文档面向计算机网络课程学习者与网络协议初学者围绕UDP协议的数据格式、首部字段含义、网卡offload机制以及Linux下nc命令的使用展开帮助读者在虚拟网络拓扑中完成一次完整的UDP通信抓包与分析实验。压缩包内共1个docx文件约1.13MB以图文并茂的实验报告形式组织涵盖实验目的、分步操作记录、Wireshark抓包截图以及问题与分析等模块。资源已有180人学习内容从创建虚拟网络拓扑、配置静态路由、关闭网卡offload到用nc搭建UDP服务端与客户端、抓包解析源端口、目的端口、长度与校验和并附有校验和手动计算过程适合需要完成课程实验或巩固UDP协议细节的读者对照参考。1. 用 nc 抓一次 UDP比背十遍首部字段管用很多人学 UDP 协议止步于「无连接、不可靠、首部 8 字节」这三句话。真到线上排查一个 UDP 丢包或者校验和异常脑子里那点概念根本不够用。这份「计算机网络实验三UDP 协议探索和分析」的资源走的是另一条路用 Linux network namespace 搭一套虚拟拓扑把两台主机、若干路由器塞进一个物理机里再用nc发一行字符让 Wireshark 在 tap 接口上把 UDP 用户数据报原样抓下来逐字段对着看。它解决的是「协议格式只存在于课本、没在真实报文里验证过」这个问题适合正在做网络协议实验的学生也适合想补一补 Linux 网络调试基本功的从业者。整份资源围绕 UDP 数据格式、首部字段含义、网卡 offload 技术、nc命令四条线展开实验步骤和结果记录都给到了截图级别。2. 搭虚拟拓扑namespace、veth 与网桥怎么串起来这套实验的地基是 Linux network namespace。一个 namespace 就是一套独立的网络协议栈有自己的网卡、路由表、ARP 缓存彼此默认隔离。把多个 namespace 用 veth pair虚拟网线连起来再挂到网桥上就得到一台物理机内部的「多主机 交换机」拓扑。相比开虚拟机namespace 启动快、资源占用低抓包也方便是协议实验里很常见的做法。2.1 拓扑结构与命名约定实验里的主机叫 ns56A、ns57C 这类名字路由器另有编号接口名带 tap 前缀如 tap57C说明拓扑里用了 tap 设备或者 veth 的一端按主机名命名。搭建时我一般按「先建 namespace再建 veth最后接网桥」的顺序来脚本化之后重复执行不容易乱。核心命令就这几条# 创建两个主机 namespace 和两个路由器 namespace ip netns add ns56A ip netns add ns57C ip netns add router1 ip netns add router2 # 建一对 veth一端放进 ns56A另一端放进 router1 ip link add veth56A type veth peer name vethR1a ip link set veth56A netns ns56A ip link set vethR1a netns router1 # 在 namespace 内启用接口并配 IP ip netns exec ns56A ip link set veth56A up ip netns exec ns56A ip addr add 192.168.56.126/24 dev veth56A ip netns exec router1 ip link set vethR1a up ip netns exec router1 ip addr add 192.168.56.1/24 dev vethR1a每建一对 veth 就立刻up并配地址是为了避免后面排查时面对一堆 down 状态的接口。ip netns exec ns cmd是进入某个 namespace 执行命令的标准写法等价于先ip netns exec ns bash再在里面敲命令。实验步骤里让你「用 bash 把终端窗口模拟成主机 ns56A 的终端」本质就是起一个绑定在该 namespace 上的 shell。2.2 验证拓扑三条命令别省拓扑建完必须验证否则后面路由不通你会怀疑人生。实验里给了三条命令我建议一条不落地跑# 1. 列出所有 namespace确认数量对得上 ip netns list # 2. 在每个 namespace 内看接口配置确认 IP 和 up 状态 for ns in $(ip netns list | awk {print $1}); do echo $ns ip netns exec $ns ifconfig -a done # 3. 看网桥挂载了哪些接口 brctl showip netns list只告诉你 namespace 存在不告诉你里面接口是否 upifconfig -a能看到全部接口包括 down 的重点核对 IP 和 MTUbrctl show确认网桥的成员端口如果某个 veth 没挂上桥同网段主机之间就是不通。这三条命令覆盖了「对象存在、配置正确、连接正确」三个层面缺一个都可能留下隐患。常见翻车点是 veth 建好了但忘了ip link set up接口状态是 DOWNifconfig里看不到 RUNNING 标志这时候 ping 一定不通。3. 静态路由与 offload让报文按你预期走、按原样抓拓扑通了只是第一步跨网段通信要靠路由。实验里各路由器配静态路由让 ns56A 到 ns57C 的报文经过指定路径。这一步的意义不只是「能通」而是让路径可控——你知道报文从哪个接口出、经过哪几跳抓包时才能对上号。3.1 静态路由的配置与验证静态路由的核心是「去程和回程都要配」。只配去程请求能到、响应回不来是新手最常踩的坑。以 ns56A 所在网段经 router1 转发为例# ns56A 上去往 192.168.57.0/24 的报文交给 router1 ip netns exec ns56A ip route add 192.168.57.0/24 via 192.168.56.1 # router1 上去往 57 网段交给 router2回程 56 网段直连 ip netns exec router1 ip route add 192.168.57.0/24 via 192.168.56.2 # ns57C 上回程路由指向自己的网关 ip netns exec ns57C ip route add 192.168.56.0/24 via 192.168.57.1via后面跟的是下一跳地址必须和本接口同网段且可达。配完用ip netns exec ns ip route逐台核对再用ping或traceroute验证路径。如果路由表里出现两条同目的网段但掩码不同的条目Linux 按最长前缀匹配容易配出「看起来对、实际走错」的情况建议每条路由加完立刻ip route get 目的IP确认实际选路。3.2 关掉 offload把校验和计算还给 CPU这是整个实验里最有价值、也最容易被忽略的一步。现代网卡普遍支持 checksum offload运输层校验和的计算与填充由网卡硬件完成CPU 只负责把首部里的校验和字段先填个占位值。问题在于Wireshark 在 tap 接口抓包时抓到的是「网卡处理之前」的报文校验和字段还是占位值看起来像错的。实验要求关闭 offload就是为了让 CPU 真正算出校验和抓到的报文和课本上的格式完全一致。# 查看当前 offload 状态 ethtool -k tap57C # 关闭校验和相关的 offload ethtool -K tap57C tx off rx off tso off gso off gro off # 再次确认tx-checksumming 等应为 off ethtool -k tap57C | grep -i checksumtx off关发送方向校验和卸载rx off关接收方向tso/gso/gro关分片与聚合相关的卸载。只关 checksum 有时不够因为 TSO/GSO 会把大包在网卡侧才分段抓包看到的是超长「伪报文」。全关之后抓到的才是「CPU 视角」的真实报文。这一步不做后面手动算校验和一定对不上血泪经验。3.3 用 nc 发起一次 UDP 会话ncnetcat是实验里的通信工具UDP 模式下服务端和客户端各一条命令# ns57C 上启动 UDP 服务监听 4499 端口-l 监听 -u UDP -v 详细输出 ip netns exec ns57C nc -lvu 4499 # ns56A 上作为客户端向服务端 IP 和端口发数据 ip netns exec ns56A nc -u 192.168.57.254 4499服务端-lvu三个参数分别是监听、UDP、详细模式客户端-u指定 UDP后面跟目的地址和端口。两边都起来后在客户端敲一行字符回车服务端就能收到服务端回一行客户端也能收到。这里有个细节UDP 是无连接的客户端不发数据之前服务端根本不知道对端是谁所以「服务端什么时候获知客户端端口」这个问题的答案是——收到第一个报文时从首部的源端口字段读出来的。这也是实验问题 2 的考点。4. 抓包分析把 8 字节首部逐字段对一遍Wireshark 在 tap57C 上抓包过滤条件用udp.port 4499就够。一次完整的请求响应会抓到两个 UDP 用户数据报客户端发给服务端的、服务端回给客户端的。展开 UDP 首部四个字段一目了然源端口、目的端口、长度、校验和。4.1 首部字段与实验数据对照把实验记录里的两组数据整理成表对照着看更清楚字段客户端→服务端服务端→客户端说明源端口439904499客户端端口由系统动态分配目的端口449943990回程目的端口即请求源端口长度1518首部 8 字节 数据长度校验和0x78b70xb454覆盖伪首部 UDP 首部 数据长度字段 15 意味着数据部分是 7 字节对应hi,57C\n正好 7 个字符含换行。服务端回的 18 说明数据 10 字节。端口 43990 落在动态端口范围通常 49152 以上但 Linux 的ip_local_port_range默认从 32768 起实际分配可能更低属于临时端口由内核在首次发送时分配。这个细节实验问题 2 专门问了答「动态端口、操作系统临时分配」是对的。4.2 手动验算校验和UDP 校验和的计算分三步构造伪首部、按 16 位字求和、取反码。伪首部包含源 IP、目的 IP、协议号UDP 为 17、UDP 长度不参与传输但参与计算。以客户端报文为例伪首部192.168.56.126 | 192.168.57.254 | 0x0011 | 0x000F UDP首部0xABD6(43990) | 0x1193(4499) | 0x000F | 0x0000(计算时置0) 数据0x6869 0x2C35 0x3743 0x0A00(奇数长度补0)把所有 16 位字做反码和进位回卷加到低位结果取反就是校验和。接收方验证时把收到的校验和也加进去再求反码和结果为 0 即通过。实验里客户端报文校验通过说明发送端计算正确、路径上没有篡改。手动算一遍的意义在于你会真正理解「为什么奇数长度要补一个全 0 字节」「为什么伪首部要算进去」——因为校验和覆盖的是「伪首部 UDP 首部 数据」这个整体任何一部分变了校验和都得重算。提示Wireshark 里如果看到校验和显示「incorrect」先别急着怀疑报文有问题八成是抓包点在做 offload 的网卡上回到 3.2 把 offload 关掉再抓。5. 避坑与排查五个让实验卡住的真实问题5.1 现象ping 不通接口状态却是 UP原因通常是路由只配了单向或者某台路由器的ip_forward没开。namespace 默认继承宿主机的转发设置但独立 namespace 里net.ipv4.ip_forward可能是 0。解决ip netns exec router1 sysctl -w net.ipv4.ip_forward1逐台路由器确认再用ip route get核对选路。5.2 现象Wireshark 抓到的校验和全是错的原因就是 offload 没关干净或者只关了tx没关rx又或者抓包点在 veth 而非 tap 上。解决ethtool -K 接口 tx off rx off tso off gso off gro off全关确认ethtool -k输出里 checksum 相关项为 off再重新抓。5.3 现象nc 服务端起不来提示地址被占用原因可能是 4499 端口已被别的进程占用或者 namespace 里没有对应接口。解决ip netns exec ns57C ss -lunp | grep 4499查占用换端口或杀掉旧进程确认nc绑定的是正确接口的地址。5.4 现象客户端发了数据服务端没反应UDP 无连接服务端没收到不会报错。先确认服务端确实在监听ss -lun再确认客户端目的 IP 和端口没写错最后在中间路由器上抓包看报文到没到。常见原因是目的 IP 写成了服务端所在网段的广播地址或写错一位。5.5 现象手动算校验和对不上抓包值原因多半是伪首部里的 UDP 长度填错要用 UDP 首部长度字段的值不是 IP 总长度或者数据部分奇数长度忘了补 0又或者字节序搞反网络字节序是大端。解决按「伪首部 12 字节 UDP 首部 8 字节 数据补齐偶数」的顺序逐段核对用 Python 写个小脚本验算比手算靠谱。6. 进阶用脚本把校验和验算自动化手动算一次校验和是理解每次都手算就是折磨。我习惯写个十几行的 Python 脚本把抓包导出的十六进制串丢进去直接出结果顺便验证自己对伪首部的理解对不对import struct def udp_checksum(src_ip, dst_ip, src_port, dst_port, payload: bytes): # 构造伪首部源IP、目的IP、保留0、协议号17、UDP长度 udp_len 8 len(payload) pseudo struct.pack(!4s4sBBH, src_ip, dst_ip, 0, 17, udp_len) # UDP首部校验和字段先置0 header struct.pack(!HHHH, src_port, dst_port, udp_len, 0) data header payload # 奇数长度补一个0字节 if len(data) % 2: data b\x00 total 0 for i in range(0, len(data), 2): total (data[i] 8) data[i 1] total (total 0xFFFF) (total 16) # 进位回卷 return (~total) 0xFFFF # 用实验数据验证源 192.168.56.126 目的 192.168.57.254 src bytes([192, 168, 56, 126]) dst bytes([192, 168, 57, 254]) print(hex(udp_checksum(src, dst, 43990, 4499, bhi,57C\n)))struct.pack(!4s4sBBH, ...)里的!表示网络字节序大端4s是 4 字节字符串装 IPBBH是保留字节、协议号、长度。求和循环里每加一次就做(total 0xFFFF) (total 16)这是反码和的关键——进位要回卷加到低位不能简单截断。最后~total 0xFFFF取反并保留 16 位。跑出来如果和 Wireshark 里的 0x78b7 一致说明你对伪首部和字节序的理解都对了不一致就逐段打印中间值八成能定位到是长度填错还是补零漏了。这个脚本还能反过来用把抓到的报文数据部分喂进去算出校验和再和首部字段比对等于给抓包结果做了一次独立验证。从那以后我每次做协议实验抓完包都强制走一遍「脚本验算 Wireshark 对照」两边对上了才敢往报告里写结论。希望这套流程帮到你。本文还有配套的精品资源点击获取
返回列表