Wireshark-dev: Re: [Wireshark-dev] [tcpdump-workers] What's the difference between NdisMediumBa
From: Yang Luo <hsluoyb@xxxxxxxxx>
Date: Sun, 10 Apr 2016 10:15:40 +0800
Hi Guy,

I have fixed the bugs you pointed out:
https://github.com/nmap/npcap/commit/244e81226e99944f6bd4c12d713e97cb59139ec0

And I have also finished the radiotap "Rate" header. The looking up code is like:

------------------------------------------------------------
USHORT
NPF_LookUpDataRateMappingTable(
IN POPEN_INSTANCE pOpen,
IN UCHAR ucDataRate
)
{
UINT i;
PDOT11_DATA_RATE_MAPPING_TABLE pTable = &pOpen->DataRateMappingTable;
USHORT usRetDataRateValue = 0;
TRACE_ENTER();

if (!pOpen->HasDataRateMappingTable)
{
TRACE_MESSAGE1(PACKET_DEBUG_LOUD, "Data rate mapping table not found, Open = %p\n", pOpen);
TRACE_EXIT();
return usRetDataRateValue;
}

for (i = 0; i < pTable->uDataRateMappingLength; i ++)
{
if (pTable->DataRateMappingEntries[i].ucDataRateIndex == ucDataRate)
{
usRetDataRateValue = pTable->DataRateMappingEntries[i].usDataRateValue;
break;
}
}

TRACE_EXIT();
return usRetDataRateValue;
}
#endif
------------------------------------------------------------

And there's also a truncation from usDataRateValue (16 bits) to Radiotap "Rate" field (8 bits). I hope a direct assignment is OK:
*((UCHAR*)Dot11RadiotapHeader + cur) = (UCHAR) usDataRateValue;

My capture result is like:
Data Rate: 22.0 Mb/s

My network speed is usually 10Mb/s - 100Mb/s. So I believe this is a valid result.

And I also found the "SSI Signal" can be many values in my capture: -57 dBm, -91 dBm, -10 dBm, -94 dBm.
So I think it's somewhat meaningful too.


AFAIK, the radiotap feature is finished now. The software is:


Cheers,
Yang


On Sat, Apr 9, 2016 at 3:32 PM, Guy Harris <guy@xxxxxxxxxxxx> wrote:
On Apr 8, 2016, at 9:25 PM, Yang Luo <hsluoyb@xxxxxxxxx> wrote:

> On Thu, Apr 7, 2016 at 9:37 AM, Guy Harris <guy@xxxxxxxxxxxx> wrote:
>
>>    provide a radiotap Flags field with 0x10 set if the frame includes the FCS (you'll probably have to experiment a bit to see whether you get the FCS or not - the answer might differ for data and management frames, based on Network Monitor's behavior) and with 0x40 set if DOT11_RECV_FLAG_RAW_PACKET_FCS_FAILURE is set in uReceiveFlags;
>
> FCS is the suffix 4 bytes of a packet. Actually I didn't see those 4 bytes in the packets (only 802.11 data for now) using Wireshark (captured by npcap-nmap-0.06-r13-wifi.exe). So I'm afraid  it's the case that I can't get FCS for data frames? As I didn't start the work of capturing management frames yet. it will not be considered for now.
>
> So I think I will just leave FLags field with 0x00 then.

Yes, that's the right thing to do.

