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

struct ethernet {
    unsigned char dest[6];
    unsigned char source[6];
    uint16_t eth_type;
};

struct arp {
    uint16_t htype;
    uint16_t ptype;
    unsigned char hlen;
    unsigned char plen;
    uint16_t oper;
    /* addresses */
    unsigned char sender_ha[6];
    unsigned char sender_pa[4];
    unsigned char target_ha[6];
    unsigned char target_pa[4];
};

#define ETH_HDR_LEN 14
#define BUFF_SIZE 2048

#define ARP_PROTO 0x0806

static void dump_arp(struct arp *arp_hdr);

int main(void)
{
    int sock, err;
    void *buffer = NULL;
    ssize_t recvd_size;
    struct sockaddr_ll s_ll;
    struct ethernet *eth_hdr = NULL;
    struct arp *arp_hdr = NULL;

    if( (sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP))) == -1)
    // if( (sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1)
    {
        perror("socket(): ");
        exit(-1);
    }
    s_ll.sll_family = AF_PACKET;
    s_ll.sll_protocol = htons(ETH_P_ARP); 
    //s_ll.sll_protocol = htons(ETH_P_ARP);
    s_ll.sll_ifindex = 0; // all ifaces 
    //s_ll.sll_ifindex = 2 // eth0
    if( (err = bind(sock, (struct sockaddr *)&s_ll, sizeof(s_ll))) == -1)
    {
        perror("bind(): ");
        exit(-1);
    }

    buffer = malloc(BUFF_SIZE);
    while(1)
    {
        if( (recvd_size = recv(sock, buffer, BUFF_SIZE, 0)) == -1)
        {
            perror("recv(): ");
            free(buffer);
            close(sock);
            exit(-1);
        }
        if(recvd_size <= (sizeof(struct ethernet) + sizeof(struct arp)))
        {
            printf("Short packet. Packet len: %d\n", recvd_size);
            continue;
        }
        eth_hdr = (struct ethernet *)buffer;
        if(ntohs(eth_hdr->eth_type) != ARP_PROTO)
            continue;
        arp_hdr = (struct arp *)(buffer+ETH_HDR_LEN);
        dump_arp(arp_hdr);
    }
    free(buffer);
    close(sock);
}

static void
dump_arp(struct arp *arp_hdr)
{
    uint16_t htype = ntohs(arp_hdr->htype);
    uint16_t ptype = ntohs(arp_hdr->ptype);
    uint16_t oper = ntohs(arp_hdr->oper);
    switch(htype)
    {
        case 0x0001:
            printf("ARP HTYPE: Ethernet(0x%04X)\n", htype);
            break;
        default:
            printf("ARP HYPE: 0x%04X\n", htype);
            break;
    }
    switch(ptype)
    {
        case 0x0800:
            printf("ARP PTYPE: IPv4(0x%04X)\n", ptype);
            break;
        default:
            printf("ARP PTYPE: 0x%04X\n", ptype);
            break;
    }
    printf("ARP HLEN: %d\n", arp_hdr->hlen);
    printf("ARP PLEN: %d\n", arp_hdr->plen);
    switch(oper)
    {
        case 0x0001:
            printf("ARP OPER: Request(0x%04X)\n", oper);
            break;
        case 0x0002:
            printf("ARP OPER: Response(0x%04X)\n", oper);
            break;
        default:
            printf("ARP OPER: 0x%04X\n", oper);
            break;
    }
    printf("ARP Sender HA: %02X:%02X:%02X:%02X:%02X:%02X\n",
           arp_hdr->sender_ha[0],arp_hdr->sender_ha[1],arp_hdr->sender_ha[2],
           arp_hdr->sender_ha[3], arp_hdr->sender_ha[4], arp_hdr->sender_ha[5]);
    printf("ARP Sender PA: %d.%d.%d.%d\n", arp_hdr->sender_pa[0],
           arp_hdr->sender_pa[1], arp_hdr->sender_pa[2], arp_hdr->sender_pa[3]);
    printf("ARP Target HA: %02X:%02X:%02X:%02X:%02X:%02X\n",
           arp_hdr->target_ha[0],arp_hdr->target_ha[1],arp_hdr->target_ha[2],
           arp_hdr->target_ha[3], arp_hdr->target_ha[4], arp_hdr->target_ha[5]);
    printf("ARP Target PA: %d.%d.%d.%d\n", arp_hdr->target_pa[0],
           arp_hdr->target_pa[1], arp_hdr->target_pa[2], arp_hdr->target_pa[3]);
    printf("ARP DONE =====================\n");
}

AF_PACKET、SOCK_RAW、ETH_P_ARP 引数を使用して socket() を作成します。そして、args( AF_PACKET, ETH_P_ARP) を使用して、すべてのインターフェイス (問題ではありません) で bind() します。したがって、すべての ARP パケットはこのソケットを通過する必要があります。

私のホスト: 192.168.1.2 リモート ホスト: 192.168.1.7、ホストには 192.167.1.7 の ARP レコードが含まれていません。

ping 192.168.1.7 を作成したときのプログラム出力:

...
ARP OPER: 応答(0x0002)
ARP 送信者 HA: 50:67:F0:94:70:F5
ARP 送信者 PA: 192.168.1.7
ARP ターゲット HA: 00:22:15:A2:D0:C5
ARP ターゲット PA: 192.168.1.2
ARP 完了 =====================
...
ARP OPER: リクエスト (0x0001)
ARP 送信者 HA: 50:67:F0:94:70:F5
ARP 送信者 PA: 192.168.1.7
ARP ターゲット HA: 00:00:00:00:00:00
ARP ターゲット PA: 192.168.1.2
ARP 完了 =====================

私のソケットは 4 つのパケットのうち 2 つしか取得しませんでした (my_host 要求と my_host 応答が欠落しています)。tcpdump -n -p -i eth0 arp 4 つのパケットすべてを表示します。

socket() と bind() で ETH_P_ARP を ETH_P_ALL に変更すると、4 つのパケットすべてがソケットに送られます (IP などを使用)。

なんで?それを修正する方法は?

PS。この動作について質問できるメーリング リストのアドレスを教えてください。

4

1 に答える 1