2014-09-11 14:30:45

by Rostislav Lisovy

[permalink] [raw]
Subject: [PATCH 0/4] 802.11p OCB mode

The IEEE 802.11p amendment (already part of IEEE 802.11-2012)
specifies usage of 5 and 10 MHz wide channels in 5.9GHz band for
vehicular environment. All the 802.11p compliant devices should
be set to the newly added operation mode -- OCB (Outside the
Context of a BSS). No authentication/association is necessary to
communicate. The only parameter to be configured is the channel
(central frequency and bandwidth) to communicate on. The channel
has to be known a priori. It is set during the 'ocb join'
operation (sent via netlink message from user-space).

This patchset adds the OCB mode, OCB join and leave handling
(nl80211, cfg80211, mac80211), 802.11p EDCA parameters
modification, RX and TX path (for unicast/broadcast messages).

What aspects are ignored for now:
* Rate control -- We limit the supported datarates to the .11a
mandatory rates and use the Minstrel as usual
* Actually restricting the OCB mode to the 5.9 GHz band

Tested with the ath9k card with slightly modified driver.
User-space configuration utilities are not part of this patch.

Rostislav Lisovy (4):
mac80211: OCB mode + join and leave handling
mac80211: Use different EDCA config for OCB
cfg80211: Join and Leave handling for OCB mode
nl80211: Join and Leave handling for OCB mode

include/net/cfg80211.h | 11 ++
include/net/mac80211.h | 2 +
include/uapi/linux/nl80211.h | 11 ++
net/mac80211/Kconfig | 11 ++
net/mac80211/Makefile | 3 +-
net/mac80211/cfg.c | 14 +++
net/mac80211/chan.c | 1 +
net/mac80211/debug.h | 10 ++
net/mac80211/driver-ops.h | 3 +-
net/mac80211/ieee80211_i.h | 20 ++++
net/mac80211/iface.c | 20 ++++
net/mac80211/ocb.c | 236 +++++++++++++++++++++++++++++++++++++++++++
net/mac80211/rx.c | 33 ++++++
net/mac80211/tx.c | 15 +++
net/mac80211/util.c | 31 +++++-
net/mac80211/wme.c | 4 +
net/wireless/Makefile | 2 +-
net/wireless/chan.c | 8 ++
net/wireless/core.h | 10 ++
net/wireless/nl80211.c | 47 +++++++++
net/wireless/ocb.c | 88 ++++++++++++++++
net/wireless/rdev-ops.h | 21 ++++
net/wireless/trace.h | 21 ++++
net/wireless/util.c | 5 +-
24 files changed, 618 insertions(+), 9 deletions(-)
create mode 100644 net/mac80211/ocb.c
create mode 100644 net/wireless/ocb.c

--
2.0.0.rc4



2014-09-11 14:30:49

by Rostislav Lisovy

[permalink] [raw]
Subject: [PATCH 1/4] mac80211: OCB mode + join and leave handling

