乐于分享
好东西不私藏

跨平台底层网络库libdnet源码分析系列(八)

跨平台底层网络库libdnet源码分析系列(八)

网:http://securitytech.cc

源码分析mettle后门工具学习 所使用的依赖库

原始数据包构造深入分析

目录

  1. 数据包构造基础概念
  2. libdnet数据包构造设计
  3. 以太网帧构造
  4. ARP数据包构造
  5. IP数据包构造
  6. IPv6数据包构造
  7. TCP数据包构造
  8. UDP数据包构造
  9. ICMP数据包构造
  10. ICMPv6数据包构造
  11. SCTP数据包构造
  12. 跨平台实现对比
  13. 实际应用示例
  14. 常见问题与解决方案

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>
  2. // 转换为网络字节序
  3. uint16_t port = htons(8080);
  4. uint32_t addr = htonl(0x0a000001);
  5. // 从网络字节序转换
  6. uint16_t hport = ntohs(8080);
  7. 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;
  5. while(len >1){
  6.         sum +=*ptr++;
  7.         len -=2;
  8. }
  9. if(len ==1){
  10.         sum +=*(uint8_t*)ptr <<8;
  11. }
  12. // 反码求和
  13. while(sum >>16){
  14.         sum =(sum &0xffff)+(sum >>16);
  15. }
  16. return~sum;
  17. }

2. libdnet数据包构造设计

2.1 核心设计原则

  1. 零拷贝:直接操作缓冲区
  2. 宏封装:使用宏简化构造代码
  3. 跨平台:自动处理字节序和结构对齐
  4. 灵活扩展:支持选项和扩展头

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. 6B6B2B46-1500B4B
  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(&eth_pack_p->eth_dst,&(dst), ETH_ADDR_LEN);  \
  4.     memcpy(&eth_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>
  2. u_char frame[ETH_LEN_MAX];
  3. // MAC地址定义
  4. eth_addr_t src_mac ={0x00,0x11,0x22,0x33,0x44,0x55};
  5. eth_addr_t dst_mac ={0xff,0xff,0xff,0xff,0xff,0xff};
  6. // 构造以太网头部
  7. eth_pack_hdr(frame, dst_mac, src_mac, ETH_TYPE_IP);
  8. // 填充数据载荷
  9. memcpy(frame + ETH_HDR_LEN, payload, payload_len);
  10. // 发送
  11. 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. };
  5. // VLAN掩码
  6. #define ETH_8021Q_PRIMASK   0x0007// 优先级掩码
  7. #define ETH_8021Q_CFIMASK   0x0001// CFI掩码
  8. #define ETH_8021Q_VIDMASK   0x0fff// VID掩码
  9. // 判断是否为VLAN类型
  10. #define ETH_TYPE_IS_VLAN(type) \
  11. (((type)== ETH_TYPE_8021Q)|| \
  12. ((type)== ETH_TYPE_8021ad_0)|| \
  13. ((type)== ETH_TYPE_8021ad_1)|| \
  14. ((type)== ETH_TYPE_8021ad_2)|| \
  15. ((type)== ETH_TYPE_8021ad_3))

4. ARP数据包构造

4.1 ARP协议概述

ARP(Address Resolution Protocol)用于将IP地址解析为MAC地址。

4.2 ARP数据包结构

  1. ┌──────┬──────┬────┬────┬────┬────────────────────────┐
  2. 2B2B1B1B2B
  3. 硬件协议可变长度(通常28B
  4. 类型类型
  5. └──────┴──────┴────┴────┴────┴────────────────────────┘

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>
  2. u_char frame[ETH_HDR_LEN + ARP_HDR_LEN + ARP_ETHIP_LEN];
  3. // 以太网头部
  4. eth_pack_hdr(frame,
  5.              ETH_ADDR_BROADCAST,// 广播MAC
  6.              src_mac,
  7.              ETH_TYPE_ARP);
  8. // ARP请求
  9. eth_addr_t zero_mac ={0};
  10. arp_pack_hdr_ethip(frame + ETH_HDR_LEN,
  11.                    ARP_OP_REQUEST,
  12.                    src_mac,
  13.                    src_ip,
  14.                    zero_mac,
  15.                    target_ip);
  16. // 发送
  17. 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];
  5.     eth_pack_hdr(frame, ETH_ADDR_BROADCAST, ipi->ha.addr_eth,
  6.         ETH_TYPE_ARP);
  7.     arp_pack_hdr_ethip(frame + ETH_HDR_LEN, ARP_OP_REQUEST,
  8.         ipi->ha.addr_eth, ipi->pa.addr_ip, ETH_ADDR_BROADCAST,
  9.         dst->addr_ip);
  10.     eth_send(ipi->eth, frame,sizeof(frame));
  11. }

5. IP数据包构造

5.1 IP协议概述

IP(Internet Protocol)是网络层的核心协议,负责数据包的寻址和路由。

5.2 IPv4头部结构

  1. ┌───┬───┬────────┬────────┬──────┬────────┬──────┬────────┬────────┬────────┐
  2. 448161616881632x2
  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)|(<<16);
  15.     hdr->ip_src = src;
  16.     hdr->ip_dst = dst;
  17. }

