乐于分享
好东西不私藏

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

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

  • 网:http://securitytech.cc

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

    第 2 章:网络接口管理 (Interface Module)

    2.1 实战案例:编写跨平台”网卡信息收集器”

    案例目标

    实现一个跨平台的网卡信息收集器,能够获取网卡的以下信息:

    完整代码示例

    
    

    编译与运行

    Windows (MSVC):

    
    

    Linux (GCC):

    
    

    预期输出示例

    
    

    2.2 核心 API 详解

    2.2.1 intf_open – 打开接口句柄

    
    

    功能: 创建并初始化接口管理句柄

    返回值:

    Windows 实现源码分析 ( intf-win32.c:306-310):

    
    

    关键点:

    2.2.2 intf_loop – 遍历所有接口

    
    

    功能: 遍历系统中所有网络接口,对每个接口调用回调函数

    参数:

    返回值:

    Windows 实现源码分析 ( intf-win32.c:396-416):

    
    

    执行流程:

    2.2.3 intf_get_dst – 路由查找

    
    

    功能: 根据目标地址查找应使用的出口网卡

    参数:

    返回值:

    重要提示:

    Windows 实现源码:

    
    

    Windows 下的替代方案:

    
    

    2.3 难点攻克:Linux 与 Windows 的结构体差异

    2.3.1 Linux: 使用 ioctl 获取接口信息

    核心数据结构:

    
    

    典型实现代码:

    
    

    2.3.2 Windows: 使用 GetAdaptersAddresses 获取接口信息

    核心数据结构:

    
    

    Windows 实现源码分析 ( intf-win32.c:219-269):

    
    

    2.3.3 关键差异对比表

    特性
    Linux (ioctl)
    Windows (GetAdaptersAddresses)
    获取方式
    同步调用,单次 ioctl
    返回链表结构,需要遍历
    接口标识
    字符串名称 (eth0, wlan0)
    数字索引 (IfIndex, Ipv6IfIndex)
    MAC 地址
    ifrhwaddr.sadata
    PhysicalAddress + PhysicalAddressLength
    IP 地址
    需要多次 ioctl 调用
    FirstUnicastAddress 链表
    状态标志
    IFFUP, IFFRUNNING 等
    OperStatus 枚举值
    MTU
    ifr_mtu 字段
    Mtu 字段
    缓冲区管理
    静态结构体
    动态分配,可能需要多次重试

    2.3.4 平台差异处理策略

    
    

    2.4 避坑指南:Windows 上获取不到 MAC 地址的问题

    2.4.1 问题现象

    在 Windows 上运行网卡信息收集器时,发现部分网卡的 MAC 地址显示为空或错误值。

    2.4.2 根本原因分析

    原因一:PhysicalAddressLength 不匹配

    Windows 的 IP_ADAPTER_ADDRESSES 结构中:

    
    

    原因二:虚拟网卡/隧道网卡无 MAC 地址

    某些虚拟接口(如 VPN、隧道适配器)可能没有物理地址:

    原因三:eth_addr 联合体差异

    libdnet 中的 intf_link_addr 是一个联合体:

    
    

    2.4.3 正确的实现方式

    Windows 实现源码分析 ( intf-win32.c:176-182):

    
    

    关键点:

    2.4.4 完整的 MAC 地址处理示例

    
    

    2.4.5 常见错误对比

    错误代码
    问题
    正确代码
    memcpy(&entry->intf_link_addr,a->PhysicalAddress,a->PhysicalAddressLength)
    覆盖了 addrtype 和 addrbits 字段
    只复制 addr_eth 字段
    if(a->PhysicalAddressLength>0)
    接受非 6 字节的地址,导致后续解析错误
    if(a->PhysicalAddressLength==ETH_ADDR_LEN)
    直接访问 a->PhysicalAddress 而不检查长度
    可能访问未初始化的数据
    先检查长度再复制
    memcpy(mac,a->PhysicalAddress,8)
    复制了 2 字节填充数据
    memcpy(mac,a->PhysicalAddress,ETH_ADDR_LEN)

    2.4.6 调试技巧

    添加调试输出:

    
    

    预期输出:

    
    

    异常输出:

    
    

    2.4.7 Windows 特殊情况处理

    处理回环接口:

    
    

    处理隧道接口:

    
    

    2.5 扩展:其他常用 API

    2.5.1 intf_get_src – 根据源地址查找接口

    
    

    功能: 根据 IP 地址查找对应的网络接口

    应用场景:

    Windows 实现源码分析 ( intf-win32.c:347-373):

    
    

    2.5.2 intf_get_index – 根据索引查找接口

    
    

    功能: 根据接口索引查找接口信息

    参数:

    Windows 实现源码分析 ( intf-win32.c:329-345):

    
    

    2.5.3 intf_close – 关闭接口句柄

    
    

    功能: 释放接口句柄及相关资源

    Windows 实现源码分析 ( intf-win32.c:418-433):

    
    

    2.6 实战应用:基于源码的高级功能

    2.6.1 实现 Windows 路由查找功能

    虽然 Windows 原生 intf_get_dst 不支持,但我们可以自行实现:

    
    

    2.6.2 获取接口详细信息(Windows)

    
    

    2.7 总结

    2.7.1 关键要点

    2.7.2 常见问题排查清单

    2.7.3 推荐实践


    • Windows 上 MAC 地址显示异常?→ 检查 PhysicalAddressLength

      无法遍历所有接口?→ 检查缓冲区分配和 GetAdaptersAddresses 返回值

      路由查找失败?→ Windows 下 intf_get_dst 不支持,需自行实现

      接口名称不识别?→ 注意 Windows 接口名称格式(eth0, eth1 等)

      内存泄漏?→ 确保调用 intf_close 释放资源

    • MAC 地址长度验证
    • 接口类型映射
    • 错误处理和状态码转换
    • 使用 ioctl 系统调用
    • 直接读取 /proc 文件系统
    • 使用 GetAdaptersAddresses 获取接口信息
    • 需要处理动态缓冲区和重试逻辑
    • 接口索引与设备名称的映射
    • af: 地址族 ( AF_INET 或 AF_INET6)
    • index: 接口索引
    • 确定某个 IP 地址属于哪个网卡
    • 实现多网卡环境下的接口选择
    • PhysicalAddressLength 为 0
    • 或返回的是软件生成的虚拟地址
    • PhysicalAddress 数组大小: MAX_ADAPTER_ADDRESS_LENGTH (8 字节)
    • 实际 MAC 地址长度: ETH_ADDR_LEN (6 字节)
    • Windows 实现不支持此功能 ( intf-win32.c:375-381)
    • 返回 ENOSYS (函数未实现)
    • Linux 实现通过读取 /proc/net/route 实现
    • 成功:返回 0
    • 失败:返回 -1
    • intf: 接口句柄
    • entry: 输出参数,存储找到的接口信息
    • dst: 目标地址
    • 成功:返回回调函数的返回值
    • 失败:返回 -1,设置 errno
    • intf: 接口句柄
    • callback: 回调函数
    • arg: 用户自定义参数
    • Windows 下只是分配内存,延迟加载接口表
    • Linux 下会打开 socket 并初始化 netlink 通信
    • 成功:返回 intf_t* 句柄
    • 失败:返回 NULL,设置 errno
    • IP 地址(IPv4/IPv6)
    • MAC 地址
    • MTU(最大传输单元)
    • 网卡状态(UP/DOWN)
    1. 使用 intf_loop 而非多次 intf_get:前者一次性获取所有信息,效率更高
    2. 分配足够大的缓冲区: intf_entry.intf_len 决定了可存储的别名地址数量
    3. 检查返回值:所有 API 都可能失败,必须检查返回值
    4. 平台特定功能:对于平台不支持的 API,考虑使用平台原生 API 实现
    5. 错误处理:提供有意义的错误信息,便于调试
    1. 跨平台注意事项
    1. Linux 特性
    1. 统一接口,平台隔离:libdnet 提供了统一的 API,底层根据平台使用不同的实现
    2. Windows 特性
    1. void print_detailed_interface_info(constchar*device)
    2. {
    3. intf_t*intf;
    4. struct intf_entry entry;
    5.     u_char buf[2048];/* 更大的缓冲区以支持更多别名 */
    6. if((intf = intf_open())== NULL){
    7.         perror("intf_open");
    8. return;
    9. }
    10.     entry.intf_len =sizeof(buf);
    11.     strncpy(entry.intf_name, device,sizeof(entry.intf_name));
    12. if(intf_get(intf,&entry)<0){
    13.         perror("intf_get");
    14. }else{
    15. /* 打印详细信息 */
    16.         printf("接口: %s\n", entry.intf_name);
    17.         printf("操作系统名称: %s\n", entry.os_intf_name);
    18.         printf("驱动描述: %s\n", entry.driver_name);
    19.         printf("PCAP 设备名: %s\n", entry.pcap_intf_name);
    20. /* MAC 地址 */
    21. if(entry.intf_link_addr.addr_type == ADDR_TYPE_ETH){
    22. char mac_str[18];
    23.             eth_ntoa_r(&entry.intf_link_addr.addr_eth, mac_str);
    24.             printf("MAC: %s\n", mac_str);
    25. }
    26. /* 主 IP 地址 */
    27. if(entry.intf_addr.addr_type != ADDR_TYPE_NONE){
    28. char ip_str[INET6_ADDRSTRLEN];
    29.             addr_ntop(&entry.intf_addr, ip_str,sizeof(ip_str));
    30.             printf("主 IP: %s/%u\n", ip_str, entry.intf_addr.addr_bits);
    31. }
    32. /* 别名 IP 地址 */
    33. for(int i =0; i < entry.intf_alias_num; i++){
    34. char alias_str[INET6_ADDRSTRLEN];
    35.             addr_ntop(&entry.intf_alias_addrs[i],
    36.                       alias_str,sizeof(alias_str));
    37.             printf("别名 IP: %s/%u\n", alias_str,
    38.                    entry.intf_alias_addrs[i].addr_bits);
    39. }
    40. }
    41.     intf_close(intf);
    42. }
    1. /* Windows 路由查找实现 */
    2. int intf_get_dst_win32(intf_t*intf,struct intf_entry *entry,
    3. struct addr *dst)
    4. {
    5. if(dst->addr_type != ADDR_TYPE_IP){
    6.         errno = EINVAL;
    7. return(-1);
    8. }
    9. /* 使用 GetBestRoute API */
    10.     MIB_IPFORWARDTABLE *routeTable = NULL;
    11.     DWORD dwSize =0;
    12.     DWORD ret;
    13.     DWORD ifIndex;
    14. /* 获取路由表 */
    15. if(GetIpForwardTable(NULL,&dwSize, FALSE)== ERROR_INSUFFICIENT_BUFFER){
    16.         routeTable =(MIB_IPFORWARDTABLE *)malloc(dwSize);
    17. if(routeTable == NULL){
    18.             errno = ENOMEM;
    19. return(-1);
    20. }
    21.         ret =GetIpForwardTable(routeTable,&dwSize, FALSE);
    22. }
    23. if(ret != NO_ERROR){
    24.         free(routeTable);
    25.         errno = EIO;
    26. return(-1);
    27. }
    28. /* 查找最佳路由 */
    29.     MIB_IPFORWARDROW bestRoute;
    30.     DWORD bestMetric = INFINITE;
    31. for(DWORD i =0; i < routeTable->dwNumEntries; i++){
    32.         MIB_IPFORWARDROW *route =&routeTable->table[i];
    33. /* 检查目标地址是否匹配 */
    34. if((dst->addr_ip & route->dwForwardMask)== route->dwForwardDest){
    35. if(route->dwForwardMetric1 < bestMetric){
    36.                 bestRoute =*route;
    37.                 bestMetric = route->dwForwardMetric1;
    38. }
    39. }
    40. }
    41.     free(routeTable);
    42. if(bestMetric == INFINITE){
    43.         errno = ENETUNREACH;
    44. return(-1);
    45. }
    46. /* 获取接口信息 */
    47.     ifIndex = bestRoute.dwForwardIfIndex;
    48. return intf_get_index(intf, entry, AF_INET, ifIndex);
    49. }
    1. intf_t*
    2. intf_close(intf_t*intf)
    3. {
    4. int i;
    5. if(intf != NULL){
    6. /* 释放 ifcombo 数组中的索引数组 */
    7. for(=0; i < MIB_IF_TYPE_MAX; i++){
    8. if(intf->ifcombo[i].idx)
    9.                 free(intf->ifcombo[i].idx);
    10. }
    11. /* 释放适配器地址表 */
    12. if(intf->iftable)
    13.             free(intf->iftable);
    14. /* 释放句柄本身 */
    15.         free(intf);
    16. }
    17. return(NULL);
    18. }
    1. intf_t*intf_close(intf_t*intf);
    1. int
    2. intf_get_index(intf_t*intf,struct intf_entry *entry,
    3. int af,unsignedint index)
    4. {
    5.     IP_ADAPTER_ADDRESSES *a;
    6. if(_refresh_tables(intf)<0)
    7. return(-1);
    8.     a = _find_adapter_address_by_index(intf, af, index);
    9. if(== NULL)
    10. return(-1);
    11.     _adapter_address_to_entry(intf, a, entry);
    12. return(0);
    13. }
    1. int intf_get_index(intf_t*intf,struct intf_entry *entry,
    2. int af,unsignedint index);
    1. int
    2. intf_get_src(intf_t*intf,struct intf_entry *entry,struct addr *src)
    3. {
    4.     IP_ADAPTER_ADDRESSES *a;
    5.     IP_ADAPTER_UNICAST_ADDRESS *addr;
    6. if(src->addr_type != ADDR_TYPE_IP){
    7.         errno = EINVAL;
    8. return(-1);
    9. }
    10. if(_refresh_tables(intf)<0)
    11. return(-1);
    12. /* 遍历所有适配器的所有地址 */
    13. for(= intf->iftable; a != NULL; a = a->Next){
    14. for(addr = a->FirstUnicastAddress;
    15.              addr != NULL; addr = addr->Next){
    16. struct addr dnet_addr;
    17.             addr_ston(addr->Address.lpSockaddr,&dnet_addr);
    18. if(addr_cmp(&dnet_addr, src)==0){
    19.                 _adapter_address_to_entry(intf, a, entry);
    20. return(0);
    21. }
    22. }
    23. }
    24.     errno = ENXIO;
    25. return(-1);
    26. }
    1. int intf_get_src(intf_t*intf,struct intf_entry *entry,struct addr *src);
    1. if(adapter->IfType== IF_TYPE_TUNNEL ||
    2.     adapter->IfType== MIB_IF_TYPE_TUNNEL){
    3. /* 隧道接口可能无 MAC 地址,或有虚拟 MAC */
    4. if(adapter->PhysicalAddressLength!= ETH_ADDR_LEN){
    5.         entry->intf_link_addr.addr_type = ADDR_TYPE_NONE;
    6. return;
    7. }
    8. }
    1. if(adapter->IfType== IF_TYPE_SOFTWARE_LOOPBACK){
    2. /* 回环接口通常无 MAC 地址,这是正常的 */
    3.     entry->intf_link_addr.addr_type = ADDR_TYPE_NONE;
    4. return;
    5. }
    1. Adapter:{VPN-GUID}
    2. PhysicalAddressLength:0 MAC 地址
    3. PhysicalAddress:
    1. Adapter:{GUID}
    2. PhysicalAddressLength:6正确的 MAC 地址长度
    3. PhysicalAddress:001A2B3C4D5E
    1. void debug_mac_info(IP_ADAPTER_ADDRESSES *adapter){
    2.     printf("Adapter: %s\n", adapter->AdapterName);
    3.     printf("  PhysicalAddressLength: %lu\n", adapter->PhysicalAddressLength);
    4.     printf("  PhysicalAddress: ");
    5. for(ULONG i =0; i < adapter->PhysicalAddressLength; i++){
    6.         printf("%02X ", adapter->PhysicalAddress[i]);
    7. }
    8.     printf("\n");
    9. }
    1. void safe_copy_mac(struct intf_entry *entry,
    2.                    IP_ADAPTER_ADDRESSES *adapter)
    3. {
    4. /* 清空地址字段 */
    5.     memset(&entry->intf_link_addr,0,sizeof(entry->intf_link_addr));
    6. /* 检查是否有物理地址且长度正确 */
    7. if(adapter->PhysicalAddressLength== ETH_ADDR_LEN){
    8. /* 设置地址类型 */
    9.         entry->intf_link_addr.addr_type = ADDR_TYPE_ETH;
    10.         entry->intf_link_addr.addr_bits = ETH_ADDR_BITS;
    11. /* 精确复制 6 字节 MAC 地址 */
    12.         memcpy(&entry->intf_link_addr.addr_eth,
    13.                adapter->PhysicalAddress,
    14.                ETH_ADDR_LEN);
    15. }else{
    16. /* 无有效 MAC 地址 */
    17.         entry->intf_link_addr.addr_type = ADDR_TYPE_NONE;
    18.         entry->intf_link_addr.addr_bits =0;
    19. }
    20. }
    1. 检查长度: 必须验证 PhysicalAddressLength==ETH_ADDR_LEN
    2. 精确复制: 只复制 6 字节,不要使用 PhysicalAddressLength
    3. 设置类型: 正确设置 addr_type 和 addr_bits
    1. /* 获取硬件地址 */
    2. if(a->PhysicalAddressLength== ETH_ADDR_LEN){
    3.     entry->intf_link_addr.addr_type = ADDR_TYPE_ETH;
    4.     entry->intf_link_addr.addr_bits = ETH_ADDR_BITS;
    5.     memcpy(&entry->intf_link_addr.addr_eth, a->PhysicalAddress,
    6.            ETH_ADDR_LEN);/* 只复制 6 字节 */
    7. }
    1. struct addr {
    2. uint8_t        addr_type;/* 地址类型 */
    3. uint16_t       addr_bits;/* 地址位数 */
    4. union{
    5. uint8_t    data[16];/* 原始数据 */
    6. uint32_t   addr_ip;/* IPv4 地址 */
    7. uint8_t    addr_ip6[16];/* IPv6 地址 */
    8. eth_addr_t addr_eth;/* MAC 地址 */
    9. /* ... */
    10. } __packed;
    11. };
    1. /* 错误代码示例 */
    2. if(a->PhysicalAddressLength>0){
    3. /* 直接复制 8 字节,包含 2 字节填充 */
    4.     memcpy(mac, a->PhysicalAddress, a->PhysicalAddressLength);
    5. }
    1. /* libdnet 的统一接口实现 */
    2. /* Linux 特有实现 */
    3. #ifdef SIOCGIFCONF
    4. int intf_get(intf_t*intf,struct intf_entry *entry){
    5. int sockfd = socket(AF_INET, SOCK_DGRAM,0);
    6. struct ifreq ifr;
    7.     strcpy(ifr.ifr_name, entry->intf_name);
    8. /* 获取 IP 地址 */
    9.     ioctl(sockfd, SIOCGIFADDR,&ifr);
    10. /* ... */
    11. }
    12. #endif
    13. /* Windows 特有实现 */
    14. #ifdef _WIN32
    15. int intf_get(intf_t*intf,struct intf_entry *entry){
    16.     IP_ADAPTER_ADDRESSES *a;
    17.     _refresh_tables(intf);
    18.     a = _find_adapter_address(intf, entry->intf_name);
    19.     _adapter_address_to_entry(intf, a, entry);
    20. /* ... */
    21. }
    22. #endif
    1. staticint
    2. _refresh_tables(intf_t*intf)
    3. {
    4.     IP_ADAPTER_ADDRESSES *p;
    5.     DWORD ret;
    6.     ULONG len;
    7.     p = NULL;
    8. /* 分配初始缓冲区 (16KB) */
    9.     len =16384;
    10. do{
    11.         free(p);
    12.         p = malloc(len);
    13. if(== NULL)
    14. return(-1);
    15. /* 获取适配器地址信息 */
    16.         ret =GetAdaptersAddresses(AF_UNSPEC,
    17.                                    GAA_FLAG_INCLUDE_PREFIX |
    18.                                    GAA_FLAG_SKIP_ANYCAST |
    19.                                    GAA_FLAG_SKIP_MULTICAST,
    20.                                    NULL, p,&len);
    21. }while(ret == ERROR_BUFFER_OVERFLOW);
    22. if(ret != NO_ERROR){
    23.         free(p);
    24. return(-1);
    25. }
    26.     intf->iftable = p;
    27. /* 映射 Windows 接口索引到内部索引 */
    28. for(= intf->iftable; p != NULL; p = p->Next){
    29. int type = _if_type_canonicalize(p->IfType);
    30. if(type < MIB_IF_TYPE_MAX)
    31.             _ifcombo_add(&intf->ifcombo[type], p->IfIndex, p->Ipv6IfIndex);
    32. else
    33. return(-1);
    34. }
    35. return(0);
    36. }
    1. typedefstruct _IP_ADAPTER_ADDRESSES {
    2. union{
    3.         ULONGLONG Alignment;
    4. struct{
    5.             ULONG Length;
    6.             DWORD IfIndex;
    7. };
    8. };
    9.     PIP_ADAPTER_ADDRESSES Next;
    10. charAdapterName[MAX_ADAPTER_NAME_LENGTH];
    11.     PIP_ADAPTER_UNICAST_ADDRESS FirstUnicastAddress;
    12.     PIP_ADAPTER_PREFIX FirstPrefix;
    13.     ULONG IfType;
    14. intOperStatus;
    15.     ULONG Mtu;
    16.     BYTE PhysicalAddress[MAX_ADAPTER_ADDRESS_LENGTH];
    17.     ULONG PhysicalAddressLength;
    18. /* ... 更多字段 */
    19. } IP_ADAPTER_ADDRESSES,*PIP_ADAPTER_ADDRESSES;
    1. int sockfd = socket(AF_INET, SOCK_DGRAM,0);
    2. struct ifreq ifr;
    3. /* 获取接口标志 */
    4. strcpy(ifr.ifr_name,"eth0");
    5. ioctl(sockfd, SIOCGIFFLAGS,&ifr);
    6. /* 获取 MAC 地址 */
    7. ioctl(sockfd, SIOCGIFHWADDR,&ifr);
    8. unsignedchar*mac =(unsignedchar*)ifr.ifr_hwaddr.sa_data;
    9. /* 获取 MTU */
    10. ioctl(sockfd, SIOCGIFMTU,&ifr);
    1. /* Linux 下的 struct ifreq */
    2. struct ifreq {
    3. char ifr_name[IFNAMSIZ];/* 接口名称 */
    4. union{
    5. struct sockaddr ifr_addr;
    6. struct sockaddr ifr_dstaddr;
    7. struct sockaddr ifr_broadaddr;
    8. struct sockaddr ifr_netmask;
    9. struct sockaddr ifr_hwaddr;
    10. short ifr_flags;
    11. int ifr_ifindex;
    12. int ifr_metric;
    13. int ifr_mtu;
    14. /* ... */
    15. } ifr_ifru;
    16. };
    1. /* 使用 GetBestRoute API 实现路由查找 */
    2. MIB_IPFORWARDROW route;
    3. DWORD ifIndex;
    4. if(GetBestRoute(dst->addr_ip,0,&route)== NO_ERROR){
    5.     ifIndex = route.dwForwardIfIndex;
    6. /* 使用 intf_get_index 获取接口信息 */
    7.     intf_get_index(intf, entry, AF_INET, ifIndex);
    8. }
    1. int
    2. intf_get_dst(intf_t*intf,struct intf_entry *entry,struct addr *dst)
    3. {
    4.     errno = ENOSYS;
    5. SetLastError(ERROR_NOT_SUPPORTED);
    6. return(-1);
    7. }
    1. int intf_get_dst(intf_t*intf,struct intf_entry *entry,struct addr *dst);
    1. 调用 _refresh_tables 获取最新的适配器信息
    2. 遍历 IP_ADAPTER_ADDRESSES 链表
    3. 对每个适配器调用 _adapter_address_to_entry 转换格式
    4. 调用用户回调函数处理
    1. int
    2. intf_loop(intf_t*intf, intf_handler callback,void*arg)
    3. {
    4.     IP_ADAPTER_ADDRESSES *a;
    5. struct intf_entry *entry;
    6.     u_char ebuf[1024];
    7. int ret;
    8. /* 1. 刷新接口表 */
    9. if(_refresh_tables(intf)<0)
    10. return(-1);
    11.     entry =(struct intf_entry *)ebuf;
    12. /* 2. 遍历所有适配器 */
    13. for(= intf->iftable; a != NULL; a = a->Next){
    14.         entry->intf_len =sizeof(ebuf);
    15.         _adapter_address_to_entry(intf, a, entry);
    16. if((ret =(*callback)(entry, arg))!=0)
    17. break;
    18. }
    19. return(ret);
    20. }
    1. typedefint(*intf_handler)(struct intf_entry *entry,void*arg);
    2. int intf_loop(intf_t*intf, intf_handler callback,void*arg);
    1. intf_t*
    2. intf_open(void)
    3. {
    4. return(calloc(1,sizeof(intf_t)));
    5. }
    1. intf_t*intf_open(void);
    1. ===网卡信息收集器===
    2. [方式一]遍历所有网卡:
    3. ========================================
    4. 网卡名称: eth0
    5. 描述:RealtekPCIeGbEFamilyController
    6. 类型:以太网
    7. 状态: UP
    8. MTU:1500
    9. MAC 地址:00:1A:2B:3C:4D:5E
    10.  IP 地址:192.168.1.100/24
    11. 标志: UP MULTICAST BROADCAST
    12. ========================================
    13. ========================================
    14. 网卡名称: lo
    15. 描述:SoftwareLoopbackInterface1
    16. 类型:回环
    17. 状态: UP
    18. MTU:65536
    19.  IP 地址:127.0.0.1/8
    20. 别名 IP 地址:
    21. -::1/128
    22. 标志: UP LOOPBACK
    23. ========================================
    1. gcc -o network_collector network_collector.-I include -.-ldnet -lpthread
    2. ./network_collector
    1. cl network_collector./I include libdnet.lib iphlpapi.lib ws2_32.lib
    2. network_collector.exe
    1. /*
    2.  * 网卡信息收集器 - 跨平台实现
    3.  * 功能:获取系统所有网卡的 IP/MAC/MTU/状态信息
    4.  */
    5. #include<stdio.h>
    6. #include<stdlib.h>
    7. #include<string.h>
    8. #include"dnet.h"
    9. /* MAC 地址格式化输出 */
    10. void print_mac(consteth_addr_t*eth){
    11.     printf("%02X:%02X:%02X:%02X:%02X:%02X",
    12.            eth->data[0], eth->data[1], eth->data[2],
    13.            eth->data[3], eth->data[4], eth->data[5]);
    14. }
    15. /* IP 地址格式化输出 */
    16. void print_ip(conststruct addr *addr){
    17. char buf[128];
    18. if(addr_ntop(addr, buf,sizeof(buf))!= NULL){
    19.         printf("%s", buf);
    20. }else{
    21.         printf("N/A");
    22. }
    23. }
    24. /* 打印单个网卡信息 */
    25. void print_interface_info(struct intf_entry *entry){
    26.     printf("\n========================================\n");
    27.     printf("网卡名称: %s\n", entry->intf_name);
    28.     printf("描述: %s\n", entry->driver_name);
    29. /* 网卡类型 */
    30.     printf("类型: ");
    31. switch(entry->intf_type){
    32. case INTF_TYPE_ETH:      printf("以太网");break;
    33. case INTF_TYPE_LOOPBACK: printf("回环");break;
    34. case INTF_TYPE_PPP:      printf("PPP");break;
    35. case INTF_TYPE_TOKENRING: printf("令牌环");break;
    36. case INTF_TYPE_TUN:      printf("隧道");break;
    37. default:                  printf("其他");
    38. }
    39.     printf("\n");
    40. /* 网卡状态 */
    41.     printf("状态: %s\n",(entry->intf_flags & INTF_FLAG_UP)?"UP":"DOWN");
    42. /* MTU */
    43.     printf("MTU: %u\n", entry->intf_mtu);
    44. /* MAC 地址 */
    45. if(entry->intf_link_addr.addr_type == ADDR_TYPE_ETH){
    46.         printf("MAC 地址: ");
    47.         print_mac(&entry->intf_link_addr.addr_eth);
    48.         printf("\n");
    49. }
    50. /* 主 IP 地址 */
    51. if(entry->intf_addr.addr_type != ADDR_TYPE_NONE){
    52.         printf("主 IP 地址: ");
    53.         print_ip(&entry->intf_addr);
    54.         printf("/%u\n", entry->intf_addr.addr_bits);
    55. }
    56. /* 别名 IP 地址 */
    57. if(entry->intf_alias_num >0){
    58.         printf("别名 IP 地址:\n");
    59. for(int i =0; i < entry->intf_alias_num; i++){
    60.             printf("  - ");
    61.             print_ip(&entry->intf_alias_addrs[i]);
    62.             printf("/%u\n", entry->intf_alias_addrs[i].addr_bits);
    63. }
    64. }
    65. /* 标志位 */
    66.     printf("标志: ");
    67. if(entry->intf_flags & INTF_FLAG_UP)        printf("UP ");
    68. if(entry->intf_flags & INTF_FLAG_LOOPBACK)  printf("LOOPBACK ");
    69. if(entry->intf_flags & INTF_FLAG_MULTICAST) printf("MULTICAST ");
    70. if(entry->intf_flags & INTF_FLAG_BROADCAST) printf("BROADCAST ");
    71. if(entry->intf_flags & INTF_FLAG_POINTOPOINT) printf("POINTOPOINT ");
    72.     printf("\n");
    73.     printf("========================================\n");
    74. }
    75. /* 回调函数:用于 intf_loop 遍历所有网卡 */
    76. int callback(struct intf_entry *entry,void*arg){
    77.     print_interface_info(entry);
    78. return0;/* 返回 0 继续遍历,非 0 停止 */
    79. }
    80. int main(int argc,char*argv[]){
    81. intf_t*intf;
    82. int ret =0;
    83.     printf("=== 网卡信息收集器 ===\n");
    84. /* 打开接口句柄 */
    85. if((intf = intf_open())== NULL){
    86.         fprintf(stderr,"无法打开接口: %s\n", strerror(errno));
    87. return1;
    88. }
    89. /* 方式一:遍历所有网卡 */
    90.     printf("\n[方式一] 遍历所有网卡:\n");
    91. if(intf_loop(intf, callback, NULL)<0){
    92.         fprintf(stderr,"遍历网卡失败: %s\n", strerror(errno));
    93.         ret =1;
    94. goto done;
    95. }
    96. /* 方式二:获取指定网卡信息(以第一个网卡为例) */
    97.     printf("\n[方式二] 获取指定网卡信息:\n");
    98. struct intf_entry entry;
    99.     u_char buf[1024];
    100.     entry.intf_len =sizeof(buf);
    101.     strncpy(entry.intf_name,"eth0",sizeof(entry.intf_name));
    102. if(intf_get(intf,&entry)<0){
    103.         printf("获取 eth0 失败,可能不存在或名称不同\n");
    104. }else{
    105.         print_interface_info(&entry);
    106. }
    107. done:
    108. /* 关闭接口句柄 */
    109.     intf_close(intf);
    110. return ret;
    111. }
  • 公众号:安全狗的自我修养

  • vx:2207344074

  • http://gitee.com/haidragon

  • http://github.com/haidragon

  • bilibili:haidragonx

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 跨平台底层网络库libdnet源码分析系列(二)

猜你喜欢

  • 暂无文章