
简介本资源是哈尔滨工业大学计算机网络课程配套的全套实验材料面向高校计算机、网络工程等专业本科生及自学进阶者覆盖HTTP代理服务器设计、分组收发与转发、GBN可靠传输协议实现、Wireshark协议分析、局域网组建与配置六大核心实验有效支撑理论学习与工程实践结合。压缩包共22个文件含6份完整实验报告.docx、7个可运行前端/服务端代码.js/.cpp、3个网络抓包与配置文件.pkt/.json/.babelrc及2份说明文档.md总容量4.67MB结构清晰、开箱即用。已有562人学习下载内容真实还原哈工大实验教学要求提供从需求分析、代码实现、测试验证到结果分析的全流程参考尤其适合课程复习、实验复现与协议原理深度理解。1. 哈工大计算机网络实验1-6不是交作业的压缩包而是吃透TCP/IP栈的六把解剖刀你手头那个哈工大计算机网络实验1-6实验报告及源码.rar绝不是一份能抄完就删的“应试材料”。它是一套被哈工大837考研命题组反复验证过的、面向真实协议栈行为的动手闭环——从用原始套接字拼IPv4首部实验1到在用户态实现一个能被Wireshark完整抓包解析的简易TCP连接建立流程实验3再到用C语言重写UDP校验和计算逻辑并对比内核结果实验4最后在实验6里用Netfilter钩子拦截并修改ICMP报文验证TTL字段的逐跳衰减机制。这不是模拟器里的玩具是直接和Linux内核网络子系统对话的实操路径。适合两类人一是正在啃《计算机网络》谢希仁第八版、但总卡在“三次握手为什么不是两次”这种抽象问题上的学生二是想补足网络底层能力、却苦于没有可调试、可打断点、可改源码的真实环境的嵌入式/后端工程师。它不教你怎么配路由器它逼你亲手算出IP首部校验和、手动构造SYNACK的序列号与确认号、在sk_buff结构体里定位TTL字段——这才是“计算机网络”四个字该有的重量。2. 实验1用原始套接字手撕IPv4首部——从零构造合法IP包实验1是整套实验的基石。它强制你脱离sendto()的黑盒封装直面IPv4协议规范RFC 791中每一个字段的物理意义。核心目标用C语言调用socket(AF_INET, SOCK_RAW, IPPROTO_RAW)创建原始套接字手动填充IP首部20字节固定部分 可选字段封装一个ICMP Echo Requestping包发送给本地回环地址并用Wireshark验证其合法性。这不是“调用库函数发个包”而是让你成为IP首部的雕刻师。2.1 构造IP首部字段对齐、字节序、校验和三座大山IPv4首部字段必须严格按RFC 791定义的顺序和长度排布且所有多字节字段如总长度、标识、校验和必须为网络字节序大端。常见错误是直接用htons()处理单字节字段如TTL、协议号或忽略首部长度字段IHL需以4字节为单位——IHL5表示首部20字节IHL6表示24字节含4字节选项。以下是最小可行IP首部结构体定义struct iphdr { unsigned char ihl:4, version:4; // 高4位版本(4), 低4位首部长度(IHL) unsigned char tos; // 服务类型 unsigned short tot_len; // 总长度 IP首部 数据长度网络字节序 unsigned short id; // 标识网络字节序 unsigned short frag_off; // 标志片偏移网络字节序 unsigned char ttl; // 生存时间无需转换字节序 unsigned char protocol; // 协议号ICMP1, TCP6, UDP17 unsigned short check; // 首部校验和网络字节序计算时置0 unsigned int saddr; // 源IP网络字节序 unsigned int daddr; // 目的IP网络字节序 };提示struct iphdr必须用__attribute__((packed))修饰否则编译器会因内存对齐插入填充字节导致首部长度错乱。GCC下标准写法struct iphdr { // ... 字段同上 } __attribute__((packed));2.2 手动计算IP首部校验和不能依赖内核必须自己算IP首部校验和是16位反码和ones complement sum计算规则严苛将首部按16位2字节分组不足补0对所有16位组求和若最高位有进位则将进位加到最低位即“回卷”对最终和取反码。关键陷阱校验和字段自身在计算时必须置为0否则形成循环依赖。以下为标准计算函数unsigned short checksum(unsigned short *buf, int nwords) { unsigned long sum 0; for (int i 0; i nwords; i) { sum buf[i]; } while (sum 16) { // 回卷只要高位有进位就加到低位 sum (sum 0xFFFF) (sum 16); } return ~sum; // 取反码 } // 使用示例计算ip_header的校验和 struct iphdr *iph (struct iphdr*)packet; iph-check 0; // 关键先清零 iph-check checksum((unsigned short*)iph, iph-ihl * 2); // ihl是4位*2得字节数再/2得16位组数 → 等价于 ihl*2参数说明iph-ihl * 2是因为ihl值为5表示20字节而checksum()函数以unsigned short2字节为单位计数故传入20/2 10。若ihl624字节则传入12。此参数必须动态计算硬编码10会导致带选项的IP包校验失败。2.3 发送原始包绕过路由表直抵网卡使用SOCK_RAW时内核默认不进行路由查找需显式指定输出接口。最稳妥方式是绑定到INADDR_ANY并设置IP_HDRINCL套接字选项让内核跳过首部构造完全由用户控制int sock socket(AF_INET, SOCK_RAW, IPPROTO_RAW); int on 1; setsockopt(sock, IPPROTO_IP, IP_HDRINCL, on, sizeof(on)); // 关键告诉内核我已包含IP首部 struct sockaddr_in dest; dest.sin_family AF_INET; dest.sin_port htons(0); // ICMP无端口概念置0 dest.sin_addr.s_addr inet_addr(127.0.0.1); // packet指向已填充好IP首部ICMP数据的内存块 sendto(sock, packet, packet_len, 0, (struct sockaddr*)dest, sizeof(dest));注意sendto()的目标地址sin_addr必须是有效的IPv4地址即使你发的是ICMP包。内核仍需此地址决定走哪条路由回环、本地网段或默认网关。若填0.0.0.0系统可能报Invalid argument。3. 实验3用户态实现TCP三次握手——在connect()背后埋设断点实验3是整套实验的认知跃迁点。它要求你放弃connect()系统调用用原始套接字手动发送SYN、接收SYNACK、再发送ACK全程在用户空间完成TCP状态机。这不是为了造轮子而是为了看清“连接”二字在内核里究竟意味着什么struct sock如何被创建、sk-sk_state如何流转、inet_hash_connect()如何分配临时端口、tcp_v4_rcv()如何根据四元组查找socket。你将第一次看到所谓“建立连接”本质是双方在各自内存中维护了一套同步的状态快照。3.1 构建SYN包序列号、窗口、MSS选项缺一不可TCP首部20字节后必须携带选项Options否则现代Linux内核2.6.29会直接丢弃SYN包。最简必需选项是MSSMaximum Segment Size告知对方本端能接收的最大TCP载荷长度。MSS值通常为MTU - IP首部(20) - TCP首部(20) 1460以太网MTU1500。TCP选项格式为(Kind, Length, Data)三元组MSS的Kind2Length4含Kind和Length自身Data为2字节MSS值网络字节序// TCP首部结构体简化 struct tcphdr { unsigned short source; unsigned short dest; unsigned int seq; unsigned int ack_seq; unsigned char doff:4, res1:3, cwr:1; // 数据偏移首部长度单位4字节 unsigned char res2:2, ece:1, urg:1, ack:1, psh:1, rst:1, syn:1, fin:1; unsigned short window; unsigned short check; unsigned short urg_ptr; }; // 构造TCP首部 MSS选项 struct tcphdr *tcph (struct tcphdr*)(ip_header 1); tcph-source htons(54321); // 本地临时端口 tcph-dest htons(80); // 目标端口 tcph-seq htonl(0x12345678); // 初始序列号ISN需随机化实验中可固定 tcph-ack_seq 0; // SYN包不携带确认号 tcph-doff 6; // 首部长度 24字节20基础 4字节MSS选项 tcph-syn 1; // 设置SYN标志 tcph-window htons(65535); // 接收窗口 tcph-check 0; // 校验和计算前置0 // 追加MSS选项Kind2, Len4, MSS1460 unsigned char *options (unsigned char*)(tcph 1); options[0] 2; // Kind options[1] 4; // Length options[2] (1460 8) 0xFF; // MSS高字节 options[3] 1460 0xFF; // MSS低字节血泪经验tcph-doff必须精确等于(TCP首部字节数)/4。若只填520字节但实际追加了4字节选项则内核解析首部时会越界读取导致SYN包被静默丢弃Wireshark里只看到你发的SYN收不到任何响应——这是实验3初期最高频的翻车点。3.2 计算TCP校验和伪首部是灵魂缺之必死TCP校验和计算比IP复杂必须包含伪首部pseudo-header12字节由源IP、目的IP、协议号6、TCP总长度首部数据组成。伪首部仅用于校验和计算不随包发送。计算流程将伪首部12字节 TCP首部24字节 TCP数据0字节因SYN无载荷拼成连续内存按16位分组求和回卷取反码。// 伪首部结构仅用于计算 struct pseudo_hdr { unsigned int src_addr; unsigned int dst_addr; unsigned char zeros; unsigned char proto; unsigned short tcp_len; // TCP首部数据长度网络字节序 }; // 计算TCP校验和 struct pseudo_hdr ph; ph.src_addr iph-saddr; ph.dst_addr iph-daddr; ph.zeros 0; ph.proto IPPROTO_TCP; ph.tcp_len htons(tcph-doff * 4); // TCP首部长度单位字节 // 分配足够内存伪首部(12) TCP首部(24) 选项(4) 40字节 unsigned short *sum_buf malloc(40); memcpy(sum_buf, ph, 12); memcpy((char*)sum_buf 12, tcph, 24); memcpy((char*)sum_buf 36, options, 4); tcph-check checksum(sum_buf, 20); // 40字节 / 2 20个16位组 free(sum_buf);玄学提醒ph.tcp_len必须是TCP段总长度首部数据而非IP总长度。SYN包无数据故为tcph-doff * 4。若误用iph-tot_len校验和必然错误目标主机回复RST。3.3 接收SYNACK并解析从sk_buff视角看内核如何分发实验3要求你用recvfrom()接收返回的SYNACK包并从中提取ack_seq用于构造ACK的ack_seq字段和window用于后续数据传输。但关键在于你收到的包是完整的IP包需先跳过IP首部才能访问TCP首部。iph-ihl字段此时成为你的导航仪struct iphdr *r_iph (struct iphdr*)recv_buf; int ip_header_len r_iph-ihl * 4; // 动态计算IP首部长度 struct tcphdr *r_tcph (struct tcphdr*)((char*)r_iph ip_header_len); if (r_tcph-syn r_tcph-ack) { // 确认是SYNACK printf(Received SYNACK! Ack number: %u\n, ntohl(r_tcph-ack_seq)); printf(Window size: %u\n, ntohs(r_tcph-window)); // 构造ACK包ack_seq 收到的seq1, seq 自己的初始seq1 tcph-ack_seq htonl(ntohl(r_tcph-seq) 1); tcph-seq htonl(ntohl(tcph-seq) 1); tcph-ack 1; tcph-syn 0; // ... 重新计算TCP校验和发送 }避坑r_tcph-seq是对方的初始序列号ISN你的ACK包中ack_seq必须是ISN 1而非ISN。这是三次握手的铁律错1就会导致连接失败。4. 实验4UDP校验和深度验证——当用户态计算与内核结果不一致时实验4表面是重写UDP校验和计算实则是对“校验和是否可选”这一经典误解的证伪。许多教材称“UDP校验和可选”但实验会证明在Linux下若UDP校验和为0内核会将其视为禁用校验和而并非“校验和值为0”。更残酷的是当你用原始套接字发送UDP包时若未显式计算校验和并填入字段内核会自动为你计算——这导致用户态代码与内核行为出现隐蔽偏差。本实验通过强制关闭内核校验和卸载ethtool -K eth0 tx off rx off并对比Wireshark抓包结果让你亲眼看见字节级的差异。4.1 UDP校验和计算伪首部规则与全0校验和的语义UDP校验和计算同样依赖伪首部但规则略有不同伪首部源IP、目的IP、0保留字节、协议号17、UDP长度首部8字节 数据长度关键区别若UDP数据部分为奇数长度需在末尾补一个0字节仅用于校验和计算不发送若校验和计算结果为0则必须填入0xFFFF全1因为0被定义为“校验和禁用”标志。// UDP伪首部 struct udp_pseudo { unsigned int src_addr; unsigned int dst_addr; unsigned char zeros; unsigned char proto; unsigned short udp_len; // UDP首部数据长度网络字节序 }; // 计算UDP校验和data_len为实际数据长度 unsigned short udp_checksum(struct iphdr *iph, unsigned short src_port, unsigned short dst_port, char *data, int data_len) { struct udp_pseudo ph; ph.src_addr iph-saddr; ph.dst_addr iph-daddr; ph.zeros 0; ph.proto IPPROTO_UDP; ph.udp_len htons(8 data_len); // UDP首部8字节 // 分配内存伪首部(12) UDP首部(8) 数据(data_len) (data_len%21 ? 1 : 0) int total_len 12 8 data_len (data_len % 2); unsigned short *buf malloc(total_len); memcpy(buf, ph, 12); // 填充UDP首部源端口、目的端口、长度、校验和占位符0 ((unsigned short*)buf)[6] src_port; // 索引612/2即伪首部后 ((unsigned short*)buf)[7] dst_port; ((unsigned short*)buf)[8] ph.udp_len; ((unsigned short*)buf)[9] 0; // 校验和占位符 memcpy((char*)buf 20, data, data_len); // 伪首部12 UDP首部8 20字节偏移 // 若data_len为奇数在末尾补0 if (data_len % 2) { ((char*)buf)[total_len - 1] 0; } unsigned short sum checksum(buf, total_len / 2); free(buf); // 校验和为0时必须返回0xFFFF return (sum 0) ? 0xFFFF : sum; }参数说明data_len % 2判断数据长度奇偶性决定是否补0。此补0仅发生在校验和计算过程中实际发送的UDP包数据部分长度仍是data_len不会增加冗余字节。4.2 内核校验和卸载Checksum Offload实验前必须关闭的隐形开关现代网卡支持硬件校验和计算TX offload即CPU将未计算校验和的包交给网卡由网卡在发送前实时计算并填入。这导致你在用户态看到的send()缓冲区中的UDP首部check字段是0但Wireshark抓到的线缆上实际包却是正确的——因为网卡在物理层完成了计算。实验4要求你观测“用户态计算值”与“线缆上真实值”的一致性因此必须关闭此功能# 查看当前卸载状态 ethtool -k eth0 | grep checksum # 关闭发送和接收校验和卸载需root sudo ethtool -K eth0 tx off rx off # 验证已关闭 ethtool -k eth0 | grep checksum # 输出应为tx: off rx: off注意ethtool命令需在实验主机上执行且eth0需替换为你的实际网卡名可用ip link show查看。若关闭失败可能是驱动不支持或内核模块锁定此时需重启网络服务或主机。4.3 Wireshark验证用显示过滤器揪出字节级差异在Wireshark中使用显示过滤器udp ip.addr 127.0.0.1聚焦回环流量。右键UDP包 → “Protocol Preferences” → “UDP” → 勾选“Validate the UDP checksum if possible”。此时若校验和错误Wireshark会在Packet Details面板中将Checksum字段标为红色并显示Bad。更进一步使用“Follow UDP Stream”功能导出原始十六进制数据与你用户态计算的check字段htons()后逐字节比对。你会发现当你填入0x0000时Wireshark显示Checksum: 0x0000 [invalid, must be 0xffff]当你填入0xffff时Wireshark显示Checksum: 0xffff [valid]当你填入正确计算值如0x1234时Wireshark显示Checksum: 0x1234 [valid]。这直接印证了RFC 768的明文规定“If the computed checksum is zero, it is transmitted as all ones (the equivalent in one’s complement arithmetic).”5. 实验6Netfilter钩子拦截ICMP——在内核网络栈中植入自己的逻辑实验6是整套实验的技术制高点它带你跨过用户态与内核态的鸿沟用Linux内核模块LKM在Netfilter框架的NF_INET_PRE_ROUTING和NF_INET_POST_ROUTING挂点上注册回调函数实时拦截、解析、甚至修改经过的ICMP报文。你将亲手编写icmp_hook_pre()函数在IP包进入路由子系统前读取其TTL值再编写icmp_hook_post()在IP包离开本机前将其TTL减1。这不是iptables的规则配置而是直接在ip_rcv()和ip_output()函数的执行路径上“打孔”是真正意义上的网络栈手术。5.1 Netfilter钩子注册nf_register_net_hook()的生存周期管理Netfilter钩子必须在模块加载时注册卸载时注销否则会导致内核崩溃Oops。注册点选择NF_INET_PRE_ROUTING路由前和NF_INET_POST_ROUTING路由后最为典型。关键在于struct nf_hook_ops的初始化#include linux/netfilter.h #include linux/netfilter_ipv4.h static struct nf_hook_ops icmp_pre_ops { .hook icmp_hook_pre, .pf PF_INET, .hooknum NF_INET_PRE_ROUTING, .priority NF_IP_PRI_FIRST, // 最高优先级最早被调用 }; static struct nf_hook_ops icmp_post_ops { .hook icmp_hook_post, .pf PF_INET, .hooknum NF_INET_POST_ROUTING, .priority NF_IP_PRI_LAST, // 最低优先级最后被调用 }; static int __init icmp_init(void) { int ret; ret nf_register_net_hook(init_net, icmp_pre_ops); if (ret 0) { printk(KERN_ERR Failed to register PRE hook\n); return ret; } ret nf_register_net_hook(init_net, icmp_post_ops); if (ret 0) { printk(KERN_ERR Failed to register POST hook\n); nf_unregister_net_hook(init_net, icmp_pre_ops); return ret; } printk(KERN_INFO ICMP hooks registered\n); return 0; } static void __exit icmp_exit(void) { nf_unregister_net_hook(init_net, icmp_pre_ops); nf_unregister_net_hook(init_net, icmp_post_ops); printk(KERN_INFO ICMP hooks unregistered\n); }提示init_net是主网络命名空间实验环境通常只需操作它。priority字段决定同一挂点上多个钩子的执行顺序NF_IP_PRI_FIRST-2147483648确保你的钩子最先执行避免被其他模块如iptables干扰。5.2 解析ICMP报文从sk_buff中安全提取IP和ICMP首部在钩子函数中输入参数是struct sk_buff *skb它是一个复杂的网络缓冲区结构。安全提取IP和ICMP首部需遵循内核API规范绝不能直接指针运算unsigned int icmp_hook_pre(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { struct iphdr *iph; struct icmphdr *icmph; unsigned char *payload; // 1. 确保skb有足够的IP首部空间 if (!pskb_may_pull(skb, sizeof(struct iphdr))) { return NF_ACCEPT; // 数据不足放行 } iph ip_hdr(skb); // 安全获取IP首部指针 if (iph-protocol ! IPPROTO_ICMP) { return NF_ACCEPT; // 非ICMP包放行 } // 2. 确保skb有足够的IP首部ICMP首部空间 if (!pskb_may_pull(skb, iph-ihl * 4 sizeof(struct icmphdr))) { return NF_ACCEPT; } // 3. 重新获取IP首部因pskb_may_pull可能移动数据 iph ip_hdr(skb); payload skb_network_header(skb) iph-ihl * 4; icmph (struct icmphdr*)payload; printk(KERN_INFO PRE: ICMP Type%d, TTL%d\n, icmph-type, iph-ttl); return NF_ACCEPT; }避坑pskb_may_pull()是关键安全函数它确保skb的数据区至少有指定字节数可用。若不调用直接ip_hdr(skb)可能导致访问非法内存。skb_network_header(skb)返回IP首部起始地址iph-ihl * 4是IP首部长度二者相加即为ICMP首部地址。5.3 修改TTL字段skb_make_writable()是唯一安全途径要修改IP首部的ttl字段必须确保skb的数据区是可写的Writable。内核为优化性能常将skb数据区设为只读如来自网卡DMA缓冲区。直接修改iph-ttl会导致内核Oops。正确做法是调用skb_make_writable()unsigned int icmp_hook_post(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { struct iphdr *iph; if (!pskb_may_pull(skb, sizeof(struct iphdr))) { return NF_ACCEPT; } iph ip_hdr(skb); if (iph-protocol ! IPPROTO_ICMP) { return NF_ACCEPT; } // 关键确保IP首部可写 if (!skb_make_writable(skb, iph-ihl * 4)) { return NF_DROP; // 内存分配失败丢弃包 } // 重新获取IP首部因skb_make_writable可能克隆skb iph ip_hdr(skb); if (iph-ttl 1) { iph-ttl--; // TTL减1 iph-check 0; // TTL改变IP校验和失效 iph-check ip_fast_csum(iph, iph-ihl); // 重新计算快速校验和 printk(KERN_INFO POST: TTL decremented to %d\n, iph-ttl); } return NF_ACCEPT; }参数说明skb_make_writable(skb, len)尝试使skb的前len字节可写。若成功返回1若失败如内存不足返回0此时必须返回NF_DROP或NF_STOLEN绝不能继续操作。ip_fast_csum()是内核提供的高效IP校验和计算函数专用于首部未变、仅字段更新的场景。6. 避坑指南哈工大计算机网络实验1-6的5个致命陷阱与血泪解法这些坑是我当年在实验室熬通宵、抓狂重启、对着Wireshark发呆两小时后用printk和gdb一行行扒出来的。它们不写在实验指导书里但足以让90%的人卡在同一个地方超过24小时。6.1 现象实验1的IP包发出去Wireshark能看到但目标主机无任何响应不回ICMP Echo Reply原因IP首部的protocol字段填错。常见错误是填IPPROTO_ICMP值为2但RFC 792明确规定ICMP协议号是1。IPPROTO_ICMP是socket()系统调用的参数不是IP首部的协议字段。IP首部协议字段必须是1ICMP、6TCP或17UDP。解决检查iph-protocol 1;而非iph-protocol IPPROTO_ICMP;。用Wireshark的Packet Details面板展开IP首部确认Protocol: ICMP (1)。6.2 现象实验3的SYN包发出后收到RST而非SYNACK原因TCP校验和计算时伪首部中的tcp_len填了IP总长度iph-tot_len而非TCP段长度tcph-doff * 4。SYN包无数据tcp_len应为246*4若填了iph-tot_len如60校验和必然错误目标主机内核tcp_v4_rcv()校验失败直接回复RST。解决严格使用tcph-doff * 4作为伪首部的tcp_len。打印调试printk(TCP len for checksum: %d\n, ntohs(ph.tcp_len));确认输出为24。6.3 现象实验4的UDP包用户态计算校验和为0x1234但Wireshark显示Bad checksum原因网卡校验和卸载TX offload未关闭。用户态填入的0x1234被网卡忽略网卡用自己计算的值覆盖了UDP首部check字段导致线缆上真实值与你计算的不一致。解决执行sudo ethtool -K eth0 tx off并用ethtool -k eth0确认tx: off。若命令不存在安装ethtoolsudo apt install ethtoolUbuntu或sudo yum install ethtoolCentOS。6.4 现象实验6的内核模块加载后ping命令卡死或系统变慢原因钩子函数中未正确处理pskb_may_pull()失败或在skb_make_writable()失败后未返回NF_DROP导致后续代码访问非法内存触发内核soft lockup。解决钩子函数中每一处涉及skb数据访问前必须调用pskb_may_pull()并检查返回值修改首部前必须调用skb_make_writable()并检查返回值。失败时一律返回NF_DROP或NF_ACCEPT绝不继续执行。6.5 现象实验6模块卸载后ping仍无法工作或系统日志刷屏icmp_hook_pre: invalid packet原因模块卸载时nf_unregister_net_hook()未被调用或调用顺序错误如先卸载POST钩子后卸载PRE钩子但PRE钩子内部还引用了POST钩子的资源。更隐蔽的是钩子函数仍在内核中运行竞态条件卸载后访问已释放内存。解决在__exit函数中严格按注册的逆序调用nf_unregister_net_hook()并在调用后添加msleep(10)让内核完成钩子清理。同时在钩子函数开头添加if (!skb)防护if (!skb || !skb-data) return NF_DROP;。7. 验证与进阶用tcpdump和/proc/net/实时观测协议栈行为做完六个实验别急着打包交差。真正的内功在于建立一套可观测、可验证、可反推的闭环验证方法。我坚持十年的习惯是每个实验完成后必做三件事——用tcpdump抓包比对、查/proc/net/接口看内核状态、用strace跟踪系统调用路径。这三招能把“我发了包”升级为“我看见了协议栈每一步的呼吸”。7.1tcpdump的精准过滤从海量包中揪出你的实验包tcpdump是比Wireshark更底层、更轻量的验证工具尤其适合在服务器环境或脚本中自动化验证。针对六个实验我固化了以下过滤命令实验验证目标tcpdump命令实验1抓取你构造的ICMP Echo Requestsudo tcpdump -i lo icmp[0] 8 and src host 127.0.0.1 -XX-XX显示十六进制和ASCIIicmp[0] 8匹配ICMP Type8Echo Request实验3抓取SYN包并确认序列号sudo tcpdump -i lo tcp[tcpflags] tcp-syn ! 0 and src port 54321 -nn -X-nn禁用DNS/端口解析-X显示载荷tcp[tcpflags] tcp-syn匹配SYN标志实验4抓取UDP包并验证校验和字段sudo本文还有配套的精品资源点击获取