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

admin 2026-04-02 03:45:31 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文深入分析跨平台网络库libdnet对IPv6协议的完整支持架构,涵盖IPv6地址结构定义、报文头封装、地址字符串转换算法及校验和计算等核心模块。关键发现包括采用统一地址结构兼容IPv4/IPv6、实现RFC5952合规的零压缩算法、提供高效的报文打包函数。可操作建议涉及在网络安全工具开发中直接调用libdnet的IPv6接口实现跨平台兼容。 综合评分: 75 文章分类: 代码审计,漏洞分析,WEB安全,网络安全,安全开发


cover_image

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

原创

haidragon haidragon

安全狗的自我修养

2026年4月1日 17:10 湖南

官网:http://securitytech.cc

#

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

#

第 16 章:IPv6 完整支持探析

16.1 IPv6 协议概述

16.1.1 IPv6 的产生背景

随着互联网的飞速发展,IPv4 地址空间逐渐枯竭。IPv6(Internet Protocol version 6)作为 IPv4 的继任者,由 IETF 于 1995 年标准化(RFC 2460),提供了以下关键改进:

  • 巨大的地址空间:从 IPv4 的 32 位扩展到 128 位,提供 2^128 个地址
  • 简化的报文头:固定 40 字节头部,提高路由处理效率
  • 内置安全性:原生支持 IPsec
  • 自动配置:无状态地址自动配置(SLAAC)
  • 更好的 QoS 支持:流标签字段支持

16.1.2 libdnet 中的 IPv6 支持架构

libdnet 库在底层网络编程层面提供了完整的 IPv6 支持,主要包含以下模块:

1. IPv6支持模块结构
2. ├──IPv6地址处理(addr.c, addr-util.c)
3. ├──IPv6报文头封装(ip6.h)
4. ├──ICMPv6协议支持(icmpv6.h)
5. ├──IPv6校验和计算(ip6.c)
6. └──跨平台接口适配(intf-win32.c, intf-linux.c 等)

16.2 IPv6 数据结构深度分析

16.2.1 IPv6 地址结构定义

源码位置: include/dnet/ip6.h

1. #define IP6_ADDR_LEN    16/* IPv6 地址长度(128 位/8 = 16 字节) */
2. #define IP6_ADDR_BITS   128/* IPv6 地址位数 */

4. typedefstruct ip6_addr {
5. uint8_t data[IP6_ADDR_LEN];
6. } __attribute__((__packed__))ip6_addr_t;

关键技术点

  1. 紧凑内存布局:使用 __packed__ 属性确保结构体无内存对齐填充
  2. 灵活的访问方式:可通过 data 数组直接访问原始字节
  3. 跨平台兼容:支持大端和小端字节序

16.2.2 IPv6 报文头结构

源码分析

1. struct ip6_hdr {
2. union{
3. struct ip6_hdr_ctl {
4. uint32_t ip6_un1_flow;/* 20 位流标识符 + 8 位流量类别 */
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 位流量类别高 4 位 */
10. } ip6_ctlun;
11. ip6_addr_t ip6_src;/* 源 IPv6 地址 */
12. ip6_addr_t ip6_dst;/* 目的 IPv6 地址 */
13. } __attribute__((__packed__));

字段详解

| 字段 | 位数 | 说明 | | — | — | — | | Version | 4 | IPv6 版本号(固定为 6) | | Traffic Class | 8 | 流量类别(类似 IPv4 的 ToS) | | Flow Label | 20 | 流标签,用于 QoS | | Payload Length | 16 | 有效载荷长度(不包括 IPv6 头部) | | Next Header | 8 | 下一个头部类型(TCP/UDP/ICMPv6 等) | | Hop Limit | 8 | 跳数限制(类似 IPv4 的 TTL) | | Source Address | 128 | 源 IPv6 地址 | | Destination Address | 128 | 目的 IPv6 地址 |

16.2.3 统一的地址结构

源码位置: include/dnet/addr.h

1. struct addr {
2. uint16_t addr_type;/* 地址类型:ADDR_TYPE_IP6 = 3 */
3. uint16_t addr_bits;/* 地址前缀长度 */
4. union{
5. eth_addr_t __eth;
6. ip_addr_t __ip;
7. ip6_addr_t __ip6;/* IPv6 地址 */
8. uint8_t __data8[16];
9. uint16_t __data16[8];
10. uint32_t __data32[4];
11. } __addr_u;
12. };

设计优势

  • 统一接口:通过 addr_type 区分 IPv4/IPv6/MAC 地址
  • 灵活访问:支持按 8 位、16 位、32 位、64 位访问地址数据
  • 多播支持:可方便地判断组播地址(首字节为 0xFF)

16.3 IPv6 核心功能实现分析

16.3.1 IPv6 报文头快速打包函数

源码位置: include/dnet/ip6.h

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;

11. hdr->ip6_v_c_l&nbsp;=(6<<28)|(c&nbsp;<<20)|(IP6_FLOWLABEL_MASK&nbsp;&&nbsp;l);
12. hdr->ip6_plen_nxt_hlim&nbsp;=(htons(plen)<<16)|(nxt&nbsp;<<8)|&nbsp;hlim;
13. memcpy(&hdr->ip6_src,&nbsp;src,&nbsp;IP6_ADDR_LEN);
14. memcpy(&hdr->ip6_dst,&nbsp;dst,&nbsp;IP6_ADDR_LEN);
15. }

实现要点

  1. 内联优化:使用 inline 减少函数调用开销
  2. 字节序转换: plen 使用 htons() 转换为网络字节序
  3. 位域操作:精确控制每个字段的位偏移
  4. 内存拷贝:直接使用 memcpy 复制 128 位地址

16.3.2 IPv6 地址字符串转换

(1)IPv6 地址转字符串(ip6_ntop)

源码位置: src/addr-util.c

1. char*ip6_ntop(constip6_addr_t*ip6,char*dst,size_t&nbsp;len)
2. {
3. uint16_t&nbsp;data[IP6_ADDR_LEN&nbsp;/2];
4. struct{int&nbsp;base,&nbsp;len;}&nbsp;best,&nbsp;cur;
5. char*p&nbsp;=&nbsp;dst;
6. int&nbsp;i;

8. // 复制到 16 位数组(网络字节序)
9. for(i&nbsp;=0;&nbsp;i&nbsp;<&nbsp;IP6_ADDR_LEN&nbsp;/2;&nbsp;i++){
10. data[i]=&nbsp;ip6->data[2*&nbsp;i]<<8;
11. data[i]|=&nbsp;ip6->data[2*&nbsp;i&nbsp;+1];
12. }

14. // 查找最长的连续零段(RFC 5952)
15. best.len&nbsp;=&nbsp;cur.len&nbsp;=0;
16. best.base&nbsp;=&nbsp;cur.base&nbsp;=-1;

18. for(i&nbsp;=0;&nbsp;i&nbsp;<&nbsp;IP6_ADDR_LEN;&nbsp;i&nbsp;+=2){
19. if(data[i&nbsp;/2]==0){
20. if(cur.base&nbsp;==-1){
21. cur.base&nbsp;=&nbsp;i;
22. cur.len&nbsp;=0;
23. }else
24. cur.len&nbsp;+=2;
25. }else{
26. if(cur.base&nbsp;!=-1){
27. if(best.base&nbsp;==-1||&nbsp;cur.len&nbsp;>&nbsp;best.len)
28. best&nbsp;=&nbsp;cur;
29. cur.base&nbsp;=-1;
30. }
31. }
32. }

34. // 生成压缩格式的 IPv6 字符串
35. if(best.base&nbsp;!=-1&&&nbsp;best.len&nbsp;<2)
36. best.base&nbsp;=-1;

38. if(best.base&nbsp;==0)
39. *p++=':';

41. for(i&nbsp;=0;&nbsp;i&nbsp;<&nbsp;IP6_ADDR_LEN;&nbsp;i&nbsp;+=2){
42. if(i&nbsp;==&nbsp;best.base){
43. *p++=':';
44. i&nbsp;+=&nbsp;best.len;
45. }elseif(i&nbsp;==12&&&nbsp;best.base&nbsp;==0&&
46. (best.len&nbsp;==10||(best.len&nbsp;==8&&&nbsp;data[5]==0xffff))){
47. // IPv4 映射地址 (::ffff:10.0.0.1)
48. if(ip_ntop((ip_addr_t*)&data[6],&nbsp;p,&nbsp;len&nbsp;-(p&nbsp;-&nbsp;dst))==&nbsp;NULL)
49. return(NULL);
50. return(dst);
51. }else
52. p&nbsp;+=&nbsp;sprintf(p,"%x:",&nbsp;data[i&nbsp;/2]);
53. }

55. // 处理末尾字符
56. if(best.base&nbsp;+2+&nbsp;best.len&nbsp;==&nbsp;IP6_ADDR_LEN){
57. *p&nbsp;='\0';
58. }else
59. p[-1]='\0';

61. return(dst);
62. }

