C/PCAP : ARP packet values all 0

940 views Asked by At

I'm trying to improve my C/network knowledge implementing a ARP spoofing tool with Pcap library.

I'm stuck with sniffing arp packets. I can detect the ARP type in the ethertype field of Ethernet frame. But when I "read" the ARP packet, all values are 0 (null) but hardware addr(MAC) & protocol address(ip) are very weird 8 byte number repeated (like 20e54ef12:20e54ef12:20e54ef12...). I just can't figure it out. Here is what I've done so far :

packet_struct.h (the different structures used for eth, arp, ip...)

#ifndef DEF_PACKET_STRUCT
#define DEF_PACKET_STRUCT
#include <sys/types.h>
#define BUFF_SIZE 1518
#define ETH_SIZE 14
#define ARP_SIZE 28
/* in bytes */
#define ETH_ADDR_SIZE 6
#define IP_ADDR_SIZE 4

typedef struct pkt_eth {
    unsigned char dest[ETH_ADDR_SIZE];
    unsigned char src[ETH_ADDR_SIZE];
    unsigned short type;
} pkt_eth;

#define ETHERTYPE_ARP 0x0806
#define ARP_REQUEST 1
#define ARP_REPLY 2
typedef struct pkt_arp {
    unsigned short htype;/* hardware type => ethernet , etc */
    unsigned short ptype; /*protocol type => ipv4 or ipv6 */
    unsigned char hard_addr_len; /* usually 6 bytes for ethernet */
    unsigned char proto_addr_len; /*usually 8 bytes for ipv4 */
    unsigned short opcode; /* type of arp */
    unsigned char hard_addr_send[ETH_ADDR_SIZE];
    unsigned char proto_addr_send[IP_ADDR_SIZE];
    unsigned char hard_addr_dest[ETH_ADDR_SIZE];
    unsigned char proto_addr_dest[IP_ADDR_SIZE];
} pkt_arp;

#define ETHERTYPE_IP 0x0800
typedef struct pkt_ip {
    unsigned char vhl;
    unsigned char tos;
    unsigned short len;
    unsigned short id;
    unsigned short off;
    unsigned char ttl;
    unsigned char proto;
    unsigned short crc;
    unsigned int addr_src;
    unsigned int addr_dest;
} pkt_ip;

#endif

packet_print.c (utilities to print packet information )

    #include "packet_struct.h"
#include <stdio.h>
#include <stdlib.h>

char * to_addr(unsigned char * addr, int length) {
    int i = 0;
    char string[length];
    for(i=0; i< length; i++)
        sprintf(string,"%02x:",addr[i]);
    return string;
}
void print_pkt_eth(pkt_eth * eth) {
    int i = 0;

    fprintf(stdout,"Ethernet Layer \n");
    fprintf(stdout,"\tSource:\t");
    for(i=0;i<ETH_ADDR_SIZE;i++)
        fprintf(stdout,"%02x:",eth->src[i]);
    //fprintf(stdout,"%s",to_addr(eth->src,ETH_ADDR_SIZE));
    fprintf(stdout,"\n\tDest:\t"); 
    for(i=0;i<ETH_ADDR_SIZE;i++)
        fprintf(stdout,"%02X:",eth->dest[i]);

    if(ntohs(eth->type) == ETHERTYPE_IP)
        fprintf(stdout,"\n\tType:\t IPv4");
    else if(ntohs(eth->type) == ETHERTYPE_ARP)
        fprintf(stdout,"\n\tType:\t ARP");
    printf("\n");
}

void print_pkt_arp(pkt_arp * arp) {
    int op = 0;
    int i = 0;
   printf("ARP Layer \n");
      printf("\tHardware type:\t%02d\n",arp->htype); 
      printf("\tProtocol type:\t%02d\n",arp->ptype);
      printf("\tHardware addresses length:\t%01d\n",arp->hard_addr_len);
      printf("\tProtocol addresses length:\t%01d\n",arp->proto_addr_len);
      op = ntohs(arp->opcode);
      printf("\tOperation code:\t%01u\n",op);
      printf("\tHardware sender:\t");
      for(i=0;i<ETH_ADDR_SIZE;i++)
          printf("%02x:",arp->hard_addr_send);
      printf("\n\tSoftware sender:\t");
      for(i=0;i<IP_ADDR_SIZE;i++)
          printf("%02x:",arp->proto_addr_send);
      printf("\n");

}

void print_pkt_ip(pkt_ip * ip) {
}

sniffer.c ( the tool itself )

#include<stdio.h>
#include<stdlib.h>
#include<netinet/in.h> // for addresses translation
#include<errno.h>

// for ntohs etc
// can also be necessary to include netinet/in
#include <arpa/inet.h>

#include "packet_struct.h"

#include <pcap.h>

#define SNAP_LEN 1518
int packet_count = 0;
void handleARP(const struct pkt_eth * eth) {

    const struct pkt_arp * arp = (const struct pkt_arp *) (eth + ETH_SIZE);
    print_pkt_arp(arp);
    if(ntohs(arp->htype) != 1) {
        fprintf(stderr, "Error : ARP packet does not contain a Hardware type Ethernet -> %d\n",ntohs(arp->htype));
        return;
    }

    // check protocol type
    if(ntohs(arp->ptype) != 0x800) {
        fprintf(stderr,"Error : ARP packet does not contain a IPv4 type\n");
        return;
    }

}

void sniff_callback(u_char * user, const struct  pcap_pkthdr * h,const u_char * bytes) {
    int i = 0;
    for(i=0; i < 25; i++) { printf("-"); }; printf("\n");
    printf("Received packet number %d ==> %d\n",packet_count++,h->len);
    const struct pkt_eth * eth;
    unsigned short eth_type;

    unsigned int captureLength = h->caplen;
    unsigned int packetLength = h->len;

    if(captureLength != packetLength) {
        fprintf(stderr,"Error : received packet with %d available instead of %d \n",captureLength,packetLength);
        return;
    }
    if(captureLength < ETH_SIZE) {
        fprintf(stderr,"Error : received too small packet , %d bytes",captureLength);
        return;
    }

    eth = (struct pkt_eth*)(bytes);

    // print the packet
    print_pkt_eth(eth);

    eth_type = ntohs(eth->type);

    if(eth_type == ETHERTYPE_ARP) {
        handleARP(eth);
    }

    for(i=0; i < 25; i++) { printf("-"); }; printf("\n");
    return;

}

/* returns 0 if everything went well */
int set_options(pcap_t * handle) {
    int ret = 0;

    ret = pcap_set_promisc(handle,1);
    if(ret != 0) {
        fprintf(stderr,"Error setting promiscuous mode\n");
        return ret;
    }
    ret = pcap_set_snaplen(handle,SNAP_LEN);
    if(ret != 0) {
        fprintf(stderr,"Error setting snapshot length\n");
        return ret;
    }
    ret = pcap_set_timeout(handle,1000);
    if(ret != 0) {
        fprintf(stderr,"Error setting timeout\n");
        return ret;
    }

    return ret;
}
int activate(pcap_t * handle) {
    int ret = pcap_activate(handle);
    switch(ret) {
        case 0:
            fprintf(stdout,"Activation complete\n");
            break;
        case PCAP_WARNING_PROMISC_NOTSUP:
               fprintf(stderr,"Promiscuous mode not supported\n");
                   return ret;
        case PCAP_ERROR_PERM_DENIED:
               fprintf(stderr,"Not have the permission required\n");
               return ret;
        case PCAP_ERROR_PROMISC_PERM_DENIED:
               fprintf(stderr,"Not have the permission required for promiscuous\n");
               return ret;
        default:
               fprintf(stderr,"Error occured during activation, see code\n");
               return ret;
    }
    return ret; 
}


