跨平台底层网络库libdnet源码分析系列(八)
官网:http://securitytech.cc
源码分析mettle后门工具学习 所使用的依赖库

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


夜雨聆风