All the devices compliant to the IEEE 802.11p
(dot11OCBActivated=true) should operate in the OCB
(Outside the Context of a BSS) mode. This makes it
possible to communicate without the need for
association/authentication. All participants use the
wildcard BSSID (set to all 1's).

In OCB mode there are no extra configuration options
besides the channel (center frequency and width) used
for the communication. The channel must be known a priori.
This is set when joining the network explicitly with the
'ocb join' command.

Signed-off-by: Rostislav Lisovy <[email protected]>
---
include/net/mac80211.h | 2 +
include/uapi/linux/nl80211.h | 3 +
net/mac80211/Kconfig | 11 ++
net/mac80211/Makefile | 3 +-
net/mac80211/cfg.c | 14 +++
net/mac80211/chan.c | 1 +
net/mac80211/debug.h | 10 ++
net/mac80211/driver-ops.h | 3 +-
net/mac80211/ieee80211_i.h | 20 ++++
net/mac80211/iface.c | 20 ++++
net/mac80211/ocb.c | 236 +++++++++++++++++++++++++++++++++++++++++++
net/mac80211/rx.c | 33 ++++++
net/mac80211/tx.c | 15 +++
net/mac80211/util.c | 4 +
14 files changed, 373 insertions(+), 2 deletions(-)
create mode 100644 net/mac80211/ocb.c

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index c6e6a68..255ba85 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -263,6 +263,7 @@ struct ieee80211_vif_chanctx_switch {
* @BSS_CHANGED_BANDWIDTH: The bandwidth used by this interface changed,
* note that this is only called when it changes after the channel
* context had been assigned.
+ * @BSS_CHANGED_OCB: OCB join status changed
*/
enum ieee80211_bss_change {
BSS_CHANGED_ASSOC = 1<<0,
@@ -287,6 +288,7 @@ enum ieee80211_bss_change {
BSS_CHANGED_P2P_PS = 1<<19,
BSS_CHANGED_BEACON_INFO = 1<<20,
BSS_CHANGED_BANDWIDTH = 1<<21,
+ BSS_CHANGED_OCB = 1<<22,

/* when adding here, make sure to change ieee80211_reconfig */
};
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 29c4399..cdf9ba4 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2031,6 +2031,8 @@ enum nl80211_attrs {
* and therefore can't be created in the normal ways, use the
* %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE
* commands to create and destroy one
+ * @NL80211_IF_TYPE_OCB: Outside Context of a BSS
+ * this mode corresponds to the MIB variable dot11OCBActivated=true
* @NL80211_IFTYPE_MAX: highest interface type number currently defined
* @NUM_NL80211_IFTYPES: number of defined interface types
*
@@ -2050,6 +2052,7 @@ enum nl80211_iftype {
NL80211_IFTYPE_P2P_CLIENT,
NL80211_IFTYPE_P2P_GO,
NL80211_IFTYPE_P2P_DEVICE,
+ NL80211_IFTYPE_OCB,

/* keep last */
NUM_NL80211_IFTYPES,
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index aeb6a48..b0d7684 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -169,6 +169,17 @@ config MAC80211_HT_DEBUG

Do not select this option.

+config MAC80211_OCB_DEBUG
+ bool "Verbose OCB debugging"
+ depends on MAC80211_DEBUG_MENU
+ ---help---
+ Selecting this option causes mac80211 to print out
+ very verbose OCB debugging messages. It should not
+ be selected on production systems as those messages
+ are remotely triggerable.
+
+ Do not select this option.
+
config MAC80211_IBSS_DEBUG
bool "Verbose IBSS debugging"
depends on MAC80211_DEBUG_MENU
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 7273d27..e53671b 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -27,7 +27,8 @@ mac80211-y := \
event.o \
chan.o \
trace.o mlme.o \
- tdls.o
+ tdls.o \
+ ocb.o

mac80211-$(CONFIG_MAC80211_LEDS) += led.o
mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 101ae6c..3ec87bd 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -229,6 +229,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
case NUM_NL80211_IFTYPES:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_OCB:
/* shouldn't happen */
WARN_ON_ONCE(1);
break;
@@ -1955,6 +1956,17 @@ static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev));
}

+static int ieee80211_join_ocb(struct wiphy *wiphy, struct net_device *dev,
+ struct ocb_setup *setup)
+{
+ return ieee80211_ocb_join(IEEE80211_DEV_TO_SUB_IF(dev), setup);
+}
+
+static int ieee80211_leave_ocb(struct wiphy *wiphy, struct net_device *dev)
+{
+ return ieee80211_ocb_leave(IEEE80211_DEV_TO_SUB_IF(dev));
+}
+
static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev,
int rate[IEEE80211_NUM_BANDS])
{
@@ -3541,6 +3553,8 @@ const struct cfg80211_ops mac80211_config_ops = {
.join_mesh = ieee80211_join_mesh,
.leave_mesh = ieee80211_leave_mesh,
#endif
+ .join_ocb = ieee80211_join_ocb,
+ .leave_ocb = ieee80211_leave_ocb,
.change_bss = ieee80211_change_bss,
.set_txq_params = ieee80211_set_txq_params,
.set_monitor_channel = ieee80211_set_monitor_channel,
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index bd01a9f..32636de 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -672,6 +672,7 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_OCB:
break;
default:
WARN_ON_ONCE(1);
diff --git a/net/mac80211/debug.h b/net/mac80211/debug.h
index 493d680..1956b31 100644
--- a/net/mac80211/debug.h
+++ b/net/mac80211/debug.h
@@ -2,6 +2,12 @@
#define __MAC80211_DEBUG_H
#include <net/cfg80211.h>

+#ifdef CONFIG_MAC80211_OCB_DEBUG
+#define MAC80211_OCB_DEBUG 1
+#else
+#define MAC80211_OCB_DEBUG 0
+#endif
+
#ifdef CONFIG_MAC80211_IBSS_DEBUG
#define MAC80211_IBSS_DEBUG 1
#else
@@ -131,6 +137,10 @@ do { \
_sdata_dbg(MAC80211_HT_DEBUG && net_ratelimit(), \
sdata, fmt, ##__VA_ARGS__)

+#define ocb_dbg(sdata, fmt, ...) \
+ _sdata_dbg(MAC80211_OCB_DEBUG, \
+ sdata, fmt, ##__VA_ARGS__)
+
#define ibss_dbg(sdata, fmt, ...) \
_sdata_dbg(MAC80211_IBSS_DEBUG, \
sdata, fmt, ##__VA_ARGS__)
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 196d48c..7abe44e 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -214,7 +214,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
BSS_CHANGED_BEACON_ENABLED) &&
sdata->vif.type != NL80211_IFTYPE_AP &&
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
- sdata->vif.type != NL80211_IFTYPE_MESH_POINT))
+ sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
+ sdata->vif.type != NL80211_IFTYPE_OCB))
return;

if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index c2aaec4..ea6a601 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -82,6 +82,8 @@ struct ieee80211_fragment_entry {
u8 last_pn[6]; /* PN of the last fragment if CCMP was used */
};

+/* Used in OCB mode */
+static const u8 bssid_wildcard[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

struct ieee80211_bss {
u32 device_ts_beacon, device_ts_presp;
@@ -546,6 +548,14 @@ struct ieee80211_if_ibss {
} state;
};

+struct ieee80211_if_ocb {
+ struct timer_list housekeeping_timer;
+ unsigned long wrkq_flags;
+
+ spinlock_t incomplete_lock;
+ struct list_head incomplete_stations;
+};
+
/**
* struct ieee80211_mesh_sync_ops - Extensible synchronization framework interface
*
@@ -839,6 +849,7 @@ struct ieee80211_sub_if_data {
struct ieee80211_if_managed mgd;
struct ieee80211_if_ibss ibss;
struct ieee80211_if_mesh mesh;
+ struct ieee80211_if_ocb ocb;
u32 mntr_flags;
} u;

@@ -1471,6 +1482,15 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata);
void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);

+/* OCB code */
+void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata);
+void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata,
+ const u8 *bssid, const u8 *addr, u32 supp_rates);
+void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata);
+int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata,
+ struct ocb_setup *setup);
+int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata);
+
/* mesh code */
void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index af23722..590763b 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -259,6 +259,15 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
list_for_each_entry(nsdata, &local->interfaces, list) {
if (nsdata != sdata && ieee80211_sdata_running(nsdata)) {
/*
+ * Only OCB and monitor mode may coexist
+ */
+ if ((sdata->vif.type == NL80211_IFTYPE_OCB &&
+ nsdata->vif.type != NL80211_IFTYPE_MONITOR) ||
+ (sdata->vif.type != NL80211_IFTYPE_MONITOR &&
+ nsdata->vif.type == NL80211_IFTYPE_OCB))
+ return -EBUSY;
+
+ /*
* Allow only a single IBSS interface to be up at any
* time. This is restricted because beacon distribution
* cannot work properly if both are in the same IBSS.
@@ -521,6 +530,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_P2P_DEVICE:
+ case NL80211_IFTYPE_OCB:
/* no special treatment */
break;
case NL80211_IFTYPE_UNSPECIFIED:
@@ -631,6 +641,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_OCB:
netif_carrier_off(dev);
break;
case NL80211_IFTYPE_WDS:
@@ -1279,6 +1290,9 @@ static void ieee80211_iface_work(struct work_struct *work)
break;
ieee80211_mesh_work(sdata);
break;
+ case NL80211_IFTYPE_OCB:
+ ieee80211_ocb_work(sdata);
+ break;
default:
break;
}
@@ -1349,6 +1363,10 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
ieee80211_sta_setup_sdata(sdata);
break;
+ case NL80211_IFTYPE_OCB:
+ sdata->vif.bss_conf.bssid = bssid_wildcard;
+ ieee80211_ocb_setup_sdata(sdata);
+ break;
case NL80211_IFTYPE_ADHOC:
sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
ieee80211_ibss_setup_sdata(sdata);
@@ -1396,6 +1414,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_OCB:
/*
* Could maybe also all others here?
* Just not sure how that interacts
@@ -1411,6 +1430,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_OCB:
/*
* Could probably support everything
* but WDS here (WDS do_open can fail
diff --git a/net/mac80211/ocb.c b/net/mac80211/ocb.c
new file mode 100644
index 0000000..bb51140
--- /dev/null
+++ b/net/mac80211/ocb.c
@@ -0,0 +1,236 @@
+/*
+ * OCB mode implementation
+ *
+ * Copyright: (c) 2014 Czech Technical University in Prague
+ * (c) 2014 Volkswagen Group Research
+ * Author: Rostislav Lisovy <[email protected]>
+ * Funded by: Volkswagen Group Research
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+
+#include "ieee80211_i.h"
+#include "driver-ops.h"
+#include "rate.h"
+
+#define IEEE80211_OCB_HOUSEKEEPING_INTERVAL (60 * HZ)
+#define IEEE80211_OCB_PEER_INACTIVITY_LIMIT (240 * HZ)
+#define IEEE80211_OCB_MAX_STA_ENTRIES 128
+
+enum ocb_deferred_task_flags {
+ OCB_WORK_HOUSEKEEPING,
+};
+
+void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata,
+ const u8 *bssid, const u8 *addr,
+ u32 supp_rates)
+{
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_supported_band *sband;
+ enum nl80211_bss_scan_width scan_width;
+ struct sta_info *sta;
+ int band;
+
+ /* XXX: Consider removing the least recently used entry and
+ * allow new one to be added.
+ */
+ if (local->num_sta >= IEEE80211_OCB_MAX_STA_ENTRIES) {
+ net_info_ratelimited("%s: No room for a new OCB STA entry %pM\n",
+ sdata->name, addr);
+ return;
+ }
+
+ ocb_dbg(sdata, "Adding new OCB station %pM\n", addr);
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON_ONCE(!chanctx_conf)) {
+ rcu_read_unlock();
+ return;
+ }
+ band = chanctx_conf->def.chan->band;
+ scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
+ rcu_read_unlock();
+
+ sta = sta_info_alloc(sdata, addr, GFP_KERNEL);
+ if (!sta)
+ return;
+
+ sta->last_rx = jiffies;
+
+ /* Add only mandatory rates for now */
+ sband = local->hw.wiphy->bands[band];
+ sta->sta.supp_rates[band] = ieee80211_mandatory_rates(sband, scan_width);
+ /* sta->sta.supp_rates[band] |= supp_rates; */
+
+ spin_lock(&ifocb->incomplete_lock);
+ list_add(&sta->list, &ifocb->incomplete_stations);
+ spin_unlock(&ifocb->incomplete_lock);
+ ieee80211_queue_work(&local->hw, &sdata->work);
+}
+
+/* TODO: Almost the same as ieee80211_ibss_finish_sta()
+ * Maybe use the same function for both?
+ */
+static struct sta_info *ieee80211_ocb_finish_sta(struct sta_info *sta)
+ __acquires(RCU)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ u8 addr[ETH_ALEN];
+
+ ether_addr_copy(addr, sta->sta.addr);
+
+ ocb_dbg(sdata, "Adding new IBSS station %pM (dev=%s)\n",
+ addr, sdata->name);
+
+ sta_info_move_state(sta, IEEE80211_STA_AUTH);
+ sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+ sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
+
+ rate_control_rate_init(sta);
+
+ /* If it fails, maybe we raced another insertion? */
+ if (sta_info_insert_rcu(sta))
+ return sta_info_get(sdata, addr);
+ return sta;
+}
+
+static void ieee80211_ocb_housekeeping(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+
+ ocb_dbg(sdata, "Running ocb housekeeping\n");
+
+ ieee80211_sta_expire(sdata, IEEE80211_OCB_PEER_INACTIVITY_LIMIT);
+
+ mod_timer(&ifocb->housekeeping_timer,
+ round_jiffies(jiffies + IEEE80211_OCB_HOUSEKEEPING_INTERVAL));
+}
+
+void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+ struct sta_info *sta;
+
+ sdata_lock(sdata);
+
+ spin_lock_bh(&ifocb->incomplete_lock);
+ while (!list_empty(&ifocb->incomplete_stations)) {
+ sta = list_first_entry(&ifocb->incomplete_stations,
+ struct sta_info, list);
+ list_del(&sta->list);
+ spin_unlock_bh(&ifocb->incomplete_lock);
+
+ ieee80211_ocb_finish_sta(sta);
+ rcu_read_unlock();
+ spin_lock_bh(&ifocb->incomplete_lock);
+ }
+ spin_unlock_bh(&ifocb->incomplete_lock);
+
+ if (test_and_clear_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags))
+ ieee80211_ocb_housekeeping(sdata);
+
+ sdata_unlock(sdata);
+}
+
+static void ieee80211_ocb_housekeeping_timer(unsigned long data)
+{
+ struct ieee80211_sub_if_data *sdata = (void *)data;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+
+ set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags);
+
+ ieee80211_queue_work(&local->hw, &sdata->work);
+}
+
+void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+
+ setup_timer(&ifocb->housekeeping_timer,
+ ieee80211_ocb_housekeeping_timer,
+ (unsigned long)sdata);
+ INIT_LIST_HEAD(&ifocb->incomplete_stations);
+ spin_lock_init(&ifocb->incomplete_lock);
+}
+
+int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata,
+ struct ocb_setup *setup)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+ u32 changed = BSS_CHANGED_OCB;
+ int err;
+
+ sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
+ sdata->smps_mode = IEEE80211_SMPS_OFF;
+ sdata->needed_rx_chains = sdata->local->rx_chains;
+
+ mutex_lock(&sdata->local->mtx);
+ err = ieee80211_vif_use_channel(sdata, &setup->chandef,
+ IEEE80211_CHANCTX_EXCLUSIVE);
+ mutex_unlock(&sdata->local->mtx);
+ if (err)
+ return err;
+
+ ieee80211_bss_info_change_notify(sdata, changed);
+
+ set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags);
+ ieee80211_queue_work(&local->hw, &sdata->work);
+
+ netif_carrier_on(sdata->dev);
+ return 0;
+}
+
+int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+ struct ieee80211_local *local = sdata->local;
+ struct sta_info *sta;
+
+ sta_info_flush(sdata);
+
+ spin_lock_bh(&ifocb->incomplete_lock);
+ while (!list_empty(&ifocb->incomplete_stations)) {
+ sta = list_first_entry(&ifocb->incomplete_stations,
+ struct sta_info, list);
+ list_del(&sta->list);
+ spin_unlock_bh(&ifocb->incomplete_lock);
+
+ sta_info_free(local, sta);
+ spin_lock_bh(&ifocb->incomplete_lock);
+ }
+ spin_unlock_bh(&ifocb->incomplete_lock);
+
+ netif_carrier_off(sdata->dev);
+ clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_OCB);
+
+#if 0 /* TODO */
+ drv_leave_ocb(local, sdata);
+#endif
+ mutex_lock(&sdata->local->mtx);
+ ieee80211_vif_release_channel(sdata);
+ mutex_unlock(&sdata->local->mtx);
+
+ skb_queue_purge(&sdata->skb_queue);
+
+ del_timer_sync(&sdata->u.ocb.housekeeping_timer);
+
+ return 0;
+}
+
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index b04ca40..58975f7 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1032,6 +1032,7 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
ieee80211_is_pspoll(hdr->frame_control)) &&
rx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
rx->sdata->vif.type != NL80211_IFTYPE_WDS &&
+ rx->sdata->vif.type != NL80211_IFTYPE_OCB &&
(!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))) {
/*
* accept port control frames from the AP even when it's not
@@ -1272,6 +1273,11 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
sta->last_rx_rate_vht_nss = status->vht_nss;
}
}
+ } else if (rx->sdata->vif.type == NL80211_IFTYPE_OCB) {
+ u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len,
+ NL80211_IFTYPE_OCB);
+ if (ieee80211_bssid_match(bssid, bssid_wildcard))
+ sta->last_rx = jiffies;
} else if (!is_multicast_ether_addr(hdr->addr1)) {
/*
* Mesh beacons will update last_rx when if they are found to
@@ -2820,6 +2826,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)

if (!ieee80211_vif_is_mesh(&sdata->vif) &&
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+ sdata->vif.type != NL80211_IFTYPE_OCB &&
sdata->vif.type != NL80211_IFTYPE_STATION)
return RX_DROP_MONITOR;

@@ -3130,6 +3137,32 @@ static bool prepare_for_handlers(struct ieee80211_rx_data *rx,
BIT(rate_idx));
}
break;
+ case NL80211_IFTYPE_OCB:
+ if (!bssid)
+ return false;
+ if (ieee80211_is_beacon(hdr->frame_control)) {
+ return false;
+ } else if (!ieee80211_bssid_match(bssid, bssid_wildcard)) {
+ ocb_dbg(sdata, "BSSID mismatch in OCB mode!\n");
+ return false;
+ } else if (!multicast &&
+ !ether_addr_equal(sdata->dev->dev_addr, hdr->addr1)) {
+ /* if we are in promisc mode we also accept
+ * packets not destined for us
+ */
+ if (!(sdata->dev->flags & IFF_PROMISC))
+ return false;
+ rx->flags &= ~IEEE80211_RX_RA_MATCH;
+ } else if (!rx->sta) {
+ int rate_idx;
+ if (status->flag & RX_FLAG_HT)
+ rate_idx = 0; /* TODO: HT rates */
+ else
+ rate_idx = status->rate_idx;
+ ieee80211_ocb_rx_no_sta(sdata, bssid, hdr->addr2,
+ BIT(rate_idx));
+ }
+ break;
case NL80211_IFTYPE_MESH_POINT:
if (!multicast &&
!ether_addr_equal(sdata->vif.addr, hdr->addr1)) {
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 2f7754c..2352f3a 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -296,6 +296,9 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
*/
return TX_DROP;

+ if (tx->sdata->vif.type == NL80211_IFTYPE_OCB)
+ return TX_CONTINUE;
+
if (tx->sdata->vif.type == NL80211_IFTYPE_WDS)
return TX_CONTINUE;

@@ -2013,6 +2016,17 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
goto fail_rcu;
band = chanctx_conf->def.chan->band;
break;
+ case NL80211_IFTYPE_OCB:
+ /* DA SA BSSID */
+ memcpy(hdr.addr1, skb->data, ETH_ALEN);
+ memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+ memset(hdr.addr3, 0xff, ETH_ALEN);
+ hdrlen = 24;
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf)
+ goto fail_rcu;
+ band = chanctx_conf->def.chan->band;
+ break;
case NL80211_IFTYPE_ADHOC:
/* DA SA BSSID */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
@@ -2057,6 +2071,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
* EAPOL frames from the local station.
*/
if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) &&
+ (sdata->vif.type != NL80211_IFTYPE_OCB) &&
!multicast && !authorized &&
(cpu_to_be16(ethertype) != sdata->control_port_protocol ||
!ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 3c61060..3eb4151 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1813,6 +1813,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
ieee80211_bss_info_change_notify(sdata, changed);
sdata_unlock(sdata);
break;
+ case NL80211_IFTYPE_OCB:
+ changed |= BSS_CHANGED_OCB;
+ ieee80211_bss_info_change_notify(sdata, changed);
+ break;
case NL80211_IFTYPE_ADHOC:
changed |= BSS_CHANGED_IBSS;
/* fall through */
--
2.0.0.rc4


2014-09-11 14:30:49

by Rostislav Lisovy

[permalink] [raw]
Subject: [PATCH 2/4] mac80211: Use different EDCA config for OCB

The IEEE 802.11p specifies different EDCA parameters
for the OCB mode.

Signed-off-by: Rostislav Lisovy <[email protected]>
---
net/mac80211/util.c | 27 ++++++++++++++++++++++-----
net/mac80211/wme.c | 4 ++++
2 files changed, 26 insertions(+), 5 deletions(-)

diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 3eb4151..cac57eb 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1073,6 +1073,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
struct ieee80211_chanctx_conf *chanctx_conf;
int ac;
bool use_11b, enable_qos;
+ bool is_ocb; /* Use another EDCA parameters if dot11OCBEnabled is true */
int aCWmin, aCWmax;

if (!local->ops->conf_tx)
@@ -1097,6 +1098,8 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
*/
enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);

