Networking – How do access points determine the power of an associated or non-associated client

aircrack-ngwifi-driverwireless-access-pointwireless-networking

I cannot figure out how access points determine the signal power of nearby clients.

I haven't found this information in any of the WiFi packet tutorials, so I can only assume that the power information isn't coming from the client, but is determined by the access point.

I know an application called aircrack that can determine this information, but before I jump into that and dig around, I'd like an overview of how the power is calculated. My Google skills aren't that bad, so I am surprised that so far I've been unable to dig up any information about it. Surely if aircrack can do it, this information is out there!

I was hoping that the users here can point me in the right direction?

Regarding the bounty

I guess it's time to attempt to use a bounty. In your answers, please post all links you can find that would assist anyone in understanding how this power detection works. Specific information on how it is implemented in aircrack would be perfect!

UPDATE:

As of now, using the aircrack-ng suite as an example, it looks like the power value is pulled from buf[2] in net_read(). I believe this value ultimately comes from net_cmd, which results in paired calls to net_send and net_get(). I think net_get() reads in whatever header information is sent due to the call to net_send(), and this header indicates how much data should be copied into the buffer buf[].

But now what I don't get is this — it sure looks like buf[] gets filled by the call to net_read_exact(), which is just reading data from the socket. If this is the case, and if power is a driver detail, why is the power value in the buffer? If that buffer is indeed filled by the socket data, doesn't that imply that the power is sent by the client / station? I believe that the AP / driver is in charge of supplying the power info, just like everyone has said, but just don't get this part.

Best Answer

Actually, the application which determines this information is airodump-ng, not aircrack-ng. From the airodump-ng documentation, power levels are determined as:

PWR - Signal level reported by the card. Its signification depends on the driver [...]

Well, let's see if we can do better. Looking through the latest source code of the airodump-ng.c file, we see the power gets set in the dump_add_packet(...) function:

/* only update power if packets comes from
 * the AP: either type == mgmt and SA != BSSID,
 * or FromDS == 1 and ToDS == 0 */
if (...)
    ap_cur->power_lvl[ap_cur->power_index] = ri->ri_power;

After digging through several layers of abstraction, structs, and function pointers, I found that this data is filled from the function linux_read(...) defined in the osdep/linux.c file. This is where the ri_power variable in the ri struct is filled with data, and indeed it does appear to be driver specific.

Most drivers follow the Radiotap standard (as opposed to the older prism54 standard detailed below), which have several predefined fields for determining antenna power, noise, and attenuation. These fields are used directly by airodump-ng to fill in the ri_power variable. Some of these are already available in decibel or power values.

airodump-ng will use either the antenna signal field (in dBm) or the dB antenna signal field (in dB) to compute the displayed signal power. Similar steps are used for the other fields, as they are all predetermined in the Radiotap specification linked to above. For example, the ri_power using the dB Antenna Signal field can be computed as:

case IEEE80211_RADIOTAP_DB_ANTSIGNAL:
    if(!got_signal) {
        if( *iterator.this_arg < 127 )
            ri->ri_power = *iterator.this_arg;
        else
            ri->ri_power = *iterator.this_arg - 255;

        got_signal = 1;
    }
    break;

As mentioned earlier, some devices follow the (older) prism54 specification (instead of Radiotap), which uses a fixed-length header. In this case, the RX power directly from the buffer (note this is not complete source code, just shows the paths taken to fill ri_power):

if (tmpbuf[7] == 0x40)
    ri->ri_power = tmpbuf[0x33];
else
    ri->ri_power = *(unsigned int *)( tmpbuf + 0x5C );
Related Question