5.7 构造示例

  1. #include<dnet.h>
  2. u_char packet[IP_LEN_MAX];
  3. // 构造IP头部
  4. ip_pack_hdr(packet,
  5. 0,// TOS
  6.             total_len,// 总长度
  7.             rand()&0xffff,// ID
  8. 0,// 片偏移
  9.             IP_TTL_DEFAULT,// TTL
  10.             IP_PROTO_TCP,// 协议
  11.             src_ip,// 源IP
  12.             dst_ip);// 目的IP
  13. // 计算校验和
  14. ip_checksum(packet, IP_HDR_LEN + payload_len,0);
  15. // 发送
  16. 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;
  9. if(proto != IP_PROTO_IP && proto != IP_PROTO_TCP){
  10.         errno = EINVAL;
  11. return(-1);
  12. }
  13.     ip =(struct ip_hdr *)buf;
  14.     hl = ip->ip_hl <<2;
  15.     p =(u_char *)buf + hl;
  16. if(proto == IP_PROTO_TCP){
  17.         tcp =(struct tcp_hdr *)p;
  18.         hl = tcp->th_off <<2;
  19.         p =(u_char *)tcp + hl;
  20. }
  21.     datalen = ntohs(ip->ip_len)-(-(u_char *)buf);
  22. // 计算填充到下一个字边界
  23. if((padlen =4-(optlen %4))==4)
  24.         padlen =0;
  25. if(hl + optlen + padlen > IP_HDR_LEN_MAX ||
  26.         ntohs(ip->ip_len)+ optlen + padlen > len){
  27.         errno = EINVAL;
  28. return(-1);
  29. }
  30. // 只填充类型的选项
  31. if(IP_OPT_TYPEONLY(((struct ip_opt *)optbuf)->opt_type))
  32.         optlen =1;
  33. // 移动现有数据
  34. if(datalen){
  35.         memmove(+ optlen + padlen, p, datalen);
  36. }
  37. // 填充NOP
  38. if(padlen){
  39.         memset(p, IP_OPT_NOP, padlen);
  40.         p += padlen;
  41. }
  42.     memmove(p, optbuf, optlen);
  43.     p += optlen;
  44.     optlen += padlen;
  45. if(proto == IP_PROTO_IP)
  46.         ip->ip_hl =(-(u_char *)ip)>>2;
  47. elseif(proto == IP_PROTO_TCP)
  48.         tcp->th_off =(-(u_char *)tcp)>>2;
  49.     ip->ip_len = htons(ntohs(ip->ip_len)+ optlen);
  50. return(optlen);
  51. }

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. 482088128128
  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)|(<<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>
  2. u_char packet[IP6_LEN_MAX];
  3. // IPv6地址
  4. ip6_addr_t src_ip6, dst_ip6;
  5. ip6_pton("2001:db8::1",&src_ip6);
  6. ip6_pton("2001:db8::2",&dst_ip6);
  7. // 构造IPv6头部
  8. ip6_pack_hdr(packet,
  9. 0,// 流类
  10. 0x12345,// 流标签
  11.              payload_len,// 载荷长度
  12.              IP_PROTO_TCP,// 下一个头部
  13.              IP6_HLIM_DEFAULT,// 跳数限制
  14. &src_ip6,// 源地址
  15. &dst_ip6);// 目的地址
  16. // 计算校验和
  17. ip6_checksum(packet, IP6_HDR_LEN + payload_len);
  18. // 发送
  19. 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. 16163232441616
  3. 源端口目的端口序列号确认号│偏移│保留窗口紧急
  4. │/标志指针
  5. └──────────┴──────────┴────────┴────────┴────┴────────┴────────┴────────┘
  6. └───┬───┘
  7. ┌───────┼─────────┐
  8. 68
  9. │保留位8个标志位
  10. └───────┴─────────┘

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;
  11.     hdr->th_sport_dport =(htons(sport)<<16)| htons(dport);
  12.     hdr->th_seq = htonl(seq);
  13.     hdr->th_ack = htonl(ack);
  14.     hdr->th_off_x2_flags_win =(0x50<<24)|(flags <<16)| htons(win);
  15.     hdr->th_sum_urp = htons(urp);
  16. }