+ is_ocb = (sdata->vif.type == NL80211_IFTYPE_OCB);
+
/* Set defaults according to 802.11-2007 Table 7-37 */
aCWmax = 1023;
if (use_11b)
@@ -1118,7 +1121,10 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
qparam.cw_max = aCWmax;
qparam.cw_min = aCWmin;
qparam.txop = 0;
- qparam.aifs = 7;
+ if (is_ocb)
+ qparam.aifs = 9;
+ else
+ qparam.aifs = 7;
break;
/* never happens but let's not leave undefined */
default:
@@ -1126,21 +1132,32 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
qparam.cw_max = aCWmax;
qparam.cw_min = aCWmin;
qparam.txop = 0;
- qparam.aifs = 3;
+ if (is_ocb)
+ qparam.aifs = 6;
+ else
+ qparam.aifs = 3;
break;
case IEEE80211_AC_VI:
qparam.cw_max = aCWmin;
qparam.cw_min = (aCWmin + 1) / 2 - 1;
- if (use_11b)
+ if (is_ocb)
+ qparam.txop = 0;
+ else if (use_11b)
qparam.txop = 6016/32;
else
qparam.txop = 3008/32;
- qparam.aifs = 2;
+
+ if (is_ocb)
+ qparam.aifs = 3;
+ else
+ qparam.aifs = 2;
break;
case IEEE80211_AC_VO:
qparam.cw_max = (aCWmin + 1) / 2 - 1;
qparam.cw_min = (aCWmin + 1) / 4 - 1;
- if (use_11b)
+ if (is_ocb)
+ qparam.txop = 0;
+ else if (use_11b)
qparam.txop = 3264/32;
else
qparam.txop = 1504/32;
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 3b87398..456fa1c 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -139,6 +139,10 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
case NL80211_IFTYPE_ADHOC:
ra = skb->data;
break;
+ case NL80211_IFTYPE_OCB:
+ /* all stations are required to support WME */
+ qos = true;
+ break;
default:
break;
}
--
2.0.0.rc4


