You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言ARP扫描程序开发疑问及无输出代码调试求助

问题解答与代码修复

一、ARP相关问题解答

  1. 使用ARP请求实现此需求是否合理?
    完全合理。ARP(地址解析协议)的核心作用就是实现IP地址与MAC地址的映射,通过向子网内发送ARP广播请求,活跃的设备会返回ARP响应包,其中包含自身的MAC地址。这种方式不需要依赖任何命令行工具,纯底层代码实现,完全符合需求。

  2. 是否能够获取非当前所在子网的设备MAC地址?
    不能。ARP是链路层协议,仅能在同一广播域(即同一子网)内工作。当目标IP不在当前子网时,系统会将ARP请求发送给本地网关,而非直接广播到目标子网,网关只会返回自身的MAC地址,不会转发ARP请求到其他子网,因此无法通过ARP获取跨子网设备的MAC地址。

二、代码问题分析与修复

你的代码无输出主要由多个关键问题导致,下面是修复后的完整代码及问题说明:

核心问题点

  • 无效MAC硬编码:0xQS是非法十六进制值,会导致编译或运行错误;硬编码网卡信息也容易与实际环境不符,建议自动获取。
  • 重复发送请求:原代码连续调用两次sendto,导致重复发包且第一次长度参数错误。
  • 接收逻辑错误:错误将响应的sender IP与自身IP对比,应该判断响应IP是否为请求的目标IP。
  • 无限接收无超时:原接收函数为死循环,会一直阻塞程序,需设置超时退出逻辑。
  • 缓冲区过小:过小的缓冲区可能导致数据包截断,需适当增大。

修复后的完整代码

#include <linux/if_ether.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netpacket/packet.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/ether.h>
#include <errno.h>

#define BUFFER_SIZE 1024
#define TIMEOUT_SEC 5  // 接收超时时间

struct arp_header {
    uint16_t hardware_type;
    uint16_t protocol_type;
    uint8_t hardware_len;
    uint8_t protocol_len;
    uint16_t opcode;
    uint8_t sender_mac[6];
    uint8_t sender_ip[4];
    uint8_t target_mac[6];
    uint8_t target_ip[4];
};

// 获取指定网卡的MAC和IP地址
int get_interface_info(const char* ifname, uint8_t* mac, uint8_t* ip) {
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("socket failed");
        return -1;
    }

    struct ifreq ifr;
    strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1);

    // 获取MAC地址
    if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) {
        perror("SIOCGIFHWADDR failed");
        close(sock);
        return -1;
    }
    memcpy(mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN);

    // 获取IP地址
    if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) {
        perror("SIOCGIFADDR failed");
        close(sock);
        return -1;
    }
    struct sockaddr_in* addr = (struct sockaddr_in*)&ifr.ifr_addr;
    memcpy(ip, &addr->sin_addr.s_addr, 4);
    // 转换为主机字节序
    for (int i = 0; i < 4; i++) {
        ip[i] = ((uint8_t*)&addr->sin_addr.s_addr)[i];
    }

    close(sock);
    return 0;
}

void create_arp_request(uint8_t *sender_mac, uint8_t *sender_ip, uint8_t *target_ip, uint8_t *packet) {
    // 填充以太网头部
    struct ethhdr *eth = (struct ethhdr *) packet;
    uint8_t broadcast_mac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
    memcpy(eth->h_dest, broadcast_mac, ETH_ALEN);
    memcpy(eth->h_source, sender_mac, ETH_ALEN);
    eth->h_proto = htons(ETH_P_ARP);

    // 填充ARP头部
    struct arp_header arp;
    memset(&arp, 0, sizeof(struct arp_header));
    arp.hardware_type = htons(ARPHRD_ETHER);
    arp.protocol_type = htons(ETH_P_IP);
    arp.hardware_len = ETH_ALEN;
    arp.protocol_len = sizeof(in_addr_t);
    arp.opcode = htons(ARPOP_REQUEST);
    memcpy(arp.sender_mac, sender_mac, ETH_ALEN);
    memcpy(arp.sender_ip, sender_ip, sizeof(in_addr_t));
    memset(arp.target_mac, 0, ETH_ALEN);
    memcpy(arp.target_ip, target_ip, sizeof(in_addr_t));

    memcpy(packet + sizeof(struct ethhdr), &arp, sizeof(struct arp_header));
}

