How WiFi Client-to-Client Communication Works: AP Relay vs Direct Path in Marginal Network Conditions


2 views

In a standard WiFi infrastructure network with one access point (AP) and multiple clients, all client-to-client communication must go through the AP. This is fundamentally different from wired Ethernet networks where devices can communicate directly. The IEEE 802.11 protocol specifies this routing behavior to maintain network coordination and prevent collisions.

When Client 1 sends data to Client 2:

1. Client 1 → AP: [Frame with DA=Client2, RA=AP]
2. AP → Client 2: [Frame with DA=Client2, RA=Client2]

The frame passes through the AP's MAC layer, which handles the retransmission. This two-hop process happens even if both clients can physically hear each other's transmissions.

Consider this Python simulation of throughput impact:


import math

def calculate_effective_throughput(direct_rate, ap_rate, overhead=0.2):
    """Calculate effective throughput when routing through AP"""
    return min(direct_rate, ap_rate) * (1 - overhead)

# Example with clients close but AP distant
client_link_rate = 54  # Mbps (802.11g)
ap_link_rate = 6       # Mbps (due to range)
print(f"Effective throughput: {calculate_effective_throughput(client_link_rate, ap_link_rate):.1f} Mbps")

For scenarios requiring direct client communication:

  • WiFi Direct: Bypasses the AP completely
  • Ad-hoc mode: Creates a peer-to-peer network
  • TDLS (Tunneled Direct Link Setup): Allows direct client communication after AP-assisted setup

Use Wireshark filters to observe the traffic flow:

wlan.da == client2_mac && wlan.sa == client1_mac  # Should show empty
wlan.da == ap_mac && wlan.sa == client1_mac       # Shows frames from Client1 to AP
wlan.da == client2_mac && wlan.sa == ap_mac       # Shows frames from AP to Client2

For developers working with WiFi hardware:


// Example of enabling TDLS in Linux
#include <linux/wireless.h>

int enable_tdls(int sockfd, const char *ifname, const uint8_t *peer_mac) {
    struct iwreq wrq;
    memset(&wrq, 0, sizeof(wrq));
    strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
    wrq.u.data.pointer = (void *)peer_mac;
    wrq.u.data.length = ETH_ALEN;
    return ioctl(sockfd, SIOCSIWTDLSCREATE, &wrq);
}

In standard 802.11 infrastructure mode networks, all frame transmissions between wireless clients must pass through the access point (AP). This includes both unicast and broadcast traffic between STAs (stations). The AP acts as both a bridge to the wired network and a central hub for wireless communication.

// Simplified frame flow diagram (client to client via AP)
Client1 → AP → Client2
   ↓       ↓      ↓
MAC:SA1  MAC:RA1 → MAC:SA2
          MAC:SA2 → MAC:RA2

Even when two clients are physically close to each other but associated with the same distant AP, their communication still follows this path. The 802.11 protocol doesn't implement direct client-to-client frame forwarding at the MAC layer in infrastructure mode.

Example of frame address fields changing during transit:

Client1 to AP:
Frame Control | Duration | Address1 (AP) | Address2 (Client1) | Address3 (Client2) | Sequence | Frame Body

AP to Client2:
Frame Control | Duration | Address1 (Client2) | Address2 (AP) | Address3 (Client1) | Sequence | Frame Body

This architecture has significant impacts on network performance:

  • Effectively halves available bandwidth for client-to-client communication
  • Increases latency with distant APs (RTT includes two wireless hops)
  • Creates a single point of failure for local communication

You can verify this behavior using Wireshark with the following display filter:

wlan.fc.type_subtype == 0x0020 || // QoS Data
(wlan.addr == AP_MAC && wlan.ta != wlan.ra) // Transit frames

For direct client communication, consider these technical solutions:

// Linux iw command to enable Wi-Fi Direct (P2P)
$ iw dev wlan0 set type managed
$ iw dev wlan0 connect SSID

// Or create an ad-hoc network
$ iw dev wlan0 set type ibss
$ iw dev wlan0 ibss join SSID freq

Modern protocols like Wi-Fi Direct (P2P) or 802.11z (Tunneled Direct Link Setup) enable direct client communication while maintaining AP association.

This Python script demonstrates the throughput difference between AP-routed and direct communication:

import speedtest

def test_throughput(interface, peer_ip):
    st = speedtest.Speedtest(source_address=peer_ip)
    st.get_best_server()
    return st.download(), st.upload()

# Compare AP-routed vs direct (when possible)
ap_routed = test_throughput('wlan0', '192.168.1.100')
direct = test_throughput('p2p-wlan0-0', '192.168.49.1') 
print(f"AP-routed: {ap_routed} vs Direct: {direct}")