5

Below is bit format for FC field for QoS Data:

00|01|0001  01000010

The first 2 bits represent version, the next 2 bits type, the next 4 bits subtype, with ToDS=0, FromDS=1,Protection bit=1.

So, in what order is the above data sent through the interface on the air? (i.e left to right or right to left )

I see the wireshark catching the data as "8842"(in the last segment where it shows raw packet data).

But, if I write the following code to print FC field data:

struct mgmt_header_t {
    u_int16_t    fc;          /* 2 bytes */
    u_int16_t    duration;    /* 2 bytes */
    u_int8_t     addr1[6];    /* 6 bytes */  
    u_int8_t     addr2[6];    /* 6 bytes */  
    u_int8_t     addr3[6];    /* 6 bytes */  
    u_int16_t    seq_ctrl;    /* 2 bytes */
};
void my_callback(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
    int radiotapheader_length = (unsigned short int)(*(packet+2));
    struct mgmt_header_t *mac_header = (struct mgmt_header_t *)  (packet+radiotapheader_length);
    printf("FC = %X\n", mac_header->fc);
}

The output is:

FC = 4288

So my secondary question is, shouldn't it print 8842 instead of 4288?

Update: I am updating the question so as to be more clear about what my doubt is.

Say, I want to send a QoS Data packet whose FC field is in the format below:

00|01|0001  01000010

So, should I have to write:

mac_header->fc = 0x1142 /* value if read from left to right */

Or

mac_header->fc = 0x4211

Or

mac_header->fc = 0x4288 /* value if read from right to left */

Or

mac_header->fc = 0x8842

Mine is a little endian machine.

bengaluriga
  • 319
  • 2
  • 5
  • 9

3 Answers3

9

The IEEE 802.11 standard (which, unfortunately, is not currently available from the IEEE Get program) says:

The MPDUs or frames in the MAC sublayer are described as a sequence of fields in specific order. Each figure in Clause 7 depicts the fields/subfields as they appear in the MAC frame and in the order in which they are passed to the physical layer convergence procedure (PLCP), from left to right.

In figures, all bits within fields are numbered, from 0 to k, where the length of the field is k + 1 bits. The octet boundaries within a field can be obtained by taking the bit numbers of the field modulo 8. Octets within numeric fields that are longer than a single octet are depicted in increasing order of significance, from lowest numbered bit to highest numbered bit. The octets in fields longer than a single octet are sent to the PLCP in order from the octet containing the lowest numbered bits to the octet containing the highest numbered bits.

So the first octet of the Frame Control field sent to the PLCP is the one containing B0, i.e. the octet containing the protocol version, type, and subtype fields. After that comes the octet containing To DS, From DS, More Frag, and so on. The 00|01|0001 octet is, therefore, the first octet transmitted. That turns into 10001000 in a byte in memory, from high-order bit to low-order bit rather than low-order bit to high-order bit, hence 0x88. The next octet is the 01000010 one, hence 0x42.

So that goes over the wire as 00010001 followed by 01000010, and would appear in memory as 0x88 followed by 0x42. (This, by the way, means that the FC field, like all other multi-octet integral fields in 802.11, is transmitted in little-endian byte order, rather than in big-endian byte order. "Network byte order" is big-endian byte order; not all data transmitted over a network is in "network byte order" - fields in Internet protocol standards such as IPv4, IPv6, TCP, and UDP are in "network byte order", but other protocols, including some over which IP is transmitted and some that are transmitted over TCP or UDP, may use little-endian byte order.)

As received by a little-endian machine, and treated as a 16-bit integral quantity, that would be 0x4288 - on a little-endian machine, with a multiple-octet integral quantity, the first octet in memory is the low-order octet of the quantity. Therefore, your code prints it as 0x4288 on your little-endian machine; if it were running on a big-endian machine, it would print it as 0x8842.

Printing it as 0x4288 is the "correct" way to print it, as it's little-endian "on the wire" (or, rather, "on the air", as this is 802.11 :-)). Wireshark shows the Frame Control field for your packet as 0x4288 in the "packet details" pane (the middle pane, by default); it shows up as 88 42 in the "hex dump" pane (the bottom pane, by default) because it's just showing each individual octet in the order in which they appear in memory.

You would need to convert it from little-endian byte order to host byte order if you want it to print as 0x4288 on both big-endian and little-endian machines. The easiest way to do that would be to use something such as the pletohs() macro from Wireshark:

#define pletohs(p) ((unsigned short)                       \
                    ((unsigned short)*((const unsigned char *)(p)+1)<<8|  \
                     (unsigned short)*((const unsigned char *)(p)+0)<<0))