/* Will activate device , filter & call the sniffing loop */
int sniffing_method(char * interface, char * filter,int packet_count) {

    char err[PCAP_ERRBUF_SIZE]; //error buffer
    pcap_t * handle; // handler of the interface by pcap

    struct bpf_program bpf;
    bpf_u_int32 mask; // network mask 
    bpf_u_int32 ip; // network ip
    struct in_addr addr; // network number

    int ret;

    /* get mask & ip */
    if(pcap_lookupnet(interface, &ip, &mask, err) == -1) {
        fprintf(stderr, "Couldn't get netmask for device %s: %s\n",interface,err);
        exit(EXIT_FAILURE);
    } 

    handle = pcap_create(interface,err);
    if (handle == NULL) {
        fprintf(stderr,"Error pcap_create() : %s \n",err);
        exit(EXIT_FAILURE);
    }
    if(set_options(handle) != 0) {
        fprintf(stderr,"Exiting\n");
        exit(EXIT_FAILURE);
    }
    if (activate(handle) != 0) {
        fprintf(stderr,"Exiting\n");
        exit(EXIT_FAILURE);
    }

    /* FILTER PART */
    if(filter != NULL) {
        if(pcap_compile(handle,&bpf,filter,0,ip) == -1){
            fprintf(stderr,"Couldn't compile filter expr %s : %s\n",filter,pcap_geterr(handle));
            exit(EXIT_FAILURE);
        }
        if(pcap_setfilter(handle, &bpf) == -1) {
            fprintf(stderr,"Couldn't install filter %s : %s\n",filter,pcap_geterr(handle));
            exit(EXIT_FAILURE);
        }
    }

    /* SNIFF starts */
    printf("Sniffing starting on %s ...\n",interface);
    pcap_loop(handle,packet_count,sniff_callback,NULL);

    pcap_freecode(&bpf);
    pcap_close(handle);

    return EXIT_SUCCESS;
}
void usage() {
    printf("sniff interface [filter] [count]");
    printf("interface is the interface you want to listen on. It will try to put it in monitor mode");
    printf("filter can be a filter for libpcap to apply for packets it reads");
}
int main(int argc, char * argv[])
{


    int i = 0; // counter 
    int ret;
    char * default_filter = "ip";
    char * filter;

    int pcount = -1; //take all packet by defaults

    char * interface;
    if(argc < 2) {
        fprintf(stderr, "No interfaces specified in arguments\n");
        usage();
        exit(EXIT_FAILURE);
    }
    // take command line filter
    if(argc > 2) {
        filter = argv[2];
    } else {
        filter = default_filter;
    }
    // take command line packet count limit 
    if(argc > 3) {
        pcount = atoi(argv[3]);
    }

    fprintf(stdout,"Args : ");
    for(i = 0; i < argc; i++) {
        fprintf(stdout,"\t%s",argv[i]);
    }
    printf("\n");

    interface = argv[1];

    sniffing_method(interface,filter,pcount);



}

And here is one output (all tries giving the same output anyway)

    Received packet number 2 ==> 42
Ethernet Layer 
    Source: 00:ee:bd:aa:f4:98:
    Dest:   FF:FF:FF:FF:FF:FF:
    Type:    ARP
ARP Layer 
    Hardware type:  00
    Protocol type:  00
    Hardware addresses length:  0
    Protocol addresses length:  0
    Operation code: 0
    Hardware sender:    20e9a152:20e9a152:20e9a152:20e9a152:20e9a152:20e9a152:
    Software sender:    20e9a158:20e9a158:20e9a158:20e9a158:
Error : ARP packet does not contain a Hardware type Ethernet -> 0
-------------------------
-------------------------
Received packet number 3 ==> 42
Ethernet Layer 
    Source: 00:ee:bd:aa:f4:98:
    Dest:   FF:FF:FF:FF:FF:FF:
    Type:    ARP
ARP Layer 
    Hardware type:  00
    Protocol type:  00
    Hardware addresses length:  0
    Protocol addresses length:  0
    Operation code: 0
    Hardware sender:    20e5a152:20e5a152:20e5a152:20e5a152:20e5a152:20e5a152:
    Software sender:    20e5a158:20e5a158:20e5a158:20e5a158:
1

There are 1 answers

1
nos On BEST ANSWER

This part is wrong:

void handleARP(const struct pkt_eth * eth) {

    const struct pkt_arp * arp = (const struct pkt_arp *) (eth + ETH_SIZE);

Here you're passing in a struct pkt_eth*, to which you add ETH_SIZE. Pointer arithmetic advances to the next element, not to the next byte. You're essentially looking sizeof(struct pkt_eth) * ETH_SIZE bytes past the pointer passed in.

You should just do

 const struct pkt_arp * arp = (const struct pkt_arp *) (eth + 1);

(Or pass in an unsigned char * that already starts at the layer you want to decode.)