void receive_arp_responses(int socket_fd) {
    struct timeval timeout;
    timeout.tv_sec = TIMEOUT_SEC;
    timeout.tv_usec = 0;
    // 设置socket接收超时
    if (setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
        perror("setsockopt SO_RCVTIMEO failed");
        return;
    }

    while (1) {
        uint8_t buffer[BUFFER_SIZE];
        struct sockaddr_ll recvaddr;
        socklen_t recvaddrlen = sizeof(recvaddr);

        ssize_t packet_len = recvfrom(socket_fd, buffer, sizeof(buffer), 0, (struct sockaddr *) &recvaddr, &recvaddrlen);
        if (packet_len == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                printf("接收超时,结束监听\n");
                break;
            } else {
                perror("recvfrom failed");
                break;
            }
        }

        struct ethhdr *eth = (struct ethhdr *) buffer;
        // 只处理ARP包
        if (ntohs(eth->h_proto) != ETH_P_ARP) {
            continue;
        }

        struct arp_header *arp = (struct arp_header *) (buffer + sizeof(struct ethhdr));
        // 只处理ARP响应
        if (ntohs(arp->opcode) != ARPOP_REPLY) {
            continue;
        }

        // 按照要求格式输出:ip = mac
        printf("%d.%d.%d.%d = %02x:%02x:%02x:%02x:%02x:%02x\n",
               arp->sender_ip[0], arp->sender_ip[1], arp->sender_ip[2], arp->sender_ip[3],
               arp->sender_mac[0], arp->sender_mac[1], arp->sender_mac[2],
               arp->sender_mac[3], arp->sender_mac[4], arp->sender_mac[5]);
    }
}

int main() {
    const char* ifname = "eth0"; // 替换为你的实际网卡名称,比如ens33、wlan0等
    uint8_t sender_mac[ETH_ALEN];
    uint8_t sender_ip[4];
    uint8_t packet[sizeof(struct ethhdr) + sizeof(struct arp_header)];

    // 获取网卡信息
    if (get_interface_info(ifname, sender_mac, sender_ip) != 0) {
        fprintf(stderr, "获取网卡信息失败\n");
        return 1;
    }

    // 创建原始socket
    int socket_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (socket_fd == -1) {
        perror("socket failed");
        return 1;
    }

    // 绑定到指定网卡
    struct sockaddr_ll sa;
    memset(&sa, 0, sizeof(struct sockaddr_ll));
    sa.sll_family = AF_PACKET;
    sa.sll_protocol = htons(ETH_P_ARP);
    sa.sll_ifindex = if_nametoindex(ifname);
    if (sa.sll_ifindex == 0) {
        perror("if_nametoindex failed");
        close(socket_fd);
        return 1;
    }

    if (bind(socket_fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_ll)) == -1) {
        perror("bind failed");
        close(socket_fd);
        return 1;
    }

    // 发送ARP请求:覆盖192.168.0.1-192.168.255.255网段
    for (int subnet = 0; subnet <= 255; subnet++) {
        for (int host = 1; host <= 255; host++) {
            uint8_t target_ip[4] = {192, 168, subnet, host};
            // 跳过自身IP
            if (memcmp(target_ip, sender_ip, 4) == 0) {
                continue;
            }
            create_arp_request(sender_mac, sender_ip, target_ip, packet);
            ssize_t sent_len = sendto(socket_fd, packet, sizeof(packet), 0, (struct sockaddr *)&sa, sizeof(sa));
            if (sent_len == -1) {
                perror("sendto failed");
                close(socket_fd);
                return 1;
            }
            usleep(10000); // 避免发包过快导致丢包
        }
    }

    printf("开始接收ARP响应...\n");
    receive_arp_responses(socket_fd);

    close(socket_fd);
    return 0;
}

使用说明

  1. 替换代码中的ifname为你的实际网卡名称(可通过ip addr命令查看)。
  2. 必须以root权限运行程序,因为原始socket需要管理员权限。
  3. 代码自动获取网卡的MAC和IP,无需硬编码,适配不同环境。
  4. 发送请求时加入短延迟,避免发包过快导致网络丢包。
  5. 接收时设置超时,超时后自动停止监听,避免程序无限阻塞。

内容的提问来源于stack exchange,提问作者Igor Khanaev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 23:32:32