使用c socket编程的arp请求和回复

Posted

技术标签:

【中文标题】使用c socket编程的arp请求和回复【英文标题】:arp request and reply using c socket programming 【发布时间】:2013-05-18 14:00:12 【问题描述】:

我正在尝试在 Linux (Ubuntu) 中使用 c 编程接收和发送 arp 数据包 我的程序运行良好(即运行没有任何错误),但我无法使用 Wireshark 跟踪数据包。

源代码

#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>

#include <asm/types.h>

#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>

#define BUF_SIZE 42
#define DEVICE "eth0"
#define ETH_P_NULL 0x0
#define ETH_MAC_LEN ETH_ALEN
#define ETH_ARP 0x0806

int s = 0; /*Socketdescriptor*/
void* buffer = NULL;
long total_packets = 0;
long answered_packets = 0;

void sigint(int signum);

struct __attribute__((packed)) arp_header

    unsigned short arp_hd;
    unsigned short arp_pr;
    unsigned char arp_hdl;
    unsigned char arp_prl;
    unsigned short arp_op;
    unsigned char arp_sha[6];
    unsigned char arp_spa[4];
    unsigned char arp_dha[6];
    unsigned char arp_dpa[4];
;
int main(void) 
    buffer = (void*)malloc(BUF_SIZE); /*Buffer for Ethernet Frame*/
    unsigned char* etherhead = buffer;  /*Pointer to Ethenet Header*/
    struct ethhdr *eh = (struct ethhdr *)etherhead; /*Another pointer to
                                                    ethernet header*/
    unsigned char* arphead = buffer + 14;
    struct arp_header *ah;
    unsigned char src_mac[6];    /*our MAC address*/

    struct ifreq ifr;
    struct sockaddr_ll socket_address;
    int ifindex = 0;     /*Ethernet Interface index*/
    int i;
    int length;  /*length of received packet*/
    int sent;

    printf("Server started, entering initialiation phase...\n");

    /*open socket*/
    s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (s == -1) 
        perror("socket():");
        exit(1);
    
    printf("Successfully opened socket: %i\n", s);

    /*retrieve ethernet interface index*/
    strncpy(ifr.ifr_name, DEVICE, IFNAMSIZ);
    if (ioctl(s, SIOCGIFINDEX, &ifr) == -1) 
        perror("SIOCGIFINDEX");
        exit(1);
    
    ifindex = ifr.ifr_ifindex;
    printf("Successfully got interface index: %i\n", ifindex);

    /*retrieve corresponding MAC*/
    if (ioctl(s, SIOCGIFHWADDR, &ifr) == -1) 
        perror("SIOCGIFINDEX");
        exit(1);
    
    for (i = 0; i < 6; i++) 
        src_mac[i] = ifr.ifr_hwaddr.sa_data[i];
    
    printf("Successfully got our MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
        src_mac[0],src_mac[1],src_mac[2],src_mac[3],src_mac[4],src_mac[5]);

    /*prepare sockaddr_ll*/
    socket_address.sll_family = PF_PACKET;
    socket_address.sll_protocol = htons(ETH_P_IP);
    socket_address.sll_ifindex = ifindex;
    socket_address.sll_hatype = ARPHRD_ETHER;
    socket_address.sll_pkttype = PACKET_OTHERHOST;
    socket_address.sll_halen = 0;
    socket_address.sll_addr[6] = 0x00;
    socket_address.sll_addr[7] = 0x00;
    /*establish signal handler*/
    signal(SIGINT, sigint);
    printf("Successfully established signal handler for SIGINT\n");
    printf("We are in production state, waiting for incoming packets....\n");

    while (1) 
        /*Wait for incoming packet...*/
        length = recvfrom(s, buffer, BUF_SIZE, 0, NULL, NULL);
        if (length == -1)
        
            perror("recvfrom():");
            exit(1);
        
        if(htons(eh->h_proto) == 0x806)
        

            unsigned char buf_arp_dha[6];
            unsigned char buf_arp_dpa[4];

            ah = (struct arp_header *)arphead;
            if(htons(ah->arp_op) != 0x0001)
                continue;
            printf("buffer is---------------- %s \n",(char*)ah);
            printf("H/D TYPE : %x PROTO TYPE : %x \n",ah->arp_hd,ah->arp_pr);
            printf("H/D leng : %x PROTO leng : %x \n",ah->arp_hdl,ah->arp_prl);
            printf("OPERATION : %x \n", ah->arp_op);
            printf("SENDER MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
                ah->arp_sha[0],
                ah->arp_sha[1],
                ah->arp_sha[2],
                ah->arp_sha[3],
                ah->arp_sha[4],
                ah->arp_sha[5]
            );
            printf("SENDER IP address: %02d:%02d:%02d:%02d\n",
                ah->arp_spa[0],
                ah->arp_spa[1],
                ah->arp_spa[2],
                ah->arp_spa[3]
            );
            if(ah->arp_spa[0]==10&&ah->arp_spa[1]==00&&ah->arp_spa[2]==00&&ah->arp_spa[3]==01)
            
                printf("Sender ip is .............bam bam..........................................\n");
                system("sudo arp -s 10.0.0.1  00:1e:73:91:04:0d");
            
            printf("TARGET MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
                ah->arp_dha[0],
                ah->arp_dha[1],
                ah->arp_dha[2],
                ah->arp_dha[3],
                ah->arp_dha[4],
                ah->arp_dha[5]
            );
            printf("TARGET IP address: %02d:%02d:%02d:%02d\n",
                ah->arp_dpa[0],
                ah->arp_dpa[1],
                ah->arp_dpa[2],
                ah->arp_dpa[3]
            );

            printf("+++++++++++++++++++++++++++++++++++++++\n" );
            printf("ETHER DST MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
                eh->h_dest[0],
                eh->h_dest[1],
                eh->h_dest[2],
                eh->h_dest[3],
                eh->h_dest[4],
                eh->h_dest[5]
            );
            printf("ETHER SRC MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
                eh->h_source[0],
                eh->h_source[1],
                eh->h_source[2],
                eh->h_source[3],
                eh->h_source[4],
                eh->h_source[5]
            );
            memcpy( (void*)etherhead, (const void*)(etherhead+ETH_MAC_LEN),
                ETH_MAC_LEN);
            memcpy( (void*)(etherhead+ETH_MAC_LEN), (const void*)src_mac,
                ETH_MAC_LEN);
            eh->h_proto = ETH_ARP;
            printf("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& \n");
            printf("ETHER DST MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
                eh->h_dest[0],
                eh->h_dest[1],
                eh->h_dest[2],
                eh->h_dest[3],
                eh->h_dest[4],
                eh->h_dest[5]
            );
            printf("ETHER SRC MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
                eh->h_source[0],
                eh->h_source[1],
                eh->h_source[2],
                eh->h_source[3],
                eh->h_source[4],
                eh->h_source[5]
            );
            ah->arp_hd = ntohs(ah->arp_hd);
            ah->arp_pr = ntohs(ah->arp_pr);

            ah->arp_op = 0x0002;

            buf_arp_dpa[0] = ah->arp_dpa[0];
            buf_arp_dpa[1] = ah->arp_dpa[1];
            buf_arp_dpa[2] = ah->arp_dpa[2];
            buf_arp_dpa[3] = ah->arp_dpa[3];

            ah->arp_dha[0] = ah->arp_sha[0];
            ah->arp_dha[1] = ah->arp_sha[1];
            ah->arp_dha[2] = ah->arp_sha[2];
            ah->arp_dha[3] = ah->arp_sha[3];
            ah->arp_dha[4] = ah->arp_sha[4];
            ah->arp_dha[5] = ah->arp_sha[5];

            ah->arp_dpa[0] = ah->arp_spa[0];
            ah->arp_dpa[1] = ah->arp_spa[1];
            ah->arp_dpa[2] = ah->arp_spa[2];
            ah->arp_dpa[3] = ah->arp_spa[3];

            ah->arp_spa[0] = buf_arp_dpa[0];
            ah->arp_spa[1] = buf_arp_dpa[1];
            ah->arp_spa[2] = buf_arp_dpa[2];
            ah->arp_spa[3] = buf_arp_dpa[3];
            //change the sender mac address
            ah->arp_sha[0] = 0x00;
            ah->arp_sha[1] = 0x1e;
            ah->arp_sha[2] = 0x73;
            ah->arp_sha[3] = 0x78;
            ah->arp_sha[4] = 0x9a;
            ah->arp_sha[5] = 0x0d;

            socket_address.sll_addr[0] = eh->h_dest[0];
            socket_address.sll_addr[1] = eh->h_dest[1];
            socket_address.sll_addr[2] = eh->h_dest[2];
            socket_address.sll_addr[3] = eh->h_dest[3];
            socket_address.sll_addr[4] = eh->h_dest[4];
            socket_address.sll_addr[5] = eh->h_dest[5];
            printf("=======================================\n" );
            printf("SENDER MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
                ah->arp_sha[0],
                ah->arp_sha[1],
                ah->arp_sha[2],
                ah->arp_sha[3],
                ah->arp_sha[4],
                ah->arp_sha[5]
            );
            printf("SENDER IP address: %02d:%02d:%02d:%02d\n",
                ah->arp_spa[0],
                ah->arp_spa[1],
                ah->arp_spa[2],
                ah->arp_spa[3]
            );
            if((ah->arp_spa[0]==10 && ah->arp_spa[1]==0 && ah->arp_spa[2]==0 && ah->arp_spa[3]==1))
                printf("------------------------------------------10.0.0.1-----------------------------------------\n");
            printf("TARGET MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
                ah->arp_dha[0],
                ah->arp_dha[1],
                ah->arp_dha[2],
                ah->arp_dha[3],
                ah->arp_dha[4],
                ah->arp_dha[5]
            );
            printf("TARGET IP address: %02d:%02d:%02d:%02d\n",
                ah->arp_dpa[0],
                ah->arp_dpa[1],
                ah->arp_dpa[2],
                ah->arp_dpa[3]
            );
            printf("H/D TYPE : %x PROTO TYPE : %x \n",ah->arp_hd,ah->arp_pr);
            printf("H/D leng : %x PROTO leng : %x \n",ah->arp_hdl,ah->arp_prl);
            printf("OPERATION : %x \n", ah->arp_op);

            sent = sendto(s, buffer, BUF_SIZE, 0, (struct
                sockaddr*)&socket_address, sizeof(socket_address));
            if (sent == -1)
            
                perror("sendto():");
                exit(1);
            

            answered_packets++;

        

        total_packets++;

    


void sigint(int signum) 
    /*Clean up.......*/

    struct ifreq ifr;

    if (s == -1)
        return;

    strncpy(ifr.ifr_name, DEVICE, IFNAMSIZ);
    ioctl(s, SIOCGIFFLAGS, &ifr);
    ifr.ifr_flags &= ~IFF_PROMISC;
    ioctl(s, SIOCSIFFLAGS, &ifr);
    close(s);

    free(buffer);

    printf("Server terminating....\n");

    printf("Totally received: %ld packets\n", total_packets);
    printf("Answered %ld packets\n", answered_packets);
    exit(0);

【问题讨论】:

最佳形式 ASK @ Wireshark ,它是否显示您 PC 上其他应用程序的数据包 是的,我可以看到其他应用程序的数据包。 问题肯定出在代码上,但编译器无法发现。 不,请阅读此问题Capturing UDP packets sent from my own app,尝试按您在代码中使用的端口进行过滤 说实话,我使用直通电缆将我的电脑连接到另一台电脑。但我可以从第二台电脑上看到其他应用程序的 arp 数据包。 【参考方案1】:

我拿了user6343961的代码,做了一些清理和拼接,实现了对自动获取接口IP地址的支持。 参数也来自 CLI 而不是硬编码。 bind() 也用于从我们想要的接口只获取 ARP。 玩得开心。这段代码对我有用。

#include <sys/socket.h>
#include <sys/ioctl.h>
#include <asm/types.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <arpa/inet.h>  //htons etc

#define PROTO_ARP 0x0806
#define ETH2_HEADER_LEN 14
#define HW_TYPE 1
#define MAC_LENGTH 6
#define IPV4_LENGTH 4
#define ARP_REQUEST 0x01
#define ARP_REPLY 0x02
#define BUF_SIZE 60

#define debug(x...) printf(x);printf("\n");
#define info(x...) printf(x);printf("\n");
#define warn(x...) printf(x);printf("\n");
#define err(x...) printf(x);printf("\n");

struct arp_header 
    unsigned short hardware_type;
    unsigned short protocol_type;
    unsigned char hardware_len;
    unsigned char protocol_len;
    unsigned short opcode;
    unsigned char sender_mac[MAC_LENGTH];
    unsigned char sender_ip[IPV4_LENGTH];
    unsigned char target_mac[MAC_LENGTH];
    unsigned char target_ip[IPV4_LENGTH];
;

/*
 * Converts struct sockaddr with an IPv4 address to network byte order uin32_t.
 * Returns 0 on success.
 */
int int_ip4(struct sockaddr *addr, uint32_t *ip)

    if (addr->sa_family == AF_INET) 
        struct sockaddr_in *i = (struct sockaddr_in *) addr;
        *ip = i->sin_addr.s_addr;
        return 0;
     else 
        err("Not AF_INET");
        return 1;
    


/*
 * Formats sockaddr containing IPv4 address as human readable string.
 * Returns 0 on success.
 */
int format_ip4(struct sockaddr *addr, char *out)

    if (addr->sa_family == AF_INET) 
        struct sockaddr_in *i = (struct sockaddr_in *) addr;
        const char *ip = inet_ntoa(i->sin_addr);
        if (!ip) 
            return -2;
         else 
            strcpy(out, ip);
            return 0;
        
     else 
        return -1;
    


/*
 * Writes interface IPv4 address as network byte order to ip.
 * Returns 0 on success.
 */
int get_if_ip4(int fd, const char *ifname, uint32_t *ip) 
    int err = -1;
    struct ifreq ifr;
    memset(&ifr, 0, sizeof(struct ifreq));
    if (strlen(ifname) > (IFNAMSIZ - 1)) 
        err("Too long interface name");
        goto out;
    

    strcpy(ifr.ifr_name, ifname);
    if (ioctl(fd, SIOCGIFADDR, &ifr) == -1) 
        perror("SIOCGIFADDR");
        goto out;
    

    if (int_ip4(&ifr.ifr_addr, ip)) 
        goto out;
    
    err = 0;
out:
    return err;


/*
 * Sends an ARP who-has request to dst_ip
 * on interface ifindex, using source mac src_mac and source ip src_ip.
 */
int send_arp(int fd, int ifindex, const unsigned char *src_mac, uint32_t src_ip, uint32_t dst_ip)

    int err = -1;
    unsigned char buffer[BUF_SIZE];
    memset(buffer, 0, sizeof(buffer));

    struct sockaddr_ll socket_address;
    socket_address.sll_family = AF_PACKET;
    socket_address.sll_protocol = htons(ETH_P_ARP);
    socket_address.sll_ifindex = ifindex;
    socket_address.sll_hatype = htons(ARPHRD_ETHER);
    socket_address.sll_pkttype = (PACKET_BROADCAST);
    socket_address.sll_halen = MAC_LENGTH;
    socket_address.sll_addr[6] = 0x00;
    socket_address.sll_addr[7] = 0x00;

    struct ethhdr *send_req = (struct ethhdr *) buffer;
    struct arp_header *arp_req = (struct arp_header *) (buffer + ETH2_HEADER_LEN);
    int index;
    ssize_t ret, length = 0;

    //Broadcast
    memset(send_req->h_dest, 0xff, MAC_LENGTH);

    //Target MAC zero
    memset(arp_req->target_mac, 0x00, MAC_LENGTH);

    //Set source mac to our MAC address
    memcpy(send_req->h_source, src_mac, MAC_LENGTH);
    memcpy(arp_req->sender_mac, src_mac, MAC_LENGTH);
    memcpy(socket_address.sll_addr, src_mac, MAC_LENGTH);

    /* Setting protocol of the packet */
    send_req->h_proto = htons(ETH_P_ARP);

    /* Creating ARP request */
    arp_req->hardware_type = htons(HW_TYPE);
    arp_req->protocol_type = htons(ETH_P_IP);
    arp_req->hardware_len = MAC_LENGTH;
    arp_req->protocol_len = IPV4_LENGTH;
    arp_req->opcode = htons(ARP_REQUEST);

    debug("Copy IP address to arp_req");
    memcpy(arp_req->sender_ip, &src_ip, sizeof(uint32_t));
    memcpy(arp_req->target_ip, &dst_ip, sizeof(uint32_t));

    ret = sendto(fd, buffer, 42, 0, (struct sockaddr *) &socket_address, sizeof(socket_address));
    if (ret == -1) 
        perror("sendto():");
        goto out;
    
    err = 0;
out:
    return err;


/*
 * Gets interface information by name:
 * IPv4
 * MAC
 * ifindex
 */
int get_if_info(const char *ifname, uint32_t *ip, char *mac, int *ifindex)

    debug("get_if_info for %s", ifname);
    int err = -1;
    struct ifreq ifr;
    int sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
    if (sd <= 0) 
        perror("socket()");
        goto out;
    
    if (strlen(ifname) > (IFNAMSIZ - 1)) 
        printf("Too long interface name, MAX=%i\n", IFNAMSIZ - 1);
        goto out;
    

    strcpy(ifr.ifr_name, ifname);

    //Get interface index using name
    if (ioctl(sd, SIOCGIFINDEX, &ifr) == -1) 
        perror("SIOCGIFINDEX");
        goto out;
    
    *ifindex = ifr.ifr_ifindex;
    printf("interface index is %d\n", *ifindex);

    //Get MAC address of the interface
    if (ioctl(sd, SIOCGIFHWADDR, &ifr) == -1) 
        perror("SIOCGIFINDEX");
        goto out;
    

    //Copy mac address to output
    memcpy(mac, ifr.ifr_hwaddr.sa_data, MAC_LENGTH);

    if (get_if_ip4(sd, ifname, ip)) 
        goto out;
    
    debug("get_if_info OK");

    err = 0;
out:
    if (sd > 0) 
        debug("Clean up temporary socket");
        close(sd);
    
    return err;


/*
 * Creates a raw socket that listens for ARP traffic on specific ifindex.
 * Writes out the socket's FD.
 * Return 0 on success.
 */
int bind_arp(int ifindex, int *fd)

    debug("bind_arp: ifindex=%i", ifindex);
    int ret = -1;

    // Submit request for a raw socket descriptor.
    *fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
    if (*fd < 1) 
        perror("socket()");
        goto out;
    

    debug("Binding to ifindex %i", ifindex);
    struct sockaddr_ll sll;
    memset(&sll, 0, sizeof(struct sockaddr_ll));
    sll.sll_family = AF_PACKET;
    sll.sll_ifindex = ifindex;
    if (bind(*fd, (struct sockaddr*) &sll, sizeof(struct sockaddr_ll)) < 0) 
        perror("bind");
        goto out;
    

    ret = 0;
out:
    if (ret && *fd > 0) 
        debug("Cleanup socket");
        close(*fd);
    
    return ret;


/*
 * Reads a single ARP reply from fd.
 * Return 0 on success.
 */
int read_arp(int fd)

    debug("read_arp");
    int ret = -1;
    unsigned char buffer[BUF_SIZE];
    ssize_t length = recvfrom(fd, buffer, BUF_SIZE, 0, NULL, NULL);
    int index;
    if (length == -1) 
        perror("recvfrom()");
        goto out;
    
    struct ethhdr *rcv_resp = (struct ethhdr *) buffer;
    struct arp_header *arp_resp = (struct arp_header *) (buffer + ETH2_HEADER_LEN);
    if (ntohs(rcv_resp->h_proto) != PROTO_ARP) 
        debug("Not an ARP packet");
        goto out;
    
    if (ntohs(arp_resp->opcode) != ARP_REPLY) 
        debug("Not an ARP reply");
        goto out;
    
    debug("received ARP len=%ld", length);
    struct in_addr sender_a;
    memset(&sender_a, 0, sizeof(struct in_addr));
    memcpy(&sender_a.s_addr, arp_resp->sender_ip, sizeof(uint32_t));
    debug("Sender IP: %s", inet_ntoa(sender_a));

    debug("Sender MAC: %02X:%02X:%02X:%02X:%02X:%02X",
          arp_resp->sender_mac[0],
          arp_resp->sender_mac[1],
          arp_resp->sender_mac[2],
          arp_resp->sender_mac[3],
          arp_resp->sender_mac[4],
          arp_resp->sender_mac[5]);

    ret = 0;

out:
    return ret;


/*
 *
 * Sample code that sends an ARP who-has request on
 * interface <ifname> to IPv4 address <ip>.
 * Returns 0 on success.
 */
int test_arping(const char *ifname, const char *ip) 
    int ret = -1;
    uint32_t dst = inet_addr(ip);
    if (dst == 0 || dst == 0xffffffff) 
        printf("Invalid source IP\n");
        return 1;
    

    int src;
    int ifindex;
    char mac[MAC_LENGTH];
    if (get_if_info(ifname, &src, mac, &ifindex)) 
        err("get_if_info failed, interface %s not found or no IP set?", ifname);
        goto out;
    
    int arp_fd;
    if (bind_arp(ifindex, &arp_fd)) 
        err("Failed to bind_arp()");
        goto out;
    

    if (send_arp(arp_fd, ifindex, mac, src, dst)) 
        err("Failed to send_arp");
        goto out;
    

    while(1) 
        int r = read_arp(arp_fd);
        if (r == 0) 
            info("Got reply, break out");
            break;
        
    

    ret = 0;
out:
    if (arp_fd) 
        close(arp_fd);
        arp_fd = 0;
    
    return ret;


int main(int argc, const char **argv) 
    int ret = -1;
    if (argc != 3) 
        printf("Usage: %s <INTERFACE> <DEST_IP>\n", argv[0]);
        return 1;
    
    const char *ifname = argv[1];
    const char *ip = argv[2];
    return test_arping(ifname, ip);

【讨论】:

+1 不错的代码。但请使用 if-else 语句而不是 if+goto。那很脏,就像不先洗手就吃饭一样。同时删除所有未使用的变量。 我不得不不同意 goto,IHMO 这是进行错误处理和内存释放的最干净的方法。我已经看到了替代解决方案,但没有什么能接近。这大约是 goto 应该使用的唯一方法,即跳到最后,在返回值之前执行一些释放。这是一个关于此的示例讨论:***.com/questions/570317/… 您没有看到您确实写了if-else 块吗?唯一的区别是您使用 goto 关键字代替了 else 关键字。 其实没有,有区别:比如在get_if_info中,需要在处理错误后释放一些资源。如果...否则不这样做。您需要在每个 else 块中处理资源释放。 无论如何,这在其他主题中有更详细的讨论。我认为这是一个见仁见智的问题,这对我来说是最干净的方式。【参考方案2】:

一些事情可以让你的数据包在电线/空中。

arp 回复的正确 .sll_protocol 是 ETH_P_ARP,来自&lt;linux/if_ether.h&gt;

设置 ah->arp_op 时出现字节顺序错误。它是 2 个八位字节的网络字节序字段,所以使用 htons()。

一般来说,代码对网络和主机字节序有些混淆。它目前发出的回复非常混乱,但我不清楚这是代码的恶意意图,还是意外。如果您想发送真实、正确的 IP 地址,请在构建回复时使用 htonl 和 htons。

修复字节顺序:

正确包含&lt;arpa/inet.h&gt; 始终使用 htons()、htonl()、ntohs() 和 ntohl()。如果您的平台不需要,他们的实施使其成为 NOP。 在设置要从主机发送的数据时,始终使用 hton*() 处理它 在解释来自网络的数据时,在与局部变量进行比较之前,请始终 ntoh*() 它。

总的来说,我所做的更改是 1) .sll_protocol = htons(ETH_P_ARP)。 (发送数据时) 2) ah->arp_op = htons(ARPOP_REPLY) (在回复 arp 中) 3) 删除了 ah->arp_hd 和 ah->arp_pr 上无意义的 ntohs()。在填充发送缓冲区时,您不想将数据转换为主机字节顺序(除非您真的真的这样做) 4)在一些比较中添加了 ntohs() 转换和正确定义 5)其他一些小修复 6)禁用位做系统("sudo...")!

完整代码pastebin。这是一个差异:

thuovila@glx:~/src/so/arp$ diff arp2.c arp_orig.c 
13d12
< #include <arpa/inet.h>
20c19
< #define DEVICE "eth1"
---
> #define DEVICE "eth0"
25c24
< int s = -1; /*Socketdescriptor*/
---
> int s = 0; /*Socketdescriptor*/
92c91
<       socket_address.sll_protocol = htons(ETH_P_ARP);
---
>       socket_address.sll_protocol = htons(ETH_P_IP);
95c94
<       socket_address.sll_pkttype = 0; //PACKET_OTHERHOST;
---
>       socket_address.sll_pkttype = PACKET_OTHERHOST;
112c111
<               if(ntohs(eh->h_proto) == ETH_P_ARP)
---
>               if(htons(eh->h_proto) == 0x806)
119c118
<                               if(ntohs(ah->arp_op) != ARPOP_REQUEST)
---
>                               if(htons(ah->arp_op) != 0x0001)
139d137
<                               #if 0
145d142
<                               #endif
182c179
<                               eh->h_proto = htons(ETH_P_ARP);
---
>                               eh->h_proto = ETH_ARP;
200,201c197,198
<                               //ah->arp_hd = ntohs(ah->arp_hd);
<                               //ah->arp_pr = ntohs(ah->arp_pr);
---
>                               ah->arp_hd = ntohs(ah->arp_hd);
>                               ah->arp_pr = ntohs(ah->arp_pr);
203c200
<                               ah->arp_op = htons(ARPOP_REPLY);
---
>                               ah->arp_op = 0x0002;

编辑 一些wireshark 建议。捕获ether proto 0x0806(或简称​​arp)。使用捕获任何数据包的伪设备。您的数据包应该可见。

在 linux 上,如果要阻止网络堆栈干扰,请使用:echo "8" > /proc/sys/net/ipv4/conf/all/arp_ignore

EDIT #2 我不完全确定 ETH_P_ARP。对我来说,这可能是一个仓促的判断。在 ARP 标头字段中使用 ETH_P_IP 是正确的,但我不确定将哪一个用于数据包套接字 sll_protocol。还要注意socket_address.sll_pkttype = PACKET_OTHERHOST;在发送时没有效果(见man 7 packet)。也是强制性的 SO 观察,您应该始终使用 至少 -Wall(使用 gcc 或 clang 时)作为编译标志。

EDIT #3 我对程序进行了一些改动。并相应地更新了答案和差异。令人惊讶的是,.sll_protocol 确实需要是 ETH_P_ARP。我的 man 7 数据包 的副本甚至没有说它可用于任何用途,但没有它,数据包不会作为 ARP 发送出去。

【讨论】:

还是同样的问题吗?它似乎对我有用。我看到数据包出去了,wireshark 报告检测到 ARP 冲突/重复 MAC。发出 ARP 请求的主机似乎也接受了自己的回复。【参考方案3】:

我知道这是一篇很老的帖子。这段代码对我帮助很大。我修改了代码以向 IP 发送 ARP 请求并从回复中提取 MAC 地址。请在下面找到我的代码

#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>

#include <asm/types.h>

#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>

#define PROTO_ARP 0x0806
#define ETH2_HEADER_LEN 14
#define HW_TYPE 1
#define PROTOCOL_TYPE 0x800
#define MAC_LENGTH 6
#define IPV4_LENGTH 4
#define ARP_REQUEST 0x01
#define ARP_REPLY 0x02
#define BUF_SIZE 60

struct arp_header

        unsigned short hardware_type;
        unsigned short protocol_type;
        unsigned char hardware_len;
        unsigned char  protocol_len;
        unsigned short opcode;
        unsigned char sender_mac[MAC_LENGTH];
        unsigned char sender_ip[IPV4_LENGTH];
        unsigned char target_mac[MAC_LENGTH];
        unsigned char target_ip[IPV4_LENGTH];
;

int main()

        int sd;
        unsigned char buffer[BUF_SIZE];
        unsigned char source_ip[4] = 10,222,190,160;
        unsigned char target_ip[4] = 10,222,190,139;
        struct ifreq ifr;
        struct ethhdr *send_req = (struct ethhdr *)buffer;
        struct ethhdr *rcv_resp= (struct ethhdr *)buffer;
        struct arp_header *arp_req = (struct arp_header *)(buffer+ETH2_HEADER_LEN);
        struct arp_header *arp_resp = (struct arp_header *)(buffer+ETH2_HEADER_LEN);
        struct sockaddr_ll socket_address;
        int index,ret,length=0,ifindex;

memset(buffer,0x00,60);
        /*open socket*/
        sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
        if (sd == -1) 
                perror("socket():");
                exit(1);
        
        strcpy(ifr.ifr_name,"eth1.30");
    /*retrieve ethernet interface index*/
    if (ioctl(sd, SIOCGIFINDEX, &ifr) == -1) 
        perror("SIOCGIFINDEX");
        exit(1);
    
    ifindex = ifr.ifr_ifindex;
printf("interface index is %d\n",ifindex);

        /*retrieve corresponding MAC*/
        if (ioctl(sd, SIOCGIFHWADDR, &ifr) == -1) 
                perror("SIOCGIFINDEX");
                exit(1);
        
close (sd);

        for (index = 0; index < 6; index++)
        

                send_req->h_dest[index] = (unsigned char)0xff;
                arp_req->target_mac[index] = (unsigned char)0x00;
                /* Filling the source  mac address in the header*/
                send_req->h_source[index] = (unsigned char)ifr.ifr_hwaddr.sa_data[index];
                arp_req->sender_mac[index] = (unsigned char)ifr.ifr_hwaddr.sa_data[index];
                socket_address.sll_addr[index] = (unsigned char)ifr.ifr_hwaddr.sa_data[index];
        
        printf("Successfully got eth1 MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
                        send_req->h_source[0],send_req->h_source[1],send_req->h_source[2],
                        send_req->h_source[3],send_req->h_source[4],send_req->h_source[5]);
        printf(" arp_reqMAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
                        arp_req->sender_mac[0],arp_req->sender_mac[1],arp_req->sender_mac[2],
                        arp_req->sender_mac[3],arp_req->sender_mac[4],arp_req->sender_mac[5]);
        printf("socket_address MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
                        socket_address.sll_addr[0],socket_address.sll_addr[1],socket_address.sll_addr[2],
                        socket_address.sll_addr[3],socket_address.sll_addr[4],socket_address.sll_addr[5]);

        /*prepare sockaddr_ll*/
        socket_address.sll_family = AF_PACKET;
        socket_address.sll_protocol = htons(ETH_P_ARP);
        socket_address.sll_ifindex = ifindex;
        socket_address.sll_hatype = htons(ARPHRD_ETHER);
        socket_address.sll_pkttype = (PACKET_BROADCAST);
        socket_address.sll_halen = MAC_LENGTH;
        socket_address.sll_addr[6] = 0x00;
        socket_address.sll_addr[7] = 0x00;

        /* Setting protocol of the packet */
        send_req->h_proto = htons(ETH_P_ARP);

        /* Creating ARP request */
        arp_req->hardware_type = htons(HW_TYPE);
        arp_req->protocol_type = htons(ETH_P_IP);
        arp_req->hardware_len = MAC_LENGTH;
        arp_req->protocol_len =IPV4_LENGTH;
        arp_req->opcode = htons(ARP_REQUEST);
        for(index=0;index<5;index++)
        
                arp_req->sender_ip[index]=(unsigned char)source_ip[index];
                arp_req->target_ip[index]=(unsigned char)target_ip[index];
        
  // Submit request for a raw socket descriptor.
  if ((sd = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0) 
    perror ("socket() failed ");
    exit (EXIT_FAILURE);
  

buffer[32]=0x00;
        ret = sendto(sd, buffer, 42, 0, (struct  sockaddr*)&socket_address, sizeof(socket_address));
        if (ret == -1)
        
                perror("sendto():");
                exit(1);
        
        else
        
                printf(" Sent the ARP REQ \n\t");
                for(index=0;index<42;index++)
                
                        printf("%02X ",buffer[index]);
                        if(index % 16 ==0 && index !=0)
                        printf("\n\t");
                
        
printf("\n\t");
        memset(buffer,0x00,60);
        while(1)
        
                length = recvfrom(sd, buffer, BUF_SIZE, 0, NULL, NULL);
                if (length == -1)
                
                        perror("recvfrom():");
                        exit(1);
                
                if(htons(rcv_resp->h_proto) == PROTO_ARP)
                
                        //if( arp_resp->opcode == ARP_REPLY )
                        printf(" RECEIVED ARP RESP len=%d \n",length);
                        printf(" Sender IP :");
                        for(index=0;index<4;index++)
                                printf("%u.",(unsigned int)arp_resp->sender_ip[index]);

                        printf("\n Sender MAC :");
                        for(index=0;index<6;index++)
                                printf(" %02X:",arp_resp->sender_mac[index]);

                        printf("\nReceiver  IP :");
                        for(index=0;index<4;index++)
                                printf(" %u.",arp_resp->target_ip[index]);

                        printf("\n Self MAC :");
                        for(index=0;index<6;index++)
                                printf(" %02X:",arp_resp->target_mac[index]);

                        printf("\n  :");

                        break;
                
        

        return 0;

再次感谢 阿伦库马尔P

【讨论】:

以上是关于使用c socket编程的arp请求和回复的主要内容,如果未能解决你的问题,请参考以下文章

网络编程基础day08:socke编程入门

socket编程python+c

socket

在C/C++/ObjC编程语言中有没有啥方法获取 socket 是不是连接啊?就像C#中Socke

Python 网络编程和Socket

C++学习之Socket