4
votes

I am trying to write a c++ app (linux) that would capture a wireless packet along with the associated Signal strength (in dBm). The capturing part is easy but the problem is I can't find any documentation on how to get the signal strength for each packet.

Is it part of the header?

Here's what I have so far:

printf("Device: %s\n", dev);
printf("Number of packets: %d\n", num_packets);
printf("Filter expression: %s\n", filter_exp);

/* open capture device */
    pcap_t *handler = pcap_create("wlan0", errbuf);
    if (handler == NULL)
    {
        exit(-1);
    }
    if(pcap_set_rfmon(handler,1)==0 )
    {
        printf("monitor mode enabled\n");
    }
    pcap_set_snaplen(handler, 2048);  // Set the snapshot length to 2048
    pcap_set_promisc(handler, 0); // Turn promiscuous mode off
    pcap_set_timeout(handler, 512); // Set the timeout to 512 milliseconds
    int status = pcap_activate(handler);


/* now we can set our callback function */
pcap_loop(handle, num_packets, got_packet, NULL);

Here is the got_packet code:

/* define ethernet header */
ethernet = (struct sniff_ethernet*)(packet);

/* define/compute ip header offset */
ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
size_ip = IP_HL(ip)*4;
if (size_ip < 20) {
    printf("   * Invalid IP header length: %u bytes\n", size_ip);
    return;
}

/* print source and destination IP addresses */
printf("       From: %s\n", inet_ntoa(ip->ip_src));
printf("         To: %s\n", inet_ntoa(ip->ip_dst));

/* determine protocol */    
switch(ip->ip_p) {
    case IPPROTO_TCP:
        printf("   Protocol: TCP\n");
        break;
    case IPPROTO_UDP:
        printf("   Protocol: UDP\n");
        return;
    case IPPROTO_ICMP:
        printf("   Protocol: ICMP\n");
        return;
    case IPPROTO_IP:
        printf("   Protocol: IP\n");
        return;
    default:
        printf("   Protocol: unknown\n");
        return;
}

/* define/compute tcp header offset */
tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
size_tcp = TH_OFF(tcp)*4;
if (size_tcp < 20) {
    printf("   * Invalid TCP header length: %u bytes\n", size_tcp);
    return;
}

printf("   Src port: %d\n", ntohs(tcp->th_sport));
printf("   Dst port: %d\n", ntohs(tcp->th_dport));

/* define/compute tcp payload (segment) offset */
payload = (char *)(packet + SIZE_ETHERNET + size_ip + size_tcp);

/* compute tcp payload (segment) size */
size_payload = ntohs(ip->ip_len) - (size_ip + size_tcp);

/*
 * Print payload data; it might be binary, so don't just
 * treat it as a string.
 */
if (size_payload > 0) {
    printf("   Payload (%d bytes):\n", size_payload);
    //print_payload(payload, size_payload);
}

Any help would be much appreciate it.

UPDATE: /***************/ Here's an update: So based on my research and as Guy Scott mentioned I was looking for a wrong info. I need to look at wireless packets and was instead loading ethernet packet. So here's the updated code:

 pcap_set_snaplen(handle, 2048);  // Set the snapshot length to 2048
    pcap_set_promisc(handle, 1);     // Turn promiscuous mode off
    pcap_set_timeout(handle, 512);   // Set the timeout to 512 milliseconds
    int status = pcap_activate(handle);

    if(pcap_set_datalink(handle, DLT_IEEE802_11_RADIO) == -1) {
        printf("Couldn't set datalink type %s: %s\n", device, pcap_geterr(handle));
    }

So the problem now is parsing the packet which seems to be a pretty hard issue. I am interested in the source address, destination address and the associated signal etc. I don't know how to match and load the data from the packet and match it to the radiotap struct.

struct ieee80211_radiotap_header {
u_int8_t    it_version; /* set to 0 */
u_int8_t    it_pad;
u_int16_t   it_len; /* entire length */
u_int32_t   it_present; /* fields present */
} __attribute__((__packed__));

/* Presence bits */
#define RADIOTAP_TSFT   0
#define RADIOTAP_FLAGS  1
#define RADIOTAP_RATE   2
#define RADIOTAP_CHANNEL    3
#define RADIOTAP_FHSS   4
#define RADIOTAP_ANTENNA_SIGNAL 5
#define RADIOTAP_ANTENNA_NOISE  6
#define RADIOTAP_LOCK_QUALITY   7
#define RADIOTAP_TX_ATTENUATION 8
#define RADIOTAP_DB_TX_ATTENUATION  9
#define RADIOTAP_DBM_TX_POWER   10
#define RADIOTAP_ANTENNA    11
#define RADIOTAP_DB_ANTENNA_SIGNAL  12

void process_packet (u_char * args, const struct pcap_pkthdr *header, const u_char * packet)
{
    struct ieee80211_radiotap_header *packet_header = (struct ieee80211_radiotap_header *) header;
    // This is where I am stuck

Can someone tell me once I've captured the packet, how do I pull out the above values from it?

Thanks

2
ok so after a little more research I realized that i've been doing this wrong. so the packet I want to capture isn't really an Ethernet packet but rather a wifi packet. The wifi packet has a signal field.Sean D

2 Answers

1
votes

Every program that calls pcap_open_live(), pcap_create()/pcap_activate(), or pcap_open_offline() should, if the calls succeed, call pcap_datalink() to find out what the link-layer header type of the capture is.

There are no exceptions to this rule.

Then see the link-layer header types page for tcpdump.org to see what the values returned by pcap_datalink() mean. Compare them against the DLT_ values listed there. The ones you're likely to get are DLT_IEEE802_11, which has no signal-strength information, and DLT_PRISM_HEADER, DLT_IEEE802_11_RADIO, and DLT_IEEE802_11_RADIO_AVS, which do have signal strength information. See the links for the latter three possibilities for information on how the signal strength information (and other radio metadata) is represented in the packet data.

(And, yes, that's three options for radio metadata, so the link given in another answer is to an incomplete source; most of the time you will probably get radiotap headers, not AVS or Prism headers. Radiotap is more general, as it's designed to be extensible, but it's more complicated to parse.)

0
votes

I was stumbling upon the same problem, thought I should share my solution. The signal strength is not in the header of PCAP callback, but within the packet. For DLT_IEEE802_11_RADIO the signal and noise could be:

void process_packet (u_char * args,
                     const struct pcap_pkthdr *header,
                     const u_char * packet){
    int8_t signal_dbm = packet[22];
    int8_t noise_dbm = packet[23];
    ....
}

But I found 22 and 23 by looking into WireShark.

The nicer solution would be of course using the radiotap (GitHub).

Here is an example:

void process_packet (u_char * args,
                     const struct pcap_pkthdr *header,
                     const u_char * packet) {
    struct ieee80211_radiotap_iterator iterator;
    int ret = ieee80211_radiotap_iterator_init(&iterator, packet, header->len, NULL);
    while (!ret) {
        ret = ieee80211_radiotap_iterator_next(&iterator);
        if (ret) break;
        switch (iterator.this_arg_index) {
            case IEEE80211_RADIOTAP_TSFT:
                printf("timestamp: %lu\n", (uint64_t)*iterator.this_arg);
                break;
            case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:
                printf("sig: %d\n", (int8_t)*iterator.this_arg);
                break;
            case IEEE80211_RADIOTAP_DBM_ANTNOISE:
                printf("noise: %d\n", (int8_t)*iterator.this_arg);
                break;
            default:
                break;
        }
    }
}