Commit 20fb9e50 authored by John W. Linville's avatar John W. Linville
parents 0f496df2 3b4797bc
......@@ -542,9 +542,8 @@ brcms_ops_bss_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_ARP_FILTER) {
/* Hardware ARP filter address list or state changed */
brcms_err(core, "%s: arp filtering: enabled %s, count %d"
" (implement)\n", __func__, info->arp_filter_enabled ?
"true" : "false", info->arp_addr_cnt);
brcms_err(core, "%s: arp filtering: %d addresses"
" (implement)\n", __func__, info->arp_addr_cnt);
}
if (changed & BSS_CHANGED_QOS) {
......
......@@ -1154,6 +1154,7 @@ static int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw)
}
static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
enum ieee80211_rssi_event rssi_event)
{
struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
......
......@@ -4239,8 +4239,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
wlvif->sta.qos = bss_conf->qos;
WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS);
if (bss_conf->arp_addr_cnt == 1 &&
bss_conf->arp_filter_enabled) {
if (bss_conf->arp_addr_cnt == 1 && bss_conf->assoc) {
wlvif->ip_addr = addr;
/*
* The template should have been configured only upon
......
......@@ -1898,7 +1898,10 @@ enum ieee80211_sa_query_action {
/* AKM suite selectors */
#define WLAN_AKM_SUITE_8021X 0x000FAC01
#define WLAN_AKM_SUITE_PSK 0x000FAC02
#define WLAN_AKM_SUITE_SAE 0x000FAC08
#define WLAN_AKM_SUITE_8021X_SHA256 0x000FAC05
#define WLAN_AKM_SUITE_PSK_SHA256 0x000FAC06
#define WLAN_AKM_SUITE_TDLS 0x000FAC07
#define WLAN_AKM_SUITE_SAE 0x000FAC08
#define WLAN_AKM_SUITE_FT_OVER_SAE 0x000FAC09
#define WLAN_MAX_KEY_LEN 32
......
......@@ -527,6 +527,26 @@ struct cfg80211_beacon_data {
size_t probe_resp_len;
};
struct mac_address {
u8 addr[ETH_ALEN];
};
/**
* struct cfg80211_acl_data - Access control list data
*
* @acl_policy: ACL policy to be applied on the station's
entry specified by mac_addr
* @n_acl_entries: Number of MAC address entries passed
* @mac_addrs: List of MAC addresses of stations to be used for ACL
*/
struct cfg80211_acl_data {
enum nl80211_acl_policy acl_policy;
int n_acl_entries;
/* Keep it last */
struct mac_address mac_addrs[];
};
/**
* struct cfg80211_ap_settings - AP configuration
*
......@@ -546,6 +566,8 @@ struct cfg80211_beacon_data {
* @inactivity_timeout: time in seconds to determine station's inactivity.
* @p2p_ctwindow: P2P CT Window
* @p2p_opp_ps: P2P opportunistic PS
* @acl: ACL configuration used by the drivers which has support for
* MAC address based access control
*/
struct cfg80211_ap_settings {
struct cfg80211_chan_def chandef;
......@@ -562,6 +584,7 @@ struct cfg80211_ap_settings {
int inactivity_timeout;
u8 p2p_ctwindow;
bool p2p_opp_ps;
const struct cfg80211_acl_data *acl;
};
/**
......@@ -1796,6 +1819,13 @@ struct cfg80211_gtk_rekey_data {
*
* @start_p2p_device: Start the given P2P device.
* @stop_p2p_device: Stop the given P2P device.
*
* @set_mac_acl: Sets MAC address control list in AP and P2P GO mode.
* Parameters include ACL policy, an array of MAC address of stations
* and the number of MAC addresses. If there is already a list in driver
* this new list replaces the existing one. Driver has to clear its ACL
* when number of MAC addresses entries is passed as 0. Drivers which
* advertise the support for MAC based ACL have to implement this callback.
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
......@@ -2016,6 +2046,9 @@ struct cfg80211_ops {
struct wireless_dev *wdev);
void (*stop_p2p_device)(struct wiphy *wiphy,
struct wireless_dev *wdev);
int (*set_mac_acl)(struct wiphy *wiphy, struct net_device *dev,
const struct cfg80211_acl_data *params);
};
/*
......@@ -2181,10 +2214,6 @@ struct ieee80211_iface_combination {
u8 radar_detect_widths;
};
struct mac_address {
u8 addr[ETH_ALEN];
};
struct ieee80211_txrx_stypes {
u16 tx, rx;
};
......@@ -2325,6 +2354,9 @@ struct wiphy_wowlan_support {
* @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features.
* @ht_capa_mod_mask: Specify what ht_cap values can be over-ridden.
* If null, then none can be over-ridden.
*
* @max_acl_mac_addrs: Maximum number of MAC addresses that the device
* supports for ACL.
*/
struct wiphy {
/* assign these fields before you register the wiphy */
......@@ -2346,6 +2378,8 @@ struct wiphy {
/* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */
u16 interface_modes;
u16 max_acl_mac_addrs;
u32 flags, features;
u32 ap_sme_capa;
......
......@@ -297,11 +297,9 @@ enum ieee80211_rssi_event {
* may filter ARP queries targeted for other addresses than listed here.
* The driver must allow ARP queries targeted for all address listed here
* to pass through. An empty list implies no ARP queries need to pass.
* @arp_addr_cnt: Number of addresses currently on the list.
* @arp_filter_enabled: Enable ARP filtering - if enabled, the hardware may
* filter ARP queries based on the @arp_addr_list, if disabled, the
* hardware must not perform any ARP filtering. Note, that the filter will
* be enabled also in promiscuous mode.
* @arp_addr_cnt: Number of addresses currently on the list. Note that this
* may be larger than %IEEE80211_BSS_ARP_ADDR_LIST_LEN (the arp_addr_list
* array size), it's up to the driver what to do in that case.
* @qos: This is a QoS-enabled BSS.
* @idle: This interface is idle. There's also a global idle flag in the
* hardware config which may be more appropriate depending on what
......@@ -338,8 +336,7 @@ struct ieee80211_bss_conf {
u32 cqm_rssi_hyst;
struct cfg80211_chan_def chandef;
__be32 arp_addr_list[IEEE80211_BSS_ARP_ADDR_LIST_LEN];
u8 arp_addr_cnt;
bool arp_filter_enabled;
int arp_addr_cnt;
bool qos;
bool idle;
bool ps;
......@@ -1630,6 +1627,10 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb);
* rekeying), it will not include a valid phase 1 key. The valid phase 1 key is
* provided by update_tkip_key only. The trigger that makes mac80211 call this
* handler is software decryption with wrap around of iv16.
*
* The set_default_unicast_key() call updates the default WEP key index
* configured to the hardware for WEP encryption type. This is required
* for devices that support offload of data packets (e.g. ARP responses).
*/
/**
......@@ -2208,6 +2209,10 @@ enum ieee80211_rate_control_changed {
* After rekeying was done it should (for example during resume) notify
* userspace of the new replay counter using ieee80211_gtk_rekey_notify().
*
* @set_default_unicast_key: Set the default (unicast) key index, useful for
* WEP when the device sends data packets autonomously, e.g. for ARP
* offloading. The index can be 0-3, or -1 for unsetting it.
*
* @hw_scan: Ask the hardware to service the scan request, no need to start
* the scan state machine in stack. The scan must honour the channel
* configuration done by the regulatory agent in the wiphy's
......@@ -2492,6 +2497,9 @@ enum ieee80211_rate_control_changed {
* driver's resume function returned 1, as this is just like an "inline"
* hardware restart. This callback may sleep.
*
* @ipv6_addr_change: IPv6 address assignment on the given interface changed.
* Currently, this is only called for managed or P2P client interfaces.
* This callback is optional; it must not sleep.
*/
struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw,
......@@ -2539,6 +2547,8 @@ struct ieee80211_ops {
void (*set_rekey_data)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_gtk_rekey_data *data);
void (*set_default_unicast_key)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, int idx);
int (*hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct cfg80211_scan_request *req);
void (*cancel_hw_scan)(struct ieee80211_hw *hw,
......@@ -2623,6 +2633,7 @@ struct ieee80211_ops {
int (*set_bitrate_mask)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
const struct cfg80211_bitrate_mask *mask);
void (*rssi_callback)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
enum ieee80211_rssi_event rssi_event);
void (*allow_buffered_frames)(struct ieee80211_hw *hw,
......@@ -2665,6 +2676,12 @@ struct ieee80211_ops {
struct ieee80211_chanctx_conf *ctx);
void (*restart_complete)(struct ieee80211_hw *hw);
#if IS_ENABLED(CONFIG_IPV6)
void (*ipv6_addr_change)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct inet6_dev *idev);
#endif
};
/**
......
......@@ -170,7 +170,8 @@
* %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE,
* %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS,
* %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY,
* %NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT.
* %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT,
* %NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS.
* The channel to use can be set on the interface or be given using the
* %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width.
* @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP
......@@ -586,6 +587,16 @@
* @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames
* for IBSS or MESH vif.
*
* @NL80211_CMD_SET_MAC_ACL: sets ACL for MAC address based access control.
* This is to be used with the drivers advertising the support of MAC
* address based access control. List of MAC addresses is passed in
* %NL80211_ATTR_MAC_ADDRS and ACL policy is passed in
* %NL80211_ATTR_ACL_POLICY. Driver will enable ACL with this list, if it
* is not already done. The new list will replace any existing list. Driver
* will clear its ACL when the list of MAC addresses passed is empty. This
* command is used in AP/P2P GO mode. Driver has to make sure to clear its
* ACL list during %NL80211_CMD_STOP_AP.
*
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
......@@ -736,6 +747,8 @@ enum nl80211_commands {
NL80211_CMD_SET_MCAST_RATE,
NL80211_CMD_SET_MAC_ACL,
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
......@@ -1313,6 +1326,16 @@ enum nl80211_commands {
* @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode
* defined in &enum nl80211_mesh_power_mode.
*
* @NL80211_ATTR_ACL_POLICY: ACL policy, see &enum nl80211_acl_policy,
* carried in a u32 attribute
*
* @NL80211_ATTR_MAC_ADDRS: Array of nested MAC addresses, used for
* MAC ACL.
*
* @NL80211_ATTR_MAC_ACL_MAX: u32 attribute to advertise the maximum
* number of MAC addresses that a device can support for MAC
* ACL.
*
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
......@@ -1585,6 +1608,12 @@ enum nl80211_attrs {
NL80211_ATTR_LOCAL_MESH_POWER_MODE,
NL80211_ATTR_ACL_POLICY,
NL80211_ATTR_MAC_ADDRS,
NL80211_ATTR_MAC_ACL_MAX,
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
......@@ -3248,7 +3277,7 @@ enum nl80211_probe_resp_offload_support_attr {
* enum nl80211_connect_failed_reason - connection request failed reasons
* @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be
* handled by the AP is reached.
* @NL80211_CONN_FAIL_BLOCKED_CLIENT: Client's MAC is in the AP's blocklist.
* @NL80211_CONN_FAIL_BLOCKED_CLIENT: Connection request is rejected due to ACL.
*/
enum nl80211_connect_failed_reason {
NL80211_CONN_FAIL_MAX_CLIENTS,
......@@ -3276,4 +3305,22 @@ enum nl80211_scan_flags {
NL80211_SCAN_FLAG_AP = 1<<2,
};
/**
* enum nl80211_acl_policy - access control policy
*
* Access control policy is applied on a MAC list set by
* %NL80211_CMD_START_AP and %NL80211_CMD_SET_MAC_ACL, to
* be used with %NL80211_ATTR_ACL_POLICY.
*
* @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are
* listed in ACL, i.e. allow all the stations which are not listed
* in ACL to authenticate.
* @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow the stations which are listed
* in ACL, i.e. deny all the stations which are not listed in ACL.
*/
enum nl80211_acl_policy {
NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED,
NL80211_ACL_POLICY_DENY_UNLESS_LISTED,
};
#endif /* __LINUX_NL80211_H */
......@@ -83,8 +83,8 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP,
&sta->sta, tid, NULL, 0))
sdata_info(sta->sdata,
"HW problem - can not stop rx aggregation for tid %d\n",
tid);
"HW problem - can not stop rx aggregation for %pM tid %d\n",
sta->sta.addr, tid);
/* check if this is a self generated aggregation halt */
if (initiator == WLAN_BACK_RECIPIENT && tx)
......@@ -159,7 +159,8 @@ static void sta_rx_agg_session_timer_expired(unsigned long data)
}
rcu_read_unlock();
ht_dbg(sta->sdata, "rx session timer expired on tid %d\n", (u16)*ptid);
ht_dbg(sta->sdata, "RX session timer expired on %pM tid %d\n",
sta->sta.addr, (u16)*ptid);
set_bit(*ptid, sta->ampdu_mlme.tid_rx_timer_expired);
ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work);
......@@ -247,7 +248,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
status = WLAN_STATUS_REQUEST_DECLINED;
if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
ht_dbg(sta->sdata, "Suspend in progress - Denying ADDBA request\n");
ht_dbg(sta->sdata,
"Suspend in progress - Denying ADDBA request (%pM tid %d)\n",
sta->sta.addr, tid);
goto end_no_lock;
}
......@@ -317,7 +320,8 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START,
&sta->sta, tid, &start_seq_num, 0);
ht_dbg(sta->sdata, "Rx A-MPDU request on tid %d result %d\n", tid, ret);
ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n",
sta->sta.addr, tid, ret);
if (ret) {
kfree(tid_agg_rx->reorder_buf);
kfree(tid_agg_rx->reorder_time);
......
......@@ -296,7 +296,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
IEEE80211_AMPDU_TX_STOP_FLUSH_CONT,
&sta->sta, tid, NULL, 0);
WARN_ON_ONCE(ret);
goto remove_tid_tx;
return 0;
}
if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) {
......@@ -354,12 +354,15 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
*/
}
if (reason == AGG_STOP_DESTROY_STA) {
remove_tid_tx:
spin_lock_bh(&sta->lock);
ieee80211_remove_tid_tx(sta, tid);
spin_unlock_bh(&sta->lock);
}
/*
* In the case of AGG_STOP_DESTROY_STA, the driver won't
* necessarily call ieee80211_stop_tx_ba_cb(), so this may
* seem like we can leave the tid_tx data pending forever.
* This is true, in a way, but "forever" is only until the
* station struct is actually destroyed. In the meantime,
* leaving it around ensures that we don't transmit packets
* to the driver on this TID which might confuse it.
*/
return 0;
}
......@@ -387,12 +390,13 @@ static void sta_addba_resp_timer_expired(unsigned long data)
test_bit(HT_AGG_STATE_RESPONSE_RECEIVED, &tid_tx->state)) {
rcu_read_unlock();
ht_dbg(sta->sdata,
"timer expired on tid %d but we are not (or no longer) expecting addBA response there\n",
tid);
"timer expired on %pM tid %d but we are not (or no longer) expecting addBA response there\n",
sta->sta.addr, tid);
return;
}
ht_dbg(sta->sdata, "addBA response timer expired on tid %d\n", tid);
ht_dbg(sta->sdata, "addBA response timer expired on %pM tid %d\n",
sta->sta.addr, tid);
ieee80211_stop_tx_ba_session(&sta->sta, tid);
rcu_read_unlock();
......@@ -429,7 +433,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
&sta->sta, tid, &start_seq_num, 0);
if (ret) {
ht_dbg(sdata,
"BA request denied - HW unavailable for tid %d\n", tid);
"BA request denied - HW unavailable for %pM tid %d\n",
sta->sta.addr, tid);
spin_lock_bh(&sta->lock);
ieee80211_agg_splice_packets(sdata, tid_tx, tid);
ieee80211_assign_tid_tx(sta, tid, NULL);
......@@ -442,7 +447,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
/* activate the timer for the recipient's addBA response */
mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL);
ht_dbg(sdata, "activated addBA response timer on tid %d\n", tid);
ht_dbg(sdata, "activated addBA response timer on %pM tid %d\n",
sta->sta.addr, tid);
spin_lock_bh(&sta->lock);
sta->ampdu_mlme.last_addba_req_time[tid] = jiffies;
......@@ -489,7 +495,8 @@ static void sta_tx_agg_session_timer_expired(unsigned long data)
rcu_read_unlock();
ht_dbg(sta->sdata, "tx session timer expired on tid %d\n", (u16)*ptid);
ht_dbg(sta->sdata, "tx session timer expired on %pM tid %d\n",
sta->sta.addr, (u16)*ptid);
ieee80211_stop_tx_ba_session(&sta->sta, *ptid);
}
......@@ -525,7 +532,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
ht_dbg(sdata,
"BA sessions blocked - Denying BA session request\n");
"BA sessions blocked - Denying BA session request %pM tid %d\n",
sta->sta.addr, tid);
return -EINVAL;
}
......@@ -566,8 +574,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
time_before(jiffies, sta->ampdu_mlme.last_addba_req_time[tid] +
HT_AGG_RETRIES_PERIOD)) {
ht_dbg(sdata,
"BA request denied - waiting a grace period after %d failed requests on tid %u\n",
sta->ampdu_mlme.addba_req_num[tid], tid);
"BA request denied - waiting a grace period after %d failed requests on %pM tid %u\n",
sta->ampdu_mlme.addba_req_num[tid], sta->sta.addr, tid);
ret = -EBUSY;
goto err_unlock_sta;
}
......@@ -576,8 +584,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
/* check if the TID is not in aggregation flow already */
if (tid_tx || sta->ampdu_mlme.tid_start_tx[tid]) {
ht_dbg(sdata,
"BA request denied - session is not idle on tid %u\n",
tid);
"BA request denied - session is not idle on %pM tid %u\n",
sta->sta.addr, tid);
ret = -EAGAIN;
goto err_unlock_sta;
}
......@@ -632,7 +640,8 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
ht_dbg(sta->sdata, "Aggregation is on for tid %d\n", tid);
ht_dbg(sta->sdata, "Aggregation is on for %pM tid %d\n",
sta->sta.addr, tid);
drv_ampdu_action(local, sta->sdata,
IEEE80211_AMPDU_TX_OPERATIONAL,
......@@ -802,7 +811,9 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
if (!tid_tx || !test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) {
ht_dbg(sdata, "unexpected callback to A-MPDU stop\n");
ht_dbg(sdata,
"unexpected callback to A-MPDU stop for %pM tid %d\n",
sta->sta.addr, tid);
goto unlock_sta;
}
......@@ -861,13 +872,15 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
goto out;
if (mgmt->u.action.u.addba_resp.dialog_token != tid_tx->dialog_token) {
ht_dbg(sta->sdata, "wrong addBA response token, tid %d\n", tid);
ht_dbg(sta->sdata, "wrong addBA response token, %pM tid %d\n",
sta->sta.addr, tid);
goto out;
}
del_timer_sync(&tid_tx->addba_resp_timer);
ht_dbg(sta->sdata, "switched off addBA timer for tid %d\n", tid);
ht_dbg(sta->sdata, "switched off addBA timer for %pM tid %d\n",
sta->sta.addr, tid);
/*
* addba_resp_timer may have fired before we got here, and
......@@ -877,8 +890,8 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
if (test_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state) ||
test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) {
ht_dbg(sta->sdata,
"got addBA resp for tid %d but we already gave up\n",
tid);
"got addBA resp for %pM tid %d but we already gave up\n",
sta->sta.addr, tid);
goto out;
}
......
......@@ -569,7 +569,8 @@ static inline void drv_sta_rc_update(struct ieee80211_local *local,
check_sdata_in_driver(sdata);
WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED &&
sdata->vif.type != NL80211_IFTYPE_ADHOC);
(sdata->vif.type != NL80211_IFTYPE_ADHOC &&
sdata->vif.type != NL80211_IFTYPE_MESH_POINT));
trace_drv_sta_rc_update(local, sdata, sta, changed);
if (local->ops->sta_rc_update)
......@@ -845,11 +846,12 @@ static inline void drv_set_rekey_data(struct ieee80211_local *local,
}
static inline void drv_rssi_callback(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
const enum ieee80211_rssi_event event)
{
trace_drv_rssi_callback(local, event);
trace_drv_rssi_callback(local, sdata, event);
if (local->ops->rssi_callback)
local->ops->rssi_callback(&local->hw, event);
local->ops->rssi_callback(&local->hw, &sdata->vif, event);
trace_drv_return_void(local);
}
......@@ -1020,4 +1022,32 @@ static inline void drv_restart_complete(struct ieee80211_local *local)
trace_drv_return_void(local);
}
static inline void
drv_set_default_unicast_key(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
int key_idx)
{
check_sdata_in_driver(sdata);
WARN_ON_ONCE(key_idx < -1 || key_idx > 3);
trace_drv_set_default_unicast_key(local, sdata, key_idx);
if (local->ops->set_default_unicast_key)
local->ops->set_default_unicast_key(&local->hw, &sdata->vif,
key_idx);
trace_drv_return_void(local);
}
#if IS_ENABLED(CONFIG_IPV6)
static inline void drv_ipv6_addr_change(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct inet6_dev *idev)
{
trace_drv_ipv6_addr_change(local, sdata);
if (local->ops->ipv6_addr_change)
local->ops->ipv6_addr_change(&local->hw, &sdata->vif, idev);
trace_drv_return_void(local);
}
#endif
#endif /* __MAC80211_DRIVER_OPS */
......@@ -747,8 +747,6 @@ struct ieee80211_sub_if_data {
struct work_struct work;
struct sk_buff_head skb_queue;
bool arp_filter_state;
u8 needed_rx_chains;
enum ieee80211_smps_mode smps_mode;
......@@ -1129,6 +1127,7 @@ struct ieee80211_local {
struct timer_list dynamic_ps_timer;
struct notifier_block network_latency_notifier;
struct notifier_block ifa_notifier;
struct notifier_block ifa6_notifier;
/*
* The dynamic ps timeout configured from user space via WEXT -
......
......@@ -1574,9 +1574,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
/* initialise type-independent data */
sdata->wdev.wiphy = local->hw.wiphy;
sdata->local = local;
#ifdef CONFIG_INET
sdata->arp_filter_state = true;
#endif
for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
skb_queue_head_init(&sdata->fragments[i].skb_list);
......
......@@ -204,8 +204,11 @@ static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata,
if (idx >= 0 && idx < NUM_DEFAULT_KEYS)
key = key_mtx_dereference(sdata->local, sdata->keys[idx]);
if (uni)
if (uni) {
rcu_assign_pointer(sdata->default_unicast_key, key);
drv_set_default_unicast_key(sdata->local, sdata, idx);
}
if (multi)
rcu_assign_pointer(sdata->default_multicast_key, key);
......
......@@ -23,6 +23,7 @@
#include <linux/inetdevice.h>
#include <net/net_namespace.h>
#include <net/cfg80211.h>
#include <net/addrconf.h>
#include "ieee80211_i.h"
#include "driver-ops.h"
......@@ -349,27 +350,19 @@ static int ieee80211_ifa_changed(struct notifier_block *nb,
/* Copy the addresses to the bss_conf list */
ifa = idev->ifa_list;
while (c < IEEE80211_BSS_ARP_ADDR_LIST_LEN && ifa) {
bss_conf->arp_addr_list[c] = ifa->ifa_address;
while (ifa) {
if (c < IEEE80211_BSS_ARP_ADDR_LIST_LEN)
bss_conf->arp_addr_list[c] = ifa->ifa_address;
ifa = ifa->ifa_next;
c++;
}
/* If not all addresses fit the list, disable filtering */
if (ifa) {
sdata->arp_filter_state = false;
c = 0;
} else {
sdata->arp_filter_state = true;
}
bss_conf->arp_addr_cnt = c;
/* Configure driver only if associated (which also implies it is up) */
if (ifmgd->associated) {
bss_conf->arp_filter_enabled = sdata->arp_filter_state;
if (ifmgd->associated)
ieee80211_bss_info_change_notify(sdata,
BSS_CHANGED_ARP_FILTER);
}
mutex_unlock(&ifmgd->mtx);
......@@ -377,6 +370,37 @@ static int ieee80211_ifa_changed(struct notifier_block *nb,
}
#endif
#if IS_ENABLED(CONFIG_IPV6)
static int ieee80211_ifa6_changed(struct notifier_block *nb,
unsigned long data, void *arg)
{
struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)arg;
struct inet6_dev *idev = ifa->idev;
struct net_device *ndev = ifa->idev->dev;
struct ieee80211_local *local =
container_of(nb, struct ieee80211_local, ifa6_notifier);
struct wireless_dev *wdev = ndev->ieee80211_ptr;
struct ieee80211_sub_if_data *sdata;
/* Make sure it's our interface that got changed */
if (!wdev || wdev->wiphy != local->hw.wiphy)
return NOTIFY_DONE;
sdata = IEEE80211_DEV_TO_SUB_IF(ndev);
/*
* For now only support station mode. This is mostly because
* doing AP would have to handle AP_VLAN in some way ...
*/
if (sdata->vif.type != NL80211_IFTYPE_STATION)
return NOTIFY_DONE;
drv_ipv6_addr_change(local, sdata, idev);
return NOTIFY_DONE;
}
#endif
static int ieee80211_napi_poll(struct napi_struct *napi, int budget)
{
struct ieee80211_local *local =
......@@ -985,12 +1009,25 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
goto fail_ifa;
#endif
#if IS_ENABLED(CONFIG_IPV6)
local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed;
result = register_inet6addr_notifier(&local->ifa6_notifier);
if (result)
goto fail_ifa6;
#endif
netif_napi_add(&local->napi_dev, &local->napi, ieee80211_napi_poll,
local->hw.napi_weight);
return 0;
#if IS_ENABLED(CONFIG_IPV6)
fail_ifa6:
#ifdef CONFIG_INET
unregister_inetaddr_notifier(&local->ifa_notifier);
#endif
#endif
#if defined(CONFIG_INET) || defined(CONFIG_IPV6)
fail_ifa:
pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
&local->network_latency_notifier);
......@@ -1026,6 +1063,9 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
#ifdef CONFIG_INET
unregister_inetaddr_notifier(&local->ifa_notifier);
#endif
#if IS_ENABLED(CONFIG_IPV6)
unregister_inet6addr_notifier(&local->ifa6_notifier);
#endif
rtnl_lock();
......
......@@ -55,30 +55,6 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta)
sta->plink_retries = 0;
}
/*
* Allocate mesh sta entry and insert into station table
*/
static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
u8 *hw_addr)
{
struct sta_info *sta;
if (sdata->local->num_sta >= MESH_MAX_PLINKS)
return NULL;
sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL);
if (!sta)
return NULL;
sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);
set_sta_flag(sta, WLAN_STA_WME);
return sta;
}
/**
* mesh_set_ht_prot_mode - set correct HT protection mode
*
......@@ -309,53 +285,27 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
return err;
}
/**
* mesh_peer_init - initialize new mesh peer and return resulting sta_info
*
* @sdata: local meshif
* @addr: peer's address
* @elems: IEs from beacon or mesh peering frame
*
* call under RCU
*/
static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,
u8 *addr,
struct ieee802_11_elems *elems)
static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct ieee802_11_elems *elems, bool insert)
{
struct ieee80211_local *local = sdata->local;
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_supported_band *sband;
u32 rates, basic_rates = 0;
struct sta_info *sta;
bool insert = false;
u32 rates, basic_rates = 0, changed = 0;
sband = local->hw.wiphy->bands[band];
rates = ieee80211_sta_get_rates(local, elems, band, &basic_rates);
sta = sta_info_get(sdata, addr);
if (!sta) {
/* Userspace handles peer allocation when security is enabled */
if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) {
cfg80211_notify_new_peer_candidate(sdata->dev, addr,
elems->ie_start,
elems->total_len,
GFP_ATOMIC);
return NULL;
}
sta = mesh_plink_alloc(sdata, addr);
if (!sta)
return NULL;
insert = true;
}
spin_lock_bh(&sta->lock);
sta->last_rx = jiffies;
if (sta->plink_state == NL80211_PLINK_ESTAB) {
spin_unlock_bh(&sta->lock);
return sta;
}
/* rates and capabilities don't change during peering */
if (sta->plink_state == NL80211_PLINK_ESTAB)
goto out;
if (sta->sta.supp_rates[band] != rates)
changed |= IEEE80211_RC_SUPP_RATES_CHANGED;
sta->sta.supp_rates[band] = rates;
if (elems->ht_cap_elem &&
sdata->vif.bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT)
......@@ -374,27 +324,115 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,
~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan,
elems->ht_operation, &chandef);
if (sta->ch_width != chandef.width)
changed |= IEEE80211_RC_BW_CHANGED;
sta->ch_width = chandef.width;
}
if (insert)
rate_control_rate_init(sta);
else
rate_control_rate_update(local, sband, sta, changed);
out:
spin_unlock_bh(&sta->lock);
}
static struct sta_info *
__mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr)
{
struct sta_info *sta;
if (insert && sta_info_insert(sta))
if (sdata->local->num_sta >= MESH_MAX_PLINKS)
return NULL;
sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL);
if (!sta)
return NULL;
sta->plink_state = NL80211_PLINK_LISTEN;
init_timer(&sta->plink_timer);
sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);
set_sta_flag(sta, WLAN_STA_WME);
return sta;
}
static struct sta_info *
mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr,
struct ieee802_11_elems *elems)
{
struct sta_info *sta = NULL;
/* Userspace handles peer allocation when security is enabled */
if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED)
cfg80211_notify_new_peer_candidate(sdata->dev, addr,
elems->ie_start,
elems->total_len,
GFP_KERNEL);
else
sta = __mesh_sta_info_alloc(sdata, addr);
return sta;
}
/*
* mesh_sta_info_get - return mesh sta info entry for @addr.
*
* @sdata: local meshif
* @addr: peer's address
* @elems: IEs from beacon or mesh peering frame.
*
* Return existing or newly allocated sta_info under RCU read lock.
* (re)initialize with given IEs.
*/
static struct sta_info *
mesh_sta_info_get(struct ieee80211_sub_if_data *sdata,
u8 *addr, struct ieee802_11_elems *elems) __acquires(RCU)
{
struct sta_info *sta = NULL;
rcu_read_lock();
sta = sta_info_get(sdata, addr);
if (sta) {
mesh_sta_info_init(sdata, sta, elems, false);
} else {
rcu_read_unlock();
/* can't run atomic */
sta = mesh_sta_info_alloc(sdata, addr, elems);
if (!sta) {
rcu_read_lock();
return NULL;
}
mesh_sta_info_init(sdata, sta, elems, true);
if (sta_info_insert_rcu(sta))
return NULL;
}
return sta;
}
/*
* mesh_neighbour_update - update or initialize new mesh neighbor.
*
* @sdata: local meshif
* @addr: peer's address
* @elems: IEs from beacon or mesh peering frame
*
* Initiates peering if appropriate.
*/
void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
u8 *hw_addr,
struct ieee802_11_elems *elems)
{
struct sta_info *sta;
rcu_read_lock();
sta = mesh_peer_init(sdata, hw_addr, elems);
sta = mesh_sta_info_get(sdata, hw_addr, elems);
if (!sta)
goto out;
......@@ -632,6 +670,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
(ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8))
memcpy(&llid, PLINK_GET_PLID(elems.peering), 2);
/* WARNING: Only for sta pointer, is dropped & re-acquired */
rcu_read_lock();
sta = sta_info_get(sdata, mgmt->sa);
......@@ -735,8 +774,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
}
if (event == OPN_ACPT) {
rcu_read_unlock();
/* allocate sta entry if necessary and update info */
sta = mesh_peer_init(sdata, mgmt->sa, &elems);
sta = mesh_sta_info_get(sdata, mgmt->sa, &elems);
if (!sta) {
mpl_dbg(sdata, "Mesh plink: failed to init peer!\n");
rcu_read_unlock();
......
......@@ -1465,10 +1465,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
bss_info_changed |= BSS_CHANGED_CQM;
/* Enable ARP filtering */
if (bss_conf->arp_filter_enabled != sdata->arp_filter_state) {
bss_conf->arp_filter_enabled = sdata->arp_filter_state;
if (bss_conf->arp_addr_cnt)
bss_info_changed |= BSS_CHANGED_ARP_FILTER;
}
ieee80211_bss_info_change_notify(sdata, bss_info_changed);
......@@ -1489,7 +1487,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
u32 changed = 0;
ASSERT_MGD_MTX(ifmgd);
......@@ -1521,14 +1518,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
netif_tx_stop_all_queues(sdata->dev);
netif_carrier_off(sdata->dev);
mutex_lock(&local->sta_mtx);
sta = sta_info_get(sdata, ifmgd->bssid);
if (sta) {
set_sta_flag(sta, WLAN_STA_BLOCK_BA);
ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA);
}
mutex_unlock(&local->sta_mtx);
/*
* if we want to get out of ps before disassoc (why?) we have
* to do it before sending disassoc, as otherwise the null-packet
......@@ -1582,10 +1571,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
cancel_work_sync(&local->dynamic_ps_enable_work);
/* Disable ARP filtering */
if (sdata->vif.bss_conf.arp_filter_enabled) {
sdata->vif.bss_conf.arp_filter_enabled = false;
if (sdata->vif.bss_conf.arp_addr_cnt)
changed |= BSS_CHANGED_ARP_FILTER;
}
sdata->vif.bss_conf.qos = false;
changed |= BSS_CHANGED_QOS;
......@@ -2608,12 +2595,12 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (sig > ifmgd->rssi_max_thold &&
(last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) {
ifmgd->last_ave_beacon_signal = sig;
drv_rssi_callback(local, RSSI_EVENT_HIGH);
drv_rssi_callback(local, sdata, RSSI_EVENT_HIGH);
} else if (sig < ifmgd->rssi_min_thold &&
(last_sig >= ifmgd->rssi_max_thold ||
last_sig == 0)) {
ifmgd->last_ave_beacon_signal = sig;
drv_rssi_callback(local, RSSI_EVENT_LOW);
drv_rssi_callback(local, sdata, RSSI_EVENT_LOW);
}
}
......@@ -3169,23 +3156,22 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
if (!ifmgd->associated)
mutex_lock(&ifmgd->mtx);
if (!ifmgd->associated) {
mutex_unlock(&ifmgd->mtx);
return;
}
if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) {
sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME;
mutex_lock(&ifmgd->mtx);
if (ifmgd->associated) {
mlme_dbg(sdata,
"driver requested disconnect after resume\n");
ieee80211_sta_connection_lost(sdata,
ifmgd->associated->bssid,
WLAN_REASON_UNSPECIFIED);
mutex_unlock(&ifmgd->mtx);
return;
}
mlme_dbg(sdata, "driver requested disconnect after resume\n");
ieee80211_sta_connection_lost(sdata,
ifmgd->associated->bssid,
WLAN_REASON_UNSPECIFIED);
mutex_unlock(&ifmgd->mtx);
return;
}
mutex_unlock(&ifmgd->mtx);
if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running))
add_timer(&ifmgd->timer);
......
......@@ -380,11 +380,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
#ifdef CONFIG_MAC80211_MESH
sta->plink_state = NL80211_PLINK_LISTEN;
init_timer(&sta->plink_timer);
#endif
return sta;
}
......
......@@ -347,8 +347,11 @@ TRACE_EVENT(drv_bss_info_changed,
__field(s32, cqm_rssi_hyst);
__field(u32, channel_width);
__field(u32, channel_cfreq1);
__dynamic_array(u32, arp_addr_list, info->arp_addr_cnt);
__field(bool, arp_filter_enabled);
__dynamic_array(u32, arp_addr_list,
info->arp_addr_cnt > IEEE80211_BSS_ARP_ADDR_LIST_LEN ?
IEEE80211_BSS_ARP_ADDR_LIST_LEN :
info->arp_addr_cnt);
__field(int, arp_addr_cnt);
__field(bool, qos);
__field(bool, idle);
__field(bool, ps);
......@@ -384,9 +387,11 @@ TRACE_EVENT(drv_bss_info_changed,
__entry->cqm_rssi_hyst = info->cqm_rssi_hyst;
__entry->channel_width = info->chandef.width;
__entry->channel_cfreq1 = info->chandef.center_freq1;
__entry->arp_addr_cnt = info->arp_addr_cnt;
memcpy(__get_dynamic_array(arp_addr_list), info->arp_addr_list,
sizeof(u32) * info->arp_addr_cnt);
__entry->arp_filter_enabled = info->arp_filter_enabled;
sizeof(u32) * (info->arp_addr_cnt > IEEE80211_BSS_ARP_ADDR_LIST_LEN ?
IEEE80211_BSS_ARP_ADDR_LIST_LEN :
info->arp_addr_cnt));
__entry->qos = info->qos;
__entry->idle = info->idle;
__entry->ps = info->ps;
......@@ -1184,23 +1189,26 @@ TRACE_EVENT(drv_set_rekey_data,
TRACE_EVENT(drv_rssi_callback,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
enum ieee80211_rssi_event rssi_event),
TP_ARGS(local, rssi_event),
TP_ARGS(local, sdata, rssi_event),
TP_STRUCT__entry(
LOCAL_ENTRY
VIF_ENTRY
__field(u32, rssi_event)
),
TP_fast_assign(
LOCAL_ASSIGN;
VIF_ASSIGN;
__entry->rssi_event = rssi_event;
),
TP_printk(
LOCAL_PR_FMT " rssi_event:%d",
LOCAL_PR_ARG, __entry->rssi_event
LOCAL_PR_FMT VIF_PR_FMT " rssi_event:%d",
LOCAL_PR_ARG, VIF_PR_ARG, __entry->rssi_event
)
);
......@@ -1432,6 +1440,14 @@ DEFINE_EVENT(local_only_evt, drv_restart_complete,
TP_ARGS(local)
);
#if IS_ENABLED(CONFIG_IPV6)
DEFINE_EVENT(local_sdata_evt, drv_ipv6_addr_change,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata),
TP_ARGS(local, sdata)
);
#endif
/*
* Tracing for API calls that drivers call.
*/
......@@ -1821,6 +1837,29 @@ TRACE_EVENT(stop_queue,
)
);
TRACE_EVENT(drv_set_default_unicast_key,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
int key_idx),
TP_ARGS(local, sdata, key_idx),
TP_STRUCT__entry(
LOCAL_ENTRY
VIF_ENTRY
__field(int, key_idx)
),
TP_fast_assign(
LOCAL_ASSIGN;
VIF_ASSIGN;
__entry->key_idx = key_idx;
),
TP_printk(LOCAL_PR_FMT VIF_PR_FMT " key_idx:%d",
LOCAL_PR_ARG, VIF_PR_ARG, __entry->key_idx)
);
#ifdef CONFIG_MAC80211_MESSAGE_TRACING
#undef TRACE_SYSTEM
#define TRACE_SYSTEM mac80211_msg
......
......@@ -1787,16 +1787,16 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
break;
/* fall through */
case NL80211_IFTYPE_AP:
if (sdata->vif.type == NL80211_IFTYPE_AP)
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (!chanctx_conf)
goto fail_rcu;
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
/* DA BSSID SA */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN);
memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
hdrlen = 24;
if (sdata->vif.type == NL80211_IFTYPE_AP)
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (!chanctx_conf)
goto fail_rcu;
band = chanctx_conf->def.chan->band;
break;
case NL80211_IFTYPE_WDS:
......
......@@ -478,6 +478,11 @@ int wiphy_register(struct wiphy *wiphy)
ETH_ALEN)))
return -EINVAL;
if (WARN_ON(wiphy->max_acl_mac_addrs &&
(!(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME) ||
!rdev->ops->set_mac_acl)))
return -EINVAL;
if (wiphy->addresses)
memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN);
......
......@@ -365,6 +365,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 },
[NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 },
[NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 },
[NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 },
[NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED },
};
/* policy for the key attributes */
......@@ -1268,6 +1270,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
dev->wiphy.ht_capa_mod_mask))
goto nla_put_failure;
if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
dev->wiphy.max_acl_mac_addrs &&
nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX,
dev->wiphy.max_acl_mac_addrs))
goto nla_put_failure;
return genlmsg_end(msg, hdr);
nla_put_failure:
......@@ -2491,6 +2499,97 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
return err;
}
/* This function returns an error or the number of nested attributes */
static int validate_acl_mac_addrs(struct nlattr *nl_attr)
{
struct nlattr *attr;
int n_entries = 0, tmp;
nla_for_each_nested(attr, nl_attr, tmp) {
if (nla_len(attr) != ETH_ALEN)
return -EINVAL;
n_entries++;
}
return n_entries;
}
/*
* This function parses ACL information and allocates memory for ACL data.
* On successful return, the calling function is responsible to free the
* ACL buffer returned by this function.
*/
static struct cfg80211_acl_data *parse_acl_data(struct wiphy *wiphy,
struct genl_info *info)
{
enum nl80211_acl_policy acl_policy;
struct nlattr *attr;
struct cfg80211_acl_data *acl;
int i = 0, n_entries, tmp;
if (!wiphy->max_acl_mac_addrs)
return ERR_PTR(-EOPNOTSUPP);
if (!info->attrs[NL80211_ATTR_ACL_POLICY])
return ERR_PTR(-EINVAL);
acl_policy = nla_get_u32(info->attrs[NL80211_ATTR_ACL_POLICY]);
if (acl_policy != NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED &&
acl_policy != NL80211_ACL_POLICY_DENY_UNLESS_LISTED)
return ERR_PTR(-EINVAL);
if (!info->attrs[NL80211_ATTR_MAC_ADDRS])
return ERR_PTR(-EINVAL);
n_entries = validate_acl_mac_addrs(info->attrs[NL80211_ATTR_MAC_ADDRS]);
if (n_entries < 0)
return ERR_PTR(n_entries);
if (n_entries > wiphy->max_acl_mac_addrs)
return ERR_PTR(-ENOTSUPP);
acl = kzalloc(sizeof(*acl) + (sizeof(struct mac_address) * n_entries),
GFP_KERNEL);
if (!acl)
return ERR_PTR(-ENOMEM);
nla_for_each_nested(attr, info->attrs[NL80211_ATTR_MAC_ADDRS], tmp) {
memcpy(acl->mac_addrs[i].addr, nla_data(attr), ETH_ALEN);
i++;
}
acl->n_acl_entries = n_entries;
acl->acl_policy = acl_policy;
return acl;
}
static int nl80211_set_mac_acl(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 cfg80211_acl_data *acl;
int err;
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
return -EOPNOTSUPP;
if (!dev->ieee80211_ptr->beacon_interval)
return -EINVAL;
acl = parse_acl_data(&rdev->wiphy, info);
if (IS_ERR(acl))
return PTR_ERR(acl);
err = rdev_set_mac_acl(rdev, dev, acl);
kfree(acl);
return err;
}
static int nl80211_parse_beacon(struct genl_info *info,
struct cfg80211_beacon_data *bcn)
{
......@@ -2734,6 +2833,12 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
if (err)
return err;
if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
params.acl = parse_acl_data(&rdev->wiphy, info);
if (IS_ERR(params.acl))
return PTR_ERR(params.acl);
}
err = rdev_start_ap(rdev, dev, &params);
if (!err) {
wdev->preset_chandef = params.chandef;
......@@ -2742,6 +2847,9 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
wdev->ssid_len = params.ssid_len;
memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
}
kfree(params.acl);
return err;
}
......@@ -7876,6 +7984,14 @@ static struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_NETDEV |
NL80211_FLAG_NEED_RTNL,
},
{
.cmd = NL80211_CMD_SET_MAC_ACL,
.doit = nl80211_set_mac_acl,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_NETDEV |
NL80211_FLAG_NEED_RTNL,
},
};
static struct genl_multicast_group nl80211_mlme_mcgrp = {
......
......@@ -875,4 +875,16 @@ static inline void rdev_stop_p2p_device(struct cfg80211_registered_device *rdev,
rdev->ops->stop_p2p_device(&rdev->wiphy, wdev);
trace_rdev_return_void(&rdev->wiphy);
}
static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_acl_data *params)
{
int ret;
trace_rdev_set_mac_acl(&rdev->wiphy, dev, params);
ret = rdev->ops->set_mac_acl(&rdev->wiphy, dev, params);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
#endif /* __CFG80211_RDEV_OPS */
......@@ -1767,6 +1767,24 @@ DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_p2p_device,
TP_ARGS(wiphy, wdev)
);
TRACE_EVENT(rdev_set_mac_acl,
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
struct cfg80211_acl_data *params),
TP_ARGS(wiphy, netdev, params),
TP_STRUCT__entry(
WIPHY_ENTRY
NETDEV_ENTRY
__field(u32, acl_policy)
),
TP_fast_assign(
WIPHY_ASSIGN;
WIPHY_ASSIGN;
__entry->acl_policy = params->acl_policy;
),
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", acl policy: %d",
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->acl_policy)
);
/*************************************************************
* cfg80211 exported functions traces *
*************************************************************/
......
......@@ -1212,7 +1212,8 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_P2P_GO:
case NL80211_IFTYPE_WDS:
radar_required = !!(chan->flags & IEEE80211_CHAN_RADAR);
radar_required = !!(chan &&
(chan->flags & IEEE80211_CHAN_RADAR));
break;
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_STATION:
......
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