算法亮点

  • 零压缩算法:自动查找最长连续零段并使用 :: 压缩
  • RFC 5952 合规:遵循 IPv6 地址文本表示最佳实践
  • IPv4 映射支持:自动识别并转换为 ::ffff:x.x.x.x 格式
  • 边界检查:确保输出缓冲区足够大(至少 46 字节)

(2)字符串转 IPv6 地址(ip6_pton)

1. int&nbsp;ip6_pton(constchar*p,ip6_addr_t*ip6)
2. {
3. uint16_t&nbsp;data[8],*u&nbsp;=(uint16_t*)ip6->data;
4. int&nbsp;i,&nbsp;j,&nbsp;n,&nbsp;z&nbsp;=-1;// z 记录 :: 的位置
5. char*ep;
6. long&nbsp;l;

8. if(*p&nbsp;==':')
9. p++;

11. for(n&nbsp;=0;&nbsp;n&nbsp;<8;&nbsp;n++){
12. l&nbsp;=&nbsp;strtol(p,&ep,16);

14. if(ep&nbsp;==&nbsp;p){
15. // 遇到 :: 压缩标记
16. if(ep[0]==':'&&&nbsp;z&nbsp;==-1){
17. z&nbsp;=&nbsp;n;
18. p++;
19. }elseif(ep[0]=='\0'){
20. break;
21. }else{
22. return(-1);
23. }
24. }elseif(ep[0]=='.'&&&nbsp;n&nbsp;<=6){
25. // IPv4 后缀格式(如 ::ffff:192.168.1.1)
26. if(ip_pton(p,(ip_addr_t*)(data&nbsp;+&nbsp;n))<0)
27. return(-1);
28. n&nbsp;+=2;
29. ep&nbsp;="";
30. break;
31. }elseif(l&nbsp;>=0&&&nbsp;l&nbsp;<=0xffff){
32. data[n]=&nbsp;htons((uint16_t)l);

34. if(ep[0]=='\0'){
35. n++;
36. break;
37. }elseif(ep[0]!=':'||&nbsp;ep[1]=='\0')
38. return(-1);

40. p&nbsp;=&nbsp;ep&nbsp;+1;
41. }else
42. return(-1);
43. }

45. // 验证格式并填充零段
46. if(n&nbsp;==0||*ep&nbsp;!='\0'||(z&nbsp;==-1&&&nbsp;n&nbsp;!=8))
47. return(-1);

49. // 复制 :: 之前的部分
50. for(i&nbsp;=0;&nbsp;i&nbsp;<&nbsp;z;&nbsp;i++){
51. u[i]=&nbsp;data[i];
52. }

54. // 填充零段
55. while(i&nbsp;<8-(n&nbsp;-&nbsp;z&nbsp;-1)){
56. u[i++]=0;
57. }

59. // 复制 :: 之后的部分
60. for(j&nbsp;=&nbsp;z&nbsp;+1;&nbsp;i&nbsp;<8;&nbsp;i++,&nbsp;j++){
61. u[i]=&nbsp;data[j];
62. }

64. return(0);
65. }

