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 */
369a40a8c17b22ea0ce6d54c04a2e77630768691338Bob Copeland	data = cfg80211_find_ie(WLAN_EID_RSN, ifmsh->ie, ifmsh->ie_len);
370a40a8c17b22ea0ce6d54c04a2e77630768691338Bob Copeland	if (!data)
371a40a8c17b22ea0ce6d54c04a2e77630768691338Bob Copeland		return 0;
372082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
373a40a8c17b22ea0ce6d54c04a2e77630768691338Bob Copeland	len = data[1] + 2;
374a40a8c17b22ea0ce6d54c04a2e77630768691338Bob Copeland
375a40a8c17b22ea0ce6d54c04a2e77630768691338Bob Copeland	if (skb_tailroom(skb) < len)
376a40a8c17b22ea0ce6d54c04a2e77630768691338Bob Copeland		return -ENOMEM;
377a40a8c17b22ea0ce6d54c04a2e77630768691338Bob Copeland	memcpy(skb_put(skb, len), data, len);
378082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
379082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	return 0;
380082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen}
381082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
382bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Bergstatic int mesh_add_ds_params_ie(struct ieee80211_sub_if_data *sdata,
383bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg				 struct sk_buff *skb)
384082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen{
38555de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	struct ieee80211_chanctx_conf *chanctx_conf;
38655de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	struct ieee80211_channel *chan;
387082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	u8 *pos;
3882e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
389082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	if (skb_tailroom(skb) < 3)
390082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen		return -ENOMEM;
391082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen
39255de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	rcu_read_lock();
39355de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
39455de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	if (WARN_ON(!chanctx_conf)) {
39555de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg		rcu_read_unlock();
39655de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg		return -EINVAL;
39755de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	}
3984bf88530be971bf95a7830ca61b4120980bf4347Johannes Berg	chan = chanctx_conf->def.chan;
39955de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	rcu_read_unlock();
40055de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg
401601513aa208f27ea87400a410d42c978421530ecEmanuel Taube	pos = skb_put(skb, 2 + 1);
402601513aa208f27ea87400a410d42c978421530ecEmanuel Taube	*pos++ = WLAN_EID_DS_PARAMS;
403601513aa208f27ea87400a410d42c978421530ecEmanuel Taube	*pos++ = 1;
404601513aa208f27ea87400a410d42c978421530ecEmanuel Taube	*pos++ = ieee80211_frequency_to_channel(chan->center_freq);
405be125c60e46e165fbfe33db36a4a9d943d560a5bRui Paulo
406082ebb0c258d28af7452b19df9ef8b7553f37690Thomas Pedersen	return 0;
4072e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo}
4082e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
409bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Bergint mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata,
410bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg		       struct sk_buff *skb)
411176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen{
412176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	struct ieee80211_local *local = sdata->local;
41355de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
414176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	struct ieee80211_supported_band *sband;
415176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	u8 *pos;
416176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen
41755de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	sband = local->hw.wiphy->bands[band];
418176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	if (!sband->ht_cap.ht_supported ||
4190418a445838749c51cf1e31a9c7ace6685ae87cdSimon Wunderlich	    sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
4200418a445838749c51cf1e31a9c7ace6685ae87cdSimon Wunderlich	    sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
4210418a445838749c51cf1e31a9c7ace6685ae87cdSimon Wunderlich	    sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10)
422176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen		return 0;
423176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen
424176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap))
425176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen		return -ENOMEM;
426176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen
427176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_cap));
428ef96a84202ccfb48a4569256ffba45e32308f7eeBen Greear	ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, sband->ht_cap.cap);
429176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen
430176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	return 0;
431176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen}
432176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen
433bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Bergint mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata,
434bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg			struct sk_buff *skb)
435176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen{
436176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	struct ieee80211_local *local = sdata->local;
43755de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	struct ieee80211_chanctx_conf *chanctx_conf;
43855de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	struct ieee80211_channel *channel;
439466f310d100ff54f346c1be481af9935c42467b3Johannes Berg	enum nl80211_channel_type channel_type =
4404bf88530be971bf95a7830ca61b4120980bf4347Johannes Berg		cfg80211_get_chandef_type(&sdata->vif.bss_conf.chandef);
44155de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	struct ieee80211_supported_band *sband;
44255de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	struct ieee80211_sta_ht_cap *ht_cap;
443176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	u8 *pos;
444176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen
44555de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	rcu_read_lock();
44655de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
44755de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	if (WARN_ON(!chanctx_conf)) {
44855de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg		rcu_read_unlock();
44955de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg		return -EINVAL;
45055de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	}
4514bf88530be971bf95a7830ca61b4120980bf4347Johannes Berg	channel = chanctx_conf->def.chan;
45255de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	rcu_read_unlock();
45355de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg
45455de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	sband = local->hw.wiphy->bands[channel->band];
45555de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg	ht_cap = &sband->ht_cap;
45655de908ab292c03f1eb280f51170ddb9c6b57e31Johannes Berg
457176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	if (!ht_cap->ht_supported || channel_type == NL80211_CHAN_NO_HT)
458176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen		return 0;
459176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen
460074d46d1d23f27488a3f314e29cae2453541f17dJohannes Berg	if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_operation))
461176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen		return -ENOMEM;
462176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen
463074d46d1d23f27488a3f314e29cae2453541f17dJohannes Berg	pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation));
4644bf88530be971bf95a7830ca61b4120980bf4347Johannes Berg	ieee80211_ie_build_ht_oper(pos, ht_cap, &sdata->vif.bss_conf.chandef,
465431e31542383b71bc5f2642572a1e6ef07f1bb87Ashok Nagarajan				   sdata->vif.bss_conf.ht_operation_mode);
466176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen
467176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen	return 0;
468176f36086e8a00bdf701dc6e4c5a8784ef6529dfThomas Pedersen}
469bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg
4702e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobostatic void ieee80211_mesh_path_timer(unsigned long data)
4712e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo{
4722e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo	struct ieee80211_sub_if_data *sdata =
4732e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo		(struct ieee80211_sub_if_data *) data;
4745bb644a0fd25a5e083ecbfaa92a211db99aa6ef7Johannes Berg
475690205f18fd069898c70d743f498ba42798e5c4eStanislaw Gruszka	ieee80211_queue_work(&sdata->local->hw, &sdata->work);
4762e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo}
4772e3c8736820bf72a8ad10721c7e31d36d4fa7790Luis Carlos Cobo
478e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulostatic void ieee80211_mesh_path_root_timer(unsigned long data)
479e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo{
480e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo	struct ieee80211_sub_if_data *sdata =
481e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo		(struct ieee80211_sub_if_data *) data;
482e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
483e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo
484e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo	set_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags);
485e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo
486690205f18fd069898c70d743f498ba42798e5c4eStanislaw Gruszka	ieee80211_queue_work(&sdata->local->hw, &sdata->work);
487e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo}
488e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo
48963c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulovoid ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh)
49063c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo{
491dbb912cd4ce64e763c5610b49a85529d2634e9d8Chun-Yeow Yeoh	if (ifmsh->mshcfg.dot11MeshHWMPRootMode > IEEE80211_ROOTMODE_ROOT)
49263c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo		set_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags);
49363c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo	else {
49463c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo		clear_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags);
49563c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo		/* stop running timer */
49663c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo		del_timer_sync(&ifmsh->mesh_path_root_timer);
49763c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo	}
49863c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo}
49963c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo
500902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg/**
5013c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona * ieee80211_fill_mesh_addresses - fill addresses of a locally originated mesh frame
502bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg * @hdr:	802.11 frame header
5033c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona * @fc:		frame control field
5043c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona * @meshda:	destination address in the mesh
5053c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona * @meshsa:	source address address in the mesh.  Same as TA, as frame is
5063c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona *              locally originated.
5073c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona *
5083c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona * Return the length of the 802.11 (does not include a mesh control header)
5093c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona */
51015ff63653e507ec928a4a4386405a82446e096b1Johannes Bergint ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc,
51115ff63653e507ec928a4a4386405a82446e096b1Johannes Berg				  const u8 *meshda, const u8 *meshsa)
51215ff63653e507ec928a4a4386405a82446e096b1Johannes Berg{
5133c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona	if (is_multicast_ether_addr(meshda)) {
5143c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		*fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
5153c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		/* DA TA SA */
5163c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		memcpy(hdr->addr1, meshda, ETH_ALEN);
5173c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		memcpy(hdr->addr2, meshsa, ETH_ALEN);
5183c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		memcpy(hdr->addr3, meshsa, ETH_ALEN);
5193c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		return 24;
5203c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona	} else {
5212154c81c32fa44364f83218a10d8dbec4e76d4f5Javier Cardona		*fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
5223c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		/* RA TA DA SA */
5233c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		memset(hdr->addr1, 0, ETH_ALEN);   /* RA is resolved later */
5243c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		memcpy(hdr->addr2, meshsa, ETH_ALEN);
5253c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		memcpy(hdr->addr3, meshda, ETH_ALEN);
5263c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		memcpy(hdr->addr4, meshsa, ETH_ALEN);
5273c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		return 30;
5283c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona	}
5293c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona}
5303c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona
5313c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona/**
532902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg * ieee80211_new_mesh_header - create a new mesh header
533902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg * @sdata:	mesh interface to be used
534bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg * @meshhdr:    uninitialized mesh header
53561ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona * @addr4or5:   1st address in the ae header, which may correspond to address 4
53661ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona *              (if addr6 is NULL) or address 5 (if addr6 is present). It may
53761ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona *              be NULL.
53861ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona * @addr6:	2nd address in the ae header, which corresponds to addr6 of the
53961ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona *              mesh frame
540902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg *
541902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg * Return the header length.
542902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg */
543bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Bergint ieee80211_new_mesh_header(struct ieee80211_sub_if_data *sdata,
544bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg			      struct ieee80211s_hdr *meshhdr,
545bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg			      const char *addr4or5, const char *addr6)
546902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg{
547bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	if (WARN_ON(!addr4or5 && addr6))
548bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg		return 0;
549bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg
5500c3cee72a403e3b4992a5478c9c33d668c246c22Julia Lawall	memset(meshhdr, 0, sizeof(*meshhdr));
551bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg
552472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	meshhdr->ttl = sdata->u.mesh.mshcfg.dot11MeshTTL;
553bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg
554bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	/* FIXME: racy -- TX on multiple queues can be concurrent */
555472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &meshhdr->seqnum);
556472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	sdata->u.mesh.mesh_seqnum++;
557bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg
55861ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona	if (addr4or5 && !addr6) {
5593c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		meshhdr->flags |= MESH_FLAGS_AE_A4;
56061ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona		memcpy(meshhdr->eaddr1, addr4or5, ETH_ALEN);
561bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg		return 2 * ETH_ALEN;
56261ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona	} else if (addr4or5 && addr6) {
5633c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona		meshhdr->flags |= MESH_FLAGS_AE_A5_A6;
56461ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona		memcpy(meshhdr->eaddr1, addr4or5, ETH_ALEN);
56561ad5394590c5c5338ab4ec50553d809a9996d50Javier Cardona		memcpy(meshhdr->eaddr2, addr6, ETH_ALEN);
566bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg		return 3 * ETH_ALEN;
5673c5772a5279de9eadfff7adb5ddea08106495fffJavier Cardona	}
568bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg
569bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	return ETH_ALEN;
570902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg}
571902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg
572bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Bergstatic void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata)
573472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg{
574bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
575df32381896f5f0c78a371df2e49ab7c776b1a5baMarco Porsch	u32 changed;
576472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
57766de671374f003467b5ef7c65ecbe1930480c8c9Colleen Twitty	ieee80211_sta_expire(sdata, ifmsh->mshcfg.plink_timeout * HZ);
578472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	mesh_path_expire(sdata);
579472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
580df32381896f5f0c78a371df2e49ab7c776b1a5baMarco Porsch	changed = mesh_accept_plinks_update(sdata);
5812b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	ieee80211_mbss_info_change_notify(sdata, changed);
582472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
583472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	mod_timer(&ifmsh->housekeeping_timer,
584bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg		  round_jiffies(jiffies +
585bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg				IEEE80211_MESH_HOUSEKEEPING_INTERVAL));
586472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg}
587472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
588e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulostatic void ieee80211_mesh_rootpath(struct ieee80211_sub_if_data *sdata)
589e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo{
590e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
591a69cc44fe9ebb806c5f3f8bd83fb4a50ca63647bChun-Yeow Yeoh	u32 interval;
592e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo
593e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo	mesh_path_tx_root_frame(sdata);
594a69cc44fe9ebb806c5f3f8bd83fb4a50ca63647bChun-Yeow Yeoh
595a69cc44fe9ebb806c5f3f8bd83fb4a50ca63647bChun-Yeow Yeoh	if (ifmsh->mshcfg.dot11MeshHWMPRootMode == IEEE80211_PROACTIVE_RANN)
596a69cc44fe9ebb806c5f3f8bd83fb4a50ca63647bChun-Yeow Yeoh		interval = ifmsh->mshcfg.dot11MeshHWMPRannInterval;
597a69cc44fe9ebb806c5f3f8bd83fb4a50ca63647bChun-Yeow Yeoh	else
598a69cc44fe9ebb806c5f3f8bd83fb4a50ca63647bChun-Yeow Yeoh		interval = ifmsh->mshcfg.dot11MeshHWMProotInterval;
599a69cc44fe9ebb806c5f3f8bd83fb4a50ca63647bChun-Yeow Yeoh
600e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo	mod_timer(&ifmsh->mesh_path_root_timer,
601a69cc44fe9ebb806c5f3f8bd83fb4a50ca63647bChun-Yeow Yeoh		  round_jiffies(TU_TO_EXP_TIME(interval)));
602e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo}
603e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo
6042b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersenstatic int
6052b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersenieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
6062b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen{
6072b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	struct beacon_data *bcn;
6082b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	int head_len, tail_len;
6092b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	struct sk_buff *skb;
6102b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	struct ieee80211_mgmt *mgmt;
6112b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	struct ieee80211_chanctx_conf *chanctx_conf;
612b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	struct mesh_csa_settings *csa;
6132b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	enum ieee80211_band band;
6142b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	u8 *pos;
6152b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	struct ieee80211_sub_if_data *sdata;
6162b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) +
6172b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		      sizeof(mgmt->u.beacon);
6182b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
6192b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	sdata = container_of(ifmsh, struct ieee80211_sub_if_data, u.mesh);
6202b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	rcu_read_lock();
6212b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
6222b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	band = chanctx_conf->def.chan->band;
6232b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	rcu_read_unlock();
6242b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
6252b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	head_len = hdr_len +
6262b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		   2 + /* NULL SSID */
627b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		   /* Channel Switch Announcement */
628b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		   2 + sizeof(struct ieee80211_channel_sw_ie) +
629b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		   /* Mesh Channel Swith Parameters */
630b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		   2 + sizeof(struct ieee80211_mesh_chansw_params_ie) +
6312b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		   2 + 8 + /* supported rates */
6322b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		   2 + 3; /* DS params */
6332b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
6342b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		   2 + sizeof(struct ieee80211_ht_cap) +
6352b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		   2 + sizeof(struct ieee80211_ht_operation) +
6362b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		   2 + ifmsh->mesh_id_len +
6372b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		   2 + sizeof(struct ieee80211_meshconf_ie) +
6382b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		   2 + sizeof(__le16) + /* awake window */
6392b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		   ifmsh->ie_len;
6402b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
6412b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	bcn = kzalloc(sizeof(*bcn) + head_len + tail_len, GFP_KERNEL);
6422b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	/* need an skb for IE builders to operate on */
6432b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	skb = dev_alloc_skb(max(head_len, tail_len));
6442b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
6452b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	if (!bcn || !skb)
6462b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		goto out_free;
6472b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
6482b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	/*
6492b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	 * pointers go into the block we allocated,
6502b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	 * memory is | beacon_data | head | tail |
6512b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	 */
6522b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	bcn->head = ((u8 *) bcn) + sizeof(*bcn);
6532b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
6542b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	/* fill in the head */
6552b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
6562b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	memset(mgmt, 0, hdr_len);
6572b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
6582b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen					  IEEE80211_STYPE_BEACON);
6592b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	eth_broadcast_addr(mgmt->da);
6602b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
6612b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
6622b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	ieee80211_mps_set_frame_flags(sdata, NULL, (void *) mgmt);
6632b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	mgmt->u.beacon.beacon_int =
6642b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		cpu_to_le16(sdata->vif.bss_conf.beacon_int);
6652b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	mgmt->u.beacon.capab_info |= cpu_to_le16(
6662b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		sdata->u.mesh.security ? WLAN_CAPABILITY_PRIVACY : 0);
6672b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
6682b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	pos = skb_put(skb, 2);
6692b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	*pos++ = WLAN_EID_SSID;
6702b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	*pos++ = 0x0;
6712b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
672b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	rcu_read_lock();
673b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	csa = rcu_dereference(ifmsh->csa);
674b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	if (csa) {
675b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		pos = skb_put(skb, 13);
676b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		memset(pos, 0, 13);
677b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		*pos++ = WLAN_EID_CHANNEL_SWITCH;
678b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		*pos++ = 3;
679b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		*pos++ = 0x0;
680b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		*pos++ = ieee80211_frequency_to_channel(
681b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh				csa->settings.chandef.chan->center_freq);
682af296bdb8da4d0a4284de10fc4a61497272ddf11Michal Kazior		bcn->csa_counter_offsets[0] = hdr_len + 6;
683b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		*pos++ = csa->settings.count;
684b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		*pos++ = WLAN_EID_CHAN_SWITCH_PARAM;
685b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		*pos++ = 6;
6860cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho		if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT) {
687b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh			*pos++ = ifmsh->mshcfg.dot11MeshTTL;
688b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh			*pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
689b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		} else {
690b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh			*pos++ = ifmsh->chsw_ttl;
691b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		}
692b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		*pos++ |= csa->settings.block_tx ?
693b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh			  WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
694b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos);
695b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		pos += 2;
696ca91dc97b8a0ffd05721806654eaff2cf13ba5cbChun-Yeow Yeoh		put_unaligned_le16(ifmsh->pre_value, pos);
697b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		pos += 2;
698b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	}
699b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	rcu_read_unlock();
700b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
7012b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
702bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	    mesh_add_ds_params_ie(sdata, skb))
7032b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		goto out_free;
7042b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7052b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	bcn->head_len = skb->len;
7062b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	memcpy(bcn->head, skb->data, bcn->head_len);
7072b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7082b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	/* now the tail */
7092b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	skb_trim(skb, 0);
7102b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	bcn->tail = bcn->head + bcn->head_len;
7112b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7122b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	if (ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
713bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	    mesh_add_rsn_ie(sdata, skb) ||
714bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	    mesh_add_ht_cap_ie(sdata, skb) ||
715bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	    mesh_add_ht_oper_ie(sdata, skb) ||
716bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	    mesh_add_meshid_ie(sdata, skb) ||
717bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	    mesh_add_meshconf_ie(sdata, skb) ||
718bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	    mesh_add_awake_window_ie(sdata, skb) ||
719bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg	    mesh_add_vendor_ies(sdata, skb))
7202b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		goto out_free;
7212b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7222b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	bcn->tail_len = skb->len;
7232b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	memcpy(bcn->tail, skb->data, bcn->tail_len);
72443552be1da3c420931c89727b6115b7fa35368f8Thomas Pedersen	bcn->meshconf = (struct ieee80211_meshconf_ie *)
72543552be1da3c420931c89727b6115b7fa35368f8Thomas Pedersen					(bcn->tail + ifmsh->meshconf_offset);
7262b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7272b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	dev_kfree_skb(skb);
7282b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	rcu_assign_pointer(ifmsh->beacon, bcn);
7292b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	return 0;
7302b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersenout_free:
7312b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	kfree(bcn);
7322b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	dev_kfree_skb(skb);
7332b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	return -ENOMEM;
7342b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen}
7352b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7362b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersenstatic int
7378d61ffa5e01c5f676431d12caba17db164a48a86Johannes Bergieee80211_mesh_rebuild_beacon(struct ieee80211_sub_if_data *sdata)
7382b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen{
7392b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	struct beacon_data *old_bcn;
7402b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	int ret;
7412b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7428d61ffa5e01c5f676431d12caba17db164a48a86Johannes Berg	old_bcn = rcu_dereference_protected(sdata->u.mesh.beacon,
7438d61ffa5e01c5f676431d12caba17db164a48a86Johannes Berg					    lockdep_is_held(&sdata->wdev.mtx));
7448d61ffa5e01c5f676431d12caba17db164a48a86Johannes Berg	ret = ieee80211_mesh_build_beacon(&sdata->u.mesh);
7452b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	if (ret)
7462b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		/* just reuse old beacon */
7478d61ffa5e01c5f676431d12caba17db164a48a86Johannes Berg		return ret;
7482b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7492b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	if (old_bcn)
7502b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		kfree_rcu(old_bcn, rcu_head);
7518d61ffa5e01c5f676431d12caba17db164a48a86Johannes Berg	return 0;
7522b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen}
7532b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7542b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersenvoid ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata,
7552b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen				       u32 changed)
7562b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen{
757f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
758f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	unsigned long bits = changed;
759f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	u32 bit;
760f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen
761f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	if (!bits)
762f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen		return;
763f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen
764f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	/* if we race with running work, worst case this work becomes a noop */
765f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	for_each_set_bit(bit, &bits, sizeof(changed) * BITS_PER_BYTE)
766f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen		set_bit(bit, &ifmsh->mbss_changed);
767f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	set_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags);
768f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	ieee80211_queue_work(&sdata->local->hw, &sdata->work);
7692b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen}
7702b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
7712b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersenint ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
772472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg{
773472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
774472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_local *local = sdata->local;
775f4eabc918c3b88763bc20dd9e2b248aa6c757005Chun-Yeow Yeoh	u32 changed = BSS_CHANGED_BEACON |
776f4eabc918c3b88763bc20dd9e2b248aa6c757005Chun-Yeow Yeoh		      BSS_CHANGED_BEACON_ENABLED |
777f4eabc918c3b88763bc20dd9e2b248aa6c757005Chun-Yeow Yeoh		      BSS_CHANGED_HT |
778f4eabc918c3b88763bc20dd9e2b248aa6c757005Chun-Yeow Yeoh		      BSS_CHANGED_BASIC_RATES |
779f4eabc918c3b88763bc20dd9e2b248aa6c757005Chun-Yeow Yeoh		      BSS_CHANGED_BEACON_INT;
780472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
78109b174702601079c3a04806754be30ffbd70db4dJohannes Berg	local->fif_other_bss++;
78209b174702601079c3a04806754be30ffbd70db4dJohannes Berg	/* mesh ifaces must set allmulti to forward mcast traffic */
78309b174702601079c3a04806754be30ffbd70db4dJohannes Berg	atomic_inc(&local->iff_allmultis);
78409b174702601079c3a04806754be30ffbd70db4dJohannes Berg	ieee80211_configure_filter(local);
78509b174702601079c3a04806754be30ffbd70db4dJohannes Berg
786c7108a7111cd9e592d6ad498be37276dbea75d2bJavier Cardona	ifmsh->mesh_cc_id = 0;	/* Disabled */
787dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona	/* register sync ops from extensible synchronization framework */
788dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona	ifmsh->sync_ops = ieee80211_mesh_sync_ops_get(ifmsh->mesh_sp_id);
789dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona	ifmsh->adjusting_tbtt = false;
790dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona	ifmsh->sync_offset_clockdrift_max = 0;
7916b9ac4425d6ec871faf54540e0f1c5ff420a8f29Rui Paulo	set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags);
79263c5723bc3af8d4e86984dd4ff0c78218de418d0Rui Paulo	ieee80211_mesh_root_setup(ifmsh);
79364592c8fc0e99d445fc3fdedddeb6088e20086f1Johannes Berg	ieee80211_queue_work(&local->hw, &sdata->work);
79470c33eaae79e53f9e48324736c0cb85534d3f093Ashok Nagarajan	sdata->vif.bss_conf.ht_operation_mode =
79570c33eaae79e53f9e48324736c0cb85534d3f093Ashok Nagarajan				ifmsh->mshcfg.ht_opmode;
796d6a83228823fc0cc8d79d95c9f0bf568b7317862Johannes Berg	sdata->vif.bss_conf.enable_beacon = true;
797f4eabc918c3b88763bc20dd9e2b248aa6c757005Chun-Yeow Yeoh
79839886b618aba3c39e650c191d601e26ec581ce0fThomas Pedersen	changed |= ieee80211_mps_local_status_update(sdata);
7993f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch
8002b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	if (ieee80211_mesh_build_beacon(ifmsh)) {
8012b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		ieee80211_stop_mesh(sdata);
8022b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen		return -ENOMEM;
8032b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	}
8042b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen
805057d5f4ba1e421185a8e7e0b7fadf253d41a3e83Thomas Pedersen	ieee80211_recalc_dtim(local, sdata);
806f4eabc918c3b88763bc20dd9e2b248aa6c757005Chun-Yeow Yeoh	ieee80211_bss_info_change_notify(sdata, changed);
807c405c6298eacd423098afacf6020ddbda1b0378bJohannes Berg
808c405c6298eacd423098afacf6020ddbda1b0378bJohannes Berg	netif_carrier_on(sdata->dev);
8092b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	return 0;
810472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg}
811472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
812472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Bergvoid ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
813472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg{
81409b174702601079c3a04806754be30ffbd70db4dJohannes Berg	struct ieee80211_local *local = sdata->local;
81529cbe68c516a48a9a88b3226878570c6cbd83c02Johannes Berg	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
8162b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	struct beacon_data *bcn;
81729cbe68c516a48a9a88b3226878570c6cbd83c02Johannes Berg
818c405c6298eacd423098afacf6020ddbda1b0378bJohannes Berg	netif_carrier_off(sdata->dev);
819c405c6298eacd423098afacf6020ddbda1b0378bJohannes Berg
8200d466b9c6798d431141ab15ae6d5ea413b4d09b2Thomas Pedersen	/* stop the beacon */
82129cbe68c516a48a9a88b3226878570c6cbd83c02Johannes Berg	ifmsh->mesh_id_len = 0;
822d6a83228823fc0cc8d79d95c9f0bf568b7317862Johannes Berg	sdata->vif.bss_conf.enable_beacon = false;
823d6a83228823fc0cc8d79d95c9f0bf568b7317862Johannes Berg	clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
82429cbe68c516a48a9a88b3226878570c6cbd83c02Johannes Berg	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
8252b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	bcn = rcu_dereference_protected(ifmsh->beacon,
8268d61ffa5e01c5f676431d12caba17db164a48a86Johannes Berg					lockdep_is_held(&sdata->wdev.mtx));
8270c2bef4621c5feb5bda9068c9964b2e9acf57017Monam Agarwal	RCU_INIT_POINTER(ifmsh->beacon, NULL);
8282b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	kfree_rcu(bcn, rcu_head);
8290d466b9c6798d431141ab15ae6d5ea413b4d09b2Thomas Pedersen
8300d466b9c6798d431141ab15ae6d5ea413b4d09b2Thomas Pedersen	/* flush STAs and mpaths on this iface */
831b998e8bb3e1c6eeae5eab9d6a434563270286c3bJohannes Berg	sta_info_flush(sdata);
8320d466b9c6798d431141ab15ae6d5ea413b4d09b2Thomas Pedersen	mesh_path_flush_by_iface(sdata);
83309b174702601079c3a04806754be30ffbd70db4dJohannes Berg
8343f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	/* free all potentially still buffered group-addressed frames */
8353f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	local->total_ps_buffered -= skb_queue_len(&ifmsh->ps.bc_buf);
8363f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	skb_queue_purge(&ifmsh->ps.bc_buf);
8373f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch
838472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	del_timer_sync(&sdata->u.mesh.housekeeping_timer);
839e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo	del_timer_sync(&sdata->u.mesh.mesh_path_root_timer);
840dd4c9260e7f23f2e951cbfb2726e468c6d30306cJohannes Berg	del_timer_sync(&sdata->u.mesh.mesh_path_timer);
84109b174702601079c3a04806754be30ffbd70db4dJohannes Berg
842f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	/* clear any mesh work (for next join) we may have accrued */
843f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	ifmsh->wrkq_flags = 0;
844f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	ifmsh->mbss_changed = 0;
845f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen
84609b174702601079c3a04806754be30ffbd70db4dJohannes Berg	local->fif_other_bss--;
84709b174702601079c3a04806754be30ffbd70db4dJohannes Berg	atomic_dec(&local->iff_allmultis);
84809b174702601079c3a04806754be30ffbd70db4dJohannes Berg	ieee80211_configure_filter(local);
849472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg}
850472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
85133a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeohstatic bool
85233a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeohieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
85333a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh				 struct ieee802_11_elems *elems, bool beacon)
85433a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh{
85533a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	struct cfg80211_csa_settings params;
85633a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	struct ieee80211_csa_ie csa_ie;
85733a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
85833a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
8590cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	int err;
86033a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	u32 sta_flags;
86133a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
862dbd72850dcc9738b42a9762ef8c4a1a66b30d897Michal Kazior	sdata_assert_lock(sdata);
863dbd72850dcc9738b42a9762ef8c4a1a66b30d897Michal Kazior
86433a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	sta_flags = IEEE80211_STA_DISABLE_VHT;
86533a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	switch (sdata->vif.bss_conf.chandef.width) {
86633a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	case NL80211_CHAN_WIDTH_20_NOHT:
86733a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		sta_flags |= IEEE80211_STA_DISABLE_HT;
86833a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	case NL80211_CHAN_WIDTH_20:
86933a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		sta_flags |= IEEE80211_STA_DISABLE_40MHZ;
87033a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		break;
87133a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	default:
87233a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		break;
87333a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	}
87433a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
87533a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	memset(&params, 0, sizeof(params));
87633a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	memset(&csa_ie, 0, sizeof(csa_ie));
87784469a45a1bedec9918e94ab2f78c5dc0739e4a7Luciano Coelho	err = ieee80211_parse_ch_switch_ie(sdata, elems, band,
87833a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh					   sta_flags, sdata->vif.addr,
87933a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh					   &csa_ie);
88033a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	if (err < 0)
88133a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		return false;
88233a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	if (err)
88333a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		return false;
88433a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
88533a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	params.chandef = csa_ie.chandef;
88633a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	params.count = csa_ie.count;
88733a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
88833a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, &params.chandef,
88933a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh				     IEEE80211_CHAN_DISABLED)) {
89033a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		sdata_info(sdata,
89133a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh			   "mesh STA %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), aborting\n",
89233a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh			   sdata->vif.addr,
89333a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh			   params.chandef.chan->center_freq,
89433a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh			   params.chandef.width,
89533a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh			   params.chandef.center_freq1,
89633a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh			   params.chandef.center_freq2);
89733a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		return false;
89833a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	}
89933a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
90033a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
9012beb6dab2d799ee8934cb0801845e551ad8c70f2Luciano Coelho					    &params.chandef,
9022beb6dab2d799ee8934cb0801845e551ad8c70f2Luciano Coelho					    NL80211_IFTYPE_MESH_POINT);
90333a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	if (err < 0)
90433a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		return false;
9052beb6dab2d799ee8934cb0801845e551ad8c70f2Luciano Coelho	if (err > 0)
90633a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		/* TODO: DFS not (yet) supported */
90733a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		return false;
9082beb6dab2d799ee8934cb0801845e551ad8c70f2Luciano Coelho
9092beb6dab2d799ee8934cb0801845e551ad8c70f2Luciano Coelho	params.radar_required = err;
91033a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
9110cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	if (cfg80211_chandef_identical(&params.chandef,
9120cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho				       &sdata->vif.bss_conf.chandef)) {
9130cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho		mcsa_dbg(sdata,
9140cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho			 "received csa with an identical chandef, ignoring\n");
9150cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho		return true;
9160cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	}
91733a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
91833a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	mcsa_dbg(sdata,
91933a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		 "received channel switch announcement to go to channel %d MHz\n",
92033a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		 params.chandef.chan->center_freq);
92133a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
92233a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	params.block_tx = csa_ie.mode & WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT;
9233f718fd8401d7db86b9efc3ea1cdf5df41354b9fChun-Yeow Yeoh	if (beacon) {
92433a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		ifmsh->chsw_ttl = csa_ie.ttl - 1;
9253f718fd8401d7db86b9efc3ea1cdf5df41354b9fChun-Yeow Yeoh		if (ifmsh->pre_value >= csa_ie.pre_value)
9263f718fd8401d7db86b9efc3ea1cdf5df41354b9fChun-Yeow Yeoh			return false;
9273f718fd8401d7db86b9efc3ea1cdf5df41354b9fChun-Yeow Yeoh		ifmsh->pre_value = csa_ie.pre_value;
9283f718fd8401d7db86b9efc3ea1cdf5df41354b9fChun-Yeow Yeoh	}
92933a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
9300cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	if (ifmsh->chsw_ttl >= ifmsh->mshcfg.dot11MeshTTL)
9313f718fd8401d7db86b9efc3ea1cdf5df41354b9fChun-Yeow Yeoh		return false;
93233a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
9330cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_REPEATER;
93433a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
9350cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	if (ieee80211_channel_switch(sdata->local->hw.wiphy, sdata->dev,
9360cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho				     &params) < 0)
9370cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho		return false;
93833a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
93933a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	return true;
94033a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh}
94133a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
9429fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersenstatic void
9439fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersenieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
9449fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen			    struct ieee80211_mgmt *mgmt, size_t len)
9459fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen{
9469fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	struct ieee80211_local *local = sdata->local;
9479fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
9489fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	struct sk_buff *presp;
9499fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	struct beacon_data *bcn;
9509fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	struct ieee80211_mgmt *hdr;
9519fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	struct ieee802_11_elems elems;
9529fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	size_t baselen;
953511044ea0bfc06614d903263ad094d1071fa172fJohannes Berg	u8 *pos;
9549fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen
9559fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	pos = mgmt->u.probe_req.variable;
9569fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	baselen = (u8 *) pos - (u8 *) mgmt;
9579fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	if (baselen > len)
9589fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen		return;
9599fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen
960b2e506bfc4d752b68a0ccaae1e977898263eba4cJohannes Berg	ieee802_11_parse_elems(pos, len - baselen, false, &elems);
9619fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen
962a4ef66a915b957416a89a48365aea2ec2dc551f6Chun-Yeow Yeoh	if (!elems.mesh_id)
963a4ef66a915b957416a89a48365aea2ec2dc551f6Chun-Yeow Yeoh		return;
964a4ef66a915b957416a89a48365aea2ec2dc551f6Chun-Yeow Yeoh
9659fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	/* 802.11-2012 10.1.4.3.2 */
9669fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	if ((!ether_addr_equal(mgmt->da, sdata->vif.addr) &&
9679fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	     !is_broadcast_ether_addr(mgmt->da)) ||
9689fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	    elems.ssid_len != 0)
9699fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen		return;
9709fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen
9719fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	if (elems.mesh_id_len != 0 &&
9729fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	    (elems.mesh_id_len != ifmsh->mesh_id_len ||
9739fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	     memcmp(elems.mesh_id, ifmsh->mesh_id, ifmsh->mesh_id_len)))
9749fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen		return;
9759fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen
9769fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	rcu_read_lock();
9779fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	bcn = rcu_dereference(ifmsh->beacon);
9789fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen
9799fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	if (!bcn)
9809fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen		goto out;
9819fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen
9829fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	presp = dev_alloc_skb(local->tx_headroom +
9839fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen			      bcn->head_len + bcn->tail_len);
9849fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	if (!presp)
9859fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen		goto out;
9869fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen
9879fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	skb_reserve(presp, local->tx_headroom);
9889fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	memcpy(skb_put(presp, bcn->head_len), bcn->head, bcn->head_len);
9899fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	memcpy(skb_put(presp, bcn->tail_len), bcn->tail, bcn->tail_len);
9909fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	hdr = (struct ieee80211_mgmt *) presp->data;
9919fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
9929fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen					 IEEE80211_STYPE_PROBE_RESP);
9939fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	memcpy(hdr->da, mgmt->sa, ETH_ALEN);
9949fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	IEEE80211_SKB_CB(presp)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
9959fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	ieee80211_tx_skb(sdata, presp);
9969fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersenout:
9979fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	rcu_read_unlock();
9989fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen}
9999fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen
1000472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Bergstatic void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
1001472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg					u16 stype,
1002472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg					struct ieee80211_mgmt *mgmt,
1003472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg					size_t len,
1004472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg					struct ieee80211_rx_status *rx_status)
1005472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg{
1006c6a1fa12d206882757264869f8e32d606b930e2aJohannes Berg	struct ieee80211_local *local = sdata->local;
1007dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
1008472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee802_11_elems elems;
1009472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_channel *channel;
1010472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	size_t baselen;
1011472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	int freq;
1012472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	enum ieee80211_band band = rx_status->band;
1013472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1014472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	/* ignore ProbeResp to foreign address */
1015472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	if (stype == IEEE80211_STYPE_PROBE_RESP &&
1016b203ca39126bad99583c908be587df067820a1eaJoe Perches	    !ether_addr_equal(mgmt->da, sdata->vif.addr))
1017472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		return;
1018472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1019472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
1020472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	if (baselen > len)
1021472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		return;
1022472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1023472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
1024b2e506bfc4d752b68a0ccaae1e977898263eba4cJohannes Berg			       false, &elems);
1025472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
10269a90bc81914ee77edcd6eb7e881639b7f7bf1667Thomas Pedersen	/* ignore non-mesh or secure / unsecure mismatch */
10279a90bc81914ee77edcd6eb7e881639b7f7bf1667Thomas Pedersen	if ((!elems.mesh_id || !elems.mesh_config) ||
10289a90bc81914ee77edcd6eb7e881639b7f7bf1667Thomas Pedersen	    (elems.rsn && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) ||
10299a90bc81914ee77edcd6eb7e881639b7f7bf1667Thomas Pedersen	    (!elems.rsn && sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE))
10305cff5e01e818029a5d2c3c31b7ae5e5e7ee70452Javier Cardona		return;
10315cff5e01e818029a5d2c3c31b7ae5e5e7ee70452Javier Cardona
10321cd8e88e17729f57a9c7f751103e522596bb5de2Johannes Berg	if (elems.ds_params)
103359eb21a6504731fc16db4cf9463065dd61093e08Bruno Randolf		freq = ieee80211_channel_to_frequency(elems.ds_params[0], band);
1034472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	else
1035472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		freq = rx_status->freq;
1036472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1037472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	channel = ieee80211_get_channel(local->hw.wiphy, freq);
1038472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1039472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
1040472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		return;
1041472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
10429a90bc81914ee77edcd6eb7e881639b7f7bf1667Thomas Pedersen	if (mesh_matches_local(sdata, &elems))
1043f743ff4907fa5bc2b460f48ace831a560806a9fbThomas Pedersen		mesh_neighbour_update(sdata, mgmt->sa, &elems);
1044dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona
1045dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona	if (ifmsh->sync_ops)
1046dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona		ifmsh->sync_ops->rx_bcn_presp(sdata,
1047dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona			stype, mgmt, &elems, rx_status);
104833a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
10490cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	if (ifmsh->csa_role != IEEE80211_MESH_CSA_ROLE_INIT &&
10500cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	    !sdata->vif.csa_active)
105133a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		ieee80211_mesh_process_chnswitch(sdata, &elems, true);
1052472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg}
1053472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1054b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeohint ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
1055b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh{
1056b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
1057b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	struct mesh_csa_settings *tmp_csa_settings;
1058b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	int ret = 0;
1059faf046e7231bf008715bbffe5cca2ed3aa31be1bMichal Kazior	int changed = 0;
1060b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
1061b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	/* Reset the TTL value and Initiator flag */
10620cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;
1063b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	ifmsh->chsw_ttl = 0;
1064b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
1065b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	/* Remove the CSA and MCSP elements from the beacon */
1066b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	tmp_csa_settings = rcu_dereference(ifmsh->csa);
10670c2bef4621c5feb5bda9068c9964b2e9acf57017Monam Agarwal	RCU_INIT_POINTER(ifmsh->csa, NULL);
106866e01cf99e0a9d0cbff21b0288c049654d5acf3eLuciano Coelho	if (tmp_csa_settings)
106966e01cf99e0a9d0cbff21b0288c049654d5acf3eLuciano Coelho		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
1074faf046e7231bf008715bbffe5cca2ed3aa31be1bMichal Kazior	changed |= 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);
1078faf046e7231bf008715bbffe5cca2ed3aa31be1bMichal Kazior	return changed;
1079b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh}
1080b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
1081b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeohint ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
108266e01cf99e0a9d0cbff21b0288c049654d5acf3eLuciano Coelho			      struct cfg80211_csa_settings *csa_settings)
1083b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh{
1084b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
1085b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	struct mesh_csa_settings *tmp_csa_settings;
1086b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	int ret = 0;
1087b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
1088b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	tmp_csa_settings = kmalloc(sizeof(*tmp_csa_settings),
1089b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh				   GFP_ATOMIC);
1090b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	if (!tmp_csa_settings)
1091b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		return -ENOMEM;
1092b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
1093b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	memcpy(&tmp_csa_settings->settings, csa_settings,
1094b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	       sizeof(struct cfg80211_csa_settings));
1095b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
1096b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	rcu_assign_pointer(ifmsh->csa, tmp_csa_settings);
1097b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
1098b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	ret = ieee80211_mesh_rebuild_beacon(sdata);
1099b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	if (ret) {
1100b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		tmp_csa_settings = rcu_dereference(ifmsh->csa);
11010c2bef4621c5feb5bda9068c9964b2e9acf57017Monam Agarwal		RCU_INIT_POINTER(ifmsh->csa, NULL);
1102b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		kfree_rcu(tmp_csa_settings, rcu_head);
1103b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh		return ret;
1104b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh	}
1105b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
1106b58e81e96a81c80886011ad87cdbe73585dec4f7Luciano Coelho	return BSS_CHANGED_BEACON;
1107b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh}
1108b8456a14e9d2770846fcf74de18ff95b676149a3Chun-Yeow Yeoh
11098f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeohstatic int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata,
11108f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh			       struct ieee80211_mgmt *mgmt, size_t len)
11118f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh{
11128f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	struct ieee80211_mgmt *mgmt_fwd;
11138f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	struct sk_buff *skb;
11148f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	struct ieee80211_local *local = sdata->local;
11158f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	u8 *pos = mgmt->u.action.u.chan_switch.variable;
11168f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	size_t offset_ttl;
11178f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11188f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	skb = dev_alloc_skb(local->tx_headroom + len);
11198f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	if (!skb)
11208f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh		return -ENOMEM;
11218f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	skb_reserve(skb, local->tx_headroom);
11228f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	mgmt_fwd = (struct ieee80211_mgmt *) skb_put(skb, len);
11238f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11248f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	/* offset_ttl is based on whether the secondary channel
1125b314c669905cbef00fed33028c61c96efeea08f5Antonio Ospite	 * offset is available or not. Subtract 1 from the mesh TTL
11268f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	 * and disable the initiator flag before forwarding.
11278f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	 */
11288f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	offset_ttl = (len < 42) ? 7 : 10;
11298f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	*(pos + offset_ttl) -= 1;
11308f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	*(pos + offset_ttl + 1) &= ~WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
11318f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11328f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	memcpy(mgmt_fwd, mgmt, len);
11338f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	eth_broadcast_addr(mgmt_fwd->da);
11348f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	memcpy(mgmt_fwd->sa, sdata->vif.addr, ETH_ALEN);
11358f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	memcpy(mgmt_fwd->bssid, sdata->vif.addr, ETH_ALEN);
11368f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11378f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	ieee80211_tx_skb(sdata, skb);
11388f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	return 0;
11398f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh}
11408f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11418f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeohstatic void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
11428f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh			      struct ieee80211_mgmt *mgmt, size_t len)
11438f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh{
11448f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
11458f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	struct ieee802_11_elems elems;
11468f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	u16 pre_value;
114733a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	bool fwd_csa = true;
11488f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	size_t baselen;
11493f718fd8401d7db86b9efc3ea1cdf5df41354b9fChun-Yeow Yeoh	u8 *pos;
11508f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11518f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	if (mgmt->u.action.u.measurement.action_code !=
11528f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	    WLAN_ACTION_SPCT_CHL_SWITCH)
11538f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh		return;
11548f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11558f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	pos = mgmt->u.action.u.chan_switch.variable;
11568f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	baselen = offsetof(struct ieee80211_mgmt,
11578f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh			   u.action.u.chan_switch.variable);
11588f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	ieee802_11_parse_elems(pos, len - baselen, false, &elems);
11598f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11603f718fd8401d7db86b9efc3ea1cdf5df41354b9fChun-Yeow Yeoh	ifmsh->chsw_ttl = elems.mesh_chansw_params_ie->mesh_ttl;
11613f718fd8401d7db86b9efc3ea1cdf5df41354b9fChun-Yeow Yeoh	if (!--ifmsh->chsw_ttl)
11628f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh		fwd_csa = false;
11638f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11648f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	pre_value = le16_to_cpu(elems.mesh_chansw_params_ie->mesh_pre_value);
11658f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	if (ifmsh->pre_value >= pre_value)
11668f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh		return;
11678f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11688f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	ifmsh->pre_value = pre_value;
11698f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
11700cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	if (!sdata->vif.csa_active &&
11710cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	    !ieee80211_mesh_process_chnswitch(sdata, &elems, false)) {
117233a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		mcsa_dbg(sdata, "Failed to process CSA action frame");
117333a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh		return;
117433a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh	}
117533a45867c56074a23d01e286890e3b61f3ff8fffChun-Yeow Yeoh
11768f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	/* forward or re-broadcast the CSA frame */
11778f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	if (fwd_csa) {
11788f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh		if (mesh_fwd_csa_frame(sdata, mgmt, len) < 0)
11798f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh			mcsa_dbg(sdata, "Failed to forward the CSA frame");
11808f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	}
11818f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh}
11828f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh
1183472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Bergstatic void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
1184472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg					  struct ieee80211_mgmt *mgmt,
1185472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg					  size_t len,
1186472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg					  struct ieee80211_rx_status *rx_status)
1187472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg{
1188472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	switch (mgmt->u.action.category) {
11898db098507c5cbe499061d0f6aea426a36e7c72d7Thomas Pedersen	case WLAN_CATEGORY_SELF_PROTECTED:
11908db098507c5cbe499061d0f6aea426a36e7c72d7Thomas Pedersen		switch (mgmt->u.action.u.self_prot.action_code) {
11918db098507c5cbe499061d0f6aea426a36e7c72d7Thomas Pedersen		case WLAN_SP_MESH_PEERING_OPEN:
11928db098507c5cbe499061d0f6aea426a36e7c72d7Thomas Pedersen		case WLAN_SP_MESH_PEERING_CLOSE:
11938db098507c5cbe499061d0f6aea426a36e7c72d7Thomas Pedersen		case WLAN_SP_MESH_PEERING_CONFIRM:
11948db098507c5cbe499061d0f6aea426a36e7c72d7Thomas Pedersen			mesh_rx_plink_frame(sdata, mgmt, len, rx_status);
11958db098507c5cbe499061d0f6aea426a36e7c72d7Thomas Pedersen			break;
11968db098507c5cbe499061d0f6aea426a36e7c72d7Thomas Pedersen		}
1197472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		break;
119825d49e4d63564c7004a4d6735d1d8c3cc41a7394Thomas Pedersen	case WLAN_CATEGORY_MESH_ACTION:
119925d49e4d63564c7004a4d6735d1d8c3cc41a7394Thomas Pedersen		if (mesh_action_is_path_sel(mgmt))
120025d49e4d63564c7004a4d6735d1d8c3cc41a7394Thomas Pedersen			mesh_rx_path_sel_frame(sdata, mgmt, len);
1201472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		break;
12028f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh	case WLAN_CATEGORY_SPECTRUM_MGMT:
12038f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh		mesh_rx_csa_frame(sdata, mgmt, len);
12048f2535b92d685c68db4bc699dd78462a646f6ef9Chun-Yeow Yeoh		break;
1205472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	}
1206472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg}
1207472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
12081fa57d017366fb26b58af110a38b36a4f0214a62Johannes Bergvoid ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
12091fa57d017366fb26b58af110a38b36a4f0214a62Johannes Berg				   struct sk_buff *skb)
1210472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg{
1211472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_rx_status *rx_status;
1212472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_mgmt *mgmt;
1213472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	u16 stype;
1214472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1215ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen	sdata_lock(sdata);
1216ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen
1217ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen	/* mesh already went down */
12181693d34416a4b07e291578b4b87dc811876046cfJohannes Berg	if (!sdata->u.mesh.mesh_id_len)
1219ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen		goto out;
1220ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen
1221f1d58c2521eb160178b2151d6326d8dc5d7c8560Johannes Berg	rx_status = IEEE80211_SKB_RXCB(skb);
1222472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	mgmt = (struct ieee80211_mgmt *) skb->data;
1223472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE;
1224472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1225472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	switch (stype) {
1226472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	case IEEE80211_STYPE_PROBE_RESP:
1227472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	case IEEE80211_STYPE_BEACON:
1228472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		ieee80211_mesh_rx_bcn_presp(sdata, stype, mgmt, skb->len,
1229472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg					    rx_status);
1230472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		break;
12319fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen	case IEEE80211_STYPE_PROBE_REQ:
12329fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen		ieee80211_mesh_rx_probe_req(sdata, mgmt, skb->len);
12339fb04b501a57ad29fa989ab3cd2123482e7eac5fThomas Pedersen		break;
1234472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	case IEEE80211_STYPE_ACTION:
1235472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		ieee80211_mesh_rx_mgmt_action(sdata, mgmt, skb->len, rx_status);
1236472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		break;
1237472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	}
1238ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersenout:
1239ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen	sdata_unlock(sdata);
1240472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg}
1241472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1242f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersenstatic void mesh_bss_info_changed(struct ieee80211_sub_if_data *sdata)
1243f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen{
1244f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
1245f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	u32 bit, changed = 0;
1246f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen
1247f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	for_each_set_bit(bit, &ifmsh->mbss_changed,
1248f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen			 sizeof(changed) * BITS_PER_BYTE) {
1249f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen		clear_bit(bit, &ifmsh->mbss_changed);
1250f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen		changed |= BIT(bit);
1251f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	}
1252f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen
1253f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	if (sdata->vif.bss_conf.enable_beacon &&
1254f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	    (changed & (BSS_CHANGED_BEACON |
1255f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen			BSS_CHANGED_HT |
1256f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen			BSS_CHANGED_BASIC_RATES |
1257f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen			BSS_CHANGED_BEACON_INT)))
1258f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen		if (ieee80211_mesh_rebuild_beacon(sdata))
1259f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen			return;
1260f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen
1261f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	ieee80211_bss_info_change_notify(sdata, changed);
1262f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen}
1263f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen
12641fa57d017366fb26b58af110a38b36a4f0214a62Johannes Bergvoid ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
1265472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg{
1266472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
1267472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1268ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen	sdata_lock(sdata);
1269ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen
1270ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen	/* mesh already went down */
12711693d34416a4b07e291578b4b87dc811876046cfJohannes Berg	if (!sdata->u.mesh.mesh_id_len)
1272ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen		goto out;
1273ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen
1274472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	if (ifmsh->preq_queue_len &&
1275472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	    time_after(jiffies,
1276472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		       ifmsh->last_preq + msecs_to_jiffies(ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval)))
1277472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		mesh_path_start_discovery(sdata);
1278472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
127918889231e4527dfe23145efe318e74744794a95dJavier Cardona	if (test_and_clear_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags))
128018889231e4527dfe23145efe318e74744794a95dJavier Cardona		mesh_mpath_table_grow();
128118889231e4527dfe23145efe318e74744794a95dJavier Cardona
1282dcac908babcd8ce21057e476c8df609b28ad2cd8Nick Ledovskikh	if (test_and_clear_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags))
128318889231e4527dfe23145efe318e74744794a95dJavier Cardona		mesh_mpp_table_grow();
128418889231e4527dfe23145efe318e74744794a95dJavier Cardona
128518889231e4527dfe23145efe318e74744794a95dJavier Cardona	if (test_and_clear_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags))
1286bf7cd94dcc71682cd6af4a9028f95307b7db41c5Johannes Berg		ieee80211_mesh_housekeeping(sdata);
1287e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo
1288e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo	if (test_and_clear_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags))
1289e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo		ieee80211_mesh_rootpath(sdata);
1290dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona
1291dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona	if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags))
1292dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona		mesh_sync_adjust_tbtt(sdata);
1293ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen
1294f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen	if (test_and_clear_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags))
1295f81a9dedaff434604c7fc3d9c299d277b76db0a8Thomas Pedersen		mesh_bss_info_changed(sdata);
1296ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersenout:
1297ecccd072b07e7fd09c54d0f86f9374e2645cde97Thomas Pedersen	sdata_unlock(sdata);
1298472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg}
1299472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1300472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Bergvoid ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local)
1301472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg{
1302472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_sub_if_data *sdata;
1303472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1304472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	rcu_read_lock();
1305472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	list_for_each_entry_rcu(sdata, &local->interfaces, list)
1306370bd005937c0e00f9104a602f9fe1dd6b21b54bBen Greear		if (ieee80211_vif_is_mesh(&sdata->vif) &&
1307370bd005937c0e00f9104a602f9fe1dd6b21b54bBen Greear		    ieee80211_sdata_running(sdata))
130864592c8fc0e99d445fc3fdedddeb6088e20086f1Johannes Berg			ieee80211_queue_work(&local->hw, &sdata->work);
1309472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	rcu_read_unlock();
1310472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg}
1311472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1312902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Bergvoid ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
1313902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg{
1314472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
1315ad2d223aa900179031feb40273881e212941573dJohannes Berg	static u8 zero_addr[ETH_ALEN] = {};
1316472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1317472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	setup_timer(&ifmsh->housekeeping_timer,
1318472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		    ieee80211_mesh_housekeeping_timer,
1319472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg		    (unsigned long) sdata);
1320472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg
1321472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	ifmsh->accepting_plinks = true;
1322472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	atomic_set(&ifmsh->mpaths, 0);
1323f698d856f65c3fea091cc303a135967965c5b880Jasper Bryant-Greene	mesh_rmc_init(sdata);
1324472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	ifmsh->last_preq = jiffies;
1325dca7e9430cb3e492437a5ce891b8b3e315c147caThomas Pedersen	ifmsh->next_perr = jiffies;
13260cb4d4dceb4b7a31c6af0159cac2cec5fbe294a2Luciano Coelho	ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;
1327902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg	/* Allocate all mesh structures when creating the first mesh interface. */
1328902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg	if (!mesh_allocated)
1329902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg		ieee80211s_init();
1330472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	setup_timer(&ifmsh->mesh_path_timer,
1331902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg		    ieee80211_mesh_path_timer,
1332902acc7896d7649fb30e4b22bd4e643c7f34b02cJohannes Berg		    (unsigned long) sdata);
1333e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo	setup_timer(&ifmsh->mesh_path_root_timer,
1334e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo		    ieee80211_mesh_path_root_timer,
1335e304bfd30f356f7b75d30cad0029ecca705fd590Rui Paulo		    (unsigned long) sdata);
1336472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	INIT_LIST_HEAD(&ifmsh->preq_queue.list);
13373f52b7e328c526fa7a592af9bf5772c591ed38a4Marco Porsch	skb_queue_head_init(&ifmsh->ps.bc_buf);
1338472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg	spin_lock_init(&ifmsh->mesh_preq_queue_lock);
1339dbf498fbafa2c23139d5a990e94ed78bafbbea19Javier Cardona	spin_lock_init(&ifmsh->sync_offset_lock);
13402b5e19677592c167d012c2d129407f39d2bdeb8dThomas Pedersen	RCU_INIT_POINTER(ifmsh->beacon, NULL);
1341ad2d223aa900179031feb40273881e212941573dJohannes Berg
1342ad2d223aa900179031feb40273881e212941573dJohannes Berg	sdata->vif.bss_conf.bssid = zero_addr;
1343472dbc45dc1966284de72d7de15690c17ed2cf33Johannes Berg}
1344