2014-09-11 14:30:47

by Rostislav Lisovy

[permalink] [raw]
Subject: [PATCH 3/4] cfg80211: Join and Leave handling for OCB mode

Signed-off-by: Rostislav Lisovy <[email protected]>
---
include/net/cfg80211.h | 11 +++++++
net/wireless/Makefile | 2 +-
net/wireless/chan.c | 8 +++++
net/wireless/core.h | 10 ++++++
net/wireless/ocb.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++
net/wireless/rdev-ops.h | 21 ++++++++++++
net/wireless/trace.h | 21 ++++++++++++
net/wireless/util.c | 5 ++-
8 files changed, 164 insertions(+), 2 deletions(-)
create mode 100644 net/wireless/ocb.c

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 0d17ec9..0897e33 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -375,6 +375,8 @@ static inline enum nl80211_channel_type
cfg80211_get_chandef_type(const struct cfg80211_chan_def *chandef)
{
switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
case NL80211_CHAN_WIDTH_20_NOHT:
return NL80211_CHAN_NO_HT;
case NL80211_CHAN_WIDTH_20:
@@ -1337,6 +1339,11 @@ struct mesh_setup {
u32 basic_rates;
};

+
+struct ocb_setup {
+ struct cfg80211_chan_def chandef;
+};
+
/**
* struct ieee80211_txq_params - TX queue parameters
* @ac: AC identifier
@@ -2396,6 +2403,10 @@ struct cfg80211_ops {
const struct mesh_setup *setup);
int (*leave_mesh)(struct wiphy *wiphy, struct net_device *dev);

+ int (*join_ocb)(struct wiphy *wiphy, struct net_device *dev,
+ struct ocb_setup *setup);
+ int (*leave_ocb)(struct wiphy *wiphy, struct net_device *dev);
+
int (*change_bss)(struct wiphy *wiphy, struct net_device *dev,
struct bss_parameters *params);

diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index a761670..4c9e39f 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -10,7 +10,7 @@ obj-$(CONFIG_WEXT_SPY) += wext-spy.o
obj-$(CONFIG_WEXT_PRIV) += wext-priv.o

cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
-cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o
+cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o
cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o
cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 72d81e2..6e6829c 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -366,6 +366,7 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy,

break;
case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_OCB:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_AP_VLAN:
@@ -892,6 +893,13 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
*radar_detect |= BIT(wdev->chandef.width);
}
return;
+ case NL80211_IFTYPE_OCB:
+ if (wdev->chandef.chan) {
+ *chan = wdev->chandef.chan;
+ *chanmode = CHAN_MODE_EXCLUSIVE;
+ return;
+ }
+ break;
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_WDS:
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 7e3a3ce..8230c18 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -290,6 +290,16 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct cfg80211_chan_def *chandef);

+/* OCB */
+int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup);
+int cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup);
+int cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev);
+
/* AP */
int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
struct net_device *dev, bool notify);
diff --git a/net/wireless/ocb.c b/net/wireless/ocb.c
new file mode 100644
index 0000000..d288ae6
--- /dev/null
+++ b/net/wireless/ocb.c
@@ -0,0 +1,88 @@
+/*
+ * OCB mode implementation
+ *
+ * Copyright: (c) 2014 Czech Technical University in Prague
+ * (c) 2014 Volkswagen Group Research
+ * Author: Rostislav Lisovy <[email protected]>
+ * Funded by: Volkswagen Group Research
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+#include "nl80211.h"
+#include "core.h"
+#include "rdev-ops.h"
+
+int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
+ return -EOPNOTSUPP;
+
+ WARN_ON(!setup->chandef.chan);
+
+ err = rdev_join_ocb(rdev, dev, setup);
+ if (!err)
+ wdev->chandef = setup->chandef;
+
+ return err;
+}
+
+int cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ wdev_lock(wdev);
+ err = __cfg80211_join_ocb(rdev, dev, setup);
+ wdev_unlock(wdev);
+
+ return err;
+}
+
+static int __cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->leave_ocb)
+ return -EOPNOTSUPP;
+
+ err = rdev_leave_ocb(rdev, dev);
+ if (!err)
+ memset(&wdev->chandef, 0, sizeof(wdev->chandef));
+
+ return err;
+}
+
+int cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ wdev_lock(wdev);
+ err = __cfg80211_leave_ocb(rdev, dev);
+ wdev_unlock(wdev);
+
+ return err;
+}
+
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 56c2240..b1f7685 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -322,6 +322,27 @@ static inline int rdev_leave_mesh(struct cfg80211_registered_device *rdev,
return ret;
}

+static inline int rdev_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup)
+{
+ int ret;
+ trace_rdev_join_ocb(&rdev->wiphy, dev, setup);
+ ret = rdev->ops->join_ocb(&rdev->wiphy, dev, setup);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_leave_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ int ret;
+ trace_rdev_leave_ocb(&rdev->wiphy, dev);
+ ret = rdev->ops->leave_ocb(&rdev->wiphy, dev);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
static inline int rdev_change_bss(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct bss_parameters *params)
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 0c524cd..465530e 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -600,6 +600,11 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_ibss,
TP_ARGS(wiphy, netdev)
);

+DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_ocb,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev)
+);
+
DEFINE_EVENT(wiphy_netdev_evt, rdev_flush_pmksa,
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
TP_ARGS(wiphy, netdev)
@@ -1246,6 +1251,22 @@ TRACE_EVENT(rdev_join_ibss,
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid)
);

+TRACE_EVENT(rdev_join_ocb,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ const struct ocb_setup *setup),
+ TP_ARGS(wiphy, netdev, setup),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG)
+);
+
TRACE_EVENT(rdev_set_wiphy_params,
TP_PROTO(struct wiphy *wiphy, u32 changed),
TP_ARGS(wiphy, changed),
diff --git a/net/wireless/util.c b/net/wireless/util.c
index a8b2816..b253f5f 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -442,7 +442,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
break;
case cpu_to_le16(0):
if (iftype != NL80211_IFTYPE_ADHOC &&
- iftype != NL80211_IFTYPE_STATION)
+ iftype != NL80211_IFTYPE_STATION &&
+ iftype != NL80211_IFTYPE_OCB)
return -1;
break;
}
@@ -519,6 +520,7 @@ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
memcpy(hdr.addr3, skb->data, ETH_ALEN);
hdrlen = 24;
break;
+ case NL80211_IFTYPE_OCB:
case NL80211_IFTYPE_ADHOC:
/* DA SA BSSID */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
@@ -937,6 +939,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
if (dev->ieee80211_ptr->use_4addr)
break;
/* fall through */
+ case NL80211_IFTYPE_OCB:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_ADHOC:
dev->priv_flags |= IFF_DONT_BRIDGE;
--
2.0.0.rc4