关键技术

  • 灵活的输入解析:支持完整格式、压缩格式、IPv4 映射格式
  • 错误检测:严格验证输入格式,防止非法地址
  • 零填充算法:正确计算并填充 :: 代表的零段

16.3.3 IPv6 校验和计算

源码位置: src/ip6.c

1. void&nbsp;ip6_checksum(void*buf,size_t&nbsp;len)
2. {
3. struct&nbsp;ip6_hdr&nbsp;*ip6&nbsp;=(struct&nbsp;ip6_hdr&nbsp;*)buf;
4. struct&nbsp;ip6_ext_hdr&nbsp;*ext;
5. u_char&nbsp;*p,&nbsp;nxt;
6. int&nbsp;i,&nbsp;sum;

8. nxt&nbsp;=&nbsp;ip6->ip6_nxt;

10. // 跳过所有扩展头部
11. for(i&nbsp;=&nbsp;IP6_HDR_LEN;&nbsp;IP6_IS_EXT(nxt);&nbsp;i&nbsp;+=(ext->ext_len&nbsp;+1)<<3){
12. if(i&nbsp;>=(int)len)return;
13. ext&nbsp;=(struct&nbsp;ip6_ext_hdr&nbsp;*)((u_char&nbsp;*)buf&nbsp;+&nbsp;i);
14. nxt&nbsp;=&nbsp;ext->ext_nxt;
15. }

17. p&nbsp;=(u_char&nbsp;*)buf&nbsp;+&nbsp;i;
18. len&nbsp;-=&nbsp;i;

20. // 根据下一头部类型计算校验和
21. if(nxt&nbsp;==&nbsp;IP_PROTO_TCP){
22. struct&nbsp;tcp_hdr&nbsp;*tcp&nbsp;=(struct&nbsp;tcp_hdr&nbsp;*)p;
23. if(len&nbsp;>=&nbsp;TCP_HDR_LEN){
24. tcp->th_sum&nbsp;=0;
25. sum&nbsp;=&nbsp;ip_cksum_add(tcp,&nbsp;len,0)+&nbsp;htons(nxt&nbsp;+(u_short)len);
26. sum&nbsp;=&nbsp;ip_cksum_add(&ip6->ip6_src,32,&nbsp;sum);// 伪头部
27. tcp->th_sum&nbsp;=&nbsp;ip_cksum_carry(sum);
28. }
29. }elseif(nxt&nbsp;==&nbsp;IP_PROTO_UDP){
30. struct&nbsp;udp_hdr&nbsp;*udp&nbsp;=(struct&nbsp;udp_hdr&nbsp;*)p;
31. if(len&nbsp;>=&nbsp;UDP_HDR_LEN){
32. udp->uh_sum&nbsp;=0;
33. sum&nbsp;=&nbsp;ip_cksum_add(udp,&nbsp;len,0)+&nbsp;htons(nxt&nbsp;+(u_short)len);
34. sum&nbsp;=&nbsp;ip_cksum_add(&ip6->ip6_src,32,&nbsp;sum);
35. if((udp->uh_sum&nbsp;=&nbsp;ip_cksum_carry(sum))==0)
36. udp->uh_sum&nbsp;=0xffff;// UDP 校验和不能为 0
37. }
38. }elseif(nxt&nbsp;==&nbsp;IP_PROTO_ICMPV6){
39. struct&nbsp;icmp_hdr&nbsp;*icmp&nbsp;=(struct&nbsp;icmp_hdr&nbsp;*)p;
40. if(len&nbsp;>=&nbsp;ICMP_HDR_LEN){
41. icmp->icmp_cksum&nbsp;=0;
42. sum&nbsp;=&nbsp;ip_cksum_add(icmp,&nbsp;len,0)+&nbsp;htons(nxt&nbsp;+(u_short)len);
43. sum&nbsp;=&nbsp;ip_cksum_add(&ip6->ip6_src,32,&nbsp;sum);
44. icmp->icmp_cksum&nbsp;=&nbsp;ip_cksum_carry(sum);
45. }
46. }
47. }