>>    provide a radiotap Channel field where the frequency value is the uChCenterFrequency field of the structure and the flags are derived from the uChCenterFrequency and uPhyId fields of the structure - assuming that the uPhyId value is one of the ones from
>>
>>         https://msdn.microsoft.com/en-us/library/windows/hardware/ff548741(v=vs.85).aspx
>>
>>    then the mapping would be:
>>
>>         dot11_phy_type_fhss - set 0x0800 in the flags (11 legacy FHSS);
>>
>>         dot11_phy_type_ofdm - set 0x0040 in the flags (11a);
>>
>>         dot11_phy_type_hrdsss - set 0x0020 in the flags (11b);
>>
>>         dot11_phy_type_erp - set 0x0040 in the flags (11g, unknown whether it's pure or not);
>>
>>    and, unless it's dot11_phy_type_irbaseband, set 0x0100 if the frequency is in the 5 GHz range or set 0x0080 if it's in the 2.4 GHz range;
>
> How to determine if uChCenterFrequency is 5GHz or 2.4GHz? What about the following conditions?
> if (pwInfo->uChCenterFrequency == 5000)
> and
> if (pwInfo->uChCenterFrequency == 2400)

No, it's a range, not a value.  A test we use in Wireshark for CommView files is to check whether the center frequency is > 2484, so do

        if (pwInfo->uChCenterFrequency > 2484)
                5 GHz band;
        else
                2.4 GHz band;

> Also I found the frequency of Radiotap "Channel" field is 16bit, but uChCenterFrequency is a ULONG type (32bit). They are both MHz unit. I hope a direct assignment is OK?

It's OK for now, but 2^16 MHz is 65 GHz, and there's apparently some discussion of 60 GHz wireless communication, so, while a 16-bit frequency field might not become a problem in the short term, it might become a problem later.  That'd require a change to radiotap.

If the frequency is > 65535 MHz, I'd leave the channel field out.

>>    provide a radiotap Antenna signal field whose value is the value of the lRSSI field in the structure;
>
> Again a size truncating issue. I found radiotap Antenna signal field is 8bit. And pwInfo->lRSSI is a LONG type (32bit). is a direct assignment OK?

100 dBm is 10,000,000,000 milliwatts, at least if I'm interpreting

        https://en.wikipedia.org/wiki/Decibel

correctly, so that's 10,000,000 watts.

I *really* would not want to be anywhere near a Wi-Fi station transmitting at that power level. :-)

-100 dBm is 1/10,000,000,000 milliwatts, which is .1 picowatt; I'm not a radio expert, but I suspect there might not be any radios capable of detecting that low a power level except in specialized physics or radio astronomy cases.  (It's 1/10th the power consumption of an average human cell:

        https://en.wikipedia.org/wiki/Orders_of_magnitude_(power)#Picowatt_.2810.E2.88.9212_watt.29

so it's not much power.)

So I don't think you have to worry about a dBm power level that doesn't fit in 8 bits.  You should leave out the antenna signal field if the value doesn't fit in 8 bits.

>>    if the phy is dot11_phy_type_ht, provide a radiotap MCS field where the known field is 0 and the other fields are also zeroed out (i.e., it's 11n, but we don't know anything else about it);
>>
>>    if the phy is dot11_phy_type_vht, provide a radiotap VHT field where the known field is 0 and the other fields are also zeroed out (i.e., it's 11ac, but we don't know anything else about it).
>
> So simply speaking, if phy is dot11_phy_type_ht, then MCS field (3 bytes) all zero.
> If phy is dot11_phy_type_vht, then VHT field (12 bytes) all zero.

Yes.  It would nice if Microsoft were to specify, and vendors' drivers were to supply, more information, but at least supplying "empty" MCS and VHT fields will let the program processing the capture know that the packet was an HT or VHT packet.

> Also, another question is, how large buffer is needed at most for the whole Radiotap header (including Radiotap data)?
> Like the below code, I allocated a buffer of 256 bytes. I don't know if this is enough big. Or if there are any better handle ways?

The beginning of the radiotap header is 8 bytes.

Then the fields you'd supply, if *all* the fields were present, would be:

        TSFT: 8 bytes

        Flags: 1 byte

        Rate: 1 byte

        Channel: 4 bytes

        Antenna signal: 1 byte, plus 1 byte of alignment *if* this is a VHT frame

        MCS or VHT: MCS = 12 bytes, VHT = 12 bytes

so the largest radiotap header you'd supply would be 8+8+1+1+4+2+12 = 36 bytes.

> For now, all the code about the Radiotap is here:
> ---------------------------------------------------------------
>
>                       PDOT11_EXTSTA_RECV_CONTEXT  pwInfo;
>                       IEEE80211_RADIOTAP_HEADER RadiotapHeader = { 0 };

>                       CHAR buf[256];
>                       int cur = 0;

You should actually initialize it to 8, to leave room for the IEEE80211_RADIOTAP_HEADER at the beginning.

It's probably easiest to construct the IEEE80211_RADIOTAP_HEADER while you're filling in the rest of the radiotap header, and then copy it in place at the end.

>                       // [Radiotap] "Flags" field.