2014-09-11 14:30:49

by Rostislav Lisovy

[permalink] [raw]
Subject: [PATCH 4/4] nl80211: Join and Leave handling for OCB mode

Signed-off-by: Rostislav Lisovy <[email protected]>
---
include/uapi/linux/nl80211.h | 8 ++++++++
net/wireless/nl80211.c | 47 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+)

diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index cdf9ba4..4b39455 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -544,6 +544,11 @@
* @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the
* network is determined by the network interface.
*
+ * @NL80211_CMD_JOIN_OCB: Join the OCB network. The center frequency and
+ * bandwidth of a channel must be given.
+ * @NL80211_CMD_LEAVE_OCB: Leave the OCB network -- no special arguments, the
+ * network is determined by the network interface.
+ *
* @NL80211_CMD_UNPROT_DEAUTHENTICATE: Unprotected deauthentication frame
* notification. This event is used to indicate that an unprotected
* deauthentication frame was dropped when MFP is in use.
@@ -833,6 +838,9 @@ enum nl80211_commands {
NL80211_CMD_JOIN_MESH,
NL80211_CMD_LEAVE_MESH,

+ NL80211_CMD_JOIN_OCB,
+ NL80211_CMD_LEAVE_OCB,
+
NL80211_CMD_UNPROT_DEAUTHENTICATE,
NL80211_CMD_UNPROT_DISASSOCIATE,

diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index cf178d2..bfbb54d 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1876,6 +1876,7 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
* operation to set the monitor channel if possible.
*/
return !wdev ||
+ wdev->iftype == NL80211_IFTYPE_OCB ||
wdev->iftype == NL80211_IFTYPE_AP ||
wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
wdev->iftype == NL80211_IFTYPE_MONITOR ||
@@ -2307,6 +2308,8 @@ static int nl80211_send_chandef(struct sk_buff *msg,
chandef->chan->center_freq))
return -ENOBUFS;
switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
case NL80211_CHAN_WIDTH_20_NOHT:
case NL80211_CHAN_WIDTH_20:
case NL80211_CHAN_WIDTH_40:
@@ -8118,6 +8121,34 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
}

