Commit 2e161f78 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville

cfg80211/mac80211: extensible frame processing

Allow userspace to register for more than just
action frames by giving the frame subtype, and
make it possible to use this in various modes
as well.

With some tweaks and some added functionality
this will, in the future, also be usable in AP
mode and be able to replace the cooked monitor
interface currently used in that case.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent ac4c977d
...@@ -39,6 +39,43 @@ ...@@ -39,6 +39,43 @@
* TODO: need more info? * TODO: need more info?
*/ */
/**
* DOC: Frame transmission/registration support
*
* Frame transmission and registration support exists to allow userspace
* management entities such as wpa_supplicant react to management frames
* that are not being handled by the kernel. This includes, for example,
* certain classes of action frames that cannot be handled in the kernel
* for various reasons.
*
* Frame registration is done on a per-interface basis and registrations
* cannot be removed other than by closing the socket. It is possible to
* specify a registration filter to register, for example, only for a
* certain type of action frame. In particular with action frames, those
* that userspace registers for will not be returned as unhandled by the
* driver, so that the registered application has to take responsibility
* for doing that.
*
* The type of frame that can be registered for is also dependent on the
* driver and interface type. The frame types are advertised in wiphy
* attributes so applications know what to expect.
*
* NOTE: When an interface changes type while registrations are active,
* these registrations are ignored until the interface type is
* changed again. This means that changing the interface type can
* lead to a situation that couldn't otherwise be produced, but
* any such registrations will be dormant in the sense that they
* will not be serviced, i.e. they will not receive any frames.
*
* Frame transmission allows userspace to send for example the required
* responses to action frames. It is subject to some sanity checking,
* but many frames can be transmitted. When a frame was transmitted, its
* status is indicated to the sending socket.
*
* For more technical details, see the corresponding command descriptions
* below.
*/
/** /**
* enum nl80211_commands - supported nl80211 commands * enum nl80211_commands - supported nl80211 commands
* *
...@@ -301,16 +338,18 @@ ...@@ -301,16 +338,18 @@
* rate selection. %NL80211_ATTR_IFINDEX is used to specify the interface * rate selection. %NL80211_ATTR_IFINDEX is used to specify the interface
* and @NL80211_ATTR_TX_RATES the set of allowed rates. * and @NL80211_ATTR_TX_RATES the set of allowed rates.
* *
* @NL80211_CMD_REGISTER_ACTION: Register for receiving certain action frames * @NL80211_CMD_REGISTER_FRAME: Register for receiving certain mgmt frames
* (via @NL80211_CMD_ACTION) for processing in userspace. This command * (via @NL80211_CMD_FRAME) for processing in userspace. This command
* requires an interface index and a match attribute containing the first * requires an interface index, a frame type attribute (optional for
* few bytes of the frame that should match, e.g. a single byte for only * backward compatibility reasons, if not given assumes action frames)
* a category match or four bytes for vendor frames including the OUI. * and a match attribute containing the first few bytes of the frame
* The registration cannot be dropped, but is removed automatically * that should match, e.g. a single byte for only a category match or
* when the netlink socket is closed. Multiple registrations can be made. * four bytes for vendor frames including the OUI. The registration
* @NL80211_CMD_ACTION: Action frame TX request and RX notification. This * cannot be dropped, but is removed automatically when the netlink
* command is used both as a request to transmit an Action frame and as an * socket is closed. Multiple registrations can be made.
* event indicating reception of an Action frame that was not processed in * @NL80211_CMD_FRAME: Management frame TX request and RX notification. This
* command is used both as a request to transmit a management frame and
* as an event indicating reception of a frame that was not processed in
* kernel code, but is for us (i.e., which may need to be processed in a * kernel code, but is for us (i.e., which may need to be processed in a
* user space application). %NL80211_ATTR_FRAME is used to specify the * user space application). %NL80211_ATTR_FRAME is used to specify the
* frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and
...@@ -320,8 +359,8 @@ ...@@ -320,8 +359,8 @@
* operational channel). When called, this operation returns a cookie * operational channel). When called, this operation returns a cookie
* (%NL80211_ATTR_COOKIE) that will be included with the TX status event * (%NL80211_ATTR_COOKIE) that will be included with the TX status event
* pertaining to the TX request. * pertaining to the TX request.
* @NL80211_CMD_ACTION_TX_STATUS: Report TX status of an Action frame * @NL80211_CMD_FRAME_TX_STATUS: Report TX status of a management frame
* transmitted with %NL80211_CMD_ACTION. %NL80211_ATTR_COOKIE identifies * transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies
* the TX command and %NL80211_ATTR_FRAME includes the contents of the * the TX command and %NL80211_ATTR_FRAME includes the contents of the
* frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged * frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged
* the frame. * the frame.
...@@ -429,9 +468,12 @@ enum nl80211_commands { ...@@ -429,9 +468,12 @@ enum nl80211_commands {
NL80211_CMD_SET_TX_BITRATE_MASK, NL80211_CMD_SET_TX_BITRATE_MASK,
NL80211_CMD_REGISTER_ACTION, NL80211_CMD_REGISTER_FRAME,
NL80211_CMD_ACTION, NL80211_CMD_REGISTER_ACTION = NL80211_CMD_REGISTER_FRAME,
NL80211_CMD_ACTION_TX_STATUS, NL80211_CMD_FRAME,
NL80211_CMD_ACTION = NL80211_CMD_FRAME,
NL80211_CMD_FRAME_TX_STATUS,
NL80211_CMD_ACTION_TX_STATUS = NL80211_CMD_FRAME_TX_STATUS,
NL80211_CMD_SET_POWER_SAVE, NL80211_CMD_SET_POWER_SAVE,
NL80211_CMD_GET_POWER_SAVE, NL80211_CMD_GET_POWER_SAVE,
...@@ -708,7 +750,16 @@ enum nl80211_commands { ...@@ -708,7 +750,16 @@ enum nl80211_commands {
* is used with %NL80211_CMD_SET_TX_BITRATE_MASK. * is used with %NL80211_CMD_SET_TX_BITRATE_MASK.
* *
* @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain
* at least one byte, currently used with @NL80211_CMD_REGISTER_ACTION. * at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME.
* @NL80211_ATTR_FRAME_TYPE: A u16 indicating the frame type/subtype for the
* @NL80211_CMD_REGISTER_FRAME command.
* @NL80211_ATTR_TX_FRAME_TYPES: wiphy capability attribute, which is a
* nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing
* information about which frame types can be transmitted with
* %NL80211_CMD_FRAME.
* @NL80211_ATTR_RX_FRAME_TYPES: wiphy capability attribute, which is a
* nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing
* information about which frame types can be registered for RX.
* *
* @NL80211_ATTR_ACK: Flag attribute indicating that the frame was * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was
* acknowledged by the recipient. * acknowledged by the recipient.
...@@ -891,6 +942,10 @@ enum nl80211_attrs { ...@@ -891,6 +942,10 @@ enum nl80211_attrs {
NL80211_ATTR_WIPHY_TX_POWER_SETTING, NL80211_ATTR_WIPHY_TX_POWER_SETTING,
NL80211_ATTR_WIPHY_TX_POWER_LEVEL, NL80211_ATTR_WIPHY_TX_POWER_LEVEL,
NL80211_ATTR_TX_FRAME_TYPES,
NL80211_ATTR_RX_FRAME_TYPES,
NL80211_ATTR_FRAME_TYPE,
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
...@@ -947,7 +1002,7 @@ enum nl80211_attrs { ...@@ -947,7 +1002,7 @@ enum nl80211_attrs {
* @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames
* @NL80211_IFTYPE_MESH_POINT: mesh point * @NL80211_IFTYPE_MESH_POINT: mesh point
* @NL80211_IFTYPE_MAX: highest interface type number currently defined * @NL80211_IFTYPE_MAX: highest interface type number currently defined
* @__NL80211_IFTYPE_AFTER_LAST: internal use * @NUM_NL80211_IFTYPES: number of defined interface types
* *
* These values are used with the %NL80211_ATTR_IFTYPE * These values are used with the %NL80211_ATTR_IFTYPE
* to set the type of an interface. * to set the type of an interface.
...@@ -964,8 +1019,8 @@ enum nl80211_iftype { ...@@ -964,8 +1019,8 @@ enum nl80211_iftype {
NL80211_IFTYPE_MESH_POINT, NL80211_IFTYPE_MESH_POINT,
/* keep last */ /* keep last */
__NL80211_IFTYPE_AFTER_LAST, NUM_NL80211_IFTYPES,
NL80211_IFTYPE_MAX = __NL80211_IFTYPE_AFTER_LAST - 1 NL80211_IFTYPE_MAX = NUM_NL80211_IFTYPES - 1
}; };
/** /**
......
...@@ -1020,7 +1020,7 @@ struct cfg80211_pmksa { ...@@ -1020,7 +1020,7 @@ struct cfg80211_pmksa {
* @cancel_remain_on_channel: Cancel an on-going remain-on-channel operation. * @cancel_remain_on_channel: Cancel an on-going remain-on-channel operation.
* This allows the operation to be terminated prior to timeout based on * This allows the operation to be terminated prior to timeout based on
* the duration value. * the duration value.
* @action: Transmit an action frame * @mgmt_tx: Transmit a management frame
* *
* @testmode_cmd: run a test mode command * @testmode_cmd: run a test mode command
* *
...@@ -1172,7 +1172,7 @@ struct cfg80211_ops { ...@@ -1172,7 +1172,7 @@ struct cfg80211_ops {
struct net_device *dev, struct net_device *dev,
u64 cookie); u64 cookie);
int (*action)(struct wiphy *wiphy, struct net_device *dev, int (*mgmt_tx)(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_channel *chan, struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type, enum nl80211_channel_type channel_type,
bool channel_type_valid, bool channel_type_valid,
...@@ -1236,6 +1236,10 @@ struct mac_address { ...@@ -1236,6 +1236,10 @@ struct mac_address {
u8 addr[ETH_ALEN]; u8 addr[ETH_ALEN];
}; };
struct ieee80211_txrx_stypes {
u16 tx, rx;
};
/** /**
* struct wiphy - wireless hardware description * struct wiphy - wireless hardware description
* @reg_notifier: the driver's regulatory notification callback * @reg_notifier: the driver's regulatory notification callback
...@@ -1286,6 +1290,10 @@ struct mac_address { ...@@ -1286,6 +1290,10 @@ struct mac_address {
* @privid: a pointer that drivers can use to identify if an arbitrary * @privid: a pointer that drivers can use to identify if an arbitrary
* wiphy is theirs, e.g. in global notifiers * wiphy is theirs, e.g. in global notifiers
* @bands: information about bands/channels supported by this device * @bands: information about bands/channels supported by this device
*
* @mgmt_stypes: bitmasks of frame subtypes that can be subscribed to or
* transmitted through nl80211, points to an array indexed by interface
* type
*/ */
struct wiphy { struct wiphy {
/* assign these fields before you register the wiphy */ /* assign these fields before you register the wiphy */
...@@ -1294,9 +1302,12 @@ struct wiphy { ...@@ -1294,9 +1302,12 @@ struct wiphy {
u8 perm_addr[ETH_ALEN]; u8 perm_addr[ETH_ALEN];
u8 addr_mask[ETH_ALEN]; u8 addr_mask[ETH_ALEN];
u16 n_addresses;
struct mac_address *addresses; struct mac_address *addresses;
const struct ieee80211_txrx_stypes *mgmt_stypes;
u16 n_addresses;
/* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */ /* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */
u16 interface_modes; u16 interface_modes;
...@@ -1492,8 +1503,8 @@ struct cfg80211_cached_keys; ...@@ -1492,8 +1503,8 @@ struct cfg80211_cached_keys;
* set by driver (if supported) on add_interface BEFORE registering the * set by driver (if supported) on add_interface BEFORE registering the
* netdev and may otherwise be used by driver read-only, will be update * netdev and may otherwise be used by driver read-only, will be update
* by cfg80211 on change_interface * by cfg80211 on change_interface
* @action_registrations: list of registrations for action frames * @mgmt_registrations: list of registrations for management frames
* @action_registrations_lock: lock for the list * @mgmt_registrations_lock: lock for the list
* @mtx: mutex used to lock data in this struct * @mtx: mutex used to lock data in this struct
* @cleanup_work: work struct used for cleanup that can't be done directly * @cleanup_work: work struct used for cleanup that can't be done directly
*/ */
...@@ -1505,8 +1516,8 @@ struct wireless_dev { ...@@ -1505,8 +1516,8 @@ struct wireless_dev {
struct list_head list; struct list_head list;
struct net_device *netdev; struct net_device *netdev;
struct list_head action_registrations; struct list_head mgmt_registrations;
spinlock_t action_registrations_lock; spinlock_t mgmt_registrations_lock;
struct mutex mtx; struct mutex mtx;
...@@ -2373,37 +2384,38 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, ...@@ -2373,37 +2384,38 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
struct station_info *sinfo, gfp_t gfp); struct station_info *sinfo, gfp_t gfp);
/** /**
* cfg80211_rx_action - notification of received, unprocessed Action frame * cfg80211_rx_mgmt - notification of received, unprocessed management frame
* @dev: network device * @dev: network device
* @freq: Frequency on which the frame was received in MHz * @freq: Frequency on which the frame was received in MHz
* @buf: Action frame (header + body) * @buf: Management frame (header + body)
* @len: length of the frame data * @len: length of the frame data
* @gfp: context flags * @gfp: context flags
* Returns %true if a user space application is responsible for rejecting the *
* unrecognized Action frame; %false if no such application is registered * Returns %true if a user space application has registered for this frame.
* (i.e., the driver is responsible for rejecting the unrecognized Action * For action frames, that makes it responsible for rejecting unrecognized
* frame) * action frames; %false otherwise, in which case for action frames the
* driver is responsible for rejecting the frame.
* *
* This function is called whenever an Action frame is received for a station * This function is called whenever an Action frame is received for a station
* mode interface, but is not processed in kernel. * mode interface, but is not processed in kernel.
*/ */
bool cfg80211_rx_action(struct net_device *dev, int freq, const u8 *buf, bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf,
size_t len, gfp_t gfp); size_t len, gfp_t gfp);
/** /**
* cfg80211_action_tx_status - notification of TX status for Action frame * cfg80211_mgmt_tx_status - notification of TX status for management frame
* @dev: network device * @dev: network device
* @cookie: Cookie returned by cfg80211_ops::action() * @cookie: Cookie returned by cfg80211_ops::mgmt_tx()
* @buf: Action frame (header + body) * @buf: Management frame (header + body)
* @len: length of the frame data * @len: length of the frame data
* @ack: Whether frame was acknowledged * @ack: Whether frame was acknowledged
* @gfp: context flags * @gfp: context flags
* *
* This function is called whenever an Action frame was requested to be * This function is called whenever a management frame was requested to be
* transmitted with cfg80211_ops::action() to report the TX status of the * transmitted with cfg80211_ops::mgmt_tx() to report the TX status of the
* transmission attempt. * transmission attempt.
*/ */
void cfg80211_action_tx_status(struct net_device *dev, u64 cookie, void cfg80211_mgmt_tx_status(struct net_device *dev, u64 cookie,
const u8 *buf, size_t len, bool ack, gfp_t gfp); const u8 *buf, size_t len, bool ack, gfp_t gfp);
......
...@@ -1521,7 +1521,7 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, ...@@ -1521,7 +1521,7 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
return ieee80211_wk_cancel_remain_on_channel(sdata, cookie); return ieee80211_wk_cancel_remain_on_channel(sdata, cookie);
} }
static int ieee80211_action(struct wiphy *wiphy, struct net_device *dev, static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_channel *chan, struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type, enum nl80211_channel_type channel_type,
bool channel_type_valid, bool channel_type_valid,
...@@ -1625,6 +1625,6 @@ struct cfg80211_ops mac80211_config_ops = { ...@@ -1625,6 +1625,6 @@ struct cfg80211_ops mac80211_config_ops = {
.set_bitrate_mask = ieee80211_set_bitrate_mask, .set_bitrate_mask = ieee80211_set_bitrate_mask,
.remain_on_channel = ieee80211_remain_on_channel, .remain_on_channel = ieee80211_remain_on_channel,
.cancel_remain_on_channel = ieee80211_cancel_remain_on_channel, .cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
.action = ieee80211_action, .mgmt_tx = ieee80211_mgmt_tx,
.set_cqm_rssi_config = ieee80211_set_cqm_rssi_config, .set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,
}; };
...@@ -170,6 +170,7 @@ typedef unsigned __bitwise__ ieee80211_rx_result; ...@@ -170,6 +170,7 @@ typedef unsigned __bitwise__ ieee80211_rx_result;
#define IEEE80211_RX_RA_MATCH BIT(1) #define IEEE80211_RX_RA_MATCH BIT(1)
#define IEEE80211_RX_AMSDU BIT(2) #define IEEE80211_RX_AMSDU BIT(2)
#define IEEE80211_RX_FRAGMENTED BIT(3) #define IEEE80211_RX_FRAGMENTED BIT(3)
#define IEEE80211_MALFORMED_ACTION_FRM BIT(4)
/* only add flags here that do not change with subframes of an aMPDU */ /* only add flags here that do not change with subframes of an aMPDU */
struct ieee80211_rx_data { struct ieee80211_rx_data {
......
...@@ -177,7 +177,7 @@ static int ieee80211_open(struct net_device *dev) ...@@ -177,7 +177,7 @@ static int ieee80211_open(struct net_device *dev)
/* no special treatment */ /* no special treatment */
break; break;
case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_UNSPECIFIED:
case __NL80211_IFTYPE_AFTER_LAST: case NUM_NL80211_IFTYPES:
/* cannot happen */ /* cannot happen */
WARN_ON(1); WARN_ON(1);
break; break;
...@@ -634,7 +634,7 @@ static void ieee80211_teardown_sdata(struct net_device *dev) ...@@ -634,7 +634,7 @@ static void ieee80211_teardown_sdata(struct net_device *dev)
case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MONITOR:
break; break;
case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_UNSPECIFIED:
case __NL80211_IFTYPE_AFTER_LAST: case NUM_NL80211_IFTYPES:
BUG(); BUG();
break; break;
} }
...@@ -886,7 +886,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, ...@@ -886,7 +886,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_AP_VLAN:
break; break;
case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_UNSPECIFIED:
case __NL80211_IFTYPE_AFTER_LAST: case NUM_NL80211_IFTYPES:
BUG(); BUG();
break; break;
} }
......
...@@ -417,6 +417,41 @@ void ieee80211_napi_complete(struct ieee80211_hw *hw) ...@@ -417,6 +417,41 @@ void ieee80211_napi_complete(struct ieee80211_hw *hw)
} }
EXPORT_SYMBOL(ieee80211_napi_complete); EXPORT_SYMBOL(ieee80211_napi_complete);
/* There isn't a lot of sense in it, but you can transmit anything you like */
static const struct ieee80211_txrx_stypes
ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
[NL80211_IFTYPE_ADHOC] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4),
},
[NL80211_IFTYPE_STATION] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
},
[NL80211_IFTYPE_AP] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
BIT(IEEE80211_STYPE_DISASSOC >> 4) |
BIT(IEEE80211_STYPE_AUTH >> 4) |
BIT(IEEE80211_STYPE_DEAUTH >> 4) |
BIT(IEEE80211_STYPE_ACTION >> 4),
},
[NL80211_IFTYPE_AP_VLAN] = {
/* copy AP */
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
BIT(IEEE80211_STYPE_DISASSOC >> 4) |
BIT(IEEE80211_STYPE_AUTH >> 4) |
BIT(IEEE80211_STYPE_DEAUTH >> 4) |
BIT(IEEE80211_STYPE_ACTION >> 4),
},
};
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
const struct ieee80211_ops *ops) const struct ieee80211_ops *ops)
{ {
...@@ -446,6 +481,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, ...@@ -446,6 +481,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
if (!wiphy) if (!wiphy)
return NULL; return NULL;
wiphy->mgmt_stypes = ieee80211_default_mgmt_stypes;
wiphy->flags |= WIPHY_FLAG_NETNS_OK | wiphy->flags |= WIPHY_FLAG_NETNS_OK |
WIPHY_FLAG_4ADDR_AP | WIPHY_FLAG_4ADDR_AP |
WIPHY_FLAG_4ADDR_STATION; WIPHY_FLAG_4ADDR_STATION;
......
...@@ -1939,14 +1939,37 @@ static void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata, ...@@ -1939,14 +1939,37 @@ static void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata,
ieee80211_tx_skb(sdata, skb); ieee80211_tx_skb(sdata, skb);
} }
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx)
{
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
/*
* From here on, look only at management frames.
* Data and control frames are already handled,
* and unknown (reserved) frames are useless.
*/
if (rx->skb->len < 24)
return RX_DROP_MONITOR;
if (!ieee80211_is_mgmt(mgmt->frame_control))
return RX_DROP_MONITOR;
if (!(rx->flags & IEEE80211_RX_RA_MATCH))
return RX_DROP_MONITOR;
if (ieee80211_drop_unencrypted_mgmt(rx))
return RX_DROP_UNUSABLE;
return RX_CONTINUE;
}
static ieee80211_rx_result debug_noinline static ieee80211_rx_result debug_noinline
ieee80211_rx_h_action(struct ieee80211_rx_data *rx) ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
{ {
struct ieee80211_local *local = rx->local; struct ieee80211_local *local = rx->local;
struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_sub_if_data *sdata = rx->sdata;
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data; struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
struct sk_buff *nskb;
struct ieee80211_rx_status *status;
int len = rx->skb->len; int len = rx->skb->len;
if (!ieee80211_is_action(mgmt->frame_control)) if (!ieee80211_is_action(mgmt->frame_control))
...@@ -1962,9 +1985,6 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) ...@@ -1962,9 +1985,6 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
if (!(rx->flags & IEEE80211_RX_RA_MATCH)) if (!(rx->flags & IEEE80211_RX_RA_MATCH))
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
if (ieee80211_drop_unencrypted_mgmt(rx))
return RX_DROP_UNUSABLE;
switch (mgmt->u.action.category) { switch (mgmt->u.action.category) {
case WLAN_CATEGORY_BACK: case WLAN_CATEGORY_BACK:
/* /*
...@@ -2055,17 +2075,36 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) ...@@ -2055,17 +2075,36 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
goto queue; goto queue;
} }
return RX_CONTINUE;
invalid: invalid:
/* rx->flags |= IEEE80211_MALFORMED_ACTION_FRM;
* For AP mode, hostapd is responsible for handling any action /* will return in the next handlers */
* frames that we didn't handle, including returning unknown return RX_CONTINUE;
* ones. For all other modes we will return them to the sender,
* setting the 0x80 bit in the action category, as required by handled:
* 802.11-2007 7.3.1.11. if (rx->sta)
*/ rx->sta->rx_packets++;
if (sdata->vif.type == NL80211_IFTYPE_AP || dev_kfree_skb(rx->skb);
sdata->vif.type == NL80211_IFTYPE_AP_VLAN) return RX_QUEUED;
return RX_DROP_MONITOR;
queue:
rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME;
skb_queue_tail(&sdata->skb_queue, rx->skb);
ieee80211_queue_work(&local->hw, &sdata->work);
if (rx->sta)
rx->sta->rx_packets++;
return RX_QUEUED;
}
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
{
struct ieee80211_rx_status *status;
/* skip known-bad action frames and return them in the next handler */
if (rx->flags & IEEE80211_MALFORMED_ACTION_FRM)
return RX_CONTINUE;
/* /*
* Getting here means the kernel doesn't know how to handle * Getting here means the kernel doesn't know how to handle
...@@ -2075,10 +2114,44 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) ...@@ -2075,10 +2114,44 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
*/ */
status = IEEE80211_SKB_RXCB(rx->skb); status = IEEE80211_SKB_RXCB(rx->skb);
if (cfg80211_rx_action(rx->sdata->dev, status->freq, if (cfg80211_rx_mgmt(rx->sdata->dev, status->freq,
rx->skb->data, rx->skb->len, rx->skb->data, rx->skb->len,
GFP_ATOMIC)) GFP_ATOMIC)) {
goto handled; if (rx->sta)
rx->sta->rx_packets++;
dev_kfree_skb(rx->skb);
return RX_QUEUED;
}
return RX_CONTINUE;
}
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx)
{
struct ieee80211_local *local = rx->local;
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
struct sk_buff *nskb;
struct ieee80211_sub_if_data *sdata = rx->sdata;
if (!ieee80211_is_action(mgmt->frame_control))
return RX_CONTINUE;
/*
* For AP mode, hostapd is responsible for handling any action
* frames that we didn't handle, including returning unknown
* ones. For all other modes we will return them to the sender,
* setting the 0x80 bit in the action category, as required by
* 802.11-2007 7.3.1.11.
* Newer versions of hostapd shall also use the management frame
* registration mechanisms, but older ones still use cooked
* monitor interfaces so push all frames there.
*/
if (!(rx->flags & IEEE80211_MALFORMED_ACTION_FRM) &&
(sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN))
return RX_DROP_MONITOR;
/* do not return rejected action frames */ /* do not return rejected action frames */
if (mgmt->u.action.category & 0x80) if (mgmt->u.action.category & 0x80)
...@@ -2097,20 +2170,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) ...@@ -2097,20 +2170,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
ieee80211_tx_skb(rx->sdata, nskb); ieee80211_tx_skb(rx->sdata, nskb);
} }
handled:
if (rx->sta)
rx->sta->rx_packets++;
dev_kfree_skb(rx->skb); dev_kfree_skb(rx->skb);
return RX_QUEUED; return RX_QUEUED;
queue:
rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME;
skb_queue_tail(&sdata->skb_queue, rx->skb);
ieee80211_queue_work(&local->hw, &sdata->work);
if (rx->sta)
rx->sta->rx_packets++;
return RX_QUEUED;
} }
static ieee80211_rx_result debug_noinline static ieee80211_rx_result debug_noinline
...@@ -2121,15 +2182,6 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) ...@@ -2121,15 +2182,6 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
struct ieee80211_mgmt *mgmt = (void *)rx->skb->data; struct ieee80211_mgmt *mgmt = (void *)rx->skb->data;
__le16 stype; __le16 stype;
if (!(rx->flags & IEEE80211_RX_RA_MATCH))
return RX_DROP_MONITOR;
if (rx->skb->len < 24)
return RX_DROP_MONITOR;
if (ieee80211_drop_unencrypted_mgmt(rx))
return RX_DROP_UNUSABLE;
rxs = ieee80211_work_rx_mgmt(rx->sdata, rx->skb); rxs = ieee80211_work_rx_mgmt(rx->sdata, rx->skb);
if (rxs != RX_CONTINUE) if (rxs != RX_CONTINUE)
return rxs; return rxs;
...@@ -2374,7 +2426,10 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx, ...@@ -2374,7 +2426,10 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,
if (res != RX_CONTINUE) if (res != RX_CONTINUE)
goto rxh_next; goto rxh_next;
CALL_RXH(ieee80211_rx_h_mgmt_check)
CALL_RXH(ieee80211_rx_h_action) CALL_RXH(ieee80211_rx_h_action)
CALL_RXH(ieee80211_rx_h_userspace_mgmt)
CALL_RXH(ieee80211_rx_h_action_return)
CALL_RXH(ieee80211_rx_h_mgmt) CALL_RXH(ieee80211_rx_h_mgmt)
rxh_next: rxh_next:
...@@ -2527,7 +2582,7 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata, ...@@ -2527,7 +2582,7 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
break; break;
case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_UNSPECIFIED:
case __NL80211_IFTYPE_AFTER_LAST: case NUM_NL80211_IFTYPES:
/* should never get here */ /* should never get here */
WARN_ON(1); WARN_ON(1);
break; break;
......
...@@ -296,7 +296,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) ...@@ -296,7 +296,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
} }
if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX)
cfg80211_action_tx_status( cfg80211_mgmt_tx_status(
skb->dev, (unsigned long) skb, skb->data, skb->len, skb->dev, (unsigned long) skb, skb->data, skb->len,
!!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC); !!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC);
......
...@@ -471,7 +471,7 @@ void ieee80211_iterate_active_interfaces( ...@@ -471,7 +471,7 @@ void ieee80211_iterate_active_interfaces(
list_for_each_entry(sdata, &local->interfaces, list) { list_for_each_entry(sdata, &local->interfaces, list) {
switch (sdata->vif.type) { switch (sdata->vif.type) {
case __NL80211_IFTYPE_AFTER_LAST: case NUM_NL80211_IFTYPES:
case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_UNSPECIFIED:
case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_AP_VLAN:
...@@ -505,7 +505,7 @@ void ieee80211_iterate_active_interfaces_atomic( ...@@ -505,7 +505,7 @@ void ieee80211_iterate_active_interfaces_atomic(
list_for_each_entry_rcu(sdata, &local->interfaces, list) { list_for_each_entry_rcu(sdata, &local->interfaces, list) {
switch (sdata->vif.type) { switch (sdata->vif.type) {
case __NL80211_IFTYPE_AFTER_LAST: case NUM_NL80211_IFTYPES:
case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_UNSPECIFIED:
case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_AP_VLAN:
...@@ -1189,7 +1189,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) ...@@ -1189,7 +1189,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
/* ignore virtual */ /* ignore virtual */
break; break;
case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_UNSPECIFIED:
case __NL80211_IFTYPE_AFTER_LAST: case NUM_NL80211_IFTYPES:
WARN_ON(1); WARN_ON(1);
break; break;
} }
......
...@@ -433,7 +433,7 @@ int wiphy_register(struct wiphy *wiphy) ...@@ -433,7 +433,7 @@ int wiphy_register(struct wiphy *wiphy)
/* sanity check ifmodes */ /* sanity check ifmodes */
WARN_ON(!ifmodes); WARN_ON(!ifmodes);
ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1; ifmodes &= ((1 << NUM_NL80211_IFTYPES) - 1) & ~1;
if (WARN_ON(ifmodes != wiphy->interface_modes)) if (WARN_ON(ifmodes != wiphy->interface_modes))
wiphy->interface_modes = ifmodes; wiphy->interface_modes = ifmodes;
...@@ -685,8 +685,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, ...@@ -685,8 +685,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work);
INIT_LIST_HEAD(&wdev->event_list); INIT_LIST_HEAD(&wdev->event_list);
spin_lock_init(&wdev->event_lock); spin_lock_init(&wdev->event_lock);
INIT_LIST_HEAD(&wdev->action_registrations); INIT_LIST_HEAD(&wdev->mgmt_registrations);
spin_lock_init(&wdev->action_registrations_lock); spin_lock_init(&wdev->mgmt_registrations_lock);
mutex_lock(&rdev->devlist_mtx); mutex_lock(&rdev->devlist_mtx);
list_add_rcu(&wdev->list, &rdev->netdev_list); list_add_rcu(&wdev->list, &rdev->netdev_list);
...@@ -806,7 +806,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, ...@@ -806,7 +806,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
sysfs_remove_link(&dev->dev.kobj, "phy80211"); sysfs_remove_link(&dev->dev.kobj, "phy80211");
list_del_rcu(&wdev->list); list_del_rcu(&wdev->list);
rdev->devlist_generation++; rdev->devlist_generation++;
cfg80211_mlme_purge_actions(wdev); cfg80211_mlme_purge_registrations(wdev);
#ifdef CONFIG_CFG80211_WEXT #ifdef CONFIG_CFG80211_WEXT
kfree(wdev->wext.keys); kfree(wdev->wext.keys);
#endif #endif
......
...@@ -331,11 +331,12 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, ...@@ -331,11 +331,12 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
const u8 *resp_ie, size_t resp_ie_len, const u8 *resp_ie, size_t resp_ie_len,
u16 status, bool wextev, u16 status, bool wextev,
struct cfg80211_bss *bss); struct cfg80211_bss *bss);
int cfg80211_mlme_register_action(struct wireless_dev *wdev, u32 snd_pid, int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid,
const u8 *match_data, int match_len); u16 frame_type, const u8 *match_data,
void cfg80211_mlme_unregister_actions(struct wireless_dev *wdev, u32 nlpid); int match_len);
void cfg80211_mlme_purge_actions(struct wireless_dev *wdev); void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid);
int cfg80211_mlme_action(struct cfg80211_registered_device *rdev, void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev);
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
struct net_device *dev, struct net_device *dev,
struct ieee80211_channel *chan, struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type, enum nl80211_channel_type channel_type,
......
...@@ -748,31 +748,51 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, ...@@ -748,31 +748,51 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
} }
EXPORT_SYMBOL(cfg80211_new_sta); EXPORT_SYMBOL(cfg80211_new_sta);
struct cfg80211_action_registration { struct cfg80211_mgmt_registration {
struct list_head list; struct list_head list;
u32 nlpid; u32 nlpid;
int match_len; int match_len;
__le16 frame_type;
u8 match[]; u8 match[];
}; };
int cfg80211_mlme_register_action(struct wireless_dev *wdev, u32 snd_pid, int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid,
const u8 *match_data, int match_len) u16 frame_type, const u8 *match_data,
int match_len)
{ {
struct cfg80211_action_registration *reg, *nreg; struct cfg80211_mgmt_registration *reg, *nreg;
int err = 0; int err = 0;
u16 mgmt_type;
if (!wdev->wiphy->mgmt_stypes)
return -EOPNOTSUPP;
if ((frame_type & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT)
return -EINVAL;
if (frame_type & ~(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE))
return -EINVAL;
mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].rx & BIT(mgmt_type)))
return -EINVAL;
nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL); nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL);
if (!nreg) if (!nreg)
return -ENOMEM; return -ENOMEM;
spin_lock_bh(&wdev->action_registrations_lock); spin_lock_bh(&wdev->mgmt_registrations_lock);
list_for_each_entry(reg, &wdev->action_registrations, list) { list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
int mlen = min(match_len, reg->match_len); int mlen = min(match_len, reg->match_len);
if (frame_type != le16_to_cpu(reg->frame_type))
continue;
if (memcmp(reg->match, match_data, mlen) == 0) { if (memcmp(reg->match, match_data, mlen) == 0) {
err = -EALREADY; err = -EALREADY;
break; break;
...@@ -787,44 +807,45 @@ int cfg80211_mlme_register_action(struct wireless_dev *wdev, u32 snd_pid, ...@@ -787,44 +807,45 @@ int cfg80211_mlme_register_action(struct wireless_dev *wdev, u32 snd_pid,
memcpy(nreg->match, match_data, match_len); memcpy(nreg->match, match_data, match_len);
nreg->match_len = match_len; nreg->match_len = match_len;
nreg->nlpid = snd_pid; nreg->nlpid = snd_pid;
list_add(&nreg->list, &wdev->action_registrations); nreg->frame_type = cpu_to_le16(frame_type);
list_add(&nreg->list, &wdev->mgmt_registrations);
out: out:
spin_unlock_bh(&wdev->action_registrations_lock); spin_unlock_bh(&wdev->mgmt_registrations_lock);
return err; return err;
} }
void cfg80211_mlme_unregister_actions(struct wireless_dev *wdev, u32 nlpid) void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid)
{ {
struct cfg80211_action_registration *reg, *tmp; struct cfg80211_mgmt_registration *reg, *tmp;
spin_lock_bh(&wdev->action_registrations_lock); spin_lock_bh(&wdev->mgmt_registrations_lock);
list_for_each_entry_safe(reg, tmp, &wdev->action_registrations, list) { list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
if (reg->nlpid == nlpid) { if (reg->nlpid == nlpid) {
list_del(&reg->list); list_del(&reg->list);
kfree(reg); kfree(reg);
} }
} }
spin_unlock_bh(&wdev->action_registrations_lock); spin_unlock_bh(&wdev->mgmt_registrations_lock);
} }
void cfg80211_mlme_purge_actions(struct wireless_dev *wdev) void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
{ {
struct cfg80211_action_registration *reg, *tmp; struct cfg80211_mgmt_registration *reg, *tmp;
spin_lock_bh(&wdev->action_registrations_lock); spin_lock_bh(&wdev->mgmt_registrations_lock);
list_for_each_entry_safe(reg, tmp, &wdev->action_registrations, list) { list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
list_del(&reg->list); list_del(&reg->list);
kfree(reg); kfree(reg);
} }
spin_unlock_bh(&wdev->action_registrations_lock); spin_unlock_bh(&wdev->mgmt_registrations_lock);
} }
int cfg80211_mlme_action(struct cfg80211_registered_device *rdev, int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
struct net_device *dev, struct net_device *dev,
struct ieee80211_channel *chan, struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type, enum nl80211_channel_type channel_type,
...@@ -833,16 +854,28 @@ int cfg80211_mlme_action(struct cfg80211_registered_device *rdev, ...@@ -833,16 +854,28 @@ int cfg80211_mlme_action(struct cfg80211_registered_device *rdev,
{ {
struct wireless_dev *wdev = dev->ieee80211_ptr; struct wireless_dev *wdev = dev->ieee80211_ptr;
const struct ieee80211_mgmt *mgmt; const struct ieee80211_mgmt *mgmt;
u16 stype;
if (rdev->ops->action == NULL) if (!wdev->wiphy->mgmt_stypes)
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (!rdev->ops->mgmt_tx)
return -EOPNOTSUPP;
if (len < 24 + 1) if (len < 24 + 1)
return -EINVAL; return -EINVAL;
mgmt = (const struct ieee80211_mgmt *) buf; mgmt = (const struct ieee80211_mgmt *) buf;
if (!ieee80211_is_action(mgmt->frame_control))
if (!ieee80211_is_mgmt(mgmt->frame_control))
return -EINVAL;
stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE;
if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].tx & BIT(stype >> 4)))
return -EINVAL; return -EINVAL;
if (mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) {
if (ieee80211_is_action(mgmt->frame_control) &&
mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) {
/* Verify that we are associated with the destination AP */ /* Verify that we are associated with the destination AP */
wdev_lock(wdev); wdev_lock(wdev);
...@@ -863,40 +896,51 @@ int cfg80211_mlme_action(struct cfg80211_registered_device *rdev, ...@@ -863,40 +896,51 @@ int cfg80211_mlme_action(struct cfg80211_registered_device *rdev,
return -EINVAL; return -EINVAL;
/* Transmit the Action frame as requested by user space */ /* Transmit the Action frame as requested by user space */
return rdev->ops->action(&rdev->wiphy, dev, chan, channel_type, return rdev->ops->mgmt_tx(&rdev->wiphy, dev, chan, channel_type,
channel_type_valid, buf, len, cookie); channel_type_valid, buf, len, cookie);
} }
bool cfg80211_rx_action(struct net_device *dev, int freq, const u8 *buf, bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf,
size_t len, gfp_t gfp) size_t len, gfp_t gfp)
{ {
struct wireless_dev *wdev = dev->ieee80211_ptr; struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy; struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
struct cfg80211_action_registration *reg; struct cfg80211_mgmt_registration *reg;
const u8 *action_data; const struct ieee80211_txrx_stypes *stypes =
int action_data_len; &wiphy->mgmt_stypes[wdev->iftype];
struct ieee80211_mgmt *mgmt = (void *)buf;
const u8 *data;
int data_len;
bool result = false; bool result = false;
__le16 ftype = mgmt->frame_control &
cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE);
u16 stype;
stype = (le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE) >> 4;
/* frame length - min size excluding category */ if (!(stypes->rx & BIT(stype)))
action_data_len = len - (IEEE80211_MIN_ACTION_SIZE - 1); return false;
/* action data starts with category */ data = buf + ieee80211_hdrlen(mgmt->frame_control);
action_data = buf + IEEE80211_MIN_ACTION_SIZE - 1; data_len = len - ieee80211_hdrlen(mgmt->frame_control);
spin_lock_bh(&wdev->action_registrations_lock); spin_lock_bh(&wdev->mgmt_registrations_lock);
list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
if (reg->frame_type != ftype)
continue;
list_for_each_entry(reg, &wdev->action_registrations, list) { if (reg->match_len > data_len)
if (reg->match_len > action_data_len)
continue; continue;
if (memcmp(reg->match, action_data, reg->match_len)) if (memcmp(reg->match, data, reg->match_len))
continue; continue;
/* found match! */ /* found match! */
/* Indicate the received Action frame to user space */ /* Indicate the received Action frame to user space */
if (nl80211_send_action(rdev, dev, reg->nlpid, freq, if (nl80211_send_mgmt(rdev, dev, reg->nlpid, freq,
buf, len, gfp)) buf, len, gfp))
continue; continue;
...@@ -904,13 +948,13 @@ bool cfg80211_rx_action(struct net_device *dev, int freq, const u8 *buf, ...@@ -904,13 +948,13 @@ bool cfg80211_rx_action(struct net_device *dev, int freq, const u8 *buf,
break; break;
} }
spin_unlock_bh(&wdev->action_registrations_lock); spin_unlock_bh(&wdev->mgmt_registrations_lock);
return result; return result;
} }
EXPORT_SYMBOL(cfg80211_rx_action); EXPORT_SYMBOL(cfg80211_rx_mgmt);
void cfg80211_action_tx_status(struct net_device *dev, u64 cookie, void cfg80211_mgmt_tx_status(struct net_device *dev, u64 cookie,
const u8 *buf, size_t len, bool ack, gfp_t gfp) const u8 *buf, size_t len, bool ack, gfp_t gfp)
{ {
struct wireless_dev *wdev = dev->ieee80211_ptr; struct wireless_dev *wdev = dev->ieee80211_ptr;
...@@ -918,9 +962,9 @@ void cfg80211_action_tx_status(struct net_device *dev, u64 cookie, ...@@ -918,9 +962,9 @@ void cfg80211_action_tx_status(struct net_device *dev, u64 cookie,
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
/* Indicate TX status of the Action frame to user space */ /* Indicate TX status of the Action frame to user space */
nl80211_send_action_tx_status(rdev, dev, cookie, buf, len, ack, gfp); nl80211_send_mgmt_tx_status(rdev, dev, cookie, buf, len, ack, gfp);
} }
EXPORT_SYMBOL(cfg80211_action_tx_status); EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
void cfg80211_cqm_rssi_notify(struct net_device *dev, void cfg80211_cqm_rssi_notify(struct net_device *dev,
enum nl80211_cqm_rssi_threshold_event rssi_event, enum nl80211_cqm_rssi_threshold_event rssi_event,
......
...@@ -156,6 +156,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { ...@@ -156,6 +156,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 }, [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 }, [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 },
[NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 },
}; };
/* policy for the attributes */ /* policy for the attributes */
...@@ -437,6 +438,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, ...@@ -437,6 +438,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
struct ieee80211_rate *rate; struct ieee80211_rate *rate;
int i; int i;
u16 ifmodes = dev->wiphy.interface_modes; u16 ifmodes = dev->wiphy.interface_modes;
const struct ieee80211_txrx_stypes *mgmt_stypes =
dev->wiphy.mgmt_stypes;
hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY); hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
if (!hdr) if (!hdr)
...@@ -587,7 +590,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, ...@@ -587,7 +590,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
CMD(flush_pmksa, FLUSH_PMKSA); CMD(flush_pmksa, FLUSH_PMKSA);
CMD(remain_on_channel, REMAIN_ON_CHANNEL); CMD(remain_on_channel, REMAIN_ON_CHANNEL);
CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
CMD(action, ACTION); CMD(mgmt_tx, FRAME);
if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
i++; i++;
NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
...@@ -608,6 +611,53 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, ...@@ -608,6 +611,53 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
nla_nest_end(msg, nl_cmds); nla_nest_end(msg, nl_cmds);
if (mgmt_stypes) {
u16 stypes;
struct nlattr *nl_ftypes, *nl_ifs;
enum nl80211_iftype ift;
nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
if (!nl_ifs)
goto nla_put_failure;
for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
nl_ftypes = nla_nest_start(msg, ift);
if (!nl_ftypes)
goto nla_put_failure;
i = 0;
stypes = mgmt_stypes[ift].tx;
while (stypes) {
if (stypes & 1)
NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE,
(i << 4) | IEEE80211_FTYPE_MGMT);
stypes >>= 1;
i++;
}
nla_nest_end(msg, nl_ftypes);
}
nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
if (!nl_ifs)
goto nla_put_failure;
for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
nl_ftypes = nla_nest_start(msg, ift);
if (!nl_ftypes)
goto nla_put_failure;
i = 0;
stypes = mgmt_stypes[ift].rx;
while (stypes) {
if (stypes & 1)
NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE,
(i << 4) | IEEE80211_FTYPE_MGMT);
stypes >>= 1;
i++;
}
nla_nest_end(msg, nl_ftypes);
}
nla_nest_end(msg, nl_ifs);
}
return genlmsg_end(msg, hdr); return genlmsg_end(msg, hdr);
nla_put_failure: nla_put_failure:
...@@ -4732,17 +4782,18 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, ...@@ -4732,17 +4782,18 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
return err; return err;
} }
static int nl80211_register_action(struct sk_buff *skb, struct genl_info *info) static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
{ {
struct cfg80211_registered_device *rdev; struct cfg80211_registered_device *rdev;
struct net_device *dev; struct net_device *dev;
u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION;
int err; int err;
if (!info->attrs[NL80211_ATTR_FRAME_MATCH]) if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
return -EINVAL; return -EINVAL;
if (nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]) < 1) if (info->attrs[NL80211_ATTR_FRAME_TYPE])
return -EINVAL; frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]);
rtnl_lock(); rtnl_lock();
...@@ -4757,12 +4808,13 @@ static int nl80211_register_action(struct sk_buff *skb, struct genl_info *info) ...@@ -4757,12 +4808,13 @@ static int nl80211_register_action(struct sk_buff *skb, struct genl_info *info)
} }
/* not much point in registering if we can't reply */ /* not much point in registering if we can't reply */
if (!rdev->ops->action) { if (!rdev->ops->mgmt_tx) {
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
goto out; goto out;
} }
err = cfg80211_mlme_register_action(dev->ieee80211_ptr, info->snd_pid, err = cfg80211_mlme_register_mgmt(dev->ieee80211_ptr, info->snd_pid,
frame_type,
nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]), nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH])); nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
out: out:
...@@ -4773,7 +4825,7 @@ static int nl80211_register_action(struct sk_buff *skb, struct genl_info *info) ...@@ -4773,7 +4825,7 @@ static int nl80211_register_action(struct sk_buff *skb, struct genl_info *info)
return err; return err;
} }
static int nl80211_action(struct sk_buff *skb, struct genl_info *info) static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
{ {
struct cfg80211_registered_device *rdev; struct cfg80211_registered_device *rdev;
struct net_device *dev; struct net_device *dev;
...@@ -4796,7 +4848,7 @@ static int nl80211_action(struct sk_buff *skb, struct genl_info *info) ...@@ -4796,7 +4848,7 @@ static int nl80211_action(struct sk_buff *skb, struct genl_info *info)
if (err) if (err)
goto unlock_rtnl; goto unlock_rtnl;
if (!rdev->ops->action) { if (!rdev->ops->mgmt_tx) {
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
goto out; goto out;
} }
...@@ -4839,13 +4891,13 @@ static int nl80211_action(struct sk_buff *skb, struct genl_info *info) ...@@ -4839,13 +4891,13 @@ static int nl80211_action(struct sk_buff *skb, struct genl_info *info)
} }
hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
NL80211_CMD_ACTION); NL80211_CMD_FRAME);
if (IS_ERR(hdr)) { if (IS_ERR(hdr)) {
err = PTR_ERR(hdr); err = PTR_ERR(hdr);
goto free_msg; goto free_msg;
} }
err = cfg80211_mlme_action(rdev, dev, chan, channel_type, err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, channel_type,
channel_type_valid, channel_type_valid,
nla_data(info->attrs[NL80211_ATTR_FRAME]), nla_data(info->attrs[NL80211_ATTR_FRAME]),
nla_len(info->attrs[NL80211_ATTR_FRAME]), nla_len(info->attrs[NL80211_ATTR_FRAME]),
...@@ -5348,14 +5400,14 @@ static struct genl_ops nl80211_ops[] = { ...@@ -5348,14 +5400,14 @@ static struct genl_ops nl80211_ops[] = {
.flags = GENL_ADMIN_PERM, .flags = GENL_ADMIN_PERM,
}, },
{ {
.cmd = NL80211_CMD_REGISTER_ACTION, .cmd = NL80211_CMD_REGISTER_FRAME,
.doit = nl80211_register_action, .doit = nl80211_register_mgmt,
.policy = nl80211_policy, .policy = nl80211_policy,
.flags = GENL_ADMIN_PERM, .flags = GENL_ADMIN_PERM,
}, },
{ {
.cmd = NL80211_CMD_ACTION, .cmd = NL80211_CMD_FRAME,
.doit = nl80211_action, .doit = nl80211_tx_mgmt,
.policy = nl80211_policy, .policy = nl80211_policy,
.flags = GENL_ADMIN_PERM, .flags = GENL_ADMIN_PERM,
}, },
...@@ -6055,7 +6107,7 @@ void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, ...@@ -6055,7 +6107,7 @@ void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
nl80211_mlme_mcgrp.id, gfp); nl80211_mlme_mcgrp.id, gfp);
} }
int nl80211_send_action(struct cfg80211_registered_device *rdev, int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
struct net_device *netdev, u32 nlpid, struct net_device *netdev, u32 nlpid,
int freq, const u8 *buf, size_t len, gfp_t gfp) int freq, const u8 *buf, size_t len, gfp_t gfp)
{ {
...@@ -6067,7 +6119,7 @@ int nl80211_send_action(struct cfg80211_registered_device *rdev, ...@@ -6067,7 +6119,7 @@ int nl80211_send_action(struct cfg80211_registered_device *rdev,
if (!msg) if (!msg)
return -ENOMEM; return -ENOMEM;
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION); hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
if (!hdr) { if (!hdr) {
nlmsg_free(msg); nlmsg_free(msg);
return -ENOMEM; return -ENOMEM;
...@@ -6095,7 +6147,7 @@ int nl80211_send_action(struct cfg80211_registered_device *rdev, ...@@ -6095,7 +6147,7 @@ int nl80211_send_action(struct cfg80211_registered_device *rdev,
return -ENOBUFS; return -ENOBUFS;
} }
void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
struct net_device *netdev, u64 cookie, struct net_device *netdev, u64 cookie,
const u8 *buf, size_t len, bool ack, const u8 *buf, size_t len, bool ack,
gfp_t gfp) gfp_t gfp)
...@@ -6107,7 +6159,7 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, ...@@ -6107,7 +6159,7 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev,
if (!msg) if (!msg)
return; return;
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION_TX_STATUS); hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS);
if (!hdr) { if (!hdr) {
nlmsg_free(msg); nlmsg_free(msg);
return; return;
...@@ -6194,7 +6246,7 @@ static int nl80211_netlink_notify(struct notifier_block * nb, ...@@ -6194,7 +6246,7 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list)
list_for_each_entry_rcu(wdev, &rdev->netdev_list, list) list_for_each_entry_rcu(wdev, &rdev->netdev_list, list)
cfg80211_mlme_unregister_actions(wdev, notify->pid); cfg80211_mlme_unregister_socket(wdev, notify->pid);
rcu_read_unlock(); rcu_read_unlock();
......
...@@ -74,10 +74,10 @@ void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, ...@@ -74,10 +74,10 @@ void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
struct net_device *dev, const u8 *mac_addr, struct net_device *dev, const u8 *mac_addr,
struct station_info *sinfo, gfp_t gfp); struct station_info *sinfo, gfp_t gfp);
int nl80211_send_action(struct cfg80211_registered_device *rdev, int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
struct net_device *netdev, u32 nlpid, int freq, struct net_device *netdev, u32 nlpid, int freq,
const u8 *buf, size_t len, gfp_t gfp); const u8 *buf, size_t len, gfp_t gfp);
void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
struct net_device *netdev, u64 cookie, struct net_device *netdev, u64 cookie,
const u8 *buf, size_t len, bool ack, const u8 *buf, size_t len, bool ack,
gfp_t gfp); gfp_t gfp);
......
...@@ -823,7 +823,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, ...@@ -823,7 +823,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
/* monitor can't bridge anyway */ /* monitor can't bridge anyway */
break; break;
case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_UNSPECIFIED:
case __NL80211_IFTYPE_AFTER_LAST: case NUM_NL80211_IFTYPES:
/* not happening */ /* not happening */
break; break;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment