Search code examples
cmacoswifipcaplibpcap

Processing incorrect mac addresses from 802.11 frames with pcap


I'm working throurgh a project with pcap and wireless. Following an example posted in response to oe of my earlier questions I am trying to extract the mac addresses from wireless frames. I have created structures for the radiotap header and a basic management frame. For some reason when it comes to trying to output the mac addresses I am printing out the wrong data. When I compare to wireshark I don't see why the radio tap data is printing out correctly but the mac addresses are not. I don't see any additional padding in the hex dump that wireshark displays when i look at the packets and compare the packets that I have captured. I am somewhat famialar with c but not an expert so maybe I am not using the pointers and structures properly could someone help show me what I am doing wrong?

Thanks, Quentin

//  main.c
//  MacSniffer
//


#include <pcap.h>
#include <string.h>
#include <stdlib.h>

#define MAXBYTES2CAPTURE 65535

#ifdef WORDS_BIGENDIAN
typedef struct frame_control
{
    unsigned int subtype:4; /*frame subtype field*/
    unsigned int protoVer:2; /*frame type field*/
    unsigned int version:2; /*protocol version*/

    unsigned int order:1;  
    unsigned int protected:1;
    unsigned int moreDate:1;
    unsigned int power_management:1;

    unsigned int retry:1; 
    unsigned int moreFrag:1;
    unsigned int fromDS:1;
    unsigned int toDS:1;
}frame_control;

struct ieee80211_radiotap_header{
    u_int8_t it_version;
    u_int8_t it_pad;
    u_int16_t it_len;
    u_int32_t it_present;
    u_int64_t MAC_timestamp;
    u_int8_t flags;
    u_int8_t dataRate;
    u_int16_t channelfrequency;
    u_int16_t channFreq_pad;
    u_int16_t channelType;
    u_int16_t channType_pad;
    u_int8_t ssiSignal;
    u_int8_t ssiNoise;
    u_int8_t antenna;
};

#else
typedef struct frame_control
{
    unsigned int protoVer:2; /* protocol version*/
    unsigned int type:2; /*frame type field (Management,Control,Data)*/
    unsigned int subtype:4; /* frame subtype*/

    unsigned int toDS:1; /* frame coming from Distribution system */
    unsigned int fromDS:1; /*frame coming from Distribution system */
    unsigned int moreFrag:1; /* More fragments?*/
    unsigned int retry:1; /*was this frame retransmitted*/

    unsigned int powMgt:1; /*Power Management*/
    unsigned int moreDate:1; /*More Date*/
    unsigned int protectedData:1; /*Protected Data*/
    unsigned int order:1; /*Order*/
}frame_control;

struct ieee80211_radiotap_header{
    u_int8_t it_version;
    u_int8_t it_pad;
    u_int16_t it_len;
    u_int32_t it_present;
    u_int64_t MAC_timestamp;
    u_int8_t flags;
    u_int8_t dataRate;
    u_int16_t channelfrequency;
    u_int16_t channelType;
    int ssiSignal:8;
    int ssiNoise:8;
};
#endif
struct wi_frame {
    u_int16_t fc;
    u_int16_t wi_duration;
    u_int8_t wi_add1[6];
    u_int8_t wi_add2[6];
    u_int8_t wi_add3[6];
    u_int16_t wi_sequenceControl;
    // u_int8_t wi_add4[6];
    //unsigned int qosControl:2;
    //unsigned int frameBody[23124];
};

void processPacket(u_char *arg, const struct pcap_pkthdr* pkthdr, const u_char* packet)
{
    int i= 0, *counter = (int *) arg;
    struct ieee80211_radiotap_header *rh =(struct ieee80211_radiotap_header *)packet;
    struct wi_frame *fr= (struct wi_frame *)(packet + rh->it_len);
    u_char *ptr;
    //printf("Frame Type: %d",fr->wi_fC->type);
    printf("Packet count: %d\n", ++(*counter));
    printf("Received Packet Size: %d\n", pkthdr->len);
    if(rh->it_version != NULL)
    {
      printf("Radiotap Version: %d\n",rh->it_version);
    }
    if(rh->it_pad!=NULL)
    {
      printf("Radiotap Pad: %d\n",rh->it_pad);
    }
    if(rh->it_len != NULL)
    {
        printf("Radiotap Length: %d\n",rh->it_len);
    }
    if(rh->it_present != NULL)
    {
        printf("Radiotap Present: %c\n",rh->it_present);
    }
    if(rh->MAC_timestamp != NULL)
    {
        printf("Radiotap Timestamp: %u\n",rh->MAC_timestamp);
    }

    if(rh->dataRate != NULL)
    {
        printf("Radiotap Data Rate: %u\n",rh->dataRate);
    }
    if(rh->channelfrequency != NULL)
    {
        printf("Radiotap Channel Freq: %u\n",rh->channelfrequency);
    }
    if(rh->channelType != NULL)
    {
    printf("Radiotap Channel Type: %06x\n",rh->channelType);
    }
    if(rh->ssiSignal != NULL)
    {
        printf("Radiotap SSI signal: %d\n",rh->ssiSignal);
    } 
    if(rh->ssiNoise != NULL)
    {
        printf("Radiotap SSI Noise: %d\n",rh->ssiNoise);
    } 

ptr = fr->wi_add1;
int k= 6;
printf("Destination Address:");
do{
    printf("%s%X",(k==6)?" ":":",*ptr++);
}
while(--k>0);
printf("\n");

ptr = fr->wi_add2;
k=0;
printf("Source Address:");
do{
    printf("%s%X",(k==6)?" ":":",*ptr++);
}while(--k>0);
printf("\n");

ptr = fr->wi_add3;
k=0;
do{
    printf("%s%X",(k==6)?" ":":",*ptr++);
}
while(--k>0);
printf("\n");
/* for(int j = 0; j < 23124;j++)
 {
 if(fr->frameBody[j]!= NULL)
 {
 printf("%x",fr->frameBody[j]);
 }
 }
 */
for (i = 0;i<pkthdr->len;i++)
{

    if(isprint(packet[i +rh->it_len]))
    {
        printf("%c",packet[i + rh->it_len]);    
    }

    else{printf(".");}



    //print newline after each section of the packet
    if((i%16 ==0 && i!=0) ||(i==pkthdr->len-1))
    {
        printf("\n");
    }

}
return;
}
int main(int argc, char** argv)
{

int count = 0;
pcap_t* descr = NULL;
char errbuf[PCAP_ERRBUF_SIZE], *device = NULL;
struct bpf_program fp;
char filter[]="wlan broadcast";
const u_char* packet;
memset(errbuf,0,PCAP_ERRBUF_SIZE);
device = argv[1];

if(device == NULL)
{
    fprintf(stdout,"Supply a device name ");
}

descr = pcap_create(device,errbuf);
pcap_set_rfmon(descr,1);
pcap_set_promisc(descr,1);
pcap_set_snaplen(descr,30);
pcap_set_timeout(descr,10000);

pcap_activate(descr);
int dl =pcap_datalink(descr);
printf("The Data Link type is %s",pcap_datalink_val_to_name(dl));
//pcap_dispatch(descr,MAXBYTES2CAPTURE,1,512,errbuf);
//Open device in promiscuous mode
//descr = pcap_open_live(device,MAXBYTES2CAPTURE,1,512,errbuf);

/* if(pcap_compile(descr,&fp,filter,0,PCAP_NETMASK_UNKNOWN)==-1)
 {
 fprintf(stderr,"Error compiling filter\n");
 exit(1);
 }

 if(pcap_setfilter(descr,&fp)==-1)
 {
 fprintf(stderr,"Error setting filter\n");
 exit(1);
 }
 */
pcap_loop(descr,0, processPacket, (u_char *) &count);

return 0;
}

Solution

  • You're doing several things wrong.

    The first thing you're doing wrong is declaring the radiotap header as a structure with more fields than it_version, it_pad, it_len, and it_present. There is absolutely no guarantee that on in arbitrary radiotap header there will, for example, be a 64-bit MAC_timestamp field following the it_present field. You have to look at the it_present field to see which of the fields in the header are actually present. See the radiotap Web site for details on how to process a radiotap header.

    Comparing the values of the fields against 0 (or NULL) does not work - if a field isn't present, it simply isn't present.

    Your code might happen to work with particular versions of drivers for particular networking adapters on particular OSes, but it might fail if the driver is changed or if you run on a machine with a different type of adapter (for example, Atheros vs. Broadcom adapters on Macs) or if you try to run this on a different operating system (Linux, for example).

    If you expect this code to run on a big-endian machine, you will also need to fetch fields from the radiotap header more carefully, as they're all little-endian. (The #define in your code is not sufficient for that.)

    Except for the byte-order issue, which would show up on a Mac only if you're running on a PowerPC Mac, you are correctly skipping past the radiotap header, so that's not the issue.

    Also, the MAC timestamp is a 64-bit integer, and on a 32-bit machine it'd have to be printed with %llu rather than %u.

    You should also be checking for errors. pcap_create() and pcap_activate() are presumably not failing if you're seeing packets, so that's probably not the immediate problem, but you should check for failure anyway. The pcap_set_ routines are also probably not failing, at least not on Wi-Fi devices, but you should check anyway.

    If you're going to assume packets are 802.11+radiotap packets, you should probably at least check to make sure the return value of pcap_datalink() is DLT_IEEE802_11_RADIO and fail if it's not. While you're at it, add a newline to the end of the message printed for the link-layer type.

    But the main think you're doing wrong is capturing no more than 30 bytes of every packet! When you do pcap_set_snaplen(descr,30);, you're saying "don't capture any more than 30 bytes"; the radiotap header is probably longer than that, so you're not even going to get all of the radiotap header, much less getting any of the 802.11 header.

    If you want to capture the entire packet, just leave the pcap_set_snaplen() call out.

    Oh, and if you want to be really careful, make sure, when you're looking at the radiotap and 802.11 header, that you haven't gone past pkthdr->caplen.

    That also means your loop that checks pkthdr->len should check pkthdr->caplen AND should either start with packet[0] or should subtract rh->it_len from pkthdr->caplen (as you should have checked to make sure rh->it_len is greater than or equal to pkthdr->caplen while or before you parsed the radiotap header, the result of that subtraction will be positive). The snapshot length includes all pseudo-headers such as the radiotap header.