3

I'm practicing in networking programming. Currently I'm trying to make a program that crafts a single ARP packet and receives the response. Further, I want to expand it to handle the whole subnet.

But, I have a problem with the recvfrom() function. It always returns -1 and the error is: recvfrom: Resource temporarily unavailable. The code successfully sends the ARP packet, but never gets the response. However, responses do exist. I saw it with a packet sniffer. They just don't arrive to my program for some reason.

This code has a small config.txt file which looks like:

INTERFACE
MY MAC ADDRESS
BROADCAST MAC

I hardcoded "wlo1" in the code because if_nametoindex() constantly returns 0 instead of the index, but this is a question for another post.

I am a beginner, so don't throw too many rotten tomatoes, please.

main.c

#include "arpscanner.h"

int main(int argc, char* argv[])
{
    struct Config cfg;
    struct sockaddr_ll addr;
    struct ethhdr ehdr;
    struct myarphdr ahdr;
    unsigned char packet[PACKETLEN];

    ParseArg(argc, argv, &cfg);
    LoadConfig(&cfg, "config.txt");
    int sockfd = CreateRawSocket();
    BindRawSocket(sockfd, &addr, &cfg);
    CreateEthernetHeader(&ehdr, &cfg);
    CreateARPHeader(&ahdr, &cfg);
    ConstructPacket(packet, &ehdr, &ahdr);
    SendPacket(sockfd, packet, &addr);
    ListenPacket(sockfd, packet);
    close(sockfd);

    return 0;
}

arpscanner.h

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <sys/types.h>
#include <ifaddrs.h>
#include <fcntl.h>

#define IPV4_ADDR_LEN 4
#define PACKETLEN 42 //42 for ARP packet


struct Config {
    char interface[16];
    unsigned char src_mac[6];
    unsigned char dst_mac[6];
    uint32_t src_ip;
    uint32_t dst_ip;
};

#pragma pack(1)
struct myarphdr {
    uint16_t hardware_type;     // Hardware type (e.g., Ethernet)
    uint16_t protocol_type;     // Protocol type (e.g., IPv4)
    uint8_t hardware_size;      // Length of hardware address (6 for MAC)
    uint8_t protocol_size;      // Length of protocol address (4 for IPv4)
    uint16_t operation;          // Operation (1 for request, 2 for reply)
    uint8_t sender_mac[ETH_ALEN]; // Sender's MAC address
    uint32_t sender_ip;         // Sender's IP address
    uint8_t target_mac[ETH_ALEN]; // Target's MAC address
    uint32_t target_ip;
};

void ParseArg(int argc, char* argv[], struct Config* cfg);
void LoadConfig(struct Config* cfg, const char* filename);
int CreateRawSocket();
void BindRawSocket(int sockfd, struct sockaddr_ll* addr, struct Config* cfg);
void CreateEthernetHeader(struct ethhdr* ehdr, struct Config* cfg);
void CreateARPHeader(struct myarphdr* ahdr, struct Config* cfg);
uint32_t RetrieveLocalIP();    //used in CreateARPHeader()
void ConstructPacket(unsigned char* packet, struct ethhdr* ehdr, struct myarphdr* ahdr);
void SendPacket(int sockfd, unsigned char* packet, struct sockaddr_ll* addr);
void ListenPacket(int sockfd, unsigned char* packet);
void PrintMAC(unsigned char* address);  //Used in ListenPacket()
void PrintIP(uint32_t* address);

arpscanner.c

#include "arpscanner.h"

void ParseArg(int argc, char* argv[], struct Config* cfg) {
    if(argc != 2) {
        printf("Wrong input\n");
        exit(EXIT_FAILURE);
    }

    struct in_addr addr;
    inet_pton(AF_INET, argv[1], &addr);
    cfg->dst_ip = addr.s_addr;
}

void LoadConfig(struct Config* cfg, const char* filename) {
    FILE* file = fopen(filename, "r");
    if(!file) {
        perror("fopen");
        exit(EXIT_FAILURE);
    }

    if(fscanf(file, "%15s", cfg->interface) != 1) goto fail;
    if(fscanf(file, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &cfg->src_mac[0], &cfg->src_mac[1], &cfg->src_mac[2], &cfg->src_mac[3], &cfg->src_mac[4], &cfg->src_mac[5]) != 6) goto fail;
    if(fscanf(file, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &cfg->dst_mac[0], &cfg->dst_mac[1], &cfg->dst_mac[2], &cfg->dst_mac[3], &cfg->dst_mac[4], &cfg->dst_mac[5]) != 6) goto fail;
    fclose(file);
    return;

    fail:
        fclose(file);
        printf("Failed to parse the config file\n");
        exit(EXIT_FAILURE);
}