IPv6 校验和特点

  1. 伪头部包含源和目的地址:增强端到端完整性检查
  2. 扩展头部处理:自动跳过 Hop-by-Hop、Routing、Fragment 等扩展头
  3. UDP 特殊处理:校验和为 0 时设置为 0xFFFF(RFC 2460 要求)
  4. 16 位反码求和:使用标准的 Internet 校验和算法

16.4 ICMPv6 协议支持

16.4.1 ICMPv6 头部结构

源码位置: include/dnet/icmpv6.h

1. struct&nbsp;icmpv6_hdr&nbsp;{
2. uint8_t&nbsp;icmpv6_type;/* 消息类型 */
3. uint8_t&nbsp;icmpv6_code;/* 子代码 */
4. uint16_t&nbsp;icmpv6_cksum;/* 校验和 */
5. };

16.4.2 ICMPv6 重要消息类型

| 类型值 | 名称 | 说明 | | — | — | — | | 1 | ICMPV6_UNREACH | 目的地不可达 | | 3 | ICMPV6_TIMEXCEED | 超时消息 | | 4 | ICMPV6_PARAMPROBLEM | 参数问题 | | 128 | ICMPV6_ECHO | Echo Request(Ping) | | 129 | ICMPV6_ECHOREPLY | Echo Reply(Ping 响应) | | 135 | ICMPV6NEIGHBORSOLICITATION | 邻居请求(NS) | | 136 | ICMPV6NEIGHBORADVERTISEMENT | 邻居通告(NA) |

16.4.3 邻居发现协议(NDP)支持

NDP 消息结构

1. struct&nbsp;icmpv6_msg_nd&nbsp;{
2. uint32_t&nbsp;icmpv6_flags;/* NS/NA 标志位 */
3. ip6_addr_t&nbsp;icmpv6_target;/* 目标地址 */
4. uint8_t&nbsp;icmpv6_option_type;/* 选项类型 */
5. uint8_t&nbsp;icmpv6_option_length;/* 选项长度 */
6. eth_addr_t&nbsp;icmpv6_mac;/* MAC 地址 */
7. };

NDP 核心功能

  • 无 ARP:使用 ICMPv6 NS/NA 消息替代 ARP
  • 重复地址检测(DAD):发送 NS 消息检测地址冲突
  • 路由器发现:RS/RA 消息实现无状态自动配置

16.5 IPv6 扩展头部机制

16.5.1 扩展头部结构

源码分析

1. struct&nbsp;ip6_ext_hdr&nbsp;{
2. uint8_t&nbsp;ext_nxt;/* 下一个头部类型 */
3. uint8_t&nbsp;ext_len;/* 长度(以 8 字节为单位,不包括前 8 字节) */
4. union{
5. struct&nbsp;ip6_ext_data_routing routing;
6. struct&nbsp;ip6_ext_data_fragment fragment;
7. }&nbsp;ext_data;
8. }&nbsp;__attribute__((__packed__));

16.5.2 推荐的扩展头部顺序

根据 RFC 2460 第 4.1 节:

1. IPv6基本头部
2. ↓
3. Hop-by-HopOptions(IP_PROTO_HOPOPTS)
4. ↓
5. DestinationOptions(IP_PROTO_DSTOPTS)-第一个
6. ↓
7. RoutingHeader(IP_PROTO_ROUTING)
8. ↓
9. FragmentHeader(IP_PROTO_FRAGMENT)
10. ↓
11. AuthenticationHeader(IP_PROTO_AH)
12. ↓
13. EncapsulatingSecurityPayload(IP_PROTO_ESP)
14. ↓
15. DestinationOptions(IP_PROTO_DSTOPTS)-第二个
16. ↓
17. 上层协议头部(TCP/UDP/ICMPv6等)

16.5.3 Fragment 扩展头部

