-
Notifications
You must be signed in to change notification settings - Fork 54
Multi Channel MBSS
The 802.11 standard provides a set of rules for membership among stations participating in a mesh basic service set (MBSS). The standard requires that stations operating on the same channel use compatible settings, but it does not preclude incorporating multiple devices on multiple channels into the same MBSS. Adding additional channels to the mesh brings improvements in achievable throughput by reducing overall contention on forwarding nodes. This document describes modifications to open80211s to achieve multiple channels in an MBSS with multiple devices. Each device joins a mesh on a given channel, and the kernel forwards frames between the interfaces to present a single unified mesh among the devices and across the channels. In contrast to a software bridge approach, the system described here uses the standard mesh HWMP method for loop avoidance instead of a non-wireless protocol such as STP.
The primary modifications for this feature are made to the Linux kernel. An abstraction representing a mesh configuration is added, which network interfaces can elect to share. Frames that originate from, or are destined for, any shared interface are forwarded to the others. By default, interfaces with matching mesh parameters are shared. Using the iw tool, sharing may be disabled on a given interface.
In order to group devices based on the mesh configuration, we have introduced a new global tracking list called mesh_bss_list
, which contains a list of local mesh configurations. The list is declared at the top of net/mac80211/mesh.c
:
static DEFINE_MUTEX(mesh_bss_mtx);
static LIST_HEAD(mesh_bss_list);
Each entry is a struct mesh_local_bss
, defined as follows in net/mac80211/ieee80211_i.h
:
/** mesh_local_bss - a mesh BSS running on this host
*
* @mesh_id: the mesh id
* @mesh_id_len: size of mesh id in bytes
* @sync_method: which synchronization method to use
* @path_sel_proto: which path selection protocol to use
* @path_metric: which metric to use
* @is_secure: true if the mesh is secure
* @can_share: true if this bss can be shared (user-configurable per-if)
* @list: listptr for siblings in mesh_bss_list
* @if_list: interfaces sharing this bss
*/
struct mesh_local_bss {
u8 mesh_id[IEEE80211_MAX_SSID_LEN];
u8 mesh_id_len;
u8 sync_method;
u8 path_sel_proto;
u8 path_metric;
bool is_secure;
bool can_share;
struct list_head list;
struct list_head if_list;
};
A struct mesh_local_bss
will be shared for any network interface which joins a mesh with a matching mesh ID, sync method, path selection protocol, path selection metric, security setting, and has elected to participate in MBSS sharing. Each struct mesh_local_bss
contains a list in .if_list
of the interfaces in the MBSS. Additionally, each interface contains a few fields related to the active MBSS:
struct ieee80211_if_mesh {
[...]
/* mbss sharing */
bool share_mbss;
struct mesh_local_bss *mesh_bss;
struct list_head if_list;
};
The mesh_bss
pointer contains the active MBSS, so an interface may reach the other interfaces in the same MBSS by iterating over mesh_bss->if_list
.
Changes to the mesh_bss_list
must be protected by the mesh_bss_mtx
mutex. This may happen whenever an interface joins or leaves a mesh. However, because a mutex may not be taken on the fastpath when reading the list of interfaces, the if_list
is protected by read-copy-update (RCU). Under this synchronization method, readers are able to traverse the list without a heavyweight lock, relying on memory barriers when dereferencing the list pointers. The writers only mutate the list under a lock, and they use a barrier synchronization (synchronize_rcu()
) after an element is removed to wait for readers to complete their critical sections.
The manipulations to the MBSS list can be found in net/mac80211/mesh.c
, in the following functions:
-
mesh_bss_matches()
- returns true if a candidate mesh configuration matches amesh_local_bss
. -
mesh_bss_find()
- given a mesh configuration, finds a matchingmesh_local_bss
, if any. -
mesh_bss_create()
- creates a newmesh_local_bss
for a specific mesh configuration -
mesh_bss_add()
- given an interface and mesh configuration, searches the MBSS list withmesh_bss_find
, and adds itself to the MBSS. If no matching MBSS is found, callsmesh_bss_create
. -
mesh_bss_remove()
- removes an interface from its MBSS. If this is the last such interface, themesh_local_bss
is removed.
Whether sharing the MBSS or not, every interface belongs to an MBSS group: if not sharing, the group has only one member. The mesh_bss_add()
function is called from ieee80211_start_mesh()
to setup the pointer on mesh join. Similarly, on mesh leave, ieee80211_stop_mesh()
calls mesh_bss_remove()
.
When looking for a path to a mesh node, we now consider all interfaces that are in the MBSS group shared with an interface, rather than a single interface. This was done by renaming mpath_lookup()
to __mpath_lookup()
, and creating a wrapper that iterates over all interfaces and returns a matching path.
When updating the next hop in a path, we may potentially use a different interface; in this case we update the mpath->sdata
pointer, and update all queued frames on the path frame queue to include the new interface.
Several APIs that previously took a struct ieee80211_subif_data *
(interface) now take a struct mesh_local_bss *
to reflect the change in search criteria.
The recent multicast cache (RMC) is used to suppress excess retransmissions of multicast frames. The RMC was previously per-interface. With these changes, it has been made global so that multicast frames received on any interface are considered for duplicate detection on all other interfaces. The interface parameter is no longer necessary; only the source address and frame header is passed in for cache lookups.
For path selection in multi-interface scenarios, we would like to automatically forward path action frames (PREQ, PREP, PERR) when received on one interface to the other interfaces for retransmission on another channel. The main function that handles sending path selection action frames is mesh_path_sel_frame_tx()
. For this project we have renamed the function __mesh_path_sel_frame_tx()
, and then created a new wrapper that iterates over all of the interfaces in the MBSS group, calling the renamed function accordingly. Broadcast frames, such as the initial PREQ, are sent on all interfaces. Individually directed frames, such as a PREP, are sent only on the matching interface (this may be a different interface from the receiving one, for example, if the PREP is directed to a target address that lies on a different channel).
When we receive a path selection frame, instead of checking that the target address matches the receive address, we now check for matches on any shared interface address using the new function mesh_bss_matches_addr()
. This enables proper PREP generation when a PREQ is received on a different channel from the target.
Data frames that do not terminate at any interface on the local mesh node must be forwarded towards the destination. Prior to the multi-channel changes, the outgoing interface would always be the same as the receiver. Forwarding happens in the function ieee80211_rx_h_fwding()
in net/mac80211/rx.c
.
For unicast frames, the next hop is resolved using mesh_nexthop_lookup()
. This function has been changed to find the outgoing interface (stored in the path table) which is associated with the necessary path. The interface is stored in the outgoing frame's struct sk_buff
control block, and then the packet is added to the interface's pending queue via ieee80211_add_pending_skb()
for deferred transmission.
Multicast data frames instead go out on every interface, except the receiving one (prior to these changes, they were not forwarded). The logic to implement this is encapsulated in the function mesh_local_bss_forward()
, which copies the frame to each shared interface's pending queue.
Unicast data frames which originate from one interface must be directed to the appropriate interface based on the destination. The next hop is determined in mesh_nexthop_resolve()
, called from ieee80211_xmit()
. This function has been modified to queue the packet on the pending queue of the correct outgoing interface, in the case that the outbound path does not match the originating interface.
Multicast data frames are again copied to other interfaces using mesh_local_bss_forward()
, called from ieee80211_xmit()
.
When passing frames up to higher levels, it is necessary to return them on the correct net device. For example, an ICMP echo packet should be received at the device with the matching IP in order for a ping reply to be generated. If the ultimate destination of a data frame is on the local host, the proper interface is determined just before the frame is passed up. This happens in ieee80211_rx_h_data()
.
For unicast frames, the interface matching the destination address is found using mesh_bss_find_if()
. This information is stored in the struct ieee80211_rx_data
structure before passing the frame to ieee80211_deliver_skb()
.
Multicast frames are delivered to every interface. The interface list is iterated, and a copy of the frame is passed to ieee80211_deliver_skb()
for each interface other than the receiver. The receiver receives a copy via the original code path.
To the existing NL80211_CMD_JOIN_MESH
API, a new attribute was added to select sharing mode:
- NL80211_MESH_SETUP_CAN_SHARE - (u8) if nonzero, the interface shares the mesh with other matching interfaces.
To display the state of all mesh paths associated with a single MBSS, a new flag was added to NL80211_CMD_GET_MPATH
:
- NL80211_ATTR_MPATH_DUMP_MBSS - (flag) if set, all paths matching an MBSS are returned.
linux
To test the multi channel MBSS operation, you just need our custom linux kernel:
repo: [email protected]:cozybit/open80211s.git
branch: ft-multi-if
iw (optional)
A new iw is not required since the kernel will enable forwarding between matching meshIDs by default, but in case disabling this feature is necessary, check out:
repo: [email protected]:cozybit/iw.git
branch: ft-mbss-sharing
Here we assume the reader is familiar with the build and installation process for the above.
MBSS sharing
MBSS sharing across mesh interfaces is enabled by default. To disable this sharing, add the new mesh setup parameter share {on, off}
while joining a mesh ID:
$ iw wlan0 mesh join meshtest share off
Now wlan0 will be in an isolated mesh from other interfaces on the same system, even if they share the same meshID.
Otherwise path selection information sharing and frame forwarding will take place between interfaces sharing the meshID meshtest
.
mpath dump
Usually, dumping the mpaths from iw will return a list of mpaths associated with the requested interface. To see all mpaths associated with the local mbss, just add the mbss
parameter to a dump request.
$ iw wlan1 mpath dump mbss
DEST ADDR NEXT HOP IFACE SN METRIC QLEN EXPTIME DTIM DRET FLAGS
00:03:7f:10:4e:11 00:03:7f:10:4e:11 wlan1 4 127 0 0 00 0x14
00:03:7f:10:4e:1b 00:03:7f:10:4e:1b wlan0 4 71 0 0 00 0x14
You will need 3 mesh nodes, where at least one of them has 2 physical radios. We'll call these node_1, node_2, and node_3.
As an example, let's simulate the following linear topology using multi-channel MBSS:
node_1(0) <-----> [node_2(0), node_2(1)] <-----> node_3(0)
The radio pairs (node_1(0), node_2(0)) and (node_2(1), node_3(0)) are on orthogonal channels as a means of enforcing a certain link topology.
node_1
$ iw wlan0 set type mp
$ iw wlan0 set channel 1
$ ip link set wlan0 up
$ iw wlan0 mesh join meshtest
$ ip addr add 10.10.10.1/24 dev wlan0
node_2
$ iw wlan0 set type mp
$ wi wlan1 set type mp
$ iw wlan0 set channel 1
$ iw wlan1 set channel 149
$ ip link set wlan0 up
$ ip link set wlan1 up
$ iw wlan0 mesh join meshtest
$ iw wlan1 mesh join meshtest
node_3
$ iw wlan0 set type mp
$ iw wlan0 set channel 149
$ ip link set wlan0 up
$ iw wlan0 mesh join meshtest
$ ip addr add 10.10.10.2/24 dev wlan0
Now you can ping node_3 from node_1 through node_2!
If you would like to enable multi-channel MBSS for a gateway node with dual radios for wired Internet access, in example eth0, you can bridge either wlan0 or wlan1 to the eth0 as follow:
node_2 as gateway node
$ iw wlan0 set type mp
$ wi wlan1 set type mp
$ iw wlan0 set channel 1
$ iw wlan1 set channel 149
$ ip link set wlan0 up
$ ip link set wlan1 up
$ iw wlan0 mesh join meshtest
$ iw wlan1 mesh join meshtest
$ brctl addbr br-wan
$ brctl addif br-wan eth0
$ brctl addif br-wan wlan0
None at this time.