+static int nl80211_join_ocb(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct ocb_setup setup;
+ int err;
+
+ /* start with default */
+ memset(&setup, 0x0, sizeof(setup));
+
+ if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
+ return -EINVAL;
+
+ err = nl80211_parse_chandef(rdev, info, &setup.chandef);
+ if (err)
+ return err;
+
+ return cfg80211_join_ocb(rdev, dev, &setup);
+}
+
+static int nl80211_leave_ocb(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+
+ return cfg80211_leave_ocb(rdev, dev);
+}
+
static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -9952,6 +9983,22 @@ static const struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
+ {
+ .cmd = NL80211_CMD_JOIN_OCB,
+ .doit = nl80211_join_ocb,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_LEAVE_OCB,
+ .doit = nl80211_leave_ocb,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
#ifdef CONFIG_PM
{
.cmd = NL80211_CMD_GET_WOWLAN,
--
2.0.0.rc4


2014-10-20 09:40:56

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 1/4] mac80211: OCB mode + join and leave handling

On Thu, 2014-10-16 at 18:33 +0200, Rostislav Lisovy wrote:

> You are right. I hope the following is a reasonable solution (in form of
> a patch to my previous patch; comment stolen from some prehistoric
> version of mesh.c):
>
> @@ -127,6 +127,9 @@ void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata)
> struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
> struct sta_info *sta;
>
> + if (!netif_running(sdata->dev))
> + return;

Not sure, it seems you should check "is it operating in OCB mode"? OTOH,
when it's not operating but still around it probably doesn't matter?

> @@ -229,6 +232,13 @@ int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata)
> skb_queue_purge(&sdata->skb_queue);
>
> del_timer_sync(&sdata->u.ocb.housekeeping_timer);
> + /*
> + * If the timer fired while we waited for it, it will have
> + * requeued the work. Now the work will be running again
> + * but will not rearm the timer again because it checks
> + * whether the interface is running, which, at this point,
> + * it no longer is.
> + */

Well, the comment is wrong, since leave() can and will be done while the
interface is running.

> > This isn't safe - ocb_rx_no_sta() used GFP_KERNEL, that's clearly not
> > allowed in this context. But it does answer my previous question about
> > the function being exported - I had assumed that you wouldn't call it
> > here since it would be unsafe :)
>
> A call to sta_info_alloc(sdata, addr, GFP_ATOMIC);
> in ieee80211_ocb_rx_no_sta() should solve this.

Yeah, I guess so, didn't check in detail now.

johannes


2014-10-09 08:23:28

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 1/4] mac80211: OCB mode + join and leave handling

On Thu, 2014-09-11 at 16:30 +0200, Rostislav Lisovy wrote:

[...]

Nice work. Let me (finally) look in more detail ...


> +++ b/include/uapi/linux/nl80211.h
> @@ -2031,6 +2031,8 @@ enum nl80211_attrs {
> * and therefore can't be created in the normal ways, use the
> * %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE
> * commands to create and destroy one
> + * @NL80211_IF_TYPE_OCB: Outside Context of a BSS
> + * this mode corresponds to the MIB variable dot11OCBActivated=true

This change should be part of the cfg80211 patch, as a consequence that
patch must come first in the series.

> +++ b/net/mac80211/cfg.c
> @@ -229,6 +229,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
> case NUM_NL80211_IFTYPES:
> case NL80211_IFTYPE_P2P_CLIENT:
> case NL80211_IFTYPE_P2P_GO:
> + case NL80211_IFTYPE_OCB:
> /* shouldn't happen */

There's no encryption in OCB at all?

> +++ b/net/mac80211/ieee80211_i.h
> @@ -82,6 +82,8 @@ struct ieee80211_fragment_entry {
> u8 last_pn[6]; /* PN of the last fragment if CCMP was used */
> };
>
> +/* Used in OCB mode */
> +static const u8 bssid_wildcard[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

Please don't put data into header files, it'll be duplicated all over.
You can put an extern definition here, or just static const
definition(s) where needed.

> +struct ieee80211_if_ocb {
> + struct timer_list housekeeping_timer;
> + unsigned long wrkq_flags;
> +
> + spinlock_t incomplete_lock;
> + struct list_head incomplete_stations;
> +};

Please add some comments explaining the purpose of the fields.

> +void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata,
> + const u8 *bssid, const u8 *addr,
> + u32 supp_rates)

Does this have to be visible outside the file? I may have missed the
reference(s) but it seems maybe it doesn't have to.

> +/* TODO: Almost the same as ieee80211_ibss_finish_sta()
> + * Maybe use the same function for both?
> + */
> +static struct sta_info *ieee80211_ocb_finish_sta(struct sta_info *sta)
> + __acquires(RCU)
> +{
> + struct ieee80211_sub_if_data *sdata = sta->sdata;
> + u8 addr[ETH_ALEN];
> +
> + ether_addr_copy(addr, sta->sta.addr);

for ether_addr_copy() you need alignment on addr:

u8 addr[ETH_ALEN] __aligned(2);

> +int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata,
> + struct ocb_setup *setup)
> +{
> + struct ieee80211_local *local = sdata->local;
> + struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
> + u32 changed = BSS_CHANGED_OCB;
> + int err;
> +
> + sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
> + sdata->smps_mode = IEEE80211_SMPS_OFF;
> + sdata->needed_rx_chains = sdata->local->rx_chains;
> +
> + mutex_lock(&sdata->local->mtx);
> + err = ieee80211_vif_use_channel(sdata, &setup->chandef,
> + IEEE80211_CHANCTX_EXCLUSIVE);

Do you really need to use the channel context exclusively? I see no
reason for that since the channel is fixed, and doesn't change like in
IBSS, afaict? IBSS needs this, but only because it might change the
channel on the fly.

> +#if 0 /* TODO */
> + drv_leave_ocb(local, sdata);
> +#endif

I'd just remove that for now - probably no current driver needs it?

> + mutex_lock(&sdata->local->mtx);
> + ieee80211_vif_release_channel(sdata);
> + mutex_unlock(&sdata->local->mtx);
> +
> + skb_queue_purge(&sdata->skb_queue);
> +
> + del_timer_sync(&sdata->u.ocb.housekeeping_timer);

That might call the timer - is it safe if that happens here? Looks like
maybe the housekeeping would still get triggered or so.

> + } else if (rx->sdata->vif.type == NL80211_IFTYPE_OCB) {
> + u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len,
> + NL80211_IFTYPE_OCB);
> + if (ieee80211_bssid_match(bssid, bssid_wildcard))
> + sta->last_rx = jiffies;

Calling ieee80211_bssid_match() here is rather pointless - see what it
does. You really just want is_broadcast_ether_addr(bssid) instead of the
ether_addr_equal() that does exactly the same thing.

> @@ -3130,6 +3137,32 @@ static bool prepare_for_handlers(struct ieee80211_rx_data *rx,
> BIT(rate_idx));
> }
> break;
> + case NL80211_IFTYPE_OCB:
> + if (!bssid)
> + return false;
> + if (ieee80211_is_beacon(hdr->frame_control)) {
> + return false;
> + } else if (!ieee80211_bssid_match(bssid, bssid_wildcard)) {
> + ocb_dbg(sdata, "BSSID mismatch in OCB mode!\n");
> + return false;

same here.

And then I guess you don't need bssid_wildcard at all any more (see
previous comments)