7.6 构造示例

  1. #include<dnet.h>
  2. u_char packet[IP_HDR_LEN + TCP_HDR_LEN + payload_len];
  3. struct ip_hdr *ip =(struct ip_hdr *)packet;
  4. struct tcp_hdr *tcp =(struct tcp_hdr *)(packet + IP_HDR_LEN);
  5. // 构造IP头部
  6. ip_pack_hdr(ip,0, IP_HDR_LEN + TCP_HDR_LEN + payload_len,
  7.             rand()&0xffff,0, IP_TTL_DEFAULT, IP_PROTO_TCP,
  8.             src_ip, dst_ip);
  9. // 构造TCP头部
  10. tcp_pack_hdr(tcp,
  11.              htons(src_port),// 源端口
  12.              htons(dst_port),// 目的端口
  13.              htonl(seq_num),// 序列号
  14.              htonl(ack_num),// 确认号
  15.              TH_SYN | TH_ACK,// 标志
  16.              htons(65535),// 窗口
  17. 0);// 紧急指针
  18. // 计算校验和
  19. tcp_checksum(ip, tcp, TCP_HDR_LEN + payload_len);
  20. // 发送
  21. 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. 16161616
  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>
  2. u_char packet[IP_HDR_LEN + UDP_HDR_LEN + payload_len];
  3. struct ip_hdr *ip =(struct ip_hdr *)packet;
  4. struct udp_hdr *udp =(struct udp_hdr *)(packet + IP_HDR_LEN);
  5. // 构造IP头部
  6. ip_pack_hdr(ip,0, IP_HDR_LEN + UDP_HDR_LEN + payload_len,
  7.             rand()&0xffff,0, IP_TTL_DEFAULT, IP_PROTO_UDP,
  8.             src_ip, dst_ip);
  9. // 构造UDP头部
  10. udp_pack_hdr(udp,
  11.              src_port,// 源端口
  12.              dst_port,// 目的端口
  13.              UDP_HDR_LEN + payload_len);// UDP长度
  14. // 计算校验和
  15. udp_checksum(ip, udp, UDP_HDR_LEN + payload_len);
  16. // 发送
  17. 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. 8816可变
  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)
  7. // Echo消息
  8. #define icmp_pack_hdr_echo(hdr, type, code, id, seq, data, len)do{   \
  9. struct icmp_msg_echo *echo_pack_p =(struct icmp_msg_echo *)      \
  10. ((uint8_t*)(hdr)+ ICMP_HDR_LEN);             \
  11.     icmp_pack_hdr(hdr, type, code);                    \
  12.     echo_pack_p->icmp_id = htons(id);                  \
  13.     echo_pack_p->icmp_seq = htons(seq);                \
  14. if(data != NULL) memcpy(echo_pack_p->icmp_data, data, len); \
  15. }while(0)
  16. // 掩码消息
  17. #define icmp_pack_hdr_mask(hdr, type, code, id, seq, mask)do{       \
  18. struct icmp_msg_mask *mask_pack_p =(struct icmp_msg_mask *)      \
  19. ((uint8_t*)(hdr)+ ICMP_HDR_LEN);             \
  20.     icmp_pack_hdr(hdr, type, code);                    \
  21.     mask_pack_p->icmp_id = htons(id);                  \
  22.     mask_pack_p->icmp_seq = htons(seq);                \
  23.     mask_pack_p->icmp_mask = htonl(mask);              \
  24. }while(0)

9.6 构造示例

  1. #include<dnet.h>
  2. u_char packet[IP_HDR_LEN + ICMP_HDR_LEN +sizeof(struct icmp_msg_echo)];
  3. struct ip_hdr *ip =(struct ip_hdr *)packet;
  4. struct icmp_hdr *icmp =(struct icmp_hdr *)(packet + IP_HDR_LEN);
  5. struct icmp_msg_echo *echo =(struct icmp_msg_echo *)(packet + IP_HDR_LEN + ICMP_HDR_LEN);
  6. // 构造IP头部
  7. ip_pack_hdr(ip,0, IP_HDR_LEN + ICMP_HDR_LEN +sizeof(struct icmp_msg_echo),
  8.             rand()&0xffff,0, IP_TTL_DEFAULT, IP_PROTO_ICMP,
  9.             src_ip, dst_ip);
  10. // 构造ICMP Echo请求
  11. icmp_pack_hdr_echo(icmp, ICMP_ECHO,0,
  12.                    htons(12345),// ID
  13.                    htons(1),// 序列号
  14.                    NULL,0);
  15. // 计算校验和
  16. icmp_checksum(icmp, ICMP_HDR_LEN +sizeof(struct icmp_msg_echo));
  17. // 发送
  18. 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. };
  7. // 掩码消息
  8. struct icmp_msg_mask {
  9. uint32_t icmp_id;
  10. uint32_t icmp_seq;
  11. uint32_t icmp_mask;
  12. };
  13. // 时间戳消息
  14. struct icmp_msg_tstamp {
  15. uint32_t icmp_id;
  16. uint32_t icmp_seq;
  17. uint32_t icmp_ts_orig;
  18. uint32_t icmp_ts_rx;
  19. uint32_t icmp_ts_tx;
  20. };

10. ICMPv6数据包构造

10.1 ICMPv6协议概述

ICMPv6是IPv6的控制消息协议,功能类似于ICMP但更加强大。

10.2 ICMPv6头部结构

  1. ┌────┬────┬────────┬────────────┐
  2. 8816可变
  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)
  7. // Echo消息
  8. #define icmpv6_pack_hdr_echo(hdr, type, code, id, seq, data, len)do{ \
  9. struct icmpv6_msg_echo *echo_pack_p =(struct icmpv6_msg_echo *)  \
  10. ((uint8_t*)(hdr)+ ICMPV6_HDR_LEN);                     \
  11.     icmpv6_pack_hdr(hdr, type, code);                             \
  12.     echo_pack_p->icmpv6_id = htons(id);                          \
  13.     echo_pack_p->icmpv6_seq = htons(seq);                        \
  14.     memmove(echo_pack_p->icmpv6_data, data, len);                \
  15. }while(0)
  16. // 邻居请求(带MAC选项)
  17. #define icmpv6_pack_hdr_ns_mac(hdr, targetip, srcmac)do{       \
  18. struct icmpv6_msg_nd *nd_pack_p =(struct icmpv6_msg_nd *)   \
  19. ((uint8_t*)(hdr)+ ICMPV6_HDR_LEN);                     \
  20.     icmpv6_pack_hdr(hdr, ICMPV6_NEIGHBOR_SOLICITATION,0);        \
  21.     nd_pack_p->icmpv6_flags =0;                                 \
  22.     memmove(&nd_pack_p->icmpv6_target,&(targetip), IP6_ADDR_LEN); \
  23.     nd_pack_p->icmpv6_option_type =1;                          \
  24.     nd_pack_p->icmpv6_option_length =1;                        \
  25.     memmove(&nd_pack_p->icmpv6_mac,&(srcmac), ETH_ADDR_LEN);    \
  26. }while(0)

