mesh.c revision b58e81e96a81c80886011ad87cdbe73585dec4f7
12e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo/*
2264d9b7d8a629620c8de84c614910c3164e935f8Rui Paulo * Copyright (c) 2008, 2009 open80211s Ltd.
32e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo * Authors:    Luis Carlos Cobo <luisca@cozybit.com>
42e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo * 	       Javier Cardona <javier@cozybit.com>
52e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo *
62e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo * This program is free software; you can redistribute it and/or modify
72e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo * it under the terms of the GNU General Public License version 2 as
82e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo * published by the Free Software Foundation.
92e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo */
102e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
115a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
1251ceddade0fb1e15f080b2555f3b3e1d68c6707eLuis Carlos Cobo#include <asm/unaligned.h>
132e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo#include "ieee80211_i.h"
142e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo#include "mesh.h"
15b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh#include "driver-ops.h"
162e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
17bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Bergstatic int mesh_allocated;
182e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobostatic struct kmem_cache *rm_cache;
192e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
2025d49e4d63564c7004a4d6735d1d8c3cc41a7394Thomas Pedersenbool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt)
2125d49e4d63564c7004a4d6735d1d8c3cc41a7394Thomas Pedersen{
2225d49e4d63564c7004a4d6735d1d8c3cc41a7394Thomas Pedersen	return (mgmt->u.action.u.mesh_action.action_code ==
2325d49e4d63564c7004a4d6735d1d8c3cc41a7394Thomas Pedersen			WLAN_MESH_ACTION_HWMP_PATH_SELECTION);
2425d49e4d63564c7004a4d6735d1d8c3cc41a7394Thomas Pedersen}
2525d49e4d63564c7004a4d6735d1d8c3cc41a7394Thomas Pedersen
262e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobovoid ieee80211s_init(void)
272e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo{
282e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	mesh_pathtbl_init();
292e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	mesh_allocated = 1;
302e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	rm_cache = kmem_cache_create("mesh_rmc", sizeof(struct rmc_entry),
312e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo				     0, 0, NULL);
322e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo}
332e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
342e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobovoid ieee80211s_stop(void)
352e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo{
36bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	if (!mesh_allocated)
37bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg		return;
382e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	mesh_pathtbl_unregister();
392e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	kmem_cache_destroy(rm_cache);
402e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo}
412e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
42472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Bergstatic void ieee80211_mesh_housekeeping_timer(unsigned long data)
43472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg{
44472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_sub_if_data *sdata = (void *) data;
45472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_local *local = sdata->local;
46472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
47472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
486b9ac4425d6ec871faf54540e0f1c5ff420a8f29Rui Paulo	set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags);
495bb644a0fd25a5e083ecbfaa92a211db99aa6ef7Johannes Berg
5064592c8fc0e99d445fc3fdedddeb6088e20086f1Johannes Berg	ieee80211_queue_work(&local->hw, &sdata->work);
51472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg}
52472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
532e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo/**
542e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo * mesh_matches_local - check if the config of a mesh point matches ours
552e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo *
56f698d856f65c3fea091cc303a135967965c5b880Jasper Bryant-Greene * @sdata: local mesh subif
57f743ff4907fa5bc2b460f48ace831a560806a9fbThomas Pedersen * @ie: information elements of a management frame from the mesh peer
582e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo *
592e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo * This function checks if the mesh configuration of a mesh point matches the
602e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo * local mesh configuration, i.e. if both nodes belong to the same mesh network.
612e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo */
62f743ff4907fa5bc2b460f48ace831a560806a9fbThomas Pedersenbool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
63f743ff4907fa5bc2b460f48ace831a560806a9fbThomas Pedersen			struct ieee802_11_elems *ie)
642e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo{
65472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
66f743ff4907fa5bc2b460f48ace831a560806a9fbThomas Pedersen	u32 basic_rates = 0;
674bf88530be971bf95a7830ca61b4120980bf4347Johannes Berg	struct cfg80211_chan_def sta_chan_def;
682e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
692e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	/*
702e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	 * As support for each feature is added, check for matching
712e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	 * - On mesh config capabilities
722e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	 *   - Power Save Support En
732e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	 *   - Sync support enabled
742e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	 *   - Sync support active
752e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	 *   - Sync support required from peer
762e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	 *   - MDA enabled
772e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	 * - Power management control on fc
782e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	 */
79739522baa1d6804a3ff33e8c135db0e6b2165f75Thomas Pedersen	if (!(ifmsh->mesh_id_len == ie->mesh_id_len &&
80739522baa1d6804a3ff33e8c135db0e6b2165f75Thomas Pedersen	     memcmp(ifmsh->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 &&
81739522baa1d6804a3ff33e8c135db0e6b2165f75Thomas Pedersen	     (ifmsh->mesh_pp_id == ie->mesh_config->meshconf_psel) &&
82739522baa1d6804a3ff33e8c135db0e6b2165f75Thomas Pedersen	     (ifmsh->mesh_pm_id == ie->mesh_config->meshconf_pmetric) &&
83739522baa1d6804a3ff33e8c135db0e6b2165f75Thomas Pedersen	     (ifmsh->mesh_cc_id == ie->mesh_config->meshconf_congest) &&
84739522baa1d6804a3ff33e8c135db0e6b2165f75Thomas Pedersen	     (ifmsh->mesh_sp_id == ie->mesh_config->meshconf_synch) &&
85739522baa1d6804a3ff33e8c135db0e6b2165f75Thomas Pedersen	     (ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth)))
86bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg		return false;
87739522baa1d6804a3ff33e8c135db0e6b2165f75Thomas Pedersen
882103dec14792be2c2194a454630b01120d30e5cbSimon Wunderlich	ieee80211_sta_get_rates(sdata, ie, ieee80211_get_sdata_band(sdata),
89f743ff4907fa5bc2b460f48ace831a560806a9fbThomas Pedersen				&basic_rates);
90f743ff4907fa5bc2b460f48ace831a560806a9fbThomas Pedersen
91fe40cb6274988852aa5a84440d8f81c00cea4028Ashok Nagarajan	if (sdata->vif.bss_conf.basic_rates != basic_rates)
92bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg		return false;
93fe40cb6274988852aa5a84440d8f81c00cea4028Ashok Nagarajan
944bf88530be971bf95a7830ca61b4120980bf4347Johannes Berg	ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan,
954bf88530be971bf95a7830ca61b4120980bf4347Johannes Berg				     ie->ht_operation, &sta_chan_def);
964bf88530be971bf95a7830ca61b4120980bf4347Johannes Berg
974bf88530be971bf95a7830ca61b4120980bf4347Johannes Berg	if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef,
984bf88530be971bf95a7830ca61b4120980bf4347Johannes Berg					 &sta_chan_def))
99bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg		return false;
1002e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
101739522baa1d6804a3ff33e8c135db0e6b2165f75Thomas Pedersen	return true;
1022e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo}
1032e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
1042e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo/**
1052e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo * mesh_peer_accepts_plinks - check if an mp is willing to establish peer links
1062e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo *
1072e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo * @ie: information elements of a management frame from the mesh peer
1082e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo */
109f698d856f65c3fea091cc303a135967965c5b880Jasper Bryant-Greenebool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie)
1102e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo{
111136cfa28615ccce0f9374811480e0b81c4191ea5Rui Paulo	return (ie->mesh_config->meshconf_cap &
112bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg			IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS) != 0;
1132e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo}
1142e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
1152e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo/**
1162c53040f018b6c36a46eec75b9b937aaa5f78e6dBen Hutchings * mesh_accept_plinks_update - update accepting_plink in local mesh beacons
1172e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo *
118d0709a65181beb787ef3f58cfe45536a2bb254c8Johannes Berg * @sdata: mesh interface in which mesh beacons are going to be updated
119df32381896f5f0c78a371df2e49ab7c776b1a5baMarco Porsch *
120df32381896f5f0c78a371df2e49ab7c776b1a5baMarco Porsch * Returns: beacon changed flag if the beacon content changed.
1212e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo */
122df32381896f5f0c78a371df2e49ab7c776b1a5baMarco Porschu32 mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata)
1232e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo{
1242e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	bool free_plinks;
125df32381896f5f0c78a371df2e49ab7c776b1a5baMarco Porsch	u32 changed = 0;
1262e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
1272e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	/* In case mesh_plink_free_count > 0 and mesh_plinktbl_capacity == 0,
1282e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	 * the mesh interface might be able to establish plinks with peers that
129b4e08ea141e6d663dec31b31d6289baeaaa2a3a2Luis Carlos Cobo	 * are already on the table but are not on PLINK_ESTAB state. However,
130b4e08ea141e6d663dec31b31d6289baeaaa2a3a2Luis Carlos Cobo	 * in general the mesh interface is not accepting peer link requests
131b4e08ea141e6d663dec31b31d6289baeaaa2a3a2Luis Carlos Cobo	 * from new peers, and that must be reflected in the beacon
1322e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	 */
1332e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	free_plinks = mesh_plink_availables(sdata);
1342e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
135df32381896f5f0c78a371df2e49ab7c776b1a5baMarco Porsch	if (free_plinks != sdata->u.mesh.accepting_plinks) {
136df32381896f5f0c78a371df2e49ab7c776b1a5baMarco Porsch		sdata->u.mesh.accepting_plinks = free_plinks;
137df32381896f5f0c78a371df2e49ab7c776b1a5baMarco Porsch		changed = BSS_CHANGED_BEACON;
138df32381896f5f0c78a371df2e49ab7c776b1a5baMarco Porsch	}
139df32381896f5f0c78a371df2e49ab7c776b1a5baMarco Porsch
140df32381896f5f0c78a371df2e49ab7c776b1a5baMarco Porsch	return changed;
1412e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo}
1422e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
14345b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen/*
14445b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen * mesh_sta_cleanup - clean up any mesh sta state
14545b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen *
14645b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen * @sta: mesh sta to clean up.
14745b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen */
14845b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersenvoid mesh_sta_cleanup(struct sta_info *sta)
14945b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen{
15045b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen	struct ieee80211_sub_if_data *sdata = sta->sdata;
15145b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen	u32 changed;
15245b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen
15345b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen	/*
15445b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen	 * maybe userspace handles peer allocation and peering, but in either
15545b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen	 * case the beacon is still generated by the kernel and we might need
15645b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen	 * an update.
15745b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen	 */
15845b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen	changed = mesh_accept_plinks_update(sdata);
159a6dad6a26e15f2f9269eea41b756c8cf0971b2bcThomas Pedersen	if (!sdata->u.mesh.user_mpm) {
16045b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen		changed |= mesh_plink_deactivate(sta);
16145b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen		del_timer_sync(&sta->plink_timer);
16245b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen	}
16345b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen
164f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	if (changed)
1652b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		ieee80211_mbss_info_change_notify(sdata, changed);
16645b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen}
16745b5028e86292284f4d5794047d5dfd742c22421Thomas Pedersen
168f698d856f65c3fea091cc303a135967965c5b880Jasper Bryant-Greeneint mesh_rmc_init(struct ieee80211_sub_if_data *sdata)
1692e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo{
1702e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	int i;
1712e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
172472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	sdata->u.mesh.rmc = kmalloc(sizeof(struct mesh_rmc), GFP_KERNEL);
173472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	if (!sdata->u.mesh.rmc)
1742e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo		return -ENOMEM;
175472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	sdata->u.mesh.rmc->idx_mask = RMC_BUCKETS - 1;
1762e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	for (i = 0; i < RMC_BUCKETS; i++)
177b7cfcd113ac2a1e6b02afc7d283295729fc178a9Thomas Pedersen		INIT_LIST_HEAD(&sdata->u.mesh.rmc->bucket[i]);
1782e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	return 0;
1792e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo}
1802e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
181f698d856f65c3fea091cc303a135967965c5b880Jasper Bryant-Greenevoid mesh_rmc_free(struct ieee80211_sub_if_data *sdata)
1822e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo{
183472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct mesh_rmc *rmc = sdata->u.mesh.rmc;
1842e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	struct rmc_entry *p, *n;
1852e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	int i;
1862e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
187472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	if (!sdata->u.mesh.rmc)
1882e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo		return;
1892e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
190bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	for (i = 0; i < RMC_BUCKETS; i++) {
191b7cfcd113ac2a1e6b02afc7d283295729fc178a9Thomas Pedersen		list_for_each_entry_safe(p, n, &rmc->bucket[i], list) {
1922e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo			list_del(&p->list);
1932e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo			kmem_cache_free(rm_cache, p);
1942e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo		}
195bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	}
1962e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
1972e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	kfree(rmc);
198472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	sdata->u.mesh.rmc = NULL;
1992e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo}
2002e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
2012e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo/**
2022e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo * mesh_rmc_check - Check frame in recent multicast cache and add if absent.
2032e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo *
204bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg * @sdata:	interface
2052e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo * @sa:		source address
2062e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo * @mesh_hdr:	mesh_header
2072e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo *
2082e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo * Returns: 0 if the frame is not in the cache, nonzero otherwise.
2092e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo *
2102e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo * Checks using the source address and the mesh sequence number if we have
2112e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo * received this frame lately. If the frame is not in the cache, it is added to
2122e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo * it.
2132e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo */
214bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Bergint mesh_rmc_check(struct ieee80211_sub_if_data *sdata,
215bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg		   const u8 *sa, struct ieee80211s_hdr *mesh_hdr)
2162e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo{
217472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct mesh_rmc *rmc = sdata->u.mesh.rmc;
2182e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	u32 seqnum = 0;
2192e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	int entries = 0;
2202e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	u8 idx;
2212e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	struct rmc_entry *p, *n;
2222e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
2232e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	/* Don't care about endianness since only match matters */
22451ceddade0fb1e15f080b2555f3b3e1d68c6707eLuis Carlos Cobo	memcpy(&seqnum, &mesh_hdr->seqnum, sizeof(mesh_hdr->seqnum));
22551ceddade0fb1e15f080b2555f3b3e1d68c6707eLuis Carlos Cobo	idx = le32_to_cpu(mesh_hdr->seqnum) & rmc->idx_mask;
226b7cfcd113ac2a1e6b02afc7d283295729fc178a9Thomas Pedersen	list_for_each_entry_safe(p, n, &rmc->bucket[idx], list) {
2272e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo		++entries;
2282e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo		if (time_after(jiffies, p->exp_time) ||
229bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg		    entries == RMC_QUEUE_MAX_LEN) {
2302e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo			list_del(&p->list);
2312e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo			kmem_cache_free(rm_cache, p);
2322e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo			--entries;
233bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg		} else if ((seqnum == p->seqnum) && ether_addr_equal(sa, p->sa))
2342e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo			return -1;
2352e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	}
2362e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
2372e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	p = kmem_cache_alloc(rm_cache, GFP_ATOMIC);
238d15b84590a1d2ec021ada00a0e67ee5851a0ea2bJoe Perches	if (!p)
2392e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo		return 0;
240d15b84590a1d2ec021ada00a0e67ee5851a0ea2bJoe Perches
2412e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	p->seqnum = seqnum;
2422e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	p->exp_time = jiffies + RMC_TIMEOUT;
2432e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	memcpy(p->sa, sa, ETH_ALEN);
244b7cfcd113ac2a1e6b02afc7d283295729fc178a9Thomas Pedersen	list_add(&p->list, &rmc->bucket[idx]);
2452e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	return 0;
2462e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo}
2472e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
248bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Bergint mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,
249bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg			 struct sk_buff *skb)
250082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen{
251082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
252082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	u8 *pos, neighbors;
253082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	u8 meshconf_len = sizeof(struct ieee80211_meshconf_ie);
254082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
255082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	if (skb_tailroom(skb) < 2 + meshconf_len)
256082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen		return -ENOMEM;
257082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
258082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	pos = skb_put(skb, 2 + meshconf_len);
259082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	*pos++ = WLAN_EID_MESH_CONFIG;
260082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	*pos++ = meshconf_len;
261082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
26243552be1da3c420931c89727b6115b7fa35368f8Thomas Pedersen	/* save a pointer for quick updates in pre-tbtt */
26343552be1da3c420931c89727b6115b7fa35368f8Thomas Pedersen	ifmsh->meshconf_offset = pos - skb->data;
26443552be1da3c420931c89727b6115b7fa35368f8Thomas Pedersen
265082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	/* Active path selection protocol ID */
266082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	*pos++ = ifmsh->mesh_pp_id;
267082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	/* Active path selection metric ID   */
268082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	*pos++ = ifmsh->mesh_pm_id;
269082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	/* Congestion control mode identifier */
270082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	*pos++ = ifmsh->mesh_cc_id;
271082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	/* Synchronization protocol identifier */
272082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	*pos++ = ifmsh->mesh_sp_id;
273082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	/* Authentication Protocol identifier */
274082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	*pos++ = ifmsh->mesh_auth_id;
275082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	/* Mesh Formation Info - number of neighbors */
2761258d97616fdca9abc0c21f2edeb1d5b21dcb128Ashok Nagarajan	neighbors = atomic_read(&ifmsh->estab_plinks);
277e05ecccdf752122a439b03c3190458d2c8f0bac6Jacob Minshall	neighbors = min_t(int, neighbors, IEEE80211_MAX_MESH_PEERINGS);
278082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	*pos++ = neighbors << 1;
279082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	/* Mesh capability */
280b60e527a729cbb9c88b49cc10738a13328cd1ed2Chun-Yeow Yeoh	*pos = 0x00;
281b60e527a729cbb9c88b49cc10738a13328cd1ed2Chun-Yeow Yeoh	*pos |= ifmsh->mshcfg.dot11MeshForwarding ?
282b60e527a729cbb9c88b49cc10738a13328cd1ed2Chun-Yeow Yeoh			IEEE80211_MESHCONF_CAPAB_FORWARDING : 0x00;
283dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona	*pos |= ifmsh->accepting_plinks ?
284bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg			IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
2853f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	/* Mesh PS mode. See IEEE802.11-2012 8.4.2.100.8 */
2863f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	*pos |= ifmsh->ps_peers_deep_sleep ?
287bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg			IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL : 0x00;
288dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona	*pos++ |= ifmsh->adjusting_tbtt ?
289bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg			IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00;
290082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	*pos++ = 0x00;
291082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
292082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	return 0;
293082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen}
294082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
295bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Bergint mesh_add_meshid_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
296082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen{
297082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
298082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	u8 *pos;
299082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
300082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	if (skb_tailroom(skb) < 2 + ifmsh->mesh_id_len)
301082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen		return -ENOMEM;
302082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
303082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	pos = skb_put(skb, 2 + ifmsh->mesh_id_len);
304082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	*pos++ = WLAN_EID_MESH_ID;
305082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	*pos++ = ifmsh->mesh_id_len;
306082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	if (ifmsh->mesh_id_len)
307082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen		memcpy(pos, ifmsh->mesh_id, ifmsh->mesh_id_len);
308082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
309082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	return 0;
310082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen}
311082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
312bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Bergstatic int mesh_add_awake_window_ie(struct ieee80211_sub_if_data *sdata,
313bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg				    struct sk_buff *skb)
3143f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch{
3153f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
3163f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	u8 *pos;
3173f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch
3183f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	/* see IEEE802.11-2012 13.14.6 */
3193f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	if (ifmsh->ps_peers_light_sleep == 0 &&
3203f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	    ifmsh->ps_peers_deep_sleep == 0 &&
3213f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	    ifmsh->nonpeer_pm == NL80211_MESH_POWER_ACTIVE)
3223f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch		return 0;
3233f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch
3243f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	if (skb_tailroom(skb) < 4)
3253f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch		return -ENOMEM;
3263f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch
3273f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	pos = skb_put(skb, 2 + 2);
3283f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	*pos++ = WLAN_EID_MESH_AWAKE_WINDOW;
3293f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	*pos++ = 2;
3303f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	put_unaligned_le16(ifmsh->mshcfg.dot11MeshAwakeWindowDuration, pos);
3313f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch
3323f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	return 0;
3333f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch}
3343f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch
335bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Bergint mesh_add_vendor_ies(struct ieee80211_sub_if_data *sdata,
336bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg			struct sk_buff *skb)
337082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen{
338082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
339082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	u8 offset, len;
340082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	const u8 *data;
341082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
342082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	if (!ifmsh->ie || !ifmsh->ie_len)
343082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen		return 0;
344082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
345082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	/* fast-forward to vendor IEs */
346082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	offset = ieee80211_ie_split_vendor(ifmsh->ie, ifmsh->ie_len, 0);
347082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
348082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	if (offset) {
349082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen		len = ifmsh->ie_len - offset;
350082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen		data = ifmsh->ie + offset;
351082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen		if (skb_tailroom(skb) < len)
352082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen			return -ENOMEM;
353082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen		memcpy(skb_put(skb, len), data, len);
354082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	}
355082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
356082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	return 0;
357082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen}
358082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
359bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Bergint mesh_add_rsn_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
360082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen{
361082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
362082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	u8 len = 0;
363082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	const u8 *data;
364082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
365082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	if (!ifmsh->ie || !ifmsh->ie_len)
366082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen		return 0;
367082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
368082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	/* find RSN IE */
369082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	data = ifmsh->ie;
370082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	while (data < ifmsh->ie + ifmsh->ie_len) {
371082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen		if (*data == WLAN_EID_RSN) {
372082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen			len = data[1] + 2;
373082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen			break;
374082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen		}
375082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen		data++;
376082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	}
377082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
378082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	if (len) {
379082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen		if (skb_tailroom(skb) < len)
380082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen			return -ENOMEM;
381082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen		memcpy(skb_put(skb, len), data, len);
382082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	}
383082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
384082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	return 0;
385082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen}
386082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
387bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Bergstatic int mesh_add_ds_params_ie(struct ieee80211_sub_if_data *sdata,
388bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg				 struct sk_buff *skb)
389082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen{
39055de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	struct ieee80211_chanctx_conf *chanctx_conf;
39155de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	struct ieee80211_channel *chan;
392082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	u8 *pos;
3932e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
394082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	if (skb_tailroom(skb) < 3)
395082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen		return -ENOMEM;
396082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
39755de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	rcu_read_lock();
39855de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
39955de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	if (WARN_ON(!chanctx_conf)) {
40055de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg		rcu_read_unlock();
40155de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg		return -EINVAL;
40255de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	}
4034bf88530be971bf95a7830ca61b4120980bf4347Johannes Berg	chan = chanctx_conf->def.chan;
40455de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	rcu_read_unlock();
40555de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg
406601513aa208f27ea87400a410d42c978421530ecEmanuel Taube	pos = skb_put(skb, 2 + 1);
407601513aa208f27ea87400a410d42c978421530ecEmanuel Taube	*pos++ = WLAN_EID_DS_PARAMS;
408601513aa208f27ea87400a410d42c978421530ecEmanuel Taube	*pos++ = 1;
409601513aa208f27ea87400a410d42c978421530ecEmanuel Taube	*pos++ = ieee80211_frequency_to_channel(chan->center_freq);
410be125c60e46e165fbfe33db36a4a9d943d560a5bRui Paulo
411082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	return 0;
4122e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo}
4132e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
414bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Bergint mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata,
415bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg		       struct sk_buff *skb)
416176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen{
417176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	struct ieee80211_local *local = sdata->local;
41855de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
419176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	struct ieee80211_supported_band *sband;
420176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	u8 *pos;
421176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen
42255de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	sband = local->hw.wiphy->bands[band];
423176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	if (!sband->ht_cap.ht_supported ||
4240418a445838749c51cf1e31a9c7ace6685ae87cdSimon Wunderlich	    sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
4250418a445838749c51cf1e31a9c7ace6685ae87cdSimon Wunderlich	    sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
4260418a445838749c51cf1e31a9c7ace6685ae87cdSimon Wunderlich	    sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10)
427176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen		return 0;
428176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen
429176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap))
430176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen		return -ENOMEM;
431176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen
432176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_cap));
433ef96a84202ccfb48a4569256ffba45e32308f7eeBen Greear	ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, sband->ht_cap.cap);
434176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen
435176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	return 0;
436176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen}
437176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen
438bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Bergint mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata,
439bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg			struct sk_buff *skb)
440176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen{
441176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	struct ieee80211_local *local = sdata->local;
44255de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	struct ieee80211_chanctx_conf *chanctx_conf;
44355de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	struct ieee80211_channel *channel;
444466f310d100ff54f346c1be481af9935c42467b3Johannes Berg	enum nl80211_channel_type channel_type =
4454bf88530be971bf95a7830ca61b4120980bf4347Johannes Berg		cfg80211_get_chandef_type(&sdata->vif.bss_conf.chandef);
44655de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	struct ieee80211_supported_band *sband;
44755de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	struct ieee80211_sta_ht_cap *ht_cap;
448176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	u8 *pos;
449176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen
45055de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	rcu_read_lock();
45155de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
45255de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	if (WARN_ON(!chanctx_conf)) {
45355de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg		rcu_read_unlock();
45455de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg		return -EINVAL;
45555de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	}
4564bf88530be971bf95a7830ca61b4120980bf4347Johannes Berg	channel = chanctx_conf->def.chan;
45755de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	rcu_read_unlock();
45855de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg
45955de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	sband = local->hw.wiphy->bands[channel->band];
46055de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	ht_cap = &sband->ht_cap;
46155de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg
462176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	if (!ht_cap->ht_supported || channel_type == NL80211_CHAN_NO_HT)
463176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen		return 0;
464176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen
465074d46d1d23f27488a3f314e29cae2453541f17dJohannes Berg	if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_operation))
466176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen		return -ENOMEM;
467176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen
468074d46d1d23f27488a3f314e29cae2453541f17dJohannes Berg	pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation));
4694bf88530be971bf95a7830ca61b4120980bf4347Johannes Berg	ieee80211_ie_build_ht_oper(pos, ht_cap, &sdata->vif.bss_conf.chandef,
470431e31542383b71bc5f2642572a1e6ef07f1bb87Ashok Nagarajan				   sdata->vif.bss_conf.ht_operation_mode);
471176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen
472176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	return 0;
473176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen}
474bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg
4752e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobostatic void ieee80211_mesh_path_timer(unsigned long data)
4762e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo{
4772e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	struct ieee80211_sub_if_data *sdata =
4782e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo		(struct ieee80211_sub_if_data *) data;
4795bb644a0fd25a5e083ecbfaa92a211db99aa6ef7Johannes Berg
480690205f18fd069898c70d743f498ba42798e5c4eStanislaw Gruszka	ieee80211_queue_work(&sdata->local->hw, &sdata->work);
4812e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo}
4822e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
483e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulostatic void ieee80211_mesh_path_root_timer(unsigned long data)
484e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo{
485e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo	struct ieee80211_sub_if_data *sdata =
486e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo		(struct ieee80211_sub_if_data *) data;
487e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
488e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo
489e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo	set_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags);
490e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo
491690205f18fd069898c70d743f498ba42798e5c4eStanislaw Gruszka	ieee80211_queue_work(&sdata->local->hw, &sdata->work);
492e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo}
493e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo
49463c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulovoid ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh)
49563c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo{
496dbb912cd4ce64e763c5610b49a85529d2634e9d8Chun-Yeow Yeoh	if (ifmsh->mshcfg.dot11MeshHWMPRootMode > IEEE80211_ROOTMODE_ROOT)
49763c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo		set_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags);
49863c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo	else {
49963c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo		clear_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags);
50063c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo		/* stop running timer */
50163c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo		del_timer_sync(&ifmsh->mesh_path_root_timer);
50263c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo	}
50363c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo}
50463c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo
505902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg/**
5063c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona * ieee80211_fill_mesh_addresses - fill addresses of a locally originated mesh frame
507bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg * @hdr:	802.11 frame header
5083c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona * @fc:		frame control field
5093c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona * @meshda:	destination address in the mesh
5103c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona * @meshsa:	source address address in the mesh.  Same as TA, as frame is
5113c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona *              locally originated.
5123c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona *
5133c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona * Return the length of the 802.11 (does not include a mesh control header)
5143c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona */
51515ff63653e507ec928a4a4386405a82446e096b1Johannes Bergint ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc,
51615ff63653e507ec928a4a4386405a82446e096b1Johannes Berg				  const u8 *meshda, const u8 *meshsa)
51715ff63653e507ec928a4a4386405a82446e096b1Johannes Berg{
5183c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona	if (is_multicast_ether_addr(meshda)) {
5193c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		*fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
5203c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		/* DA TA SA */
5213c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		memcpy(hdr->addr1, meshda, ETH_ALEN);
5223c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		memcpy(hdr->addr2, meshsa, ETH_ALEN);
5233c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		memcpy(hdr->addr3, meshsa, ETH_ALEN);
5243c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		return 24;
5253c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona	} else {
5262154c81c32fa44364f83218a10d8dbec4e76d4f5Javier Cardona		*fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
5273c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		/* RA TA DA SA */
5283c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		memset(hdr->addr1, 0, ETH_ALEN);   /* RA is resolved later */
5293c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		memcpy(hdr->addr2, meshsa, ETH_ALEN);
5303c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		memcpy(hdr->addr3, meshda, ETH_ALEN);
5313c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		memcpy(hdr->addr4, meshsa, ETH_ALEN);
5323c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		return 30;
5333c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona	}
5343c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona}
5353c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona
5363c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona/**
537902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg * ieee80211_new_mesh_header - create a new mesh header
538902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg * @sdata:	mesh interface to be used
539bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg * @meshhdr:    uninitialized mesh header
54061ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona * @addr4or5:   1st address in the ae header, which may correspond to address 4
54161ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona *              (if addr6 is NULL) or address 5 (if addr6 is present). It may
54261ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona *              be NULL.
54361ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona * @addr6:	2nd address in the ae header, which corresponds to addr6 of the
54461ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona *              mesh frame
545902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg *
546902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg * Return the header length.
547902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg */
548bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Bergint ieee80211_new_mesh_header(struct ieee80211_sub_if_data *sdata,
549bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg			      struct ieee80211s_hdr *meshhdr,
550bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg			      const char *addr4or5, const char *addr6)
551902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg{
552bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	if (WARN_ON(!addr4or5 && addr6))
553bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg		return 0;
554bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg
5550c3cee72a403e3b4992a5478c9c33d668c246c22Julia Lawall	memset(meshhdr, 0, sizeof(*meshhdr));
556bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg
557472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	meshhdr->ttl = sdata->u.mesh.mshcfg.dot11MeshTTL;
558bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg
559bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	/* FIXME: racy -- TX on multiple queues can be concurrent */
560472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &meshhdr->seqnum);
561472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	sdata->u.mesh.mesh_seqnum++;
562bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg
56361ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona	if (addr4or5 && !addr6) {
5643c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		meshhdr->flags |= MESH_FLAGS_AE_A4;
56561ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona		memcpy(meshhdr->eaddr1, addr4or5, ETH_ALEN);
566bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg		return 2 * ETH_ALEN;
56761ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona	} else if (addr4or5 && addr6) {
5683c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		meshhdr->flags |= MESH_FLAGS_AE_A5_A6;
56961ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona		memcpy(meshhdr->eaddr1, addr4or5, ETH_ALEN);
57061ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona		memcpy(meshhdr->eaddr2, addr6, ETH_ALEN);
571bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg		return 3 * ETH_ALEN;
5723c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona	}
573bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg
574bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	return ETH_ALEN;
575902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg}
576902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg
577bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Bergstatic void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata)
578472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg{
579bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
580df32381896f5f0c78a371df2e49ab7c776b1a5baMarco Porsch	u32 changed;
581472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
58266de671374f003467b5ef7c65ecbe1930480c8c9Colleen Twitty	ieee80211_sta_expire(sdata, ifmsh->mshcfg.plink_timeout * HZ);
583472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	mesh_path_expire(sdata);
584472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
585df32381896f5f0c78a371df2e49ab7c776b1a5baMarco Porsch	changed = mesh_accept_plinks_update(sdata);
5862b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	ieee80211_mbss_info_change_notify(sdata, changed);
587472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
588472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	mod_timer(&ifmsh->housekeeping_timer,
589bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg		  round_jiffies(jiffies +
590bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg				IEEE80211_MESH_HOUSEKEEPING_INTERVAL));
591472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg}
592472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
593e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulostatic void ieee80211_mesh_rootpath(struct ieee80211_sub_if_data *sdata)
594e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo{
595e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
596a69cc44fe9ebb806c5f3f8bd83fb4a50ca63647bChun-Yeow Yeoh	u32 interval;
597e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo
598e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo	mesh_path_tx_root_frame(sdata);
599a69cc44fe9ebb806c5f3f8bd83fb4a50ca63647bChun-Yeow Yeoh
600a69cc44fe9ebb806c5f3f8bd83fb4a50ca63647bChun-Yeow Yeoh	if (ifmsh->mshcfg.dot11MeshHWMPRootMode == IEEE80211_PROACTIVE_RANN)
601a69cc44fe9ebb806c5f3f8bd83fb4a50ca63647bChun-Yeow Yeoh		interval = ifmsh->mshcfg.dot11MeshHWMPRannInterval;
602a69cc44fe9ebb806c5f3f8bd83fb4a50ca63647bChun-Yeow Yeoh	else
603a69cc44fe9ebb806c5f3f8bd83fb4a50ca63647bChun-Yeow Yeoh		interval = ifmsh->mshcfg.dot11MeshHWMProotInterval;
604a69cc44fe9ebb806c5f3f8bd83fb4a50ca63647bChun-Yeow Yeoh
605e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo	mod_timer(&ifmsh->mesh_path_root_timer,
606a69cc44fe9ebb806c5f3f8bd83fb4a50ca63647bChun-Yeow Yeoh		  round_jiffies(TU_TO_EXP_TIME(interval)));
607e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo}
608e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo
6092b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersenstatic int
6102b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersenieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
6112b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen{
6122b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	struct beacon_data *bcn;
6132b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	int head_len, tail_len;
6142b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	struct sk_buff *skb;
6152b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	struct ieee80211_mgmt *mgmt;
6162b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	struct ieee80211_chanctx_conf *chanctx_conf;
617b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	struct mesh_csa_settings *csa;
6182b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	enum ieee80211_band band;
6192b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	u8 *pos;
6202b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	struct ieee80211_sub_if_data *sdata;
6212b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) +
6222b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		      sizeof(mgmt->u.beacon);
6232b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
6242b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	sdata = container_of(ifmsh, struct ieee80211_sub_if_data, u.mesh);
6252b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	rcu_read_lock();
6262b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
6272b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	band = chanctx_conf->def.chan->band;
6282b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	rcu_read_unlock();
6292b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
6302b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	head_len = hdr_len +
6312b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		   2 + /* NULL SSID */
632b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		   /* Channel Switch Announcement */
633b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		   2 + sizeof(struct ieee80211_channel_sw_ie) +
634b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		   /* Mesh Channel Swith Parameters */
635b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		   2 + sizeof(struct ieee80211_mesh_chansw_params_ie) +
6362b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		   2 + 8 + /* supported rates */
6372b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		   2 + 3; /* DS params */
6382b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
6392b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		   2 + sizeof(struct ieee80211_ht_cap) +
6402b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		   2 + sizeof(struct ieee80211_ht_operation) +
6412b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		   2 + ifmsh->mesh_id_len +
6422b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		   2 + sizeof(struct ieee80211_meshconf_ie) +
6432b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		   2 + sizeof(__le16) + /* awake window */
6442b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		   ifmsh->ie_len;
6452b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
6462b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	bcn = kzalloc(sizeof(*bcn) + head_len + tail_len, GFP_KERNEL);
6472b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	/* need an skb for IE builders to operate on */
6482b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	skb = dev_alloc_skb(max(head_len, tail_len));
6492b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
6502b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	if (!bcn || !skb)
6512b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		goto out_free;
6522b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
6532b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	/*
6542b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	 * pointers go into the block we allocated,
6552b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	 * memory is | beacon_data | head | tail |
6562b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	 */
6572b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	bcn->head = ((u8 *) bcn) + sizeof(*bcn);
6582b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
6592b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	/* fill in the head */
6602b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
6612b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	memset(mgmt, 0, hdr_len);
6622b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
6632b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen					  IEEE80211_STYPE_BEACON);
6642b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	eth_broadcast_addr(mgmt->da);
6652b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
6662b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
6672b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	ieee80211_mps_set_frame_flags(sdata, NULL, (void *) mgmt);
6682b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	mgmt->u.beacon.beacon_int =
6692b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		cpu_to_le16(sdata->vif.bss_conf.beacon_int);
6702b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	mgmt->u.beacon.capab_info |= cpu_to_le16(
6712b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		sdata->u.mesh.security ? WLAN_CAPABILITY_PRIVACY : 0);
6722b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
6732b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	pos = skb_put(skb, 2);
6742b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	*pos++ = WLAN_EID_SSID;
6752b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	*pos++ = 0x0;
6762b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
677b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	rcu_read_lock();
678b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	csa = rcu_dereference(ifmsh->csa);
679b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	if (csa) {
680b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		pos = skb_put(skb, 13);
681b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		memset(pos, 0, 13);
682b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		*pos++ = WLAN_EID_CHANNEL_SWITCH;
683b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		*pos++ = 3;
684b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		*pos++ = 0x0;
685b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		*pos++ = ieee80211_frequency_to_channel(
686b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh				csa->settings.chandef.chan->center_freq);
687b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		sdata->csa_counter_offset_beacon = hdr_len + 6;
688b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		*pos++ = csa->settings.count;
689b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		*pos++ = WLAN_EID_CHAN_SWITCH_PARAM;
690b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		*pos++ = 6;
6910cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho		if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT) {
692b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh			*pos++ = ifmsh->mshcfg.dot11MeshTTL;
693b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh			*pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
694b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		} else {
695b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh			*pos++ = ifmsh->chsw_ttl;
696b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		}
697b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		*pos++ |= csa->settings.block_tx ?
698b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh			  WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
699b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos);
700b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		pos += 2;
701ca91dc97b8a0ffd05721806654eaff2cf13ba5cbChun-Yeow Yeoh		put_unaligned_le16(ifmsh->pre_value, pos);
702b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		pos += 2;
703b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	}
704b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	rcu_read_unlock();
705b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
7062b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
707bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	    mesh_add_ds_params_ie(sdata, skb))
7082b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		goto out_free;
7092b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7102b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	bcn->head_len = skb->len;
7112b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	memcpy(bcn->head, skb->data, bcn->head_len);
7122b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7132b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	/* now the tail */
7142b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	skb_trim(skb, 0);
7152b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	bcn->tail = bcn->head + bcn->head_len;
7162b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7172b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	if (ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
718bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	    mesh_add_rsn_ie(sdata, skb) ||
719bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	    mesh_add_ht_cap_ie(sdata, skb) ||
720bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	    mesh_add_ht_oper_ie(sdata, skb) ||
721bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	    mesh_add_meshid_ie(sdata, skb) ||
722bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	    mesh_add_meshconf_ie(sdata, skb) ||
723bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	    mesh_add_awake_window_ie(sdata, skb) ||
724bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	    mesh_add_vendor_ies(sdata, skb))
7252b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		goto out_free;
7262b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7272b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	bcn->tail_len = skb->len;
7282b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	memcpy(bcn->tail, skb->data, bcn->tail_len);
72943552be1da3c420931c89727b6115b7fa35368f8Thomas Pedersen	bcn->meshconf = (struct ieee80211_meshconf_ie *)
73043552be1da3c420931c89727b6115b7fa35368f8Thomas Pedersen					(bcn->tail + ifmsh->meshconf_offset);
7312b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7322b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	dev_kfree_skb(skb);
7332b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	rcu_assign_pointer(ifmsh->beacon, bcn);
7342b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	return 0;
7352b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersenout_free:
7362b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	kfree(bcn);
7372b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	dev_kfree_skb(skb);
7382b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	return -ENOMEM;
7392b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen}
7402b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7412b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersenstatic int
7428d61ffa5e01c5f676431d12caba17db164a48a86Johannes Bergieee80211_mesh_rebuild_beacon(struct ieee80211_sub_if_data *sdata)
7432b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen{
7442b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	struct beacon_data *old_bcn;
7452b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	int ret;
7462b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7478d61ffa5e01c5f676431d12caba17db164a48a86Johannes Berg	old_bcn = rcu_dereference_protected(sdata->u.mesh.beacon,
7488d61ffa5e01c5f676431d12caba17db164a48a86Johannes Berg					    lockdep_is_held(&sdata->wdev.mtx));
7498d61ffa5e01c5f676431d12caba17db164a48a86Johannes Berg	ret = ieee80211_mesh_build_beacon(&sdata->u.mesh);
7502b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	if (ret)
7512b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		/* just reuse old beacon */
7528d61ffa5e01c5f676431d12caba17db164a48a86Johannes Berg		return ret;
7532b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7542b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	if (old_bcn)
7552b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		kfree_rcu(old_bcn, rcu_head);
7568d61ffa5e01c5f676431d12caba17db164a48a86Johannes Berg	return 0;
7572b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen}
7582b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7592b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersenvoid ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata,
7602b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen				       u32 changed)
7612b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen{
762f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
763f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	unsigned long bits = changed;
764f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	u32 bit;
765f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen
766f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	if (!bits)
767f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen		return;
768f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen
769f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	/* if we race with running work, worst case this work becomes a noop */
770f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	for_each_set_bit(bit, &bits, sizeof(changed) * BITS_PER_BYTE)
771f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen		set_bit(bit, &ifmsh->mbss_changed);
772f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	set_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags);
773f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	ieee80211_queue_work(&sdata->local->hw, &sdata->work);
7742b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen}
7752b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7762b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersenint ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
777472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg{
778472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
779472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_local *local = sdata->local;
780f4eabc918c3b88763bc20dd9e2b248aa6c757005Chun-Yeow Yeoh	u32 changed = BSS_CHANGED_BEACON |
781f4eabc918c3b88763bc20dd9e2b248aa6c757005Chun-Yeow Yeoh		      BSS_CHANGED_BEACON_ENABLED |
782f4eabc918c3b88763bc20dd9e2b248aa6c757005Chun-Yeow Yeoh		      BSS_CHANGED_HT |
783f4eabc918c3b88763bc20dd9e2b248aa6c757005Chun-Yeow Yeoh		      BSS_CHANGED_BASIC_RATES |
784f4eabc918c3b88763bc20dd9e2b248aa6c757005Chun-Yeow Yeoh		      BSS_CHANGED_BEACON_INT;
785472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
78609b174702601079c3a04806754be30ffbd70db4dJohannes Berg	local->fif_other_bss++;
78709b174702601079c3a04806754be30ffbd70db4dJohannes Berg	/* mesh ifaces must set allmulti to forward mcast traffic */
78809b174702601079c3a04806754be30ffbd70db4dJohannes Berg	atomic_inc(&local->iff_allmultis);
78909b174702601079c3a04806754be30ffbd70db4dJohannes Berg	ieee80211_configure_filter(local);
79009b174702601079c3a04806754be30ffbd70db4dJohannes Berg
791c7108a7111cd9e592d6ad498be37276dbea75d2bJavier Cardona	ifmsh->mesh_cc_id = 0;	/* Disabled */
792dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona	/* register sync ops from extensible synchronization framework */
793dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona	ifmsh->sync_ops = ieee80211_mesh_sync_ops_get(ifmsh->mesh_sp_id);
794dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona	ifmsh->adjusting_tbtt = false;
795dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona	ifmsh->sync_offset_clockdrift_max = 0;
7966b9ac4425d6ec871faf54540e0f1c5ff420a8f29Rui Paulo	set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags);
79763c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo	ieee80211_mesh_root_setup(ifmsh);
79864592c8fc0e99d445fc3fdedddeb6088e20086f1Johannes Berg	ieee80211_queue_work(&local->hw, &sdata->work);
79970c33eaae79e53f9e48324736c0cb85534d3f093Ashok Nagarajan	sdata->vif.bss_conf.ht_operation_mode =
80070c33eaae79e53f9e48324736c0cb85534d3f093Ashok Nagarajan				ifmsh->mshcfg.ht_opmode;
801d6a83228823fc0cc8d79d95c9f0bf568b7317862Johannes Berg	sdata->vif.bss_conf.enable_beacon = true;
802f4eabc918c3b88763bc20dd9e2b248aa6c757005Chun-Yeow Yeoh
80339886b618aba3c39e650c191d601e26ec581ce0fThomas Pedersen	changed |= ieee80211_mps_local_status_update(sdata);
8043f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch
8052b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	if (ieee80211_mesh_build_beacon(ifmsh)) {
8062b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		ieee80211_stop_mesh(sdata);
8072b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		return -ENOMEM;
8082b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	}
8092b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
810057d5f4ba1e421185a8e7e0b7fadf253d41a3e83Thomas Pedersen	ieee80211_recalc_dtim(local, sdata);
811f4eabc918c3b88763bc20dd9e2b248aa6c757005Chun-Yeow Yeoh	ieee80211_bss_info_change_notify(sdata, changed);
812c405c6298eacd423098afacf6020ddbda1b0378bJohannes Berg
813c405c6298eacd423098afacf6020ddbda1b0378bJohannes Berg	netif_carrier_on(sdata->dev);
8142b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	return 0;
815472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg}
816472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
817472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Bergvoid ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
818472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg{
81909b174702601079c3a04806754be30ffbd70db4dJohannes Berg	struct ieee80211_local *local = sdata->local;
82029cbe68c516a48a9a88b3226878570c6cbd83c02Johannes Berg	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
8212b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	struct beacon_data *bcn;
82229cbe68c516a48a9a88b3226878570c6cbd83c02Johannes Berg
823c405c6298eacd423098afacf6020ddbda1b0378bJohannes Berg	netif_carrier_off(sdata->dev);
824c405c6298eacd423098afacf6020ddbda1b0378bJohannes Berg
8250d466b9c6798d431141ab15ae6d5ea413b4d09b2Thomas Pedersen	/* stop the beacon */
82629cbe68c516a48a9a88b3226878570c6cbd83c02Johannes Berg	ifmsh->mesh_id_len = 0;
827d6a83228823fc0cc8d79d95c9f0bf568b7317862Johannes Berg	sdata->vif.bss_conf.enable_beacon = false;
828d6a83228823fc0cc8d79d95c9f0bf568b7317862Johannes Berg	clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
82929cbe68c516a48a9a88b3226878570c6cbd83c02Johannes Berg	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
8302b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	bcn = rcu_dereference_protected(ifmsh->beacon,
8318d61ffa5e01c5f676431d12caba17db164a48a86Johannes Berg					lockdep_is_held(&sdata->wdev.mtx));
8322b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	rcu_assign_pointer(ifmsh->beacon, NULL);
8332b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	kfree_rcu(bcn, rcu_head);
8340d466b9c6798d431141ab15ae6d5ea413b4d09b2Thomas Pedersen
8350d466b9c6798d431141ab15ae6d5ea413b4d09b2Thomas Pedersen	/* flush STAs and mpaths on this iface */
836b998e8bb3e1c6eeae5eab9d6a434563270286c3bJohannes Berg	sta_info_flush(sdata);
8370d466b9c6798d431141ab15ae6d5ea413b4d09b2Thomas Pedersen	mesh_path_flush_by_iface(sdata);
83809b174702601079c3a04806754be30ffbd70db4dJohannes Berg
8393f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	/* free all potentially still buffered group-addressed frames */
8403f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	local->total_ps_buffered -= skb_queue_len(&ifmsh->ps.bc_buf);
8413f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	skb_queue_purge(&ifmsh->ps.bc_buf);
8423f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch
843472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	del_timer_sync(&sdata->u.mesh.housekeeping_timer);
844e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo	del_timer_sync(&sdata->u.mesh.mesh_path_root_timer);
845dd4c9260e7f23f2e951cbfb2726e468c6d30306cJohannes Berg	del_timer_sync(&sdata->u.mesh.mesh_path_timer);
84609b174702601079c3a04806754be30ffbd70db4dJohannes Berg
847f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	/* clear any mesh work (for next join) we may have accrued */
848f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	ifmsh->wrkq_flags = 0;
849f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	ifmsh->mbss_changed = 0;
850f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen
85109b174702601079c3a04806754be30ffbd70db4dJohannes Berg	local->fif_other_bss--;
85209b174702601079c3a04806754be30ffbd70db4dJohannes Berg	atomic_dec(&local->iff_allmultis);
85309b174702601079c3a04806754be30ffbd70db4dJohannes Berg	ieee80211_configure_filter(local);
854472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg}
855472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
85633a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeohstatic bool
85733a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeohieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
85833a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh				 struct ieee802_11_elems *elems, bool beacon)
85933a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh{
86033a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	struct cfg80211_csa_settings params;
86133a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	struct ieee80211_csa_ie csa_ie;
86233a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
86333a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
8640cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	int err;
86533a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	u32 sta_flags;
86633a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
86733a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	sta_flags = IEEE80211_STA_DISABLE_VHT;
86833a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	switch (sdata->vif.bss_conf.chandef.width) {
86933a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	case NL80211_CHAN_WIDTH_20_NOHT:
87033a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		sta_flags |= IEEE80211_STA_DISABLE_HT;
87133a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	case NL80211_CHAN_WIDTH_20:
87233a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		sta_flags |= IEEE80211_STA_DISABLE_40MHZ;
87333a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		break;
87433a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	default:
87533a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		break;
87633a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	}
87733a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
87833a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	memset(&params, 0, sizeof(params));
87933a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	memset(&csa_ie, 0, sizeof(csa_ie));
88033a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band,
88133a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh					   sta_flags, sdata->vif.addr,
88233a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh					   &csa_ie);
88333a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	if (err < 0)
88433a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		return false;
88533a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	if (err)
88633a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		return false;
88733a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
88833a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	params.chandef = csa_ie.chandef;
88933a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	params.count = csa_ie.count;
89033a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
89133a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, &params.chandef,
89233a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh				     IEEE80211_CHAN_DISABLED)) {
89333a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		sdata_info(sdata,
89433a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh			   "mesh STA %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), aborting\n",
89533a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh			   sdata->vif.addr,
89633a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh			   params.chandef.chan->center_freq,
89733a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh			   params.chandef.width,
89833a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh			   params.chandef.center_freq1,
89933a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh			   params.chandef.center_freq2);
90033a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		return false;
90133a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	}
90233a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
90333a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
90433a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh					    &params.chandef);
90533a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	if (err < 0)
90633a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		return false;
90733a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	if (err) {
90833a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		params.radar_required = true;
90933a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		/* TODO: DFS not (yet) supported */
91033a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		return false;
91133a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	}
91233a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
9130cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	if (cfg80211_chandef_identical(&params.chandef,
9140cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho				       &sdata->vif.bss_conf.chandef)) {
9150cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho		mcsa_dbg(sdata,
9160cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho			 "received csa with an identical chandef, ignoring\n");
9170cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho		return true;
9180cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	}
91933a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
92033a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	mcsa_dbg(sdata,
92133a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		 "received channel switch announcement to go to channel %d MHz\n",
92233a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		 params.chandef.chan->center_freq);
92333a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
92433a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	params.block_tx = csa_ie.mode & WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT;
9253f718fd8401d7db86b9efc3ea1cdf5df41354b9fChun-Yeow Yeoh	if (beacon) {
92633a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		ifmsh->chsw_ttl = csa_ie.ttl - 1;
9273f718fd8401d7db86b9efc3ea1cdf5df41354b9fChun-Yeow Yeoh		if (ifmsh->pre_value >= csa_ie.pre_value)
9283f718fd8401d7db86b9efc3ea1cdf5df41354b9fChun-Yeow Yeoh			return false;
9293f718fd8401d7db86b9efc3ea1cdf5df41354b9fChun-Yeow Yeoh		ifmsh->pre_value = csa_ie.pre_value;
9303f718fd8401d7db86b9efc3ea1cdf5df41354b9fChun-Yeow Yeoh	}
93133a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
9320cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	if (ifmsh->chsw_ttl >= ifmsh->mshcfg.dot11MeshTTL)
9333f718fd8401d7db86b9efc3ea1cdf5df41354b9fChun-Yeow Yeoh		return false;
93433a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
9350cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_REPEATER;
93633a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
9370cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	if (ieee80211_channel_switch(sdata->local->hw.wiphy, sdata->dev,
9380cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho				     &params) < 0)
9390cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho		return false;
94033a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
94133a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	return true;
94233a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh}
94333a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
9449fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersenstatic void
9459fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersenieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
9469fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen			    struct ieee80211_mgmt *mgmt, size_t len)
9479fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen{
9489fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	struct ieee80211_local *local = sdata->local;
9499fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
9509fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	struct sk_buff *presp;
9519fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	struct beacon_data *bcn;
9529fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	struct ieee80211_mgmt *hdr;
9539fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	struct ieee802_11_elems elems;
9549fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	size_t baselen;
955511044ea0bfc06614d903263ad094d1071fa172fJohannes Berg	u8 *pos;
9569fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen
9579fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	pos = mgmt->u.probe_req.variable;
9589fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	baselen = (u8 *) pos - (u8 *) mgmt;
9599fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	if (baselen > len)
9609fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen		return;
9619fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen
962b2e506bfc4d752b68a0ccaae1e977898263eba4cJohannes Berg	ieee802_11_parse_elems(pos, len - baselen, false, &elems);
9639fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen
964a4ef66a915b957416a89a48365aea2ec2dc551f6Chun-Yeow Yeoh	if (!elems.mesh_id)
965a4ef66a915b957416a89a48365aea2ec2dc551f6Chun-Yeow Yeoh		return;
966a4ef66a915b957416a89a48365aea2ec2dc551f6Chun-Yeow Yeoh
9679fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	/* 802.11-2012 10.1.4.3.2 */
9689fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	if ((!ether_addr_equal(mgmt->da, sdata->vif.addr) &&
9699fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	     !is_broadcast_ether_addr(mgmt->da)) ||
9709fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	    elems.ssid_len != 0)
9719fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen		return;
9729fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen
9739fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	if (elems.mesh_id_len != 0 &&
9749fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	    (elems.mesh_id_len != ifmsh->mesh_id_len ||
9759fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	     memcmp(elems.mesh_id, ifmsh->mesh_id, ifmsh->mesh_id_len)))
9769fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen		return;
9779fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen
9789fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	rcu_read_lock();
9799fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	bcn = rcu_dereference(ifmsh->beacon);
9809fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen
9819fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	if (!bcn)
9829fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen		goto out;
9839fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen
9849fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	presp = dev_alloc_skb(local->tx_headroom +
9859fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen			      bcn->head_len + bcn->tail_len);
9869fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	if (!presp)
9879fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen		goto out;
9889fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen
9899fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	skb_reserve(presp, local->tx_headroom);
9909fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	memcpy(skb_put(presp, bcn->head_len), bcn->head, bcn->head_len);
9919fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	memcpy(skb_put(presp, bcn->tail_len), bcn->tail, bcn->tail_len);
9929fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	hdr = (struct ieee80211_mgmt *) presp->data;
9939fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
9949fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen					 IEEE80211_STYPE_PROBE_RESP);
9959fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	memcpy(hdr->da, mgmt->sa, ETH_ALEN);
9969fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	IEEE80211_SKB_CB(presp)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
9979fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	ieee80211_tx_skb(sdata, presp);
9989fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersenout:
9999fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	rcu_read_unlock();
10009fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen}
10019fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen
1002472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Bergstatic void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
1003472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg					u16 stype,
1004472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg					struct ieee80211_mgmt *mgmt,
1005472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg					size_t len,
1006472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg					struct ieee80211_rx_status *rx_status)
1007472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg{
1008c6a1fa12d206882757264869f8e32d606b930e2aJohannes Berg	struct ieee80211_local *local = sdata->local;
1009dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
1010472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee802_11_elems elems;
1011472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_channel *channel;
1012472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	size_t baselen;
1013472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	int freq;
1014472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	enum ieee80211_band band = rx_status->band;
1015472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1016472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	/* ignore ProbeResp to foreign address */
1017472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	if (stype == IEEE80211_STYPE_PROBE_RESP &&
1018b203ca39126bad99583c908be587df067820a1eaJoe Perches	    !ether_addr_equal(mgmt->da, sdata->vif.addr))
1019472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		return;
1020472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1021472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
1022472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	if (baselen > len)
1023472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		return;
1024472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1025472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
1026b2e506bfc4d752b68a0ccaae1e977898263eba4cJohannes Berg			       false, &elems);
1027472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
10289a90bc81914ee77edcd6eb7e881639b7f7bf1667Thomas Pedersen	/* ignore non-mesh or secure / unsecure mismatch */
10299a90bc81914ee77edcd6eb7e881639b7f7bf1667Thomas Pedersen	if ((!elems.mesh_id || !elems.mesh_config) ||
10309a90bc81914ee77edcd6eb7e881639b7f7bf1667Thomas Pedersen	    (elems.rsn && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) ||
10319a90bc81914ee77edcd6eb7e881639b7f7bf1667Thomas Pedersen	    (!elems.rsn && sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE))
10325cff5e01e818029a5d2c3c31b7ae5e5e7ee70452Javier Cardona		return;
10335cff5e01e818029a5d2c3c31b7ae5e5e7ee70452Javier Cardona
10341cd8e88e17729f57a9c7f751103e522596bb5de2Johannes Berg	if (elems.ds_params)
103559eb21a6504731fc16db4cf9463065dd61093e08Bruno Randolf		freq = ieee80211_channel_to_frequency(elems.ds_params[0], band);
1036472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	else
1037472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		freq = rx_status->freq;
1038472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1039472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	channel = ieee80211_get_channel(local->hw.wiphy, freq);
1040472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1041472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
1042472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		return;
1043472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
10449a90bc81914ee77edcd6eb7e881639b7f7bf1667Thomas Pedersen	if (mesh_matches_local(sdata, &elems))
1045f743ff4907fa5bc2b460f48ace831a560806a9fbThomas Pedersen		mesh_neighbour_update(sdata, mgmt->sa, &elems);
1046dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona
1047dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona	if (ifmsh->sync_ops)
1048dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona		ifmsh->sync_ops->rx_bcn_presp(sdata,
1049dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona			stype, mgmt, &elems, rx_status);
105033a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
10510cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	if (ifmsh->csa_role != IEEE80211_MESH_CSA_ROLE_INIT &&
10520cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	    !sdata->vif.csa_active)
105333a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		ieee80211_mesh_process_chnswitch(sdata, &elems, true);
1054472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg}
1055472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1056b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeohint ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
1057b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh{
1058b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
1059b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	struct mesh_csa_settings *tmp_csa_settings;
1060b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	int ret = 0;
1061b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
1062b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	/* Reset the TTL value and Initiator flag */
10630cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;
1064b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	ifmsh->chsw_ttl = 0;
1065b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
1066b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	/* Remove the CSA and MCSP elements from the beacon */
1067b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	tmp_csa_settings = rcu_dereference(ifmsh->csa);
1068b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	rcu_assign_pointer(ifmsh->csa, NULL);
1069b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	kfree_rcu(tmp_csa_settings, rcu_head);
1070b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	ret = ieee80211_mesh_rebuild_beacon(sdata);
1071b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	if (ret)
1072b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		return -EINVAL;
1073b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
1074b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
1075b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
1076b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	mcsa_dbg(sdata, "complete switching to center freq %d MHz",
1077b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		 sdata->vif.bss_conf.chandef.chan->center_freq);
1078b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	return 0;
1079b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh}
1080b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
1081b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeohint ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
1082b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh			      struct cfg80211_csa_settings *csa_settings,
1083b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh			      bool csa_action)
1084b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh{
1085b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
1086b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	struct mesh_csa_settings *tmp_csa_settings;
1087b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	int ret = 0;
1088b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
1089b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	tmp_csa_settings = kmalloc(sizeof(*tmp_csa_settings),
1090b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh				   GFP_ATOMIC);
1091b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	if (!tmp_csa_settings)
1092b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		return -ENOMEM;
1093b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
1094b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	memcpy(&tmp_csa_settings->settings, csa_settings,
1095b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	       sizeof(struct cfg80211_csa_settings));
1096b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
1097b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	rcu_assign_pointer(ifmsh->csa, tmp_csa_settings);
1098b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
1099b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	ret = ieee80211_mesh_rebuild_beacon(sdata);
1100b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	if (ret) {
1101b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		tmp_csa_settings = rcu_dereference(ifmsh->csa);
1102b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		rcu_assign_pointer(ifmsh->csa, NULL);
1103b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		kfree_rcu(tmp_csa_settings, rcu_head);
1104b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		return ret;
1105b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	}
1106b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
1107b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	if (csa_action)
1108b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		ieee80211_send_action_csa(sdata, csa_settings);
1109b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
1110b58e81e96a81c80886011ad87cdbe73585dec4f7Luciano Coelho	return BSS_CHANGED_BEACON;
1111b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh}
1112b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
11138f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeohstatic int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata,
11148f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh			       struct ieee80211_mgmt *mgmt, size_t len)
11158f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh{
11168f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	struct ieee80211_mgmt *mgmt_fwd;
11178f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	struct sk_buff *skb;
11188f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	struct ieee80211_local *local = sdata->local;
11198f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	u8 *pos = mgmt->u.action.u.chan_switch.variable;
11208f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	size_t offset_ttl;
11218f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11228f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	skb = dev_alloc_skb(local->tx_headroom + len);
11238f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	if (!skb)
11248f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh		return -ENOMEM;
11258f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	skb_reserve(skb, local->tx_headroom);
11268f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	mgmt_fwd = (struct ieee80211_mgmt *) skb_put(skb, len);
11278f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11288f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	/* offset_ttl is based on whether the secondary channel
11298f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	 * offset is available or not. Substract 1 from the mesh TTL
11308f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	 * and disable the initiator flag before forwarding.
11318f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	 */
11328f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	offset_ttl = (len < 42) ? 7 : 10;
11338f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	*(pos + offset_ttl) -= 1;
11348f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	*(pos + offset_ttl + 1) &= ~WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
11358f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11368f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	memcpy(mgmt_fwd, mgmt, len);
11378f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	eth_broadcast_addr(mgmt_fwd->da);
11388f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	memcpy(mgmt_fwd->sa, sdata->vif.addr, ETH_ALEN);
11398f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	memcpy(mgmt_fwd->bssid, sdata->vif.addr, ETH_ALEN);
11408f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11418f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	ieee80211_tx_skb(sdata, skb);
11428f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	return 0;
11438f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh}
11448f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11458f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeohstatic void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
11468f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh			      struct ieee80211_mgmt *mgmt, size_t len)
11478f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh{
11488f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
11498f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	struct ieee802_11_elems elems;
11508f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	u16 pre_value;
115133a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	bool fwd_csa = true;
11528f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	size_t baselen;
11533f718fd8401d7db86b9efc3ea1cdf5df41354b9fChun-Yeow Yeoh	u8 *pos;
11548f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11558f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	if (mgmt->u.action.u.measurement.action_code !=
11568f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	    WLAN_ACTION_SPCT_CHL_SWITCH)
11578f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh		return;
11588f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11598f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	pos = mgmt->u.action.u.chan_switch.variable;
11608f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	baselen = offsetof(struct ieee80211_mgmt,
11618f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh			   u.action.u.chan_switch.variable);
11628f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	ieee802_11_parse_elems(pos, len - baselen, false, &elems);
11638f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11643f718fd8401d7db86b9efc3ea1cdf5df41354b9fChun-Yeow Yeoh	ifmsh->chsw_ttl = elems.mesh_chansw_params_ie->mesh_ttl;
11653f718fd8401d7db86b9efc3ea1cdf5df41354b9fChun-Yeow Yeoh	if (!--ifmsh->chsw_ttl)
11668f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh		fwd_csa = false;
11678f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11688f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	pre_value = le16_to_cpu(elems.mesh_chansw_params_ie->mesh_pre_value);
11698f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	if (ifmsh->pre_value >= pre_value)
11708f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh		return;
11718f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11728f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	ifmsh->pre_value = pre_value;
11738f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11740cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	if (!sdata->vif.csa_active &&
11750cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	    !ieee80211_mesh_process_chnswitch(sdata, &elems, false)) {
117633a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		mcsa_dbg(sdata, "Failed to process CSA action frame");
117733a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		return;
117833a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	}
117933a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
11808f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	/* forward or re-broadcast the CSA frame */
11818f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	if (fwd_csa) {
11828f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh		if (mesh_fwd_csa_frame(sdata, mgmt, len) < 0)
11838f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh			mcsa_dbg(sdata, "Failed to forward the CSA frame");
11848f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	}
11858f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh}
11868f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
1187472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Bergstatic void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
1188472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg					  struct ieee80211_mgmt *mgmt,
1189472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg					  size_t len,
1190472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg					  struct ieee80211_rx_status *rx_status)
1191472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg{
1192472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	switch (mgmt->u.action.category) {
11938db098507c5cbe499061d0f6aea426a36e7c72d7Thomas Pedersen	case WLAN_CATEGORY_SELF_PROTECTED:
11948db098507c5cbe499061d0f6aea426a36e7c72d7Thomas Pedersen		switch (mgmt->u.action.u.self_prot.action_code) {
11958db098507c5cbe499061d0f6aea426a36e7c72d7Thomas Pedersen		case WLAN_SP_MESH_PEERING_OPEN:
11968db098507c5cbe499061d0f6aea426a36e7c72d7Thomas Pedersen		case WLAN_SP_MESH_PEERING_CLOSE:
11978db098507c5cbe499061d0f6aea426a36e7c72d7Thomas Pedersen		case WLAN_SP_MESH_PEERING_CONFIRM:
11988db098507c5cbe499061d0f6aea426a36e7c72d7Thomas Pedersen			mesh_rx_plink_frame(sdata, mgmt, len, rx_status);
11998db098507c5cbe499061d0f6aea426a36e7c72d7Thomas Pedersen			break;
12008db098507c5cbe499061d0f6aea426a36e7c72d7Thomas Pedersen		}
1201472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		break;
120225d49e4d63564c7004a4d6735d1d8c3cc41a7394Thomas Pedersen	case WLAN_CATEGORY_MESH_ACTION:
120325d49e4d63564c7004a4d6735d1d8c3cc41a7394Thomas Pedersen		if (mesh_action_is_path_sel(mgmt))
120425d49e4d63564c7004a4d6735d1d8c3cc41a7394Thomas Pedersen			mesh_rx_path_sel_frame(sdata, mgmt, len);
1205472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		break;
12068f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	case WLAN_CATEGORY_SPECTRUM_MGMT:
12078f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh		mesh_rx_csa_frame(sdata, mgmt, len);
12088f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh		break;
1209472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	}
1210472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg}
1211472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
12121fa57d017366fb26b58af110a38b36a4f0214a62Johannes Bergvoid ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
12131fa57d017366fb26b58af110a38b36a4f0214a62Johannes Berg				   struct sk_buff *skb)
1214472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg{
1215472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_rx_status *rx_status;
1216472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_mgmt *mgmt;
1217472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	u16 stype;
1218472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1219ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen	sdata_lock(sdata);
1220ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen
1221ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen	/* mesh already went down */
1222ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen	if (!sdata->wdev.mesh_id_len)
1223ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen		goto out;
1224ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen
1225f1d58c2521eb160178b2151d6326d8dc5d7c8560Johannes Berg	rx_status = IEEE80211_SKB_RXCB(skb);
1226472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	mgmt = (struct ieee80211_mgmt *) skb->data;
1227472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE;
1228472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1229472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	switch (stype) {
1230472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	case IEEE80211_STYPE_PROBE_RESP:
1231472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	case IEEE80211_STYPE_BEACON:
1232472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		ieee80211_mesh_rx_bcn_presp(sdata, stype, mgmt, skb->len,
1233472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg					    rx_status);
1234472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		break;
12359fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	case IEEE80211_STYPE_PROBE_REQ:
12369fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen		ieee80211_mesh_rx_probe_req(sdata, mgmt, skb->len);
12379fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen		break;
1238472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	case IEEE80211_STYPE_ACTION:
1239472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		ieee80211_mesh_rx_mgmt_action(sdata, mgmt, skb->len, rx_status);
1240472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		break;
1241472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	}
1242ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersenout:
1243ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen	sdata_unlock(sdata);
1244472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg}
1245472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1246f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersenstatic void mesh_bss_info_changed(struct ieee80211_sub_if_data *sdata)
1247f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen{
1248f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
1249f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	u32 bit, changed = 0;
1250f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen
1251f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	for_each_set_bit(bit, &ifmsh->mbss_changed,
1252f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen			 sizeof(changed) * BITS_PER_BYTE) {
1253f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen		clear_bit(bit, &ifmsh->mbss_changed);
1254f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen		changed |= BIT(bit);
1255f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	}
1256f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen
1257f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	if (sdata->vif.bss_conf.enable_beacon &&
1258f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	    (changed & (BSS_CHANGED_BEACON |
1259f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen			BSS_CHANGED_HT |
1260f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen			BSS_CHANGED_BASIC_RATES |
1261f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen			BSS_CHANGED_BEACON_INT)))
1262f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen		if (ieee80211_mesh_rebuild_beacon(sdata))
1263f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen			return;
1264f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen
1265f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	ieee80211_bss_info_change_notify(sdata, changed);
1266f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen}
1267f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen
12681fa57d017366fb26b58af110a38b36a4f0214a62Johannes Bergvoid ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
1269472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg{
1270472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
1271472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1272ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen	sdata_lock(sdata);
1273ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen
1274ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen	/* mesh already went down */
1275ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen	if (!sdata->wdev.mesh_id_len)
1276ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen		goto out;
1277ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen
1278472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	if (ifmsh->preq_queue_len &&
1279472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	    time_after(jiffies,
1280472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		       ifmsh->last_preq + msecs_to_jiffies(ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval)))
1281472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		mesh_path_start_discovery(sdata);
1282472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
128318889231e4527dfe23145efe318e74744794a95dJavier Cardona	if (test_and_clear_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags))
128418889231e4527dfe23145efe318e74744794a95dJavier Cardona		mesh_mpath_table_grow();
128518889231e4527dfe23145efe318e74744794a95dJavier Cardona
1286dcac908babcd8ce21057e476c8df609b28ad2cd8Nick Ledovskikh	if (test_and_clear_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags))
128718889231e4527dfe23145efe318e74744794a95dJavier Cardona		mesh_mpp_table_grow();
128818889231e4527dfe23145efe318e74744794a95dJavier Cardona
128918889231e4527dfe23145efe318e74744794a95dJavier Cardona	if (test_and_clear_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags))
1290bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg		ieee80211_mesh_housekeeping(sdata);
1291e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo
1292e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo	if (test_and_clear_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags))
1293e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo		ieee80211_mesh_rootpath(sdata);
1294dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona
1295dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona	if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags))
1296dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona		mesh_sync_adjust_tbtt(sdata);
1297ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen
1298f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	if (test_and_clear_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags))
1299f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen		mesh_bss_info_changed(sdata);
1300ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersenout:
1301ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen	sdata_unlock(sdata);
1302472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg}
1303472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1304472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Bergvoid ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local)
1305472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg{
1306472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_sub_if_data *sdata;
1307472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1308472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	rcu_read_lock();
1309472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	list_for_each_entry_rcu(sdata, &local->interfaces, list)
1310370bd005937c0e00f9104a602f9fe1dd6b21b54bBen Greear		if (ieee80211_vif_is_mesh(&sdata->vif) &&
1311370bd005937c0e00f9104a602f9fe1dd6b21b54bBen Greear		    ieee80211_sdata_running(sdata))
131264592c8fc0e99d445fc3fdedddeb6088e20086f1Johannes Berg			ieee80211_queue_work(&local->hw, &sdata->work);
1313472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	rcu_read_unlock();
1314472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg}
1315472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1316902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Bergvoid ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
1317902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg{
1318472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
1319ad2d223aa900179031feb40273881e212941573dJohannes Berg	static u8 zero_addr[ETH_ALEN] = {};
1320472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1321472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	setup_timer(&ifmsh->housekeeping_timer,
1322472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		    ieee80211_mesh_housekeeping_timer,
1323472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		    (unsigned long) sdata);
1324472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1325472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	ifmsh->accepting_plinks = true;
1326472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	atomic_set(&ifmsh->mpaths, 0);
1327f698d856f65c3fea091cc303a135967965c5b880Jasper Bryant-Greene	mesh_rmc_init(sdata);
1328472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	ifmsh->last_preq = jiffies;
1329dca7e9430cb3e492437a5ce891b8b3e315c147caThomas Pedersen	ifmsh->next_perr = jiffies;
13300cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;
1331902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg	/* Allocate all mesh structures when creating the first mesh interface. */
1332902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg	if (!mesh_allocated)
1333902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg		ieee80211s_init();
1334472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	setup_timer(&ifmsh->mesh_path_timer,
1335902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg		    ieee80211_mesh_path_timer,
1336902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg		    (unsigned long) sdata);
1337e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo	setup_timer(&ifmsh->mesh_path_root_timer,
1338e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo		    ieee80211_mesh_path_root_timer,
1339e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo		    (unsigned long) sdata);
1340472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	INIT_LIST_HEAD(&ifmsh->preq_queue.list);
13413f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	skb_queue_head_init(&ifmsh->ps.bc_buf);
1342472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	spin_lock_init(&ifmsh->mesh_preq_queue_lock);
1343dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona	spin_lock_init(&ifmsh->sync_offset_lock);
13442b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	RCU_INIT_POINTER(ifmsh->beacon, NULL);
1345ad2d223aa900179031feb40273881e212941573dJohannes Berg
1346ad2d223aa900179031feb40273881e212941573dJohannes Berg	sdata->vif.bss_conf.bssid = zero_addr;
1347472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg}
1348