> + } else if (!multicast &&
> + !ether_addr_equal(sdata->dev->dev_addr, hdr->addr1)) {
> + /* if we are in promisc mode we also accept
> + * packets not destined for us
> + */
> + if (!(sdata->dev->flags & IFF_PROMISC))
> + return false;
> + rx->flags &= ~IEEE80211_RX_RA_MATCH;
> + } else if (!rx->sta) {
> + int rate_idx;
> + if (status->flag & RX_FLAG_HT)
> + rate_idx = 0; /* TODO: HT rates */
> + else
> + rate_idx = status->rate_idx;
> + ieee80211_ocb_rx_no_sta(sdata, bssid, hdr->addr2,
> + BIT(rate_idx));
> + }

This isn't safe - ocb_rx_no_sta() used GFP_KERNEL, that's clearly not
allowed in this context. But it does answer my previous question about
the function being exported - I had assumed that you wouldn't call it
here since it would be unsafe :)

> + case NL80211_IFTYPE_OCB:
> + /* DA SA BSSID */
> + memcpy(hdr.addr1, skb->data, ETH_ALEN);
> + memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
> + memset(hdr.addr3, 0xff, ETH_ALEN);

Use eth_broadcast_addr(), which really is the same but more expressive.

johannes


2014-10-16 16:33:12

by Rostislav Lisovy

[permalink] [raw]
Subject: Re: [PATCH 1/4] mac80211: OCB mode + join and leave handling

Hello Johannes;
Thanks for the thorough review.

On Thu, 2014-10-09 at 10:23 +0200, Johannes Berg wrote:
> On Thu, 2014-09-11 at 16:30 +0200, Rostislav Lisovy wrote:
> > +++ b/net/mac80211/cfg.c
> > @@ -229,6 +229,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
> > case NUM_NL80211_IFTYPES:
> > case NL80211_IFTYPE_P2P_CLIENT:
> > case NL80211_IFTYPE_P2P_GO:
> > + case NL80211_IFTYPE_OCB:
> > /* shouldn't happen */
>
> There's no encryption in OCB at all?

As far as I know the standard 802.11* encryption is not used. The IEEE
1609 (WAVE protocol stack used in US) does define some encryption but it
is not part of the 802.11p.

> > +void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata,
> > + const u8 *bssid, const u8 *addr,
> > + u32 supp_rates)
>
> Does this have to be visible outside the file? I may have missed the
> reference(s) but it seems maybe it doesn't have to.
>

Please see below.

> > + mutex_lock(&sdata->local->mtx);
> > + ieee80211_vif_release_channel(sdata);
> > + mutex_unlock(&sdata->local->mtx);
> > +
> > + skb_queue_purge(&sdata->skb_queue);
> > +
> > + del_timer_sync(&sdata->u.ocb.housekeeping_timer);
>
> That might call the timer - is it safe if that happens here? Looks like
> maybe the housekeeping would still get triggered or so.

You are right. I hope the following is a reasonable solution (in form of
a patch to my previous patch; comment stolen from some prehistoric
version of mesh.c):

@@ -127,6 +127,9 @@ void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata)
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
struct sta_info *sta;

+ if (!netif_running(sdata->dev))
+ return;
+
sdata_lock(sdata);

spin_lock_bh(&ifocb->incomplete_lock);
@@ -229,6 +232,13 @@ int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata)
skb_queue_purge(&sdata->skb_queue);

del_timer_sync(&sdata->u.ocb.housekeeping_timer);
+ /*
+ * If the timer fired while we waited for it, it will have
+ * requeued the work. Now the work will be running again
+ * but will not rearm the timer again because it checks
+ * whether the interface is running, which, at this point,
+ * it no longer is.
+ */

return 0;
}


> > + } else if (!multicast &&
> > + !ether_addr_equal(sdata->dev->dev_addr, hdr->addr1)) {
> > + /* if we are in promisc mode we also accept
> > + * packets not destined for us
> > + */
> > + if (!(sdata->dev->flags & IFF_PROMISC))
> > + return false;
> > + rx->flags &= ~IEEE80211_RX_RA_MATCH;
> > + } else if (!rx->sta) {
> > + int rate_idx;
> > + if (status->flag & RX_FLAG_HT)
> > + rate_idx = 0; /* TODO: HT rates */
> > + else
> > + rate_idx = status->rate_idx;
> > + ieee80211_ocb_rx_no_sta(sdata, bssid, hdr->addr2,
> > + BIT(rate_idx));
> > + }
>
> This isn't safe - ocb_rx_no_sta() used GFP_KERNEL, that's clearly not
> allowed in this context. But it does answer my previous question about
> the function being exported - I had assumed that you wouldn't call it
> here since it would be unsafe :)

A call to sta_info_alloc(sdata, addr, GFP_ATOMIC);
in ieee80211_ocb_rx_no_sta() should solve this.


I agree with all the other comments and will fix them.

Best regards;
Rostislav;


2014-10-09 08:31:21

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 4/4] nl80211: Join and Leave handling for OCB mode

On Thu, 2014-09-11 at 16:30 +0200, Rostislav Lisovy wrote:
> Signed-off-by: Rostislav Lisovy <[email protected]>

same comment about commit log here - also this patch should either go
first in the series, or just be squashed with the cfg80211 patch,
there's no real need to distinguish between the two. I guess it can't be
first, so best just squash it.

I think you can also squash patches 1 and 2, since 2 is so small.