>                       if (TRUE) // The packet doesn't have FCS. We always have no FCS for all packets currently.
>                       {
>                               RadiotapHeader.it_present |= IEEE80211_RADIOTAP_FLAGS;
>                               *((UCHAR*)buf + cur) = 0x0; // 0x0: none
>                               cur += sizeof(UCHAR) / sizeof(UCHAR);
>                       }
>                       else // The packet has FCS.
>                       {
>                               RadiotapHeader.it_present |= IEEE80211_RADIOTAP_FLAGS;
>                               *((UCHAR*)buf + cur) = 0x10; // 0x10: frame includes FCS
>
>                               // FCS check fails.
>                               if ((pwInfo->uReceiveFlags & DOT11_RECV_FLAG_RAW_PACKET_FCS_FAILURE) == DOT11_RECV_FLAG_RAW_PACKET_FCS_FAILURE)
>                               {
>                                       *((UCHAR*)buf + cur) |= 0x40; // 0x40: frame failed FCS check
>                               }
>
>                               cur += sizeof(UCHAR) / sizeof(UCHAR);
>                       }

You're providing a Flags field in all cases, so I'd just do

                        // [Radiotap] "Flags" field.
                        RadiotapHeader.it_present |= IEEE80211_RADIOTAP_FLAGS;
                        if (TRUE) // The packet doesn't have FCS. We always have no FCS for all packets currently.
                        {
                                *((UCHAR*)buf + cur) = 0x0; // 0x0: none
                        }
                        else // The packet has FCS.
                        {
                                *((UCHAR*)buf + cur) = 0x10; // 0x10: frame includes FCS

                                // FCS check fails.
                                if ((pwInfo->uReceiveFlags & DOT11_RECV_FLAG_RAW_PACKET_FCS_FAILURE) == DOT11_RECV_FLAG_RAW_PACKET_FCS_FAILURE)
                                {
                                        *((UCHAR*)buf + cur) |= 0x40; // 0x40: frame failed FCS check
                                }
                        }
                        cur += sizeof(UCHAR) / sizeof(UCHAR);

>                               RadiotapHeader.it_present |= IEEE80211_RADIOTAP_CHANNEL;
>                               *((USHORT*)buf + cur) = flags;
>                               cur += sizeof(USHORT) / sizeof(UCHAR);
>                               *((USHORT*)buf + cur) = pwInfo->uChCenterFrequency;
>                               cur += sizeof(USHORT) / sizeof(UCHAR);
>                       }

The frequency comes before the channel, so that should be

                                *((USHORT*)buf + cur) = pwInfo->uChCenterFrequency;
                                cur += sizeof(USHORT) / sizeof(UCHAR);
                                *((USHORT*)buf + cur) = flags;
                                cur += sizeof(USHORT) / sizeof(UCHAR);

>                       // [Radiotap] "Antenna signal" field.
>                       if (TRUE)
>                       {
>                               RadiotapHeader.it_present |= IEEE80211_RADIOTAP_DBM_ANTSIGNAL;
>                               RtlCopyMemory(buf + cur, &pwInfo->lRSSI, sizeof(UCHAR) / sizeof(UCHAR));
>                               cur += sizeof(UCHAR) / sizeof(UCHAR);
>                       }
>
>                       // [Radiotap] "MCS" field.
>                       if (pwInfo->uPhyId == dot11_phy_type_ht)
>                       {
>                               RadiotapHeader.it_present |= IEEE80211_RADIOTAP_MCS;
>                               RtlZeroMemory(buf + cur, 3 * sizeof(UCHAR) / sizeof(UCHAR));
>                               cur += 3 * sizeof(UCHAR) / sizeof(UCHAR);
>                       }
>                       // [Radiotap] "VHT" field.
>                       else if (pwInfo->uPhyId == dot11_phy_type_vht)
>                       {
>                               RadiotapHeader.it_present |= IEEE80211_RADIOTAP_VHT;
>                               RtlZeroMemory(buf + cur, 12 * sizeof(UCHAR) / sizeof(UCHAR));
>                               cur += 12 * sizeof(UCHAR) / sizeof(UCHAR);
>                       }

You'd need to do

                        cur += sizeof(UCHAR) / sizeof(UCHAR);

before putting the VHT field into the packet, because the VHT field has to be aligned on a 2-byte boundary, and the antenna field is on a 2-byte boundary but is only 1 byte long.  (The MCS field, however, doesn't have to be aligned on a 2-byte boundary, so you *don't* need to pad anything for HT frames.)

After all this is done, you'd need to do

        // Set the total length of the radiotap header
        RadiotapHeader.it_len = cur;

and then do

        RtlCopyMemory(buf, &RadiotapHeader, sizeof RadiotapHeader);

to put the IEEE80211_RADIOTAP_HEADER at the beginning of the radiotap header.