and do something such as

printf("FC = %X\n", pletohs(&mac_header->fc));

As for transmitting that value, the easiest way to do that in a way that works regardless of the byte order of your machine would be to use something such as the phtoles() macro from Wireshark:

#define phtoles(p, v) \
    {                 \
        (p)[0] = (unsigned char)((v) >> 0);    \
        (p)[1] = (unsigned char)((v) >> 8);    \
    }

and do

pletohs(&mac_header->fc, 0x4288);

to set mac_header->fc.

Community
  • 1
  • 1
  • Beautiful explanation ... Thanks ... I have one question by the way. Are there macros by name: pbetohs() & phtobes() also? – bengaluriga Jul 20 '12 at 05:12
  • Wireshark does have macros that perform those functions, but they have names derived from the `ntohs()`, `ntohl()`, `htons()`, and `htonl()` - `pntohs()`, `pntohl()`, `phtons()`, and `phtonl()`. Those names reflect "big-endian" byte order being "Network" byte order. –  Jul 20 '12 at 21:04
  • This is very confusing. So the bit order for each byte if right to left? – GaussGun Mar 22 '23 at 00:09
1

This is a byte ordering bug. You can't do (unsigned short int)(*(packet+2)) unless you know that your platform has the same byte order as the packet's contents. In your case, they differ which is why you're seeing the bytes swap places.

See this Wikipedia article for more on byte order, or endianness as it's also called.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • The field to which you're referring happens to be little-endian; it's the length field in the [radiotap](http://www.radiotap.org/) header. That code works on a little-endian machine, such as a personal computer with an x86 processor. It won't work on big-endian machines such as most PowerPC-based machines. –  Jul 19 '12 at 17:53
  • @GuyHarris So,the soulution would be this: `struct radiolength` `{` `u_int16_t length;` `}` `struct radiolength *rl = (struct radiolength *)(packet + 2);` `r1->length = pletohs(r1->length);` Am I right? – bengaluriga Jul 20 '12 at 05:26
  • Or just `int radiotapheader_length = pletohs(packet+2);` –  Jul 20 '12 at 21:06
  • @GuyHarris Thanks ... but shouldn't it be `int radiotapheader_length = pletohs(*(packet+2));`? Because packet is a pointer. – bengaluriga Jul 22 '12 at 14:04
  • ...and `pletohs()` takes a pointer as an argument - that's what the "p" in "pletohs" means. It fetches the value a byte at a time, so that it can be handed an unaligned pointer and not take an alignment fault on processors (such as SPARC processors) that don't allow misaligned accesses. –  Jul 22 '12 at 18:43
  • So since plehtos() takes pointer as an argument, `radiotapheader_length` should be declared as `u_int16_t` or `unsigned short int` rather than `int`, right? Because in most systems, size of `int` is 4 bytes and if `radiotapheader_length` is declared as `int`, `plehtos()` might end reading 4 bytes from the offest `packet + 2`, whereas for correctness `plehtos()` must be reading only 2 bytes from the offset `packet + 2`. – bengaluriga Jul 23 '12 at 05:03
  • I think I got it .... in `plehtos()` s stands for "short". So, it will always read only 2 bytes from the offset `packet + 2`. By the way, thanks @GuyHarris, you have been really helpful to me and solved all my doubts in all my previous questions in this forum. Thanks a lot. Keep up the good work Harris. – bengaluriga Jul 24 '12 at 05:12
1

FC is printed as 4288 because your system uses little-endian format to store data in memory. Where as network communications follows big-endian format.

Ensure with the following program

#include <stdio.h>
int main()
{

int a = 0x12345678, j = 0;
char *b =(char*)&a;
printf("\n0x");
for(j=0; j < sizeof(int); j++)
        printf("%x", *b++);

printf("\n");

return 0;
}

if it prints 0x78563412 then your machine is little-endian. else if it prints 0x12345678 then it is big-endian.

EDIT:
I hope follwing links will be helpful.

1. http://www.cs.odu.edu/~cs476/fall03/lectures/sockets.htm
2.http://www.ccplusplus.com/2011/10/htons-example-in-c.html

Jeyaram
  • 9,158
  • 7
  • 41
  • 63
  • yep.. little endian machine ... I have more clearly specified what my doubt is in the Update. Could you please answer that? – bengaluriga Jul 19 '12 at 14:58
  • @anil.babbur Use endianess swap functions instead. http://forums.codeguru.com/showthread.php?292902-C-General-How-do-I-convert-between-big-endian-and-little-endian-values – jn1kk Jul 19 '12 at 15:38