10.6 构造示例

  1. #include<dnet.h>
  2. u_char packet[IP6_HDR_LEN + ICMPV6_HDR_LEN +sizeof(struct icmpv6_msg_echo)];
  3. struct ip6_hdr *ip6 =(struct ip6_hdr *)packet;
  4. struct icmpv6_hdr *icmpv6 =(struct icmpv6_hdr *)(packet + IP6_HDR_LEN);
  5. // 构造IPv6头部
  6. ip6_pack_hdr(ip6,0,0,
  7.              ICMPV6_HDR_LEN +sizeof(struct icmpv6_msg_echo),
  8.              IP_PROTO_ICMPV6, IP6_HLIM_DEFAULT,
  9. &src_ip6,&dst_ip6);
  10. // 构造ICMPv6 Echo请求
  11. icmpv6_pack_hdr_echo(icmpv6, ICMPV6_ECHO,0,
  12.                      htons(12345),// ID
  13.                      htons(1),// 序列号
  14.                      NULL,0);
  15. // 计算校验和(包含伪头部)
  16. ip6_checksum(packet, IP6_HDR_LEN + ICMPV6_HDR_LEN +sizeof(struct icmpv6_msg_echo));
  17. // 发送
  18. 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. 16163232
  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>
  2. u_char packet[IP_HDR_LEN + SCTP_HDR_LEN +sizeof(struct sctp_chunkhdr)];
  3. struct ip_hdr *ip =(struct ip_hdr *)packet;
  4. struct sctp_hdr *sctp =(struct sctp_hdr *)(packet + IP_HDR_LEN);
  5. struct sctp_chunkhdr *chunk =(struct sctp_chunkhdr *)(packet + IP_HDR_LEN + SCTP_HDR_LEN);
  6. // 构造IP头部
  7. ip_pack_hdr(ip,0, IP_HDR_LEN + SCTP_HDR_LEN + chunk_len,
  8.             rand()&0xffff,0, IP_TTL_DEFAULT, IP_PROTO_SCTP,
  9.             src_ip, dst_ip);
  10. // 构造SCTP头部
  11. sctp_pack_hdr(sctp,
  12.              src_port,// 源端口
  13.              dst_port,// 目的端口
  14. 0x12345678);// 验证标签
  15. // 构造块头部
  16. sctp_pack_chunkhdr(chunk, SCTP_INIT,0,sizeof(chunk_data));
  17. // 计算CRC-32C校验和
  18. ip_checksum(packet, IP_HDR_LEN + SCTP_HDR_LEN + chunk_len,0);
  19. // 发送
  20. 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. };
  4. ip_t*
  5. ip_open(void)
  6. {
  7. ip_t*i;
  8. int n;
  9. socklen_t len;
  10. if((= calloc(1,sizeof(*i)))== NULL)
  11. return(NULL);
  12. // 创建原始socket
  13. if((i->fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW))<0)
  14. return(ip_close(i));
  15. #ifdef IP_HDRINCL
  16. // 设置IP头部包含选项
  17.     n =1;
  18. if(setsockopt(i->fd, IPPROTO_IP, IP_HDRINCL,&n,sizeof(n))<0)
  19. return(ip_close(i));
  20. #endif
  21. #ifdef SO_SNDBUF
  22. // 优化发送缓冲区
  23.     len =sizeof(n);
  24. if(getsockopt(i->fd, SOL_SOCKET, SO_SNDBUF,&n,&len)<0)
  25. return(ip_close(i));
  26. for(+=128; n <1048576; n +=128){
  27. if(setsockopt(i->fd, SOL_SOCKET, SO_SNDBUF,&n, len)<0){
  28. if(errno == ENOBUFS)
  29. break;
  30. return(ip_close(i));
  31. }
  32. }
  33. #endif
  34. #ifdef SO_BROADCAST
  35. // 允许广播
  36.     n =1;
  37. if(setsockopt(i->fd, SOL_SOCKET, SO_BROADCAST,&n,sizeof(n))<0)
  38. return(ip_close(i));
  39. #endif
  40. return(i);
  41. }
  42. ssize_t
  43. ip_send(ip_t*i,constvoid*buf,size_t len)
  44. {
  45. struct ip_hdr *ip;
  46. struct sockaddr_in sin;
  47.     ip =(struct ip_hdr *)buf;
  48.     memset(&sin,0,sizeof(sin));
  49. #ifdef HAVE_SOCKADDR_SA_LEN       
  50.     sin.sin_len =sizeof(sin);
  51. #endif
  52.     sin.sin_family = AF_INET;
  53.     sin.sin_addr.s_addr = ip->ip_dst;
  54. #ifdef HAVE_RAWIP_HOST_OFFLEN
  55. // 某些平台需要特殊处理
  56.     ip->ip_len = ntohs(ip->ip_len);
  57.     ip->ip_off = ntohs(ip->ip_off);
  58.     len = sendto(i->fd, buf, len,0,
  59. (struct sockaddr *)&sin,sizeof(sin));
  60.     ip->ip_len = htons(ip->ip_len);
  61.     ip->ip_off = htons(ip->ip_off);
  62. return(len);
  63. #else
  64. return(sendto(i->fd, buf, len,0,
  65. (struct sockaddr *)&sin,sizeof(sin)));
  66. #endif
  67. }

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(=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(==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(= start; p < end;){
  91.             memcpy(ip_data, p, fraglen);
  92.             iph->ip_len = htons(ip_hl + fraglen);
  93.             iph->ip_off = htons(((+ fraglen < end)? IP_MF :0)|
  94. ((- 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. };
  6. ip_t*
  7. ip_open(void)
  8. {
  9.     BOOL on;
  10. ip_t*ip;
  11. if((ip = calloc(1,sizeof(*ip)))!= NULL){
  12. // 初始化Winsock
  13. if(WSAStartup(MAKEWORD(2,2),&ip->wsdata)!=0){
  14.             free(ip);
  15. return(NULL);
  16. }
  17. // 创建原始socket
  18. if((ip->fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW))==
  19.             INVALID_SOCKET)
  20. return(ip_close(ip));
  21.         on = TRUE;
  22. // 设置IP头部包含
  23. if(setsockopt(ip->fd, IPPROTO_IP, IP_HDRINCL,
  24. (constchar*)&on,sizeof(on))== SOCKET_ERROR){
  25. SetLastError(ERROR_NETWORK_ACCESS_DENIED);
  26. return(ip_close(ip));
  27. }
  28.         ip->sin.sin_family = AF_INET;
  29.         ip->sin.sin_port = htons(666);
  30. }
  31. return(ip);
  32. }
  33. ssize_t
  34. ip_send(ip_t*ip,constvoid*buf,size_t len)
  35. {
  36. struct ip_hdr *hdr =(struct ip_hdr *)buf;
  37.     ip->sin.sin_addr.s_addr = hdr->ip_src;
  38. if((len = sendto(ip->fd,(constchar*)buf, len,0,
  39. (struct sockaddr *)&ip->sin,sizeof(ip->sin)))!= SOCKET_ERROR)
  40. return(len);
  41. return(-1);
  42. }

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>
  6. int main(int argc,char*argv[])
  7. {
  8. ip_t*ip;
  9.     u_char packet[IP_HDR_LEN + TCP_HDR_LEN];
  10. struct ip_hdr *iph =(struct ip_hdr *)packet;
  11. struct tcp_hdr *tcph =(struct tcp_hdr *)(packet + IP_HDR_LEN);
  12. struct addr src, dst;
  13. if(argc !=3){
  14.         fprintf(stderr,"Usage: %s <src_ip> <dst_ip:port>\n", argv[0]);
  15. return1;
  16. }
  17. // 解析地址
  18. if(addr_pton(argv[1],&src)<0||
  19.         addr_pton(argv[2],&dst)<0){
  20.         perror("addr_pton");
  21. return1;
  22. }
  23. // 打开IP句柄
  24. if((ip = ip_open())== NULL){
  25.         perror("ip_open");
  26. return1;
  27. }
  28. // 构造IP头部
  29.     ip_pack_hdr(iph,
  30. 0,// TOS
  31.                 IP_HDR_LEN + TCP_HDR_LEN,// 总长度
  32.                 rand()&0xffff,// ID
  33. 0,// 片偏移
  34.                 IP_TTL_DEFAULT,// TTL
  35.                 IP_PROTO_TCP,// 协议
  36.                 src.addr_ip,// 源IP
  37.                 dst.addr_ip);// 目的IP
  38. // 构造TCP头部(SYN包)
  39.     tcp_pack_hdr(tcph,
  40.                 htons(rand()&0xffff),// 源端口
  41.                 dst.addr_port,// 目的端口
  42.                 htonl(rand()),// 序列号
  43. 0,// 确认号
  44.                 TH_SYN,// 标志
  45.                 htons(65535),// 窗口
  46. 0);// 紧急指针
  47. // 计算校验和
  48.     tcp_checksum(iph, tcph, TCP_HDR_LEN);
  49.     ip_checksum(packet, IP_HDR_LEN + TCP_HDR_LEN,0);
  50. // 发送
  51. if(ip_send(ip, packet, IP_HDR_LEN + TCP_HDR_LEN)<0){
  52.         perror("ip_send");
  53. }else{
  54.         printf("TCP SYN packet sent\n");
  55. }
  56.     ip_close(ip);
  57. return0;
  58. }

13.2 发送ICMP Echo请求

  1. #include<dnet.h>
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<string.h>
  5. #include<time.h>
  6. int main(int argc,char*argv[])
  7. {
  8. ip_t*ip;
  9.     u_char packet[IP_HDR_LEN + ICMP_HDR_LEN +sizeof(struct icmp_msg_echo)];
  10. struct ip_hdr *iph =(struct ip_hdr *)packet;
  11. struct icmp_hdr *icmph =(struct icmp_hdr *)(packet + IP_HDR_LEN);
  12. struct icmp_msg_echo *echo =(struct icmp_msg_echo *)
  13. (packet + IP_HDR_LEN + ICMP_HDR_LEN);
  14. struct addr src, dst;
  15. uint16_t id = rand()&0xffff;
  16. if(argc !=3){
  17.         fprintf(stderr,"Usage: %s <src_ip> <dst_ip>\n", argv[0]);
  18. return1;
  19. }
  20. // 解析地址
  21. if(addr_pton(argv[1],&src)<0||
  22.         addr_pton(argv[2],&dst)<0){
  23.         perror("addr_pton");
  24. return1;
  25. }
  26. // 打开IP句柄
  27. if((ip = ip_open())== NULL){
  28.         perror("ip_open");
  29. return1;
  30. }
  31. // 构造IP头部
  32.     ip_pack_hdr(iph,
  33. 0,
  34.                 IP_HDR_LEN + ICMP_HDR_LEN +sizeof(struct icmp_msg_echo),
  35.                 rand()&0xffff,
  36. 0,
  37.                 IP_TTL_DEFAULT,
  38.                 IP_PROTO_ICMP,
  39.                 src.addr_ip,
  40.                 dst.addr_ip);
  41. // 构造ICMP Echo请求
  42.     icmp_pack_hdr_echo(icmph, ICMP_ECHO,0, id, htons(1), NULL,0);
  43. // 计算校验和
  44.     icmp_checksum(icmph, ICMP_HDR_LEN +sizeof(struct icmp_msg_echo));
  45.     ip_checksum(packet, IP_HDR_LEN + ICMP_HDR_LEN +sizeof(struct icmp_msg_echo),0);
  46. // 发送
  47. if(ip_send(ip, packet, IP_HDR_LEN + ICMP_HDR_LEN +sizeof(struct icmp_msg_echo))<0){
  48.         perror("ip_send");
  49. }else{
  50.         printf("ICMP Echo request sent\n");
  51. }
  52.     ip_close(ip);
  53. return0;
  54. }

13.3 发送UDP数据包

  1. #include<dnet.h>
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<string.h>
  5. #include<time.h>
  6. int main(int argc,char*argv[])
  7. {
  8. ip_t*ip;
  9.     u_char packet[IP_HDR_LEN + UDP_HDR_LEN +32];
  10. struct ip_hdr *iph =(struct ip_hdr *)packet;
  11. struct udp_hdr *udph =(struct udp_hdr *)(packet + IP_HDR_LEN);
  12. struct addr src, dst;
  13. uint8_t payload[32];
  14. if(argc !=3){
  15.         fprintf(stderr,"Usage: %s <src_ip:port> <dst_ip:port>\n", argv[0]);
  16. return1;
  17. }
  18. // 解析地址
  19. if(addr_pton(argv[1],&src)<0||
  20.         addr_pton(argv[2],&dst)<0){
  21.         perror("addr_pton");
  22. return1;
  23. }
  24. // 准备载荷
  25.     memset(payload,0xAA,sizeof(payload));
  26. // 打开IP句柄
  27. if((ip = ip_open())== NULL){
  28.         perror("ip_open");
  29. return1;
  30. }
  31. // 构造IP头部
  32.     ip_pack_hdr(iph,
  33. 0,
  34.                 IP_HDR_LEN + UDP_HDR_LEN +sizeof(payload),
  35.                 rand()&0xffff,
  36. 0,
  37.                 IP_TTL_DEFAULT,
  38.                 IP_PROTO_UDP,
  39.                 src.addr_ip,
  40.                 dst.addr_ip);
  41. // 构造UDP头部
  42.     udp_pack_hdr(udph,
  43.                  src.addr_port,
  44.                  dst.addr_port,
  45.                  UDP_HDR_LEN +sizeof(payload));
  46. // 填充载荷
  47.     memcpy(packet + IP_HDR_LEN + UDP_HDR_LEN, payload,sizeof(payload));
  48. // 计算校验和
  49.     udp_checksum(iph, udph, UDP_HDR_LEN +sizeof(payload));
  50.     ip_checksum(packet, IP_HDR_LEN + UDP_HDR_LEN +sizeof(payload),0);
  51. // 发送
  52. if(ip_send(ip, packet, IP_HDR_LEN + UDP_HDR_LEN +sizeof(payload))<0){
  53.         perror("ip_send");
  54. }else{
  55.         printf("UDP packet sent\n");
  56. }
  57.     ip_close(ip);
  58. return0;
  59. }

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>
  5. int main(int argc,char*argv[])
  6. {
  7. eth_t*eth;
  8.     u_char frame[ETH_HDR_LEN + ARP_HDR_LEN + ARP_ETHIP_LEN];
  9. struct addr src_mac, src_ip, dst_ip;
  10. eth_addr_t zero_mac ={0};
  11. if(argc !=3){
  12.         fprintf(stderr,"Usage: %s <src_mac> <dst_ip>\n", argv[0]);
  13. return1;
  14. }
  15. // 解析地址
  16. if(addr_pton(argv[1],&src_mac)<0||
  17.         addr_pton(argv[2],&dst_ip)<0){
  18.         perror("addr_pton");
  19. return1;
  20. }
  21. // 打开以太网设备
  22. if((eth = eth_open("eth0"))== NULL){
  23.         perror("eth_open");
  24. return1;
  25. }
  26. // 获取源IP
  27. intf_t*intf;
  28. struct intf_entry entry;
  29. if((intf = intf_open())== NULL){
  30.         perror("intf_open");
  31.         eth_close(eth);
  32. return1;
  33. }
  34.     intf_get(intf,&entry);
  35.     src_ip = entry.intf_addr;
  36.     intf_close(intf);
  37. // 构造以太网头部
  38.     eth_pack_hdr(frame, ETH_ADDR_BROADCAST, src_mac.addr_eth, ETH_TYPE_ARP);
  39. // 构造ARP请求
  40.     arp_pack_hdr_ethip(frame + ETH_HDR_LEN, ARP_OP_REQUEST,
  41.                        src_mac.addr_eth, src_ip.addr_ip,
  42.                        zero_mac, dst_ip.addr_ip);
  43. // 发送
  44. if(eth_send(eth, frame,sizeof(frame))<0){
  45.         perror("eth_send");
  46. }else{
  47.         printf("ARP request sent\n");
  48. }
  49.     eth_close(eth);
  50. return0;
  51. }

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>
  7. #define SNAPLEN 65535
  8. #define TIMEOUT 1000
  9. void packet_handler(u_char *args,conststruct pcap_pkthdr *header,
  10. const u_char *packet)
  11. {
  12. struct ip_hdr *iph =(struct ip_hdr *)(packet +14);
  13. struct tcp_hdr *tcph =(struct tcp_hdr *)(packet +14+(iph->ip_hl <<2));
  14. if(iph->ip_p == IP_PROTO_TCP &&(tcph->th_flags &(TH_SYN | TH_ACK))==(TH_SYN | TH_ACK)){
  15.         printf("Port %d is OPEN\n", ntohs(tcph->th_sport));
  16. }
  17. }
  18. int main(int argc,char*argv[])
  19. {
  20. ip_t*ip;
  21. pcap_t*pcap;
  22. struct addr src, dst;
  23. char errbuf[PCAP_ERRBUF_SIZE];
  24.     u_char packet[IP_HDR_LEN + TCP_HDR_LEN];
  25. struct ip_hdr *iph =(struct ip_hdr *)packet;
  26. struct tcp_hdr *tcph =(struct tcp_hdr *)(packet + IP_HDR_LEN);
  27. int port;
  28. if(argc !=3){
  29.         fprintf(stderr,"Usage: %s <src_ip> <dst_ip>\n", argv[0]);
  30. return1;
  31. }
  32. // 解析地址
  33. if(addr_pton(argv[1],&src)<0||
  34.         addr_pton(argv[2],&dst)<0){
  35.         perror("addr_pton");
  36. return1;
  37. }
  38. // 打开IP句柄
  39. if((ip = ip_open())== NULL){
  40.         perror("ip_open");
  41. return1;
  42. }
  43. // 打开pcap用于接收响应
  44.     pcap = pcap_open_live("eth0", SNAPLEN,1, TIMEOUT, errbuf);
  45. if(pcap == NULL){
  46.         fprintf(stderr,"pcap_open_live: %s\n", errbuf);
  47.         ip_close(ip);
  48. return1;
  49. }
  50. // 设置过滤器
  51. struct bpf_program fp;
  52. char filter[256];
  53.     snprintf(filter,sizeof(filter),"src host %s and tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)", argv[2]);
  54. if(pcap_compile(pcap,&fp, filter,0, PCAP_NETMASK_UNKNOWN)<0){
  55.         fprintf(stderr,"pcap_compile: %s\n", pcap_geterr(pcap));
  56.         pcap_close(pcap);
  57.         ip_close(ip);
  58. return1;
  59. }
  60. if(pcap_setfilter(pcap,&fp)<0){
  61.         fprintf(stderr,"pcap_setfilter: %s\n", pcap_geterr(pcap));
  62.         pcap_freecode(&fp);
  63.         pcap_close(pcap);
  64.         ip_close(ip);
  65. return1;
  66. }
  67.     pcap_freecode(&fp);
  68. // 扫描常见端口
  69.     printf("Scanning %s...\n", argv[2]);
  70. for(port =1; port <=1024; port++){
  71. // 构造IP头部
  72.         ip_pack_hdr(iph,
  73. 0,
  74.                     IP_HDR_LEN + TCP_HDR_LEN,
  75.                     rand()&0xffff,
  76. 0,
  77.                     IP_TTL_DEFAULT,
  78.                     IP_PROTO_TCP,
  79.                     src.addr_ip,
  80.                     dst.addr_ip);
  81. // 构造TCP SYN包
  82.         tcp_pack_hdr(tcph,
  83.                     htons(rand()&0xffff),
  84.                     htons(port),
  85.                     htonl(rand()),
  86. 0,
  87.                     TH_SYN,
  88.                     htons(65535),
  89. 0);
  90. // 计算校验和
  91.         tcp_checksum(iph, tcph, TCP_HDR_LEN);
  92.         ip_checksum(packet, IP_HDR_LEN + TCP_HDR_LEN,0);
  93. // 发送
  94.         ip_send(ip, packet, IP_HDR_LEN + TCP_HDR_LEN);
  95. // 接收响应
  96.         pcap_dispatch(pcap,-1, packet_handler, NULL);
  97. }
  98.     pcap_close(pcap);
  99.     ip_close(ip);
  100. return0;
  101. }

14. 常见问题与解决方案

14.1 权限问题

问题:创建原始socket失败

  1. Operationnot permitted

解决方案

Unix/Linux:

  1. # 使用root权限运行
  2. sudo ./program
  3. # 或使用capabilities
  4. sudo setcap cap_net_raw+ep ./program

Windows:

  1. # 以管理员身份运行cmd
  2. # 右键点击cmd -> 以管理员身份运行

14.2 校验和错误

问题:数据包被接收方丢弃

解决方案

  1. // 确保校验和计算正确
  2. ip_checksum(packet, len,0);
  3. // 或手动计算
  4. uint16_t calc_checksum(void*buf,size_t len){
  5. uint32_t sum =0;
  6. uint16_t*ptr =(uint16_t*)buf;
  7. while(len >1){
  8.         sum +=*ptr++;
  9.         len -=2;
  10. }
  11. if(len ==1){
  12.         sum +=*(uint8_t*)ptr <<8;
  13. }
  14. while(sum >>16){
  15.         sum =(sum &0xffff)+(sum >>16);
  16. }
  17. return~sum;
  18. }

14.3 字节序问题

问题:端口或地址解析错误

解决方案

  1. // 始终使用htons/htonl转换
  2. uint16_t port = htons(80);
  3. uint32_t addr = htonl(0x0a000001);
  4. // 检查时使用ntohs/ntohl
  5. uint16_t hport = ntohs(tcph->th_sport);
  6. uint32_t haddr = ntohl(iph->ip_src);

14.4 数据包长度错误

问题:数据包被截断

解决方案

  1. // 确保总长度正确计算
  2. ip->ip_len = htons(IP_HDR_LEN + TCP_HDR_LEN + payload_len);
  3. // UDP也需要设置长度
  4. udp->uh_ulen = htons(UDP_HDR_LEN + payload_len);

14.5 分片问题

问题:大数据包无法发送

解决方案

  1. // 设置MTU或使用分片
  2. #define MTU 1500
  3. if(total_len > MTU - IP_HDR_LEN){
  4. // 使用ip-cooked模式的自动分片
  5. // 或手动分片
  6. int offset =0;
  7. while(offset < total_len){
  8. int frag_len = MTU - IP_HDR_LEN;
  9. if(offset + frag_len > total_len)
  10.             frag_len = total_len - offset;
  11. // 发送分片
  12. // ...
  13.         offset += frag_len;
  14. }
  15. }

14.6 ARP未解析

问题:数据包发送失败

解决方案

  1. // 手动发送ARP请求
  2. eth_addr_t broadcast ={0xff,0xff,0xff,0xff,0xff,0xff};
  3. eth_addr_t zero ={0};
  4. eth_pack_hdr(frame, broadcast, src_mac, ETH_TYPE_ARP);
  5. arp_pack_hdr_ethip(frame + ETH_HDR_LEN, ARP_OP_REQUEST,
  6.                    src_mac, src_ip, zero, dst_ip);
  7. eth_send(eth, frame,sizeof(frame));
  8. // 等待ARP响应
  9. 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));
  4. // 注意:Windows上某些字段会自动填充
  5. // 不要设置IP头部中的TTL、校验和等字段

14.8 IPv6校验和问题

问题:IPv6数据包校验和错误

解决方案

  1. // IPv6校验和包含伪头部
  2. // 使用libdnet提供的函数
  3. ip6_checksum(packet, len);
  4. // 或手动计算
  5. uint32_t sum =0;
  6. // 添加伪头部(源地址 + 目的地址)
  7. sum += ip_cksum_add(&ip6->ip6_src,32,0);
  8. // 添加上层协议和长度
  9. sum += htons(next_header + payload_len);
  10. // 添加上层协议数据
  11. sum += ip_cksum_add(payload, payload_len,0);
  12. // 计算最终校验和
  13. 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);
  4. if(ret <0){
  5.     perror("ip_add_option");
  6. return-1;
  7. }
  8. // 注意:选项长度必须正确
  9. // NOP选项长度为1
  10. // 其他选项至少为2字节

14.10 性能优化

问题:发送大量数据包性能低

解决方案

  1. // 1. 增加发送缓冲区
  2. int sndbuf =1024*1024;// 1MB
  3. setsockopt(fd, SOL_SOCKET, SO_SNDBUF,&sndbuf,sizeof(sndbuf));
  4. // 2. 重用缓冲区
  5. static u_char packet_buf[IP_LEN_MAX];
  6. // 3. 批量发送
  7. for(int i =0; i < count; i++){
  8. // 构造数据包
  9.     ip_pack_hdr(packet_buf,...);
  10.     tcp_pack_hdr(packet_buf + IP_HDR_LEN,...);
  11. // 发送
  12.     ip_send(ip, packet_buf, len);
  13. }
  14. // 4. 使用非阻塞IO
  15. int flags = fcntl(fd, F_GETFL,0);
  16. fcntl(fd, F_SETFL, flags | O_NONBLOCK);

总结

本文档深入分析了libdnet中原始数据包构造的实现机制,涵盖了从以太网到应用层的所有主要协议:

核心要点

  1. 分层构造:从物理层到应用层的完整数据包构造流程
  2. 宏封装:使用宏简化构造代码,提高可读性
  3. 跨平台支持:Unix/Linux、Windows等平台的差异处理
  4. 校验和计算:各种协议的校验和计算方法
  5. 实际应用:扫描器、网络工具等实用示例

关键文件

文件
说明
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