1. struct&nbsp;ip6_ext_data_fragment&nbsp;{
2. uint16_t&nbsp;offlg;/* 分片偏移 + 保留位 + 标志 */
3. uint32_t&nbsp;ident;/* 分片标识符 */
4. };

6. /* 分片相关宏定义 */
7. #define&nbsp;IP6_OFF_MASK &nbsp; &nbsp; &nbsp;0xfff8/* 分片偏移掩码 */
8. #define&nbsp;IP6_RESERVED_MASK&nbsp;0x0006/* 保留位 */
9. #define&nbsp;IP6_MORE_FRAG &nbsp; &nbsp;&nbsp;0x0001/* 更多分片标志 */

16.6 特殊 IPv6 地址

16.6.1 预定义地址常量

源码位置: include/dnet/ip6.h

1. /* 未指定地址 */
2. #define&nbsp;IP6_ADDR_UNSPEC \
3. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"

5. /* 环回地址 */
6. #define&nbsp;IP6_ADDR_LOOPBACK \
7. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"

16.6.2 常见特殊地址

| 地址 | 表示 | 用途 | | — | — | — | | :: | 全 0 | 未指定地址(类似 0.0.0.0) | | ::1 | 0…01 | 环回地址(类似 127.0.0.1) | | ::ffff:x.x.x.x | IPv4 映射地址 | 表示 IPv4 节点 | | fe80::/10 | 链路本地地址 | 同一链路上通信 | | ff00::/8 | 组播地址 | 一对多通信 | | 2000::/3 | 全球单播地址 | 全球可达地址 |

16.7 跨平台 IPv6 实现差异

16.7.1 Windows平台 IPv6 支持

特点

  • 使用 Winsock 2 API 提供 IPv6 支持
  • 需要链接 ws2_32.lib 和 iphlpapi.lib
  • Raw Socket 权限限制(需管理员权限)
  • 不支持某些低级 IPv6 操作

示例代码片段( src/ip-win32.c 风格):

1. #include<winsock2.h>
2. #include<ws2tcpip.h>

4. SOCKET sock&nbsp;=&nbsp;socket(AF_INET6,&nbsp;SOCK_RAW,&nbsp;IPPROTO_IPV6);
5. if(sock&nbsp;==&nbsp;INVALID_SOCKET){
6. // 错误处理
7. }

16.7.2 Linux 平台 IPv6 支持

特点

  • 完整的 Raw Socket 支持
  • 需要 CAPNETRAW 能力或 root 权限
  • 支持 IPv6 扩展头部操作
  • 内核提供丰富的 IPv6 选项

示例代码

1. #include<sys/socket.h>
2. #include<netinet/in.h>
3. #include<netinet/ip6.h>

5. int&nbsp;sock&nbsp;=&nbsp;socket(AF_INET6,&nbsp;SOCK_RAW,&nbsp;IPPROTO_RAW);
6. setsockopt(sock,&nbsp;IPPROTO_IPV6,&nbsp;IPV6_TCLASS,...);

16.7.3 BSD 平台 IPv6 支持

特点

  • 最早的 IPv6 实现之一
  • 提供高级 IPv6 Socket 选项
  • 支持 BPF(Berkeley Packet Filter)过滤 IPv6 包

16.8 实战案例:IPv6 Ping 工具实现

16.8.1 完整代码示例