> ---
> include/uapi/linux/nl80211.h | 8 ++++++++
> net/wireless/nl80211.c | 47 ++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 55 insertions(+)
>
> diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
> index cdf9ba4..4b39455 100644
> --- a/include/uapi/linux/nl80211.h
> +++ b/include/uapi/linux/nl80211.h
> @@ -544,6 +544,11 @@
> * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the
> * network is determined by the network interface.
> *
> + * @NL80211_CMD_JOIN_OCB: Join the OCB network. The center frequency and
> + * bandwidth of a channel must be given.
> + * @NL80211_CMD_LEAVE_OCB: Leave the OCB network -- no special arguments, the
> + * network is determined by the network interface.
> + *
> * @NL80211_CMD_UNPROT_DEAUTHENTICATE: Unprotected deauthentication frame
> * notification. This event is used to indicate that an unprotected
> * deauthentication frame was dropped when MFP is in use.
> @@ -833,6 +838,9 @@ enum nl80211_commands {
> NL80211_CMD_JOIN_MESH,
> NL80211_CMD_LEAVE_MESH,
>
> + NL80211_CMD_JOIN_OCB,
> + NL80211_CMD_LEAVE_OCB,
> +
> NL80211_CMD_UNPROT_DEAUTHENTICATE,
> NL80211_CMD_UNPROT_DISASSOCIATE,

No no, you really really really can't add anything in the middle of this
enum. The's a comments at the top of it explaining why :)

> diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
> index cf178d2..bfbb54d 100644
> --- a/net/wireless/nl80211.c
> +++ b/net/wireless/nl80211.c
> @@ -1876,6 +1876,7 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
> * operation to set the monitor channel if possible.
> */
> return !wdev ||
> + wdev->iftype == NL80211_IFTYPE_OCB ||
> wdev->iftype == NL80211_IFTYPE_AP ||
> wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
> wdev->iftype == NL80211_IFTYPE_MONITOR ||

I don't think this is right - join() handles the channel setting.

> @@ -2307,6 +2308,8 @@ static int nl80211_send_chandef(struct sk_buff *msg,
> chandef->chan->center_freq))
> return -ENOBUFS;
> switch (chandef->width) {
> + case NL80211_CHAN_WIDTH_5:
> + case NL80211_CHAN_WIDTH_10:

This is also wrong, just like the cfg80211.h change in the other patch,
since the NL80211_ATTR_WIPHY_CHANNEL_TYPE cannot encode 5/10 MHz.

You need to remove both changes.

> +static int nl80211_join_ocb(struct sk_buff *skb, struct genl_info *info)
> +{
> + struct cfg80211_registered_device *rdev = info->user_ptr[0];
> + struct net_device *dev = info->user_ptr[1];
> + struct ocb_setup setup;
> + int err;
> +
> + /* start with default */
> + memset(&setup, 0x0, sizeof(setup));

struct ocb_setup setup = {};

Would be an easier way to generate the same code. IMHO no comment is
needed.

johannes


2014-10-20 09:41:48

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 1/4] mac80211: OCB mode + join and leave handling

On Thu, 2014-10-16 at 19:20 +0200, Rostislav Lisovy wrote:

> Now I realized it is not that easy (and I confused "interface running"
> and being "connected to the network"). There seems not to be a solid
> indication that we are no longer "connected" to the network.

Oops, ignore my previous reply.

> I think a field
> enum {
> IEEE80211_OCB_STOPPED,
> IEEE80211_OCB_JOINED,
> } state;
> in struct ieee80211_if_ocb seems to be appropriate.

Yes, that seems doable. Maybe just a "bool joined" would work just as
well, but it won't make a difference in the result - whichever you
prefer.

johannes


2014-10-09 08:26:54

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 3/4] cfg80211: Join and Leave handling for OCB mode

On Thu, 2014-09-11 at 16:30 +0200, Rostislav Lisovy wrote:
> Signed-off-by: Rostislav Lisovy <[email protected]>

A bit more commit log would be good :)

> +++ b/include/net/cfg80211.h
> @@ -375,6 +375,8 @@ static inline enum nl80211_channel_type
> cfg80211_get_chandef_type(const struct cfg80211_chan_def *chandef)
> {
> switch (chandef->width) {
> + case NL80211_CHAN_WIDTH_5:
> + case NL80211_CHAN_WIDTH_10:
> case NL80211_CHAN_WIDTH_20_NOHT:
> return NL80211_CHAN_NO_HT;

This is wrong, the old nl80211_channel_type simply cannot express a 5 or
10 MHz width - so this case should WARN() or something and really never
ever be called here.

Also, it doesn't really belong into this patch?

> +struct ocb_setup {
> + struct cfg80211_chan_def chandef;
> +};

kernel-doc would be appreciated.

> @@ -892,6 +893,13 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
> *radar_detect |= BIT(wdev->chandef.width);
> }
> return;
> + case NL80211_IFTYPE_OCB:
> + if (wdev->chandef.chan) {
> + *chan = wdev->chandef.chan;
> + *chanmode = CHAN_MODE_EXCLUSIVE;
> + return;
> + }

same as in the mac80211 patch - I don't see why this should be
exclusive.

> + WARN_ON(!setup->chandef.chan);

if (WARN_ON(...))
return -EINVAL;

would be better

johannes


2014-10-16 17:20:08

by Rostislav Lisovy

[permalink] [raw]
Subject: Re: [PATCH 1/4] mac80211: OCB mode + join and leave handling

On Thu, 2014-10-16 at 18:33 +0200, Rostislav Lisovy wrote:
> > > + mutex_lock(&sdata->local->mtx);
> > > + ieee80211_vif_release_channel(sdata);
> > > + mutex_unlock(&sdata->local->mtx);
> > > +
> > > + skb_queue_purge(&sdata->skb_queue);
> > > +
> > > + del_timer_sync(&sdata->u.ocb.housekeeping_timer);
> >
> > That might call the timer - is it safe if that happens here? Looks like
> > maybe the housekeeping would still get triggered or so.
>
> You are right. I hope the following is a reasonable solution (in form of
> a patch to my previous patch; comment stolen from some prehistoric
> version of mesh.c):
>
> @@ -127,6 +127,9 @@ void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata)
> struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
> struct sta_info *sta;
>
> + if (!netif_running(sdata->dev))
> + return;
> +
> sdata_lock(sdata);
>
> spin_lock_bh(&ifocb->incomplete_lock);
> @@ -229,6 +232,13 @@ int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata)
> skb_queue_purge(&sdata->skb_queue);
>
> del_timer_sync(&sdata->u.ocb.housekeeping_timer);
> + /*
> + * If the timer fired while we waited for it, it will have
> + * requeued the work. Now the work will be running again
> + * but will not rearm the timer again because it checks
> + * whether the interface is running, which, at this point,
> + * it no longer is.
> + */
>
> return 0;
> }

Now I realized it is not that easy (and I confused "interface running"
and being "connected to the network"). There seems not to be a solid
indication that we are no longer "connected" to the network.
I think a field
enum {
IEEE80211_OCB_STOPPED,
IEEE80211_OCB_JOINED,
} state;
in struct ieee80211_if_ocb seems to be appropriate.

Best regards;
Rostislav