arp request and reply using c socket programming

dsharew picture dsharew · May 23, 2013 · Viewed 37.5k times · Source

I am trying to receive and send arp packets using c programming in Linux (Ubuntu)
My program works fine (i.e. runs without any error), but I cannot trace the packets using Wireshark.

source code:

#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);
}

Answer

Mikko Korkalo picture Mikko Korkalo · Sep 2, 2016

I took user6343961's code, did some cleaning and splicing and implemented support for automatically getting interface IP address. Also the parameters come from CLI instead of hardcoding. bind() is also used to get only ARP from the interface we want. Have fun. This code works for me.

#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);
}