1. /*
2. * ipv6_ping.c - IPv6 Ping 工具
3. * 功能:向目标 IPv6 地址发送 ICMPv6 Echo Request 并接收响应
4. * 编译:gcc -o ipv6_ping ipv6_ping.c -ldnet
5. * 运行:ipv6_ping 2001:db8::1
6. */
7. #include<stdio.h>
8. #include<stdlib.h>
9. #include<string.h>
10. #include"dnet.h"
11. #define&nbsp;ICMPV6_ECHO_REQUEST&nbsp;128
12. #define&nbsp;ICMPV6_ECHO_REPLY &nbsp;&nbsp;129
13. int&nbsp;main(int&nbsp;argc,char*argv[])
14. {
15. struct&nbsp;ip6_hdr&nbsp;*ip6;
16. struct&nbsp;icmpv6_hdr&nbsp;*icmp;
17. char&nbsp;packet[IP6_HDR_LEN&nbsp;+&nbsp;ICMP_HDR_LEN&nbsp;+64];
18. ip6_addr_t&nbsp;dst;
19. struct&nbsp;addr src_addr,&nbsp;dst_addr;
20. char&nbsp;addr_str[INET6_ADDRSTRLEN];
21. int&nbsp;sock;
22. if(argc&nbsp;!=2){
23. fprintf(stderr,"用法:%s <目标 IPv6 地址>\n",&nbsp;argv[0]);
24. exit(EXIT_FAILURE);
25. }
26. /* 解析目标地址 */
27. if(ip6_pton(argv[1],&dst)<0){
28. fprintf(stderr,"无效的 IPv6 地址:%s\n",&nbsp;argv[1]);
29. exit(EXIT_FAILURE);
30. }
31. /* 获取本地 IPv6 地址 */
32. struct&nbsp;intf_entry intf;
33. strlcpy(intf.intf_name,"eth0",sizeof(intf.intf_name));
34. struct&nbsp;intf_handle&nbsp;*intf_hdl&nbsp;=&nbsp;intf_open();
35. if(intf_get(intf_hdl,&intf)<0){
36. fprintf(stderr,"无法获取网卡信息\n");
37. intf_close(intf_hdl);
38. exit(EXIT_FAILURE);
39. }
40. /* 构造 IPv6 头部 */
41. memset(packet,0,sizeof(packet));
42. ip6&nbsp;=(struct&nbsp;ip6_hdr&nbsp;*)packet;
43. ip6_pack_hdr(packet,
44. 0,/* 流量类别 */
45. 0,/* 流标签 */
46. ICMP_HDR_LEN&nbsp;+64,/* 载荷长度 */
47. IP_PROTO_ICMPV6,/* 下一头部 */
48. 64,/* 跳限制 */
49. intf.intf_addr.addr_ip6.data,/* 源地址 */
50. dst.data);/* 目的地址 */
51. /* 构造 ICMPv6 Echo Request */
52. icmp&nbsp;=(struct&nbsp;icmpv6_hdr&nbsp;*)(packet&nbsp;+&nbsp;IP6_HDR_LEN);
53. icmp->icmpv6_type&nbsp;=&nbsp;ICMPV6_ECHO_REQUEST;
54. icmp->icmpv6_code&nbsp;=0;
55. icmp->icmpv6_cksum&nbsp;=0;
56. /* 设置 Echo 数据 */
57. struct&nbsp;icmpv6_msg_echo&nbsp;*echo&nbsp;=(struct&nbsp;icmpv6_msg_echo&nbsp;*)(icmp&nbsp;+1);
58. echo->icmpv6_id&nbsp;=&nbsp;htons(getpid()&0xFFFF);
59. echo->icmpv6_seq&nbsp;=&nbsp;htons(1);
60. /* 填充数据 */
61. memset(echo->icmpv6_data,'A',64);
62. /* 计算 ICMPv6 校验和 */
63. ip6_checksum(packet,sizeof(packet));
64. /* 发送数据包 */
65. struct&nbsp;ip6_handle&nbsp;*ip6_hdl&nbsp;=&nbsp;ip6_open();
66. if(ip6_send(ip6_hdl,&nbsp;packet,sizeof(packet))<0){
67. fprintf(stderr,"发送失败\n");
68. ip6_close(ip6_hdl);
69. exit(EXIT_FAILURE);
70. }
71. printf("已发送 ICMPv6 Echo Request 到 %s\n",&nbsp;argv[1]);
72. /* 接收响应(简化版) */
73. // 实际应用中需要使用 select/poll 等待响应
74. ip6_close(ip6_hdl);
75. intf_close(intf_hdl);
76. return0;
77. }

16.8.2 编译和运行

Windows 编译

1. gcc&nbsp;-I../include&nbsp;-L../src/.libs&nbsp;-o ipv6_ping.exe ipv6_ping.c&nbsp;-ldnet&nbsp;-lws2_32&nbsp;-liphlpapi

Linux 编译

1. gcc&nbsp;-o ipv6_ping ipv6_ping.c&nbsp;-ldnet

运行示例

1. # Windows(需要管理员权限)
2. ipv6_ping.exe&nbsp;2001:db8::1

4. # Linux(需要 root 权限)
5. sudo&nbsp;./ipv6_ping&nbsp;2001:db8::1

16.9 IPv6 编程最佳实践

16.9.1 地址处理最佳实践

  1. 始终检查地址类型
1. if(addr->addr_type&nbsp;==&nbsp;ADDR_TYPE_IP6){
2. // 处理 IPv6
3. }elseif(addr->addr_type&nbsp;==&nbsp;ADDR_TYPE_IP){
4. // 处理 IPv4
5. }
  1. 使用统一的地址转换函数
1. char&nbsp;buf[INET6_ADDRSTRLEN];
2. addr_ntop(addr,&nbsp;buf,sizeof(buf));// 自动处理 IPv4/IPv6
  1. 正确处理地址前缀长度
1. struct&nbsp;addr network;
2. addr_net(addr,&network);// 计算网络地址

16.9.2 校验和计算注意事项

  1. 必须在发送前计算
1. ip6_checksum(packet,&nbsp;packet_len);
  1. UDP 特殊处理:校验和为 0 时自动设置为 0xFFFF
  2. 扩展头部自动跳过:无需手动处理

16.9.3 跨平台兼容性建议

  1. 使用 libdnet 抽象层:避免直接使用平台特定 API
  2. 测试地址族支持
1. #ifdef&nbsp;HAVE_SOCKADDR_IN6
2. // IPv6 可用
3. #endif
  1. 处理权限差异:Windows 需要管理员,Linux 需要 CAPNETRAW

16.10 IPv6 安全考虑

16.10.1 常见安全风险

  1. 扩展头部攻击:恶意构造的扩展头链可能导致拒绝服务
  2. NDP 欺骗:伪造 NS/NA 消息进行中间人攻击
  3. 组播监听滥用:利用 IPv6 组播进行网络侦察

16.10.2 防护建议

  1. 验证扩展头部链:限制最大扩展头数量
  2. 实施 RA Guard:防止 rogue 路由器
  3. 使用 SEND 协议:安全邻居发现(RFC 3971)

16.11 总结与展望

16.11.1 libdnet IPv6 支持特性总结

✅ 完整的 IPv6 头部封装 ✅ ICMPv6 协议支持 ✅ 邻居发现协议(NDP) ✅ 扩展头部处理 ✅ 跨平台地址转换 ✅ 自动校验和计算 ✅ IPv4 映射地址支持

16.11.2 IPv6 发展趋势

  • IPv6 普及率持续增长:全球已超过 30% 的互联网流量使用 IPv6
  • IPv6-only 网络兴起:移动运营商率先部署
  • IoT 设备默认 IPv6:6LoWPAN 等低功耗协议推动
  • 5G 核心网基于 IPv6:网络切片依赖 IPv6 扩展头

16.11.3 进一步学习资源

  • RFC 文档
  • RFC 8200: IPv6 规范
  • RFC 4443: ICMPv6
  • RFC 4861: 邻居发现
  • RFC 5952: IPv6 地址文本表示
  • 实验环境
  • GNS3 / EVE-NG 网络模拟器
  • Wireshark IPv6 抓包分析
  • Linux IPv6 协议栈源码研究

下一章预告:第 17 章将深入分析 libdnet 在 Windows 和 Linux 平台上的内核交互机制,揭示 Raw Socket 在不同操作系统中的实现差异。

  • 公众号:安全狗的自我修养
  • vx:2207344074
  • http://gitee.com/haidragon
  • http://github.com/haidragon
  • bilibili:haidragonx

#


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:安全狗的自我修养 haidragon haidragon《跨平台底层网络库libdnet源码分析系列(十六)》

评论:0   参与:  0