文章总结: 本文详细解析libdnet库的数据包构造机制,涵盖以太网帧、ARP、IP、IPv6、TCP、UDP、ICMP、SCTP等各层协议。介绍了零拷贝设计、打包宏体系、跨平台字节序处理、结构对齐等核心技术,提供大量源码示例和实际应用场景。适合底层网络编程与安全工具开发参考。 综合评分: 85 文章分类: 安全开发,二进制安全,网络协议,代码审计,实战经验
跨平台底层网络库libdnet源码分析系列(八)
原创
haidragon haidragon
安全狗的自我修养
2026年3月26日 15:32 湖南
官网:http://securitytech.cc
#
源码分析mettle后门工具学习 所使用的依赖库
#
#
原始数据包构造深入分析
目录
- 数据包构造基础概念
- libdnet数据包构造设计
- 以太网帧构造
- ARP数据包构造
- IP数据包构造
- IPv6数据包构造
- TCP数据包构造
- UDP数据包构造
- ICMP数据包构造
- ICMPv6数据包构造
- SCTP数据包构造
- 跨平台实现对比
- 实际应用示例
- 常见问题与解决方案
1. 数据包构造基础概念
1.1 网络协议栈层次结构
1. ┌─────────────────────────────────────────┐
2. │应用层(Application)│
3. ├─────────────────────────────────────────┤
4. │传输层(Transport)│← TCP/UDP/SCTP
5. ├─────────────────────────────────────────┤
6. │网络层(Network)│← IP/IPv6/ARP
7. ├─────────────────────────────────────────┤
8. │数据链路层(DataLink)│←Ethernet
9. ├─────────────────────────────────────────┤
10. │物理层(Physical)│
11. └─────────────────────────────────────────┘
1.2 数据包封装顺序
1. ┌────────────────────────────────────────────────────┐
2. │应用数据│
3. ├────────────────────────────────────────────────────┤
4. │ TCP/UDP/SCTP 头部|应用数据│
5. ├────────────────────────────────────────────────────┤
6. │ IP/IPv6头部| TCP/UDP/SCTP 头部|应用数据│
7. ├────────────────────────────────────────────────────┤
8. │Ethernet头部| IP/IPv6头部|传输层...|数据│
9. └────────────────────────────────────────────────────┘
1.3 字节序问题
网络字节序:大端序(Big-Endian),高位字节在前
主机字节序:
- x86/x64:小端序(Little-Endian)
- PowerPC/ARM:大端序(Big-Endian)
libdnet提供了宏自动处理:
1. #include<dnet.h>
3. // 转换为网络字节序
4. uint16_t port = htons(8080);
5. uint32_t addr = htonl(0x0a000001);
7. // 从网络字节序转换
8. uint16_t hport = ntohs(8080);
9. uint32_t haddr = ntohl(0x0a000001);
1.4 校验和计算
目的:检测数据传输过程中的错误
算法:16位反码求和
1. // 简化的校验和算法示例
2. uint16_t checksum(void*buf,size_t len){
3. uint16_t*ptr =(uint16_t*)buf;
4. uint32_t sum =0;
6. while(len >1){
7. sum +=*ptr++;
8. len -=2;
9. }
11. if(len ==1){
12. sum +=*(uint8_t*)ptr <<8;
13. }
15. // 反码求和
16. while(sum >>16){
17. sum =(sum &0xffff)+(sum >>16);
18. }
20. return~sum;
21. }
2. libdnet数据包构造设计
2.1 核心设计原则
- 零拷贝:直接操作缓冲区
- 宏封装:使用宏简化构造代码
- 跨平台:自动处理字节序和结构对齐
- 灵活扩展:支持选项和扩展头
2.2 打包宏体系
libdnet提供了一套完整的打包宏:
| 协议 | 打包宏 | 位置 |
| — | — | — |
| Ethernet | eth_pack_hdr | include/dnet/eth.h:74-79 |
| ARP | arp_pack_hdr_ethip | include/dnet/arp.h:83-95 |
| IP | ip_pack_hdr | include/dnet/ip.h:415-431 |
| IPv6 | ip6_pack_hdr | include/dnet/ip6.h:166-179 |
| TCP | tcp_pack_hdr | include/dnet/tcp.h:177-193 |
| UDP | udp_pack_hdr | include/dnet/udp.h:24-29 |
| ICMP | icmp_pack_hdr_* | include/dnet/icmp.h:224-262 |
| ICMPv6 | icmpv6_pack_hdr_* | include/dnet/icmpv6.h:91-114 |
| SCTP | sctp_pack_hdr | include/dnet/sctp.h:31-36 |
2.3 数据结构对齐
libdnet使用 __attribute__((__packed__))确保结构紧凑:
1. // include/dnet/ip.h:35-53
2. struct ip_hdr {
3. #if DNET_BYTESEX == DNET_BIG_ENDIAN
4. uint8_t ip_v:4,// 版本号
5. ip_hl:4;// 头部长度
6. #elif DNET_BYTESEX == DNET_LIL_ENDIAN
7. uint8_t ip_hl:4, ip_v:4;
8. #endif
9. uint8_t ip_tos;// 服务类型
10. uint16_t ip_len;// 总长度
11. uint16_t ip_id;// 标识
12. uint16_t ip_off;// 片偏移
13. uint8_t ip_ttl;// 生存时间
14. uint8_t ip_p;// 协议
15. uint16_t ip_sum;// 校验和
16. ip_addr_t ip_src;// 源地址
17. ip_addr_t ip_dst;// 目的地址
18. } __attribute__((__packed__));
2.4 字节序自动处理
1. // include/config.h 中的字节序检测
2. #if defined(__BYTE_ORDER__)
3. #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
4. #define DNET_BYTESEX DNET_LIL_ENDIAN
5. #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
6. #define DNET_BYTESEX DNET_BIG_ENDIAN
7. #endif
8. #endif
3. 以太网帧构造
3.1 以太网帧结构
1. ┌──────┬──────┬──────┬────────────────────┬──────┐
2. │6B│6B│2B│46-1500B│4B│
3. │目的│源│类型│数据│ CRC │
4. │ MAC │ MAC ││││
5. └──────┴──────┴──────┴────────────────────┴──────┘
3.2 以太网头部定义
位置: include/dnet/eth.h:29-33
1. struct eth_hdr {
2. eth_addr_t eth_dst;// 目的MAC地址
3. eth_addr_t eth_src;// 源MAC地址
4. uint16_t eth_type;// 以太类型
5. };
3.3 常见以太类型
| 类型值 | 协议 | 定义位置 |
| — | — | — |
| 0x0800 | IPv4 | ETH_TYPE_IP |
| 0x0806 | ARP | ETH_TYPE_ARP |
| 0x86DD | IPv6 | ETH_TYPE_IPV6 |
| 0x8100 | 802.1Q VLAN | ETH_TYPE_8021Q |
| 0x8847 | MPLS | ETH_TYPE_MPLS |
位置: include/dnet/eth.h:38-52
3.4 打包宏实现
位置: include/dnet/eth.h:74-79
1. #define eth_pack_hdr(h, dst, src, type)do{ \
2. struct eth_hdr *eth_pack_p =(struct eth_hdr *)(h); \
3. memcpy(ð_pack_p->eth_dst,&(dst), ETH_ADDR_LEN); \
4. memcpy(ð_pack_p->eth_src,&(src), ETH_ADDR_LEN); \
5. eth_pack_p->eth_type = htons(type); \
6. }while(0)
3.5 构造示例
1. #include<dnet.h>
3. u_char frame[ETH_LEN_MAX];
5. // MAC地址定义
6. eth_addr_t src_mac ={0x00,0x11,0x22,0x33,0x44,0x55};
7. eth_addr_t dst_mac ={0xff,0xff,0xff,0xff,0xff,0xff};
9. // 构造以太网头部
10. eth_pack_hdr(frame, dst_mac, src_mac, ETH_TYPE_IP);
12. // 填充数据载荷
13. memcpy(frame + ETH_HDR_LEN, payload, payload_len);
15. // 发送
16. eth_send(eth, frame, ETH_HDR_LEN + payload_len);
3.6 VLAN标签支持
位置: include/dnet/eth.h:54-68
1. struct eth_8021q_hdr {
2. uint16_t priority_c_vid;// 优先级 | VLAN ID (TCI)
3. uint16_t len_eth_type;// 长度或类型
4. };
6. // VLAN掩码
7. #define ETH_8021Q_PRIMASK 0x0007// 优先级掩码
8. #define ETH_8021Q_CFIMASK 0x0001// CFI掩码
9. #define ETH_8021Q_VIDMASK 0x0fff// VID掩码
11. // 判断是否为VLAN类型
12. #define ETH_TYPE_IS_VLAN(type) \
13. (((type)== ETH_TYPE_8021Q)|| \
14. ((type)== ETH_TYPE_8021ad_0)|| \
15. ((type)== ETH_TYPE_8021ad_1)|| \
16. ((type)== ETH_TYPE_8021ad_2)|| \
17. ((type)== ETH_TYPE_8021ad_3))
4. ARP数据包构造
4.1 ARP协议概述
ARP(Address Resolution Protocol)用于将IP地址解析为MAC地址。
4.2 ARP数据包结构
1. ┌──────┬──────┬────┬────┬────┬────────────────────────┐
2. │2B│2B│1B│1B│2B││
3. │硬件│协议│硬│协│操│可变长度(通常28B)│
4. │类型│类型│件│议│作││
5. │││长│长│││
6. └──────┴──────┴────┴────┴────┴────────────────────────┘
4.3 ARP头部定义
位置: include/dnet/arp.h:27-33
1. struct arp_hdr {
2. uint16_t ar_hrd;// 硬件地址格式
3. uint16_t ar_pro;// 协议地址格式
4. uint8_t ar_hln;// 硬件地址长度
5. uint8_t ar_pln;// 协议地址长度
6. uint16_t ar_op;// 操作类型
7. };
4.4 ARP操作类型
| 操作 | 值 | 说明 |
| — | — | — |
| ARP_OP_REQUEST | 1 | 请求解析 |
| ARP_OP_REPLY | 2 | 响应 |
| ARP_OP_REVREQUEST | 3 | 反向请求 |
| ARP_OP_REVREPLY | 4 | 反向响应 |
位置: include/dnet/arp.h:56-59
4.5 Ethernet/IP ARP数据
位置: include/dnet/arp.h:64-69
1. struct arp_ethip {
2. eth_addr_t ar_sha;// 发送方硬件地址
3. ip_addr_t ar_spa;// 发送方协议地址
4. eth_addr_t ar_tha;// 目标硬件地址
5. ip_addr_t ar_tpa;// 目标协议地址
6. };
4.6 打包宏实现
位置: include/dnet/arp.h:83-95
1. #define arp_pack_hdr_ethip(hdr, op, sha, spa, tha, tpa)do{ \
2. struct arp_hdr *pack_arp_p =(struct arp_hdr *)(hdr); \
3. struct arp_ethip *pack_ethip_p =(struct arp_ethip *) \
4. (pack_arp_p +1); \
5. pack_arp_p->ar_hrd = htons(ARP_HRD_ETH); \
6. pack_arp_p->ar_pro = htons(ARP_PRO_IP); \
7. pack_arp_p->ar_hln = ETH_ADDR_LEN; \
8. pack_arp_p->ar_pln = IP_ADDR_LEN; \
9. pack_arp_p->ar_op = htons(op); \
10. memcpy(&pack_ethip_p->ar_sha,&(sha), ETH_ADDR_LEN); \
11. memcpy(&pack_ethip_p->ar_spa,&(spa), IP_ADDR_LEN); \
12. memcpy(&pack_ethip_p->ar_tha,&(tha), ETH_ADDR_LEN); \
13. memcpy(&pack_ethip_p->ar_tpa,&(tpa), IP_ADDR_LEN); \
14. }while(0)
4.7 构造示例
1. #include<dnet.h>
3. u_char frame[ETH_HDR_LEN + ARP_HDR_LEN + ARP_ETHIP_LEN];
5. // 以太网头部
6. eth_pack_hdr(frame,
7. ETH_ADDR_BROADCAST,// 广播MAC
8. src_mac,
9. ETH_TYPE_ARP);
11. // ARP请求
12. eth_addr_t zero_mac ={0};
13. arp_pack_hdr_ethip(frame + ETH_HDR_LEN,
14. ARP_OP_REQUEST,
15. src_mac,
16. src_ip,
17. zero_mac,
18. target_ip);
20. // 发送
21. eth_send(eth, frame,sizeof(frame));
4.8 ARP数据包发送实例
位置: src/ip-cooked.c:127-138
1. staticvoid
2. _request_arp(struct ip_intf *ipi,struct addr *dst)
3. {
4. u_char frame[ETH_HDR_LEN + ARP_HDR_LEN + ARP_ETHIP_LEN];
6. eth_pack_hdr(frame, ETH_ADDR_BROADCAST, ipi->ha.addr_eth,
7. ETH_TYPE_ARP);
8. arp_pack_hdr_ethip(frame + ETH_HDR_LEN, ARP_OP_REQUEST,
9. ipi->ha.addr_eth, ipi->pa.addr_ip, ETH_ADDR_BROADCAST,
10. dst->addr_ip);
12. eth_send(ipi->eth, frame,sizeof(frame));
13. }
5. IP数据包构造
5.1 IP协议概述
IP(Internet Protocol)是网络层的核心协议,负责数据包的寻址和路由。
5.2 IPv4头部结构
1. ┌───┬───┬────────┬────────┬──────┬────────┬──────┬────────┬────────┬────────┐
2. │4│4│8│16│16│16│8│8│16│32x2│
3. │版本│IHL│服务│总│标识│片偏移│ TTL │协议│校验和│源/目的│
4. │││类型│长度││││││地址│
5. └───┴───┴────────┴────────┴──────┴────────┴──────┴────────┴────────┴────────┘
5.3 IP头部定义
位置: include/dnet/ip.h:35-53
1. struct ip_hdr {
2. #if DNET_BYTESEX == DNET_BIG_ENDIAN
3. uint8_t ip_v:4,// 版本号 (4)
4. ip_hl:4;// 头部长度 (以4字节为单位)
5. #elif DNET_BYTESEX == DNET_LIL_ENDIAN
6. uint8_t ip_hl:4, ip_v:4;
7. #endif
8. uint8_t ip_tos;// 服务类型
9. uint16_t ip_len;// 总长度(包括头部和数据)
10. uint16_t ip_id;// 标识
11. uint16_t ip_off;// 片偏移和标志
12. uint8_t ip_ttl;// 生存时间
13. uint8_t ip_p;// 协议
14. uint16_t ip_sum;// 校验和
15. ip_addr_t ip_src;// 源地址
16. ip_addr_t ip_dst;// 目的地址
17. } __attribute__((__packed__));
5.4 IP协议类型
| 协议 | 值 | 说明 |
| — | — | — |
| IP_PROTO_ICMP | 1 | ICMP |
| IP_PROTO_TCP | 6 | TCP |
| IP_PROTO_UDP | 17 | UDP |
| IP_PROTO_SCTP | 132 | SCTP |
| IP_PROTO_IPV6 | 41 | IPv6 |
位置: include/dnet/ip.h:108-246
5.5 分片标志
| 标志 | 值 | 说明 |
| — | — | — |
| IP_DF | 0x4000 | 不分片 |
| IP_MF | 0x2000 | 更多分片 |
| IP_OFFMASK | 0x1fff | 片偏移掩码 |
位置: include/dnet/ip.h:94-97
5.6 打包宏实现
位置: include/dnet/ip.h:415-431
1. staticinlinevoid ip_pack_hdr(void*buf,uint8_t tos,uint16_t len,
2. uint16_t id,uint16_t off,uint8_t ttl,uint8_t p,ip_addr_t src,
3. ip_addr_t dst)
4. {
5. struct ip_hdr {
6. uint32_t ip_v_hl_tos_len;
7. uint32_t ip_id_off;
8. uint32_t ip_ttl_p_sum;
9. ip_addr_t ip_src;
10. ip_addr_t ip_dst;
11. }*hdr =(struct ip_hdr *)buf;
12. hdr->ip_v_hl_tos_len =(0x45<<24)|(tos <<16)| htons(len);
13. hdr->ip_id_off =(htons(id)<<16)| htons(off);
14. hdr->ip_ttl_p_sum =(ttl <<24)|(p <<16);
15. hdr->ip_src = src;
16. hdr->ip_dst = dst;
17. }
5.7 构造示例
1. #include<dnet.h>
3. u_char packet[IP_LEN_MAX];
5. // 构造IP头部
6. ip_pack_hdr(packet,
7. 0,// TOS
8. total_len,// 总长度
9. rand()&0xffff,// ID
10. 0,// 片偏移
11. IP_TTL_DEFAULT,// TTL
12. IP_PROTO_TCP,// 协议
13. src_ip,// 源IP
14. dst_ip);// 目的IP
16. // 计算校验和
17. ip_checksum(packet, IP_HDR_LEN + payload_len,0);
19. // 发送
20. ip_send(ip, packet, total_len);
5.8 IP选项处理
位置: src/ip-util.c:40-99
1. ssize_t
2. ip_add_option(void*buf,size_t len,int proto,
3. constvoid*optbuf,size_t optlen)
4. {
5. struct ip_hdr *ip;
6. struct tcp_hdr *tcp = NULL;
7. u_char *p;
8. int hl, datalen, padlen;
10. if(proto != IP_PROTO_IP && proto != IP_PROTO_TCP){
11. errno = EINVAL;
12. return(-1);
13. }
14. ip =(struct ip_hdr *)buf;
15. hl = ip->ip_hl <<2;
16. p =(u_char *)buf + hl;
18. if(proto == IP_PROTO_TCP){
19. tcp =(struct tcp_hdr *)p;
20. hl = tcp->th_off <<2;
21. p =(u_char *)tcp + hl;
22. }
23. datalen = ntohs(ip->ip_len)-(p -(u_char *)buf);
25. // 计算填充到下一个字边界
26. if((padlen =4-(optlen %4))==4)
27. padlen =0;
29. if(hl + optlen + padlen > IP_HDR_LEN_MAX ||
30. ntohs(ip->ip_len)+ optlen + padlen > len){
31. errno = EINVAL;
32. return(-1);
33. }
35. // 只填充类型的选项
36. if(IP_OPT_TYPEONLY(((struct ip_opt *)optbuf)->opt_type))
37. optlen =1;
39. // 移动现有数据
40. if(datalen){
41. memmove(p + optlen + padlen, p, datalen);
42. }
44. // 填充NOP
45. if(padlen){
46. memset(p, IP_OPT_NOP, padlen);
47. p += padlen;
48. }
49. memmove(p, optbuf, optlen);
50. p += optlen;
51. optlen += padlen;
53. if(proto == IP_PROTO_IP)
54. ip->ip_hl =(p -(u_char *)ip)>>2;
55. elseif(proto == IP_PROTO_TCP)
56. tcp->th_off =(p -(u_char *)tcp)>>2;
58. ip->ip_len = htons(ntohs(ip->ip_len)+ optlen);
60. return(optlen);
61. }
5.9 常用IP选项
| 选项 | 值 | 说明 |
| — | — | — |
| IP_OPT_EOL | 0 | 选项列表结束 |
| IP_OPT_NOP | 1 | 无操作 |
| IP_OPT_LSRR | 3 | 宽松源路由 |
| IP_OPT_SSRR | 9 | 严格源路由 |
| IP_OPT_TS | 4 | 时间戳 |
位置: include/dnet/ip.h:258-283
6. IPv6数据包构造
6.1 IPv6协议概述
IPv6是IPv4的继任者,提供更大的地址空间和简化的头部结构。
6.2 IPv6头部结构
1. ┌────┬──────┬────────┬──────┬────────┬────────┬────────┐
2. │4│8│20│8│8│128│128│
3. │版本│流│流标签│载荷│下一个│跳数│源/目的│
4. ││标签││长度│头部│限制│地址│
5. └────┴──────┴────────┴──────┴────────┴────────┴────────┘
6.3 IPv6头部定义
位置: include/dnet/ip6.h:36-48
1. struct ip6_hdr {
2. union{
3. struct ip6_hdr_ctl {
4. uint32_t ip6_un1_flow;// 流ID(20位)
5. uint16_t ip6_un1_plen;// 载荷长度
6. uint8_t ip6_un1_nxt;// 下一个头部
7. uint8_t ip6_un1_hlim;// 跳数限制
8. } ip6_un1;
9. uint8_t ip6_un2_vfc;// 版本(4位)和流量类(4位)
10. } ip6_ctlun;
11. ip6_addr_t ip6_src;
12. ip6_addr_t ip6_dst;
13. } __attribute__((__packed__));
6.4 打包宏实现
位置: include/dnet/ip6.h:166-179
1. staticinlinevoid ip6_pack_hdr(void*buf,uint8_t c,uint32_t l,
2. uint16_t plen,uint8_t nxt,uint8_t hlim,void*src,void*dst)
3. {
4. struct ip6_hdr {
5. uint32_t ip6_v_c_l;
6. uint32_t ip6_plen_nxt_hlim;
7. ip6_addr_t ip6_src;
8. ip6_addr_t ip6_dst;
9. }*hdr =(struct ip6_hdr *)buf;
10. hdr->ip6_v_c_l =(6<<28)|(c <<20)|(IP6_FLOWLABEL_MASK & l);
11. hdr->ip6_plen_nxt_hlim =(htons(plen)<<16)|(nxt <<8)| hlim;
12. memcpy(&hdr->ip6_src, src, IP6_ADDR_LEN);
13. memcpy(&hdr->ip6_dst, dst, IP6_ADDR_LEN);
14. }
6.5 构造示例
1. #include<dnet.h>
3. u_char packet[IP6_LEN_MAX];
5. // IPv6地址
6. ip6_addr_t src_ip6, dst_ip6;
7. ip6_pton("2001:db8::1",&src_ip6);
8. ip6_pton("2001:db8::2",&dst_ip6);
10. // 构造IPv6头部
11. ip6_pack_hdr(packet,
12. 0,// 流类
13. 0x12345,// 流标签
14. payload_len,// 载荷长度
15. IP_PROTO_TCP,// 下一个头部
16. IP6_HLIM_DEFAULT,// 跳数限制
17. &src_ip6,// 源地址
18. &dst_ip6);// 目的地址
20. // 计算校验和
21. ip6_checksum(packet, IP6_HDR_LEN + payload_len);
23. // 发送
24. ip_send(ip, packet, IP6_HDR_LEN + payload_len);
6.6 扩展头部支持
IPv6支持多种扩展头部:
| 扩展头部 | 协议值 | 说明 | | — | — | — | | Hop-by-Hop Options | 0 | 逐跳选项 | | Routing | 43 | 路由头部 | | Fragment | 44 | 分片头部 | | Destination Options | 60 | 目的选项 |
位置: include/dnet/ip6.h:82-118
7. TCP数据包构造
7.1 TCP协议概述
TCP(Transmission Control Protocol)是面向连接的可靠传输协议。
7.2 TCP头部结构
1. ┌──────────┬──────────┬────────┬────────┬────┬────────┬────────┬────────┐
2. │16│16│32│32│4│4│16│16│
3. │源端口│目的端口│序列号│确认号│偏移│保留│窗口│紧急│
4. ││││││/标志││指针│
5. └──────────┴──────────┴────────┴────────┴────┴────────┴────────┴────────┘
6. └───┬───┘
7. │
8. ┌───────┼─────────┐
9. │6│8│
10. │保留位│8个标志位│
11. └───────┴─────────┘
7.3 TCP头部定义
位置: include/dnet/tcp.h:26-43
1. struct tcp_hdr {
2. uint16_t th_sport;// 源端口
3. uint16_t th_dport;// 目的端口
4. uint32_t th_seq;// 序列号
5. uint32_t th_ack;// 确认号
6. #if DNET_BYTESEX == DNET_BIG_ENDIAN
7. uint8_t th_off:4,// 数据偏移(以4字节为单位)
8. th_x2:4;// 保留
9. #elif DNET_BYTESEX == DNET_LIL_ENDIAN
10. uint8_t th_x2:4, th_off:4;
11. #endif
12. uint8_t th_flags;// 控制标志
13. uint16_t th_win;// 窗口大小
14. uint16_t th_sum;// 校验和
15. uint16_t th_urp;// 紧急指针
16. } __attribute__((__packed__));
7.4 TCP标志位
| 标志 | 值 | 说明 |
| — | — | — |
| TH_FIN | 0x01 | 结束连接 |
| TH_SYN | 0x02 | 同步序列号 |
| TH_RST | 0x04 | 重置连接 |
| TH_PUSH | 0x08 | 推送数据 |
| TH_ACK | 0x10 | 确认号有效 |
| TH_URG | 0x20 | 紧急指针有效 |
| TH_ECE | 0x40 | ECN回显 |
| TH_CWR | 0x80 | 拥塞窗口减少 |
位置: include/dnet/tcp.h:65-72
7.5 打包宏实现
位置: include/dnet/tcp.h:177-193
1. staticinlinevoid tcp_pack_hdr(void*buf,uint16_t sport,uint16_t dport,
2. uint32_t seq,uint32_t ack,uint8_t flags,uint16_t win,uint16_t urp)
3. {
4. struct tcp_hdr {
5. uint32_t th_sport_dport;
6. uint32_t th_seq;
7. uint32_t th_ack;
8. uint32_t th_off_x2_flags_win;
9. uint32_t th_sum_urp;
10. }*hdr =(struct tcp_hdr *)buf;
12. hdr->th_sport_dport =(htons(sport)<<16)| htons(dport);
13. hdr->th_seq = htonl(seq);
14. hdr->th_ack = htonl(ack);
15. hdr->th_off_x2_flags_win =(0x50<<24)|(flags <<16)| htons(win);
16. hdr->th_sum_urp = htons(urp);
17. }
7.6 构造示例
1. #include<dnet.h>
3. u_char packet[IP_HDR_LEN + TCP_HDR_LEN + payload_len];
4. struct ip_hdr *ip =(struct ip_hdr *)packet;
5. struct tcp_hdr *tcp =(struct tcp_hdr *)(packet + IP_HDR_LEN);
7. // 构造IP头部
8. ip_pack_hdr(ip,0, IP_HDR_LEN + TCP_HDR_LEN + payload_len,
9. rand()&0xffff,0, IP_TTL_DEFAULT, IP_PROTO_TCP,
10. src_ip, dst_ip);
12. // 构造TCP头部
13. tcp_pack_hdr(tcp,
14. htons(src_port),// 源端口
15. htons(dst_port),// 目的端口
16. htonl(seq_num),// 序列号
17. htonl(ack_num),// 确认号
18. TH_SYN | TH_ACK,// 标志
19. htons(65535),// 窗口
20. 0);// 紧急指针
22. // 计算校验和
23. tcp_checksum(ip, tcp, TCP_HDR_LEN + payload_len);
25. // 发送
26. ip_send(ip, packet, IP_HDR_LEN + TCP_HDR_LEN + payload_len);
7.7 TCP选项
| 选项 | 值 | 说明 |
| — | — | — |
| TCP_OPT_MSS | 2 | 最大分段大小 |
| TCP_OPT_WSCALE | 3 | 窗口缩放 |
| TCP_OPT_TIMESTAMP | 8 | 时间戳 |
| TCP_OPT_SACKOK | 4 | 选择性ACK许可 |
位置: include/dnet/tcp.h:110-136
8. UDP数据包构造
8.1 UDP协议概述
UDP(User Datagram Protocol)是无连接的不可靠传输协议。
8.2 UDP头部结构
1. ┌──────────┬──────────┬────────┬────────┐
2. │16│16│16│16│
3. │源端口│目的端口│长度│校验和│
4. └──────────┴──────────┴────────┴────────┘
8.3 UDP头部定义
位置: include/dnet/udp.h:15-20
1. struct udp_hdr {
2. uint16_t uh_sport;// 源端口
3. uint16_t uh_dport;// 目的端口
4. uint16_t uh_ulen;// UDP长度(包括头部)
5. uint16_t uh_sum;// 校验和
6. };
8.4 打包宏实现
位置: include/dnet/udp.h:24-29
1. #define udp_pack_hdr(hdr, sport, dport, ulen)do{ \
2. struct udp_hdr *udp_pack_p =(struct udp_hdr *)(hdr); \
3. udp_pack_p->uh_sport = htons(sport); \
4. udp_pack_p->uh_dport = htons(dport); \
5. udp_pack_p->uh_ulen = htons(ulen); \
6. }while(0)
8.5 构造示例
1. #include<dnet.h>
3. u_char packet[IP_HDR_LEN + UDP_HDR_LEN + payload_len];
4. struct ip_hdr *ip =(struct ip_hdr *)packet;
5. struct udp_hdr *udp =(struct udp_hdr *)(packet + IP_HDR_LEN);
7. // 构造IP头部
8. ip_pack_hdr(ip,0, IP_HDR_LEN + UDP_HDR_LEN + payload_len,
9. rand()&0xffff,0, IP_TTL_DEFAULT, IP_PROTO_UDP,
10. src_ip, dst_ip);
12. // 构造UDP头部
13. udp_pack_hdr(udp,
14. src_port,// 源端口
15. dst_port,// 目的端口
16. UDP_HDR_LEN + payload_len);// UDP长度
18. // 计算校验和
19. udp_checksum(ip, udp, UDP_HDR_LEN + payload_len);
21. // 发送
22. ip_send(ip, packet, IP_HDR_LEN + UDP_HDR_LEN + payload_len);
9. ICMP数据包构造
9.1 ICMP协议概述
ICMP(Internet Control Message Protocol)用于传递控制消息和错误报告。
9.2 ICMP头部结构
1. ┌────┬────┬────────┬────────────┐
2. │8│8│16│可变│
3. │类型│代码│校验和│数据│
4. └────┴────┴────────┴────────────┘
9.3 ICMP头部定义
位置: include/dnet/icmp.h:25-29
1. struct icmp_hdr {
2. uint8_t icmp_type;// 消息类型
3. uint8_t icmp_code;// 类型子代码
4. uint16_t icmp_cksum;// 校验和
5. };
9.4 ICMP消息类型
| 类型 | 值 | 说明 |
| — | — | — |
| ICMP_ECHOREPLY | 0 | 回显应答 |
| ICMP_UNREACH | 3 | 目的不可达 |
| ICMP_ECHO | 8 | 回显请求 |
| ICMP_TIMEXCEED | 11 | 超时 |
| ICMP_REDIRECT | 5 | 重定向 |
位置: include/dnet/icmp.h:36-95
9.5 打包宏实现
位置: include/dnet/icmp.h:224-262
1. // 基本头部
2. #define icmp_pack_hdr(hdr, type, code)do{ \
3. struct icmp_hdr *icmp_pack_p =(struct icmp_hdr *)(hdr); \
4. icmp_pack_p->icmp_type = type; \
5. icmp_pack_p->icmp_code = code; \
6. }while(0)
8. // Echo消息
9. #define icmp_pack_hdr_echo(hdr, type, code, id, seq, data, len)do{ \
10. struct icmp_msg_echo *echo_pack_p =(struct icmp_msg_echo *) \
11. ((uint8_t*)(hdr)+ ICMP_HDR_LEN); \
12. icmp_pack_hdr(hdr, type, code); \
13. echo_pack_p->icmp_id = htons(id); \
14. echo_pack_p->icmp_seq = htons(seq); \
15. if(data != NULL) memcpy(echo_pack_p->icmp_data, data, len); \
16. }while(0)
18. // 掩码消息
19. #define icmp_pack_hdr_mask(hdr, type, code, id, seq, mask)do{ \
20. struct icmp_msg_mask *mask_pack_p =(struct icmp_msg_mask *) \
21. ((uint8_t*)(hdr)+ ICMP_HDR_LEN); \
22. icmp_pack_hdr(hdr, type, code); \
23. mask_pack_p->icmp_id = htons(id); \
24. mask_pack_p->icmp_seq = htons(seq); \
25. mask_pack_p->icmp_mask = htonl(mask); \
26. }while(0)
9.6 构造示例
1. #include<dnet.h>
3. u_char packet[IP_HDR_LEN + ICMP_HDR_LEN +sizeof(struct icmp_msg_echo)];
4. struct ip_hdr *ip =(struct ip_hdr *)packet;
5. struct icmp_hdr *icmp =(struct icmp_hdr *)(packet + IP_HDR_LEN);
6. struct icmp_msg_echo *echo =(struct icmp_msg_echo *)(packet + IP_HDR_LEN + ICMP_HDR_LEN);
8. // 构造IP头部
9. ip_pack_hdr(ip,0, IP_HDR_LEN + ICMP_HDR_LEN +sizeof(struct icmp_msg_echo),
10. rand()&0xffff,0, IP_TTL_DEFAULT, IP_PROTO_ICMP,
11. src_ip, dst_ip);
13. // 构造ICMP Echo请求
14. icmp_pack_hdr_echo(icmp, ICMP_ECHO,0,
15. htons(12345),// ID
16. htons(1),// 序列号
17. NULL,0);
19. // 计算校验和
20. icmp_checksum(icmp, ICMP_HDR_LEN +sizeof(struct icmp_msg_echo));
22. // 发送
23. ip_send(ip, packet, IP_HDR_LEN + ICMP_HDR_LEN +sizeof(struct icmp_msg_echo));
9.7 ICMP消息数据结构
位置: include/dnet/icmp.h:108-218
1. // Echo消息
2. struct icmp_msg_echo {
3. uint16_t icmp_id;
4. uint16_t icmp_seq;
5. uint8_t icmp_data __flexarr;
6. };
8. // 掩码消息
9. struct icmp_msg_mask {
10. uint32_t icmp_id;
11. uint32_t icmp_seq;
12. uint32_t icmp_mask;
13. };
15. // 时间戳消息
16. struct icmp_msg_tstamp {
17. uint32_t icmp_id;
18. uint32_t icmp_seq;
19. uint32_t icmp_ts_orig;
20. uint32_t icmp_ts_rx;
21. uint32_t icmp_ts_tx;
22. };
10. ICMPv6数据包构造
10.1 ICMPv6协议概述
ICMPv6是IPv6的控制消息协议,功能类似于ICMP但更加强大。
10.2 ICMPv6头部结构
1. ┌────┬────┬────────┬────────────┐
2. │8│8│16│可变│
3. │类型│代码│校验和│数据│
4. └────┴────┴────────┴────────────┘
10.3 ICMPv6头部定义
位置: include/dnet/icmpv6.h:24-28
1. struct icmpv6_hdr {
2. uint8_t icmpv6_type;// 消息类型
3. uint8_t icmpv6_code;// 类型子代码
4. uint16_t icmpv6_cksum;// 校验和
5. };
10.4 ICMPv6消息类型
| 类型 | 值 | 说明 |
| — | — | — |
| ICMPV6_ECHO | 128 | 回显请求 |
| ICMPV6_ECHOREPLY | 129 | 回显应答 |
| ICMPV6_NEIGHBOR_SOLICITATION | 135 | 邻居请求 |
| ICMPV6_NEIGHBOR_ADVERTISEMENT | 136 | 邻居通告 |
位置: include/dnet/icmpv6.h:35-56
10.5 打包宏实现
位置: include/dnet/icmpv6.h:91-114
1. // 基本头部
2. #define icmpv6_pack_hdr(hdr, type, code)do{ \
3. struct icmpv6_hdr *icmpv6_pack_p =(struct icmpv6_hdr *)(hdr); \
4. icmpv6_pack_p->icmpv6_type = type; \
5. icmpv6_pack_p->icmpv6_code = code; \
6. }while(0)
8. // Echo消息
9. #define icmpv6_pack_hdr_echo(hdr, type, code, id, seq, data, len)do{ \
10. struct icmpv6_msg_echo *echo_pack_p =(struct icmpv6_msg_echo *) \
11. ((uint8_t*)(hdr)+ ICMPV6_HDR_LEN); \
12. icmpv6_pack_hdr(hdr, type, code); \
13. echo_pack_p->icmpv6_id = htons(id); \
14. echo_pack_p->icmpv6_seq = htons(seq); \
15. memmove(echo_pack_p->icmpv6_data, data, len); \
16. }while(0)
18. // 邻居请求(带MAC选项)
19. #define icmpv6_pack_hdr_ns_mac(hdr, targetip, srcmac)do{ \
20. struct icmpv6_msg_nd *nd_pack_p =(struct icmpv6_msg_nd *) \
21. ((uint8_t*)(hdr)+ ICMPV6_HDR_LEN); \
22. icmpv6_pack_hdr(hdr, ICMPV6_NEIGHBOR_SOLICITATION,0); \
23. nd_pack_p->icmpv6_flags =0; \
24. memmove(&nd_pack_p->icmpv6_target,&(targetip), IP6_ADDR_LEN); \
25. nd_pack_p->icmpv6_option_type =1; \
26. nd_pack_p->icmpv6_option_length =1; \
27. memmove(&nd_pack_p->icmpv6_mac,&(srcmac), ETH_ADDR_LEN); \
28. }while(0)
10.6 构造示例
1. #include<dnet.h>
3. u_char packet[IP6_HDR_LEN + ICMPV6_HDR_LEN +sizeof(struct icmpv6_msg_echo)];
4. struct ip6_hdr *ip6 =(struct ip6_hdr *)packet;
5. struct icmpv6_hdr *icmpv6 =(struct icmpv6_hdr *)(packet + IP6_HDR_LEN);
7. // 构造IPv6头部
8. ip6_pack_hdr(ip6,0,0,
9. ICMPV6_HDR_LEN +sizeof(struct icmpv6_msg_echo),
10. IP_PROTO_ICMPV6, IP6_HLIM_DEFAULT,
11. &src_ip6,&dst_ip6);
13. // 构造ICMPv6 Echo请求
14. icmpv6_pack_hdr_echo(icmpv6, ICMPV6_ECHO,0,
15. htons(12345),// ID
16. htons(1),// 序列号
17. NULL,0);
19. // 计算校验和(包含伪头部)
20. ip6_checksum(packet, IP6_HDR_LEN + ICMPV6_HDR_LEN +sizeof(struct icmpv6_msg_echo));
22. // 发送
23. ip_send(ip, packet, IP6_HDR_LEN + ICMPV6_HDR_LEN +sizeof(struct icmpv6_msg_echo));
11. SCTP数据包构造
11.1 SCTP协议概述
SCTP(Stream Control Transmission Protocol)是一种可靠的传输协议,提供多流和多宿主支持。
11.2 SCTP头部结构
1. ┌──────────┬──────────┬────────┬────────┐
2. │16│16│32│32│
3. │源端口│目的端口│验证标签│校验和│
4. └──────────┴──────────┴────────┴────────┘
11.3 SCTP头部定义
位置: include/dnet/sctp.h:22-27
1. struct sctp_hdr {
2. uint16_t sh_sport;// 源端口
3. uint16_t sh_dport;// 目的端口
4. uint32_t sh_vtag;// SCTP验证标签
5. uint32_t sh_sum;// SCTP校验和(CRC-32C)
6. } __attribute__((__packed__));
11.4 SCTP块类型
| 块类型 | 值 | 说明 |
| — | — | — |
| SCTP_DATA | 0x00 | 数据块 |
| SCTP_INIT | 0x01 | 初始化块 |
| SCTP_INIT_ACK | 0x02 | 初始化确认 |
| SCTP_SACK | 0x03 | 选择性确认 |
| SCTP_HEARTBEAT | 0x04 | 心跳 |
| SCTP_ABORT | 0x06 | 中止 |
位置: include/dnet/sctp.h:44-65
11.5 打包宏实现
位置: include/dnet/sctp.h:31-36
1. #define sctp_pack_hdr(hdr, sport, dport, vtag)do{ \
2. struct sctp_hdr *sctp_pack_p =(struct sctp_hdr *)(hdr); \
3. sctp_pack_p->sh_sport = htons(sport); \
4. sctp_pack_p->sh_dport = htons(dport); \
5. sctp_pack_p->sh_vtag = htonl(vtag); \
6. }while(0)
11.6 构造示例
1. #include<dnet.h>
3. u_char packet[IP_HDR_LEN + SCTP_HDR_LEN +sizeof(struct sctp_chunkhdr)];
4. struct ip_hdr *ip =(struct ip_hdr *)packet;
5. struct sctp_hdr *sctp =(struct sctp_hdr *)(packet + IP_HDR_LEN);
6. struct sctp_chunkhdr *chunk =(struct sctp_chunkhdr *)(packet + IP_HDR_LEN + SCTP_HDR_LEN);
8. // 构造IP头部
9. ip_pack_hdr(ip,0, IP_HDR_LEN + SCTP_HDR_LEN + chunk_len,
10. rand()&0xffff,0, IP_TTL_DEFAULT, IP_PROTO_SCTP,
11. src_ip, dst_ip);
13. // 构造SCTP头部
14. sctp_pack_hdr(sctp,
15. src_port,// 源端口
16. dst_port,// 目的端口
17. 0x12345678);// 验证标签
19. // 构造块头部
20. sctp_pack_chunkhdr(chunk, SCTP_INIT,0,sizeof(chunk_data));
22. // 计算CRC-32C校验和
23. ip_checksum(packet, IP_HDR_LEN + SCTP_HDR_LEN + chunk_len,0);
25. // 发送
26. ip_send(ip, packet, IP_HDR_LEN + SCTP_HDR_LEN + chunk_len);
12. 跨平台实现对比
12.1 IP数据包发送实现对比
| 平台 | 实现文件 | Socket类型 | 特点 |
| — | — | — | — |
| Unix/Linux | src/ip.c | SOCK_RAW + IPPROTO_RAW | 直接发送,高性能 |
| Cooked | src/ip-cooked.c | 通过以太网发送 | 自动ARP解析 |
| Windows | src/ip-win32.c | SOCK_RAW + IPPROTO_RAW | 需要管理员权限 |
12.2 Unix/Linux实现
位置: src/ip.c:25-93
1. struct ip_handle {
2. int fd;
3. };
5. ip_t*
6. ip_open(void)
7. {
8. ip_t*i;
9. int n;
10. socklen_t len;
12. if((i = calloc(1,sizeof(*i)))== NULL)
13. return(NULL);
15. // 创建原始socket
16. if((i->fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW))<0)
17. return(ip_close(i));
19. #ifdef IP_HDRINCL
20. // 设置IP头部包含选项
21. n =1;
22. if(setsockopt(i->fd, IPPROTO_IP, IP_HDRINCL,&n,sizeof(n))<0)
23. return(ip_close(i));
24. #endif
25. #ifdef SO_SNDBUF
26. // 优化发送缓冲区
27. len =sizeof(n);
28. if(getsockopt(i->fd, SOL_SOCKET, SO_SNDBUF,&n,&len)<0)
29. return(ip_close(i));
31. for(n +=128; n <1048576; n +=128){
32. if(setsockopt(i->fd, SOL_SOCKET, SO_SNDBUF,&n, len)<0){
33. if(errno == ENOBUFS)
34. break;
35. return(ip_close(i));
36. }
37. }
38. #endif
39. #ifdef SO_BROADCAST
40. // 允许广播
41. n =1;
42. if(setsockopt(i->fd, SOL_SOCKET, SO_BROADCAST,&n,sizeof(n))<0)
43. return(ip_close(i));
44. #endif
45. return(i);
46. }
48. ssize_t
49. ip_send(ip_t*i,constvoid*buf,size_t len)
50. {
51. struct ip_hdr *ip;
52. struct sockaddr_in sin;
54. ip =(struct ip_hdr *)buf;
56. memset(&sin,0,sizeof(sin));
57. #ifdef HAVE_SOCKADDR_SA_LEN
58. sin.sin_len =sizeof(sin);
59. #endif
60. sin.sin_family = AF_INET;
61. sin.sin_addr.s_addr = ip->ip_dst;
63. #ifdef HAVE_RAWIP_HOST_OFFLEN
64. // 某些平台需要特殊处理
65. ip->ip_len = ntohs(ip->ip_len);
66. ip->ip_off = ntohs(ip->ip_off);
68. len = sendto(i->fd, buf, len,0,
69. (struct sockaddr *)&sin,sizeof(sin));
71. ip->ip_len = htons(ip->ip_len);
72. ip->ip_off = htons(ip->ip_off);
74. return(len);
75. #else
76. return(sendto(i->fd, buf, len,0,
77. (struct sockaddr *)&sin,sizeof(sin)));
78. #endif
79. }
12.3 Cooked实现(自动ARP)
位置: src/ip-cooked.c:32-246
1. struct ip_handle {
2. arp_t*arp;// ARP处理
3. intf_t*intf;// 接口处理
4. route_t*route;// 路由处理
5. int fd;
6. struct sockaddr_in sin;
7. LIST_HEAD(, ip_intf) ip_intf_list;
8. };
9. // 查找接口
10. staticstruct ip_intf *
11. _lookup_ip_intf(ip_t*ip,ip_addr_t dst)
12. {
13. struct ip_intf *ipi;
14. int n;
15. ip->sin.sin_addr.s_addr = dst;
16. n =sizeof(ip->sin);
17. // 使用内核路由查找源接口
18. if(connect(ip->fd,(struct sockaddr *)&ip->sin, n)<0)
19. return(NULL);
20. if(getsockname(ip->fd,(struct sockaddr *)&ip->sin,&n)<0)
21. return(NULL);
22. LIST_FOREACH(ipi,&ip->ip_intf_list, next){
23. if(ipi->pa.addr_ip == ip->sin.sin_addr.s_addr){
24. if(ipi->eth == NULL){
25. if((ipi->eth = eth_open(ipi->name))== NULL)
26. return(NULL);
27. }
28. if(ipi != LIST_FIRST(&ip->ip_intf_list)){
29. LIST_REMOVE(ipi, next);
30. LIST_INSERT_HEAD(&ip->ip_intf_list, ipi, next);
31. }
32. return(ipi);
33. }
34. }
35. return(NULL);
36. }
37. // 发送数据包
38. ssize_t
39. ip_send(ip_t*ip,constvoid*buf,size_t len)
40. {
41. struct ip_hdr *iph;
42. struct ip_intf *ipi;
43. struct arp_entry arpent;
44. struct route_entry rtent;
45. u_char frame[ETH_LEN_MAX];
46. int i, usec;
47. iph =(struct ip_hdr *)buf;
48. // 查找出接口
49. if((ipi = _lookup_ip_intf(ip, iph->ip_dst))== NULL){
50. errno = EHOSTUNREACH;
51. return(-1);
52. }
53. arpent.arp_pa.addr_type = ADDR_TYPE_IP;
54. arpent.arp_pa.addr_bits = IP_ADDR_BITS;
55. arpent.arp_pa.addr_ip = iph->ip_dst;
56. memcpy(&rtent.route_dst,&arpent.arp_pa,sizeof(rtent.route_dst));
57. // 尝试获取MAC地址(最多3次)
58. for(i =0, usec =10; i <3; i++, usec *=100){
59. if(arp_get(ip->arp,&arpent)==0)
60. break;
61. // 检查路由
62. if(route_get(ip->route,&rtent)==0&&
63. rtent.route_gw.addr_ip != ipi->pa.addr_ip){
64. memcpy(&arpent.arp_pa,&rtent.route_gw,
65. sizeof(arpent.arp_pa));
66. if(arp_get(ip->arp,&arpent)==0)
67. break;
68. }
69. // 发送ARP请求
70. _request_arp(ipi,&arpent.arp_pa);
71. usleep(usec);
72. }
73. // 未找到则使用广播
74. if(i ==3)
75. memset(&arpent.arp_ha.addr_eth,0xff, ETH_ADDR_LEN);
76. // 添加以太网头部并发送
77. eth_pack_hdr(frame, arpent.arp_ha.addr_eth,
78. ipi->ha.addr_eth, ETH_TYPE_IP);
79. if(len > ipi->mtu){
80. // 分片发送
81. u_char *p,*start,*end,*ip_data;
82. int ip_hl, fraglen;
83. ip_hl = iph->ip_hl <<2;
84. fraglen = ipi->mtu - ip_hl;
85. iph =(struct ip_hdr *)(frame + ETH_HDR_LEN);
86. memcpy(iph, buf, ip_hl);
87. ip_data =(u_char *)iph + ip_hl;
88. start =(u_char *)buf + ip_hl;
89. end =(u_char *)buf + len;
90. for(p = start; p < end;){
91. memcpy(ip_data, p, fraglen);
92. iph->ip_len = htons(ip_hl + fraglen);
93. iph->ip_off = htons(((p + fraglen < end)? IP_MF :0)|
94. ((p - start)>>3));
95. ip_checksum(iph, ip_hl + fraglen);
96. i = ETH_HDR_LEN + ip_hl + fraglen;
97. if(eth_send(ipi->eth, frame, i)!= i)
98. return(-1);
99. p += fraglen;
100. if(end - p < fraglen)
101. fraglen = end - p;
102. }
103. return(len);
104. }
105. memcpy(frame + ETH_HDR_LEN, buf, len);
106. i = ETH_HDR_LEN + len;
107. if(eth_send(ipi->eth, frame, i)!= i)
108. return(-1);
109. return(len);
110. }
12.4 Windows实现
位置: src/ip-win32.c:18-75
1. struct ip_handle {
2. WSADATA wsdata;
3. SOCKET fd;
4. struct sockaddr_in sin;
5. };
7. ip_t*
8. ip_open(void)
9. {
10. BOOL on;
11. ip_t*ip;
13. if((ip = calloc(1,sizeof(*ip)))!= NULL){
14. // 初始化Winsock
15. if(WSAStartup(MAKEWORD(2,2),&ip->wsdata)!=0){
16. free(ip);
17. return(NULL);
18. }
19. // 创建原始socket
20. if((ip->fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW))==
21. INVALID_SOCKET)
22. return(ip_close(ip));
24. on = TRUE;
25. // 设置IP头部包含
26. if(setsockopt(ip->fd, IPPROTO_IP, IP_HDRINCL,
27. (constchar*)&on,sizeof(on))== SOCKET_ERROR){
28. SetLastError(ERROR_NETWORK_ACCESS_DENIED);
29. return(ip_close(ip));
30. }
31. ip->sin.sin_family = AF_INET;
32. ip->sin.sin_port = htons(666);
33. }
34. return(ip);
35. }
37. ssize_t
38. ip_send(ip_t*ip,constvoid*buf,size_t len)
39. {
40. struct ip_hdr *hdr =(struct ip_hdr *)buf;
42. ip->sin.sin_addr.s_addr = hdr->ip_src;
44. if((len = sendto(ip->fd,(constchar*)buf, len,0,
45. (struct sockaddr *)&ip->sin,sizeof(ip->sin)))!= SOCKET_ERROR)
46. return(len);
48. return(-1);
49. }
12.5 跨平台对比表
| 特性 | Unix/Linux | Cooked | Windows |
| — | — | — | — |
| Socket类型 | SOCK_RAW | 以太网发送 | SOCK_RAW |
| 权限要求 | root/CAPNETRAW | root | 管理员 |
| 自动ARP | 否 | 是 | 否 |
| 分片支持 | 内核 | 手动 | 内核 |
| 性能 | 高 | 中 | 高 |
| 代码复杂度 | 低 | 高 | 低 |
| IPv6支持 | 是 | 否 | 是 |
13. 实际应用示例
13.1 发送自定义TCP SYN包
1. #include<dnet.h>
2. #include<stdio.h>
3. #include<stdlib.h>
4. #include<string.h>
5. #include<time.h>
7. int main(int argc,char*argv[])
8. {
9. ip_t*ip;
10. u_char packet[IP_HDR_LEN + TCP_HDR_LEN];
11. struct ip_hdr *iph =(struct ip_hdr *)packet;
12. struct tcp_hdr *tcph =(struct tcp_hdr *)(packet + IP_HDR_LEN);
13. struct addr src, dst;
15. if(argc !=3){
16. fprintf(stderr,"Usage: %s <src_ip> <dst_ip:port>\n", argv[0]);
17. return1;
18. }
20. // 解析地址
21. if(addr_pton(argv[1],&src)<0||
22. addr_pton(argv[2],&dst)<0){
23. perror("addr_pton");
24. return1;
25. }
27. // 打开IP句柄
28. if((ip = ip_open())== NULL){
29. perror("ip_open");
30. return1;
31. }
33. // 构造IP头部
34. ip_pack_hdr(iph,
35. 0,// TOS
36. IP_HDR_LEN + TCP_HDR_LEN,// 总长度
37. rand()&0xffff,// ID
38. 0,// 片偏移
39. IP_TTL_DEFAULT,// TTL
40. IP_PROTO_TCP,// 协议
41. src.addr_ip,// 源IP
42. dst.addr_ip);// 目的IP
44. // 构造TCP头部(SYN包)
45. tcp_pack_hdr(tcph,
46. htons(rand()&0xffff),// 源端口
47. dst.addr_port,// 目的端口
48. htonl(rand()),// 序列号
49. 0,// 确认号
50. TH_SYN,// 标志
51. htons(65535),// 窗口
52. 0);// 紧急指针
54. // 计算校验和
55. tcp_checksum(iph, tcph, TCP_HDR_LEN);
56. ip_checksum(packet, IP_HDR_LEN + TCP_HDR_LEN,0);
58. // 发送
59. if(ip_send(ip, packet, IP_HDR_LEN + TCP_HDR_LEN)<0){
60. perror("ip_send");
61. }else{
62. printf("TCP SYN packet sent\n");
63. }
65. ip_close(ip);
66. return0;
67. }
13.2 发送ICMP Echo请求
1. #include<dnet.h>
2. #include<stdio.h>
3. #include<stdlib.h>
4. #include<string.h>
5. #include<time.h>
7. int main(int argc,char*argv[])
8. {
9. ip_t*ip;
10. u_char packet[IP_HDR_LEN + ICMP_HDR_LEN +sizeof(struct icmp_msg_echo)];
11. struct ip_hdr *iph =(struct ip_hdr *)packet;
12. struct icmp_hdr *icmph =(struct icmp_hdr *)(packet + IP_HDR_LEN);
13. struct icmp_msg_echo *echo =(struct icmp_msg_echo *)
14. (packet + IP_HDR_LEN + ICMP_HDR_LEN);
15. struct addr src, dst;
16. uint16_t id = rand()&0xffff;
18. if(argc !=3){
19. fprintf(stderr,"Usage: %s <src_ip> <dst_ip>\n", argv[0]);
20. return1;
21. }
23. // 解析地址
24. if(addr_pton(argv[1],&src)<0||
25. addr_pton(argv[2],&dst)<0){
26. perror("addr_pton");
27. return1;
28. }
30. // 打开IP句柄
31. if((ip = ip_open())== NULL){
32. perror("ip_open");
33. return1;
34. }
36. // 构造IP头部
37. ip_pack_hdr(iph,
38. 0,
39. IP_HDR_LEN + ICMP_HDR_LEN +sizeof(struct icmp_msg_echo),
40. rand()&0xffff,
41. 0,
42. IP_TTL_DEFAULT,
43. IP_PROTO_ICMP,
44. src.addr_ip,
45. dst.addr_ip);
47. // 构造ICMP Echo请求
48. icmp_pack_hdr_echo(icmph, ICMP_ECHO,0, id, htons(1), NULL,0);
50. // 计算校验和
51. icmp_checksum(icmph, ICMP_HDR_LEN +sizeof(struct icmp_msg_echo));
52. ip_checksum(packet, IP_HDR_LEN + ICMP_HDR_LEN +sizeof(struct icmp_msg_echo),0);
54. // 发送
55. if(ip_send(ip, packet, IP_HDR_LEN + ICMP_HDR_LEN +sizeof(struct icmp_msg_echo))<0){
56. perror("ip_send");
57. }else{
58. printf("ICMP Echo request sent\n");
59. }
61. ip_close(ip);
62. return0;
63. }
13.3 发送UDP数据包
1. #include<dnet.h>
2. #include<stdio.h>
3. #include<stdlib.h>
4. #include<string.h>
5. #include<time.h>
7. int main(int argc,char*argv[])
8. {
9. ip_t*ip;
10. u_char packet[IP_HDR_LEN + UDP_HDR_LEN +32];
11. struct ip_hdr *iph =(struct ip_hdr *)packet;
12. struct udp_hdr *udph =(struct udp_hdr *)(packet + IP_HDR_LEN);
13. struct addr src, dst;
14. uint8_t payload[32];
16. if(argc !=3){
17. fprintf(stderr,"Usage: %s <src_ip:port> <dst_ip:port>\n", argv[0]);
18. return1;
19. }
21. // 解析地址
22. if(addr_pton(argv[1],&src)<0||
23. addr_pton(argv[2],&dst)<0){
24. perror("addr_pton");
25. return1;
26. }
28. // 准备载荷
29. memset(payload,0xAA,sizeof(payload));
31. // 打开IP句柄
32. if((ip = ip_open())== NULL){
33. perror("ip_open");
34. return1;
35. }
37. // 构造IP头部
38. ip_pack_hdr(iph,
39. 0,
40. IP_HDR_LEN + UDP_HDR_LEN +sizeof(payload),
41. rand()&0xffff,
42. 0,
43. IP_TTL_DEFAULT,
44. IP_PROTO_UDP,
45. src.addr_ip,
46. dst.addr_ip);
48. // 构造UDP头部
49. udp_pack_hdr(udph,
50. src.addr_port,
51. dst.addr_port,
52. UDP_HDR_LEN +sizeof(payload));
54. // 填充载荷
55. memcpy(packet + IP_HDR_LEN + UDP_HDR_LEN, payload,sizeof(payload));
57. // 计算校验和
58. udp_checksum(iph, udph, UDP_HDR_LEN +sizeof(payload));
59. ip_checksum(packet, IP_HDR_LEN + UDP_HDR_LEN +sizeof(payload),0);
61. // 发送
62. if(ip_send(ip, packet, IP_HDR_LEN + UDP_HDR_LEN +sizeof(payload))<0){
63. perror("ip_send");
64. }else{
65. printf("UDP packet sent\n");
66. }
68. ip_close(ip);
69. return0;
70. }
13.4 使用dnet命令行工具
发送TCP SYN包:
1. # 构造TCP SYN包
2. echo "Hello"| dnet tcp sport 12345 dport 80 flags SYN | \
3. dnet ip src 192.168.1.100 dst 192.168.1.1| \
4. dnet send eth0
发送ICMP Echo请求:
1. # 构造ICMP Echo请求
2. echo "Ping"| dnet icmp type 8 code 0| \
3. dnet ip src 192.168.1.100 dst 192.168.1.1| \
4. dnet send
发送UDP数据包:
1. # 构造UDP数据包
2. echo "UDP data"| dnet udp sport 12345 dport 53| \
3. dnet ip src 192.168.1.100 dst 8.8.8.8| \
4. dnet send
13.5 发送ARP请求
1. #include<dnet.h>
2. #include<stdio.h>
3. #include<stdlib.h>
4. #include<string.h>
6. int main(int argc,char*argv[])
7. {
8. eth_t*eth;
9. u_char frame[ETH_HDR_LEN + ARP_HDR_LEN + ARP_ETHIP_LEN];
10. struct addr src_mac, src_ip, dst_ip;
11. eth_addr_t zero_mac ={0};
13. if(argc !=3){
14. fprintf(stderr,"Usage: %s <src_mac> <dst_ip>\n", argv[0]);
15. return1;
16. }
18. // 解析地址
19. if(addr_pton(argv[1],&src_mac)<0||
20. addr_pton(argv[2],&dst_ip)<0){
21. perror("addr_pton");
22. return1;
23. }
25. // 打开以太网设备
26. if((eth = eth_open("eth0"))== NULL){
27. perror("eth_open");
28. return1;
29. }
31. // 获取源IP
32. intf_t*intf;
33. struct intf_entry entry;
34. if((intf = intf_open())== NULL){
35. perror("intf_open");
36. eth_close(eth);
37. return1;
38. }
39. intf_get(intf,&entry);
40. src_ip = entry.intf_addr;
41. intf_close(intf);
43. // 构造以太网头部
44. eth_pack_hdr(frame, ETH_ADDR_BROADCAST, src_mac.addr_eth, ETH_TYPE_ARP);
46. // 构造ARP请求
47. arp_pack_hdr_ethip(frame + ETH_HDR_LEN, ARP_OP_REQUEST,
48. src_mac.addr_eth, src_ip.addr_ip,
49. zero_mac, dst_ip.addr_ip);
51. // 发送
52. if(eth_send(eth, frame,sizeof(frame))<0){
53. perror("eth_send");
54. }else{
55. printf("ARP request sent\n");
56. }
58. eth_close(eth);
59. return0;
60. }
13.6 完整的TCP SYN扫描器
1. #include<dnet.h>
2. #include<stdio.h>
3. #include<stdlib.h>
4. #include<string.h>
5. #include<time.h>
6. #include<pcap.h>
8. #define SNAPLEN 65535
9. #define TIMEOUT 1000
11. void packet_handler(u_char *args,conststruct pcap_pkthdr *header,
12. const u_char *packet)
13. {
14. struct ip_hdr *iph =(struct ip_hdr *)(packet +14);
15. struct tcp_hdr *tcph =(struct tcp_hdr *)(packet +14+(iph->ip_hl <<2));
17. if(iph->ip_p == IP_PROTO_TCP &&(tcph->th_flags &(TH_SYN | TH_ACK))==(TH_SYN | TH_ACK)){
18. printf("Port %d is OPEN\n", ntohs(tcph->th_sport));
19. }
20. }
22. int main(int argc,char*argv[])
23. {
24. ip_t*ip;
25. pcap_t*pcap;
26. struct addr src, dst;
27. char errbuf[PCAP_ERRBUF_SIZE];
28. u_char packet[IP_HDR_LEN + TCP_HDR_LEN];
29. struct ip_hdr *iph =(struct ip_hdr *)packet;
30. struct tcp_hdr *tcph =(struct tcp_hdr *)(packet + IP_HDR_LEN);
31. int port;
33. if(argc !=3){
34. fprintf(stderr,"Usage: %s <src_ip> <dst_ip>\n", argv[0]);
35. return1;
36. }
38. // 解析地址
39. if(addr_pton(argv[1],&src)<0||
40. addr_pton(argv[2],&dst)<0){
41. perror("addr_pton");
42. return1;
43. }
45. // 打开IP句柄
46. if((ip = ip_open())== NULL){
47. perror("ip_open");
48. return1;
49. }
51. // 打开pcap用于接收响应
52. pcap = pcap_open_live("eth0", SNAPLEN,1, TIMEOUT, errbuf);
53. if(pcap == NULL){
54. fprintf(stderr,"pcap_open_live: %s\n", errbuf);
55. ip_close(ip);
56. return1;
57. }
59. // 设置过滤器
60. struct bpf_program fp;
61. char filter[256];
62. snprintf(filter,sizeof(filter),"src host %s and tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)", argv[2]);
63. if(pcap_compile(pcap,&fp, filter,0, PCAP_NETMASK_UNKNOWN)<0){
64. fprintf(stderr,"pcap_compile: %s\n", pcap_geterr(pcap));
65. pcap_close(pcap);
66. ip_close(ip);
67. return1;
68. }
69. if(pcap_setfilter(pcap,&fp)<0){
70. fprintf(stderr,"pcap_setfilter: %s\n", pcap_geterr(pcap));
71. pcap_freecode(&fp);
72. pcap_close(pcap);
73. ip_close(ip);
74. return1;
75. }
76. pcap_freecode(&fp);
78. // 扫描常见端口
79. printf("Scanning %s...\n", argv[2]);
80. for(port =1; port <=1024; port++){
81. // 构造IP头部
82. ip_pack_hdr(iph,
83. 0,
84. IP_HDR_LEN + TCP_HDR_LEN,
85. rand()&0xffff,
86. 0,
87. IP_TTL_DEFAULT,
88. IP_PROTO_TCP,
89. src.addr_ip,
90. dst.addr_ip);
92. // 构造TCP SYN包
93. tcp_pack_hdr(tcph,
94. htons(rand()&0xffff),
95. htons(port),
96. htonl(rand()),
97. 0,
98. TH_SYN,
99. htons(65535),
100. 0);
102. // 计算校验和
103. tcp_checksum(iph, tcph, TCP_HDR_LEN);
104. ip_checksum(packet, IP_HDR_LEN + TCP_HDR_LEN,0);
106. // 发送
107. ip_send(ip, packet, IP_HDR_LEN + TCP_HDR_LEN);
109. // 接收响应
110. pcap_dispatch(pcap,-1, packet_handler, NULL);
111. }
113. pcap_close(pcap);
114. ip_close(ip);
115. return0;
116. }
14. 常见问题与解决方案
14.1 权限问题
问题:创建原始socket失败
1. Operationnot permitted
解决方案:
Unix/Linux:
1. # 使用root权限运行
2. sudo ./program
4. # 或使用capabilities
5. sudo setcap cap_net_raw+ep ./program
Windows:
1. # 以管理员身份运行cmd
2. # 右键点击cmd -> 以管理员身份运行
14.2 校验和错误
问题:数据包被接收方丢弃
解决方案:
1. // 确保校验和计算正确
2. ip_checksum(packet, len,0);
4. // 或手动计算
5. uint16_t calc_checksum(void*buf,size_t len){
6. uint32_t sum =0;
7. uint16_t*ptr =(uint16_t*)buf;
9. while(len >1){
10. sum +=*ptr++;
11. len -=2;
12. }
14. if(len ==1){
15. sum +=*(uint8_t*)ptr <<8;
16. }
18. while(sum >>16){
19. sum =(sum &0xffff)+(sum >>16);
20. }
22. return~sum;
23. }
14.3 字节序问题
问题:端口或地址解析错误
解决方案:
1. // 始终使用htons/htonl转换
2. uint16_t port = htons(80);
3. uint32_t addr = htonl(0x0a000001);
5. // 检查时使用ntohs/ntohl
6. uint16_t hport = ntohs(tcph->th_sport);
7. uint32_t haddr = ntohl(iph->ip_src);
14.4 数据包长度错误
问题:数据包被截断
解决方案:
1. // 确保总长度正确计算
2. ip->ip_len = htons(IP_HDR_LEN + TCP_HDR_LEN + payload_len);
4. // UDP也需要设置长度
5. udp->uh_ulen = htons(UDP_HDR_LEN + payload_len);
14.5 分片问题
问题:大数据包无法发送
解决方案:
1. // 设置MTU或使用分片
2. #define MTU 1500
4. if(total_len > MTU - IP_HDR_LEN){
5. // 使用ip-cooked模式的自动分片
6. // 或手动分片
7. int offset =0;
8. while(offset < total_len){
9. int frag_len = MTU - IP_HDR_LEN;
10. if(offset + frag_len > total_len)
11. frag_len = total_len - offset;
13. // 发送分片
14. // ...
15. offset += frag_len;
16. }
17. }
14.6 ARP未解析
问题:数据包发送失败
解决方案:
1. // 手动发送ARP请求
2. eth_addr_t broadcast ={0xff,0xff,0xff,0xff,0xff,0xff};
3. eth_addr_t zero ={0};
5. eth_pack_hdr(frame, broadcast, src_mac, ETH_TYPE_ARP);
6. arp_pack_hdr_ethip(frame + ETH_HDR_LEN, ARP_OP_REQUEST,
7. src_mac, src_ip, zero, dst_ip);
8. eth_send(eth, frame,sizeof(frame));
10. // 等待ARP响应
11. sleep(1);
14.7 Windows特殊处理
问题:Windows上IP头部不生效
解决方案:
1. // 确保设置IP_HDRINCL选项
2. BOOL on = TRUE;
3. setsockopt(fd, IPPROTO_IP, IP_HDRINCL,(char*)&on,sizeof(on));
5. // 注意:Windows上某些字段会自动填充
6. // 不要设置IP头部中的TTL、校验和等字段
14.8 IPv6校验和问题
问题:IPv6数据包校验和错误
解决方案:
1. // IPv6校验和包含伪头部
2. // 使用libdnet提供的函数
3. ip6_checksum(packet, len);
5. // 或手动计算
6. uint32_t sum =0;
8. // 添加伪头部(源地址 + 目的地址)
9. sum += ip_cksum_add(&ip6->ip6_src,32,0);
11. // 添加上层协议和长度
12. sum += htons(next_header + payload_len);
14. // 添加上层协议数据
15. sum += ip_cksum_add(payload, payload_len,0);
17. // 计算最终校验和
18. checksum = ip_cksum_carry(sum);
14.9 选项添加错误
问题:添加IP或TCP选项失败
解决方案:
1. // 使用libdnet提供的函数
2. ssize_t ret = ip_add_option(packet,sizeof(packet),
3. IP_PROTO_IP, optbuf, optlen);
5. if(ret <0){
6. perror("ip_add_option");
7. return-1;
8. }
10. // 注意:选项长度必须正确
11. // NOP选项长度为1
12. // 其他选项至少为2字节
14.10 性能优化
问题:发送大量数据包性能低
解决方案:
1. // 1. 增加发送缓冲区
2. int sndbuf =1024*1024;// 1MB
3. setsockopt(fd, SOL_SOCKET, SO_SNDBUF,&sndbuf,sizeof(sndbuf));
5. // 2. 重用缓冲区
6. static u_char packet_buf[IP_LEN_MAX];
8. // 3. 批量发送
9. for(int i =0; i < count; i++){
10. // 构造数据包
11. ip_pack_hdr(packet_buf,...);
12. tcp_pack_hdr(packet_buf + IP_HDR_LEN,...);
14. // 发送
15. ip_send(ip, packet_buf, len);
16. }
18. // 4. 使用非阻塞IO
19. int flags = fcntl(fd, F_GETFL,0);
20. fcntl(fd, F_SETFL, flags | O_NONBLOCK);
总结
本文档深入分析了libdnet中原始数据包构造的实现机制,涵盖了从以太网到应用层的所有主要协议:
核心要点
- 分层构造:从物理层到应用层的完整数据包构造流程
- 宏封装:使用宏简化构造代码,提高可读性
- 跨平台支持:Unix/Linux、Windows等平台的差异处理
- 校验和计算:各种协议的校验和计算方法
- 实际应用:扫描器、网络工具等实用示例
关键文件
| 文件 | 说明 |
| — | — |
| include/dnet/eth.h | 以太网头部定义和宏 |
| include/dnet/arp.h | ARP头部定义和宏 |
| include/dnet/ip.h | IP头部定义和宏 |
| include/dnet/ip6.h | IPv6头部定义和宏 |
| include/dnet/tcp.h | TCP头部定义和宏 |
| include/dnet/udp.h | UDP头部定义和宏 |
| include/dnet/icmp.h | ICMP头部定义和宏 |
| include/dnet/icmpv6.h | ICMPv6头部定义和宏 |
| include/dnet/sctp.h | SCTP头部定义和宏 |
| src/ip.c | Unix/Linux IP实现 |
| src/ip-cooked.c | Cooked IP实现 |
| src/ip-win32.c | Windows IP实现 |
| src/ip-util.c | 校验和和选项处理 |
| src/ip6.c | IPv6校验和处理 |
下一步
建议结合以下文档进一步学习:
- 06-网络接口控制实现深入分析.md
- 07-MAC地址处理深入分析.md
- 04-ARP操作源码深入分析.md
文档版本:1.0 最后更新:2026年3月5日 作者:基于libdnet 1.13源码分析
- 公众号:安全狗的自我修养
- vx:2207344074
- http://gitee.com/haidragon
- http://github.com/haidragon
- bilibili:haidragonx
#
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:安全狗的自我修养 haidragon haidragon《跨平台底层网络库libdnet源码分析系列(八)》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论