int CreateRawSocket() {
    int sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
    if(sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    return sockfd;
}

void BindRawSocket(int sockfd, struct sockaddr_ll* addr, struct Config* cfg) {
    //fill sockaddr_ll;
    addr->sll_family = AF_PACKET;
    addr->sll_protocol = htons(ETH_P_ARP);
    addr->sll_ifindex = if_nametoindex("wlo1");
    if(addr->sll_ifindex == 0) {
        perror("if_nametoindex");
        exit(EXIT_FAILURE);
    }
    addr->sll_hatype = htons(1);
    addr->sll_pkttype = 0;
    addr->sll_halen = ETH_ALEN;
    memcpy(addr->sll_addr, cfg->src_mac, ETH_ALEN);

    //bind raw socket
    if(bind(sockfd, (struct sockaddr*)addr, sizeof(struct sockaddr_ll)) == -1) {
        perror("bind");
        exit(EXIT_FAILURE);
    }
}

void CreateEthernetHeader(struct ethhdr* ehdr, struct Config* cfg) {
    memcpy(ehdr->h_dest, cfg->dst_mac, ETH_ALEN);
    memcpy(ehdr->h_source, cfg->src_mac, ETH_ALEN);
    ehdr->h_proto = htons(ETH_P_ARP);
}

void CreateARPHeader(struct myarphdr* ahdr, struct Config* cfg) {
    ahdr->hardware_type = htons(1); // 1 for ethernet
    ahdr->protocol_type = htons(ETH_P_IP);
    ahdr->hardware_size = ETH_ALEN;
    ahdr->protocol_size = IPV4_ADDR_LEN;
    ahdr->operation = htons(1); //1 for request
    memcpy(ahdr->sender_mac, cfg->src_mac, ETH_ALEN);
    ahdr->sender_ip = RetrieveLocalIP();
    memcpy(ahdr->target_mac, cfg->dst_mac, ETH_ALEN);
    ahdr->target_ip = cfg->dst_ip;
}

//used in CreateARPHeader()
uint32_t RetrieveLocalIP() {
    struct sockaddr_in* addr;
    struct ifaddrs *ifaddr, *ifa;
    if(getifaddrs(&ifaddr) == -1) {
        perror("getifaddrs");
        exit(EXIT_FAILURE);
    }

    for(ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
        if(ifa->ifa_addr->sa_family == AF_INET) {
            addr = (struct sockaddr_in*)ifa->ifa_addr;
        }
    }

    freeifaddrs(ifaddr);
    return addr->sin_addr.s_addr;
}

void ConstructPacket(unsigned char* packet, struct ethhdr* ehdr, struct myarphdr* ahdr) {
    memset(packet, 0, PACKETLEN);
    memcpy(packet, ehdr, sizeof(struct ethhdr));
    memcpy(packet + sizeof(struct ethhdr), ahdr, sizeof(struct myarphdr));
}

void SendPacket(int sockfd, unsigned char* packet, struct sockaddr_ll* addr) {
    if(sendto(sockfd, packet, PACKETLEN, 0, (struct sockaddr*)addr, sizeof(*addr)) == -1) {
        perror("sendto");
        exit(EXIT_FAILURE);
    } else {
        printf("Packet sent successfully\n");
    }
}
void ListenPacket(int sockfd, unsigned char* packet) {
    struct sockaddr raddr = {0};
    socklen_t addr_len = sizeof(raddr);

    struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
    if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }

    while(1) {
        ssize_t num_bytes = recvfrom(sockfd, packet, PACKETLEN, 0, (struct sockaddr*)&raddr, &addr_len);
        if(num_bytes == -1) {
            perror("recvfrom");
            exit(EXIT_FAILURE);
        }

        if(num_bytes != PACKETLEN) continue;

        struct ethhdr* eth_hdr = (struct ethhdr*)packet;
        if(eth_hdr->h_proto != ETH_P_ARP) {
            continue;
        }

        struct myarphdr* arp_hdr = (struct myarphdr*)(packet + sizeof(struct ethhdr));
        if(arp_hdr->operation != 2) {
            continue;
        }

        PrintMAC(arp_hdr->sender_mac);
        PrintMAC(arp_hdr->target_mac);
        PrintIP(&arp_hdr->sender_ip);
        PrintIP(&arp_hdr->target_ip);

    }
}

//Used in ListenPacket()
void PrintMAC(unsigned char* address) {
    struct ether_addr addr;
    memcpy(&addr.ether_addr_octet, address, ETH_ALEN);
    char* mac = ether_ntoa(&addr);
    printf("%s\n", mac);
}

//Used in LIstenPacket()
void PrintIP(uint32_t* address) {
    char ip[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, address, ip, INET_ADDRSTRLEN);
    printf("%s\n", ip);
}

The recvfrom: Resource temporarily unavailable error typically arises with non-blocking sockets, but sockets are typically blocking by default. I double-checked it in my code.

0

1 Answer 1

3

I believe the problem might be the following:

You indeed receive ARP replies, but fail to compare the packet types due to endianess mismatch here:

You should wrap htons() around ETH_P_ARP and 2 for the operation (or use a define like ARPOP_REPLY).

This means you consumed all reply packets and then ran into the timeout you set with SO_RCVTIMEO .

Sign up to request clarification or add additional context in comments.

2 Comments

Or wrap ntohs() around eth_hdr->h_proto..
@user207421 This is the inferior option, because it cannot be optimized to a constant at compile time

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.