diff options
Diffstat (limited to 'net/mac80211')
31 files changed, 961 insertions, 452 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 513f85c..f5fdfcb 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -2,7 +2,6 @@ config MAC80211 tristate "Generic IEEE 802.11 Networking Stack (mac80211)" depends on CFG80211 select CRYPTO - select CRYPTO_ECB select CRYPTO_ARC4 select CRYPTO_AES select CRC32 diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c index 4bd6ef0..b9b595c 100644 --- a/net/mac80211/aes_ccm.c +++ b/net/mac80211/aes_ccm.c @@ -54,13 +54,12 @@ void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, u8 *cdata, u8 *mic) { int i, j, last_len, num_blocks; - u8 *pos, *cpos, *b, *s_0, *e, *b_0, *aad; + u8 *pos, *cpos, *b, *s_0, *e, *b_0; b = scratch; s_0 = scratch + AES_BLOCK_LEN; e = scratch + 2 * AES_BLOCK_LEN; b_0 = scratch + 3 * AES_BLOCK_LEN; - aad = scratch + 4 * AES_BLOCK_LEN; num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); last_len = data_len % AES_BLOCK_LEN; @@ -94,13 +93,12 @@ int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, u8 *cdata, size_t data_len, u8 *mic, u8 *data) { int i, j, last_len, num_blocks; - u8 *pos, *cpos, *b, *s_0, *a, *b_0, *aad; + u8 *pos, *cpos, *b, *s_0, *a, *b_0; b = scratch; s_0 = scratch + AES_BLOCK_LEN; a = scratch + 2 * AES_BLOCK_LEN; b_0 = scratch + 3 * AES_BLOCK_LEN; - aad = scratch + 4 * AES_BLOCK_LEN; num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); last_len = data_len % AES_BLOCK_LEN; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 4404973..ed3400c 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -136,7 +136,10 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, mutex_lock(&sdata->local->sta_mtx); if (mac_addr) { - sta = sta_info_get_bss(sdata, mac_addr); + if (ieee80211_vif_is_mesh(&sdata->vif)) + sta = sta_info_get(sdata, mac_addr); + else + sta = sta_info_get_bss(sdata, mac_addr); if (!sta) { ieee80211_key_free(sdata->local, key); err = -ENOENT; @@ -330,6 +333,7 @@ static void rate_idx_to_bitrate(struct rate_info *rate, struct sta_info *sta, in static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = sta->sdata; + struct timespec uptime; sinfo->generation = sdata->local->sta_generation; @@ -342,7 +346,12 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) STATION_INFO_TX_FAILED | STATION_INFO_TX_BITRATE | STATION_INFO_RX_BITRATE | - STATION_INFO_RX_DROP_MISC; + STATION_INFO_RX_DROP_MISC | + STATION_INFO_BSS_PARAM | + STATION_INFO_CONNECTED_TIME; + + do_posix_clock_monotonic_gettime(&uptime); + sinfo->connected_time = uptime.tv_sec - sta->last_connected; sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); sinfo->rx_bytes = sta->rx_bytes; @@ -389,6 +398,16 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->plink_state = sta->plink_state; #endif } + + sinfo->bss_param.flags = 0; + if (sdata->vif.bss_conf.use_cts_prot) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT; + if (sdata->vif.bss_conf.use_short_preamble) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE; + if (sdata->vif.bss_conf.use_short_slot) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; + sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period; + sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int; } @@ -675,6 +694,12 @@ static void sta_apply_parameters(struct ieee80211_local *local, if (set & BIT(NL80211_STA_FLAG_MFP)) sta->flags |= WLAN_STA_MFP; } + + if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { + sta->flags &= ~WLAN_STA_AUTH; + if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) + sta->flags |= WLAN_STA_AUTH; + } spin_unlock_irqrestore(&sta->flaglock, flags); /* @@ -712,15 +737,29 @@ static void sta_apply_parameters(struct ieee80211_local *local, params->ht_capa, &sta->sta.ht_cap); - if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) { - switch (params->plink_action) { - case PLINK_ACTION_OPEN: - mesh_plink_open(sta); - break; - case PLINK_ACTION_BLOCK: - mesh_plink_block(sta); - break; - } + if (ieee80211_vif_is_mesh(&sdata->vif)) { +#ifdef CONFIG_MAC80211_MESH + if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) + switch (params->plink_state) { + case PLINK_LISTEN: + case PLINK_ESTAB: + case PLINK_BLOCKED: + sta->plink_state = params->plink_state; + break; + default: + /* nothing */ + break; + } + else + switch (params->plink_action) { + case PLINK_ACTION_OPEN: + mesh_plink_open(sta); + break; + case PLINK_ACTION_BLOCK: + mesh_plink_block(sta); + break; + } +#endif } } @@ -1023,26 +1062,30 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, u8 *new_ie; const u8 *old_ie; - /* first allocate the new vendor information element */ + /* allocate information elements */ new_ie = NULL; - old_ie = ifmsh->vendor_ie; + old_ie = ifmsh->ie; - ifmsh->vendor_ie_len = setup->vendor_ie_len; - if (setup->vendor_ie_len) { - new_ie = kmemdup(setup->vendor_ie, setup->vendor_ie_len, + if (setup->ie_len) { + new_ie = kmemdup(setup->ie, setup->ie_len, GFP_KERNEL); if (!new_ie) return -ENOMEM; } + ifmsh->ie_len = setup->ie_len; + ifmsh->ie = new_ie; + kfree(old_ie); /* now copy the rest of the setup parameters */ ifmsh->mesh_id_len = setup->mesh_id_len; memcpy(ifmsh->mesh_id, setup->mesh_id, ifmsh->mesh_id_len); ifmsh->mesh_pp_id = setup->path_sel_proto; ifmsh->mesh_pm_id = setup->path_metric; - ifmsh->vendor_ie = new_ie; - - kfree(old_ie); + ifmsh->security = IEEE80211_MESH_SEC_NONE; + if (setup->is_authenticated) + ifmsh->security |= IEEE80211_MESH_SEC_AUTHED; + if (setup->is_secure) + ifmsh->security |= IEEE80211_MESH_SEC_SECURED; return 0; } @@ -1275,9 +1318,10 @@ static int ieee80211_set_channel(struct wiphy *wiphy, } #ifdef CONFIG_PM -static int ieee80211_suspend(struct wiphy *wiphy) +static int ieee80211_suspend(struct wiphy *wiphy, + struct cfg80211_wowlan *wowlan) { - return __ieee80211_suspend(wiphy_priv(wiphy)); + return __ieee80211_suspend(wiphy_priv(wiphy), wowlan); } static int ieee80211_resume(struct wiphy *wiphy) @@ -1320,6 +1364,31 @@ static int ieee80211_scan(struct wiphy *wiphy, return ieee80211_request_scan(sdata, req); } +static int +ieee80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_sched_scan_request *req) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (!sdata->local->ops->sched_scan_start) + return -EOPNOTSUPP; + + return ieee80211_request_sched_scan_start(sdata, req); +} + +static int +ieee80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev, + bool driver_initiated) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (!sdata->local->ops->sched_scan_stop) + return -EOPNOTSUPP; + + return ieee80211_request_sched_scan_stop(sdata, driver_initiated); +} + static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_auth_request *req) { @@ -1611,16 +1680,13 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - int i; - - /* - * This _could_ be supported by providing a hook for - * drivers for this function, but at this point it - * doesn't seem worth bothering. - */ - if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) - return -EOPNOTSUPP; + int i, ret; + if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) { + ret = drv_set_bitrate_mask(local, sdata, mask); + if (ret) + return ret; + } for (i = 0; i < IEEE80211_NUM_BANDS; i++) sdata->rc_rateidx_mask[i] = mask->control[i].legacy; @@ -2064,6 +2130,8 @@ struct cfg80211_ops mac80211_config_ops = { .suspend = ieee80211_suspend, .resume = ieee80211_resume, .scan = ieee80211_scan, + .sched_scan_start = ieee80211_sched_scan_start, + .sched_scan_stop = ieee80211_sched_scan_stop, .auth = ieee80211_auth, .assoc = ieee80211_assoc, .deauth = ieee80211_deauth, diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 51f0d78..186e02f 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -37,7 +37,7 @@ int mac80211_format_buffer(char __user *userbuf, size_t count, return simple_read_from_buffer(userbuf, count, ppos, buf, res); } -#define DEBUGFS_READONLY_FILE(name, fmt, value...) \ +#define DEBUGFS_READONLY_FILE_FN(name, fmt, value...) \ static ssize_t name## _read(struct file *file, char __user *userbuf, \ size_t count, loff_t *ppos) \ { \ @@ -45,14 +45,19 @@ static ssize_t name## _read(struct file *file, char __user *userbuf, \ \ return mac80211_format_buffer(userbuf, count, ppos, \ fmt "\n", ##value); \ -} \ - \ +} + +#define DEBUGFS_READONLY_FILE_OPS(name) \ static const struct file_operations name## _ops = { \ .read = name## _read, \ .open = mac80211_open_file_generic, \ .llseek = generic_file_llseek, \ }; +#define DEBUGFS_READONLY_FILE(name, fmt, value...) \ + DEBUGFS_READONLY_FILE_FN(name, fmt, value) \ + DEBUGFS_READONLY_FILE_OPS(name) + #define DEBUGFS_ADD(name) \ debugfs_create_file(#name, 0400, phyd, local, &name## _ops); @@ -130,7 +135,7 @@ static ssize_t reset_write(struct file *file, const char __user *user_buf, struct ieee80211_local *local = file->private_data; rtnl_lock(); - __ieee80211_suspend(&local->hw); + __ieee80211_suspend(&local->hw, NULL); __ieee80211_resume(&local->hw); rtnl_unlock(); @@ -291,11 +296,70 @@ static ssize_t channel_type_read(struct file *file, char __user *user_buf, return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); } -static const struct file_operations channel_type_ops = { - .read = channel_type_read, - .open = mac80211_open_file_generic, - .llseek = default_llseek, -}; +static ssize_t hwflags_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_local *local = file->private_data; + int mxln = 500; + ssize_t rv; + char *buf = kzalloc(mxln, GFP_KERNEL); + int sf = 0; /* how many written so far */ + + sf += snprintf(buf, mxln - sf, "0x%x\n", local->hw.flags); + if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) + sf += snprintf(buf + sf, mxln - sf, "HAS_RATE_CONTROL\n"); + if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) + sf += snprintf(buf + sf, mxln - sf, "RX_INCLUDES_FCS\n"); + if (local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING) + sf += snprintf(buf + sf, mxln - sf, + "HOST_BCAST_PS_BUFFERING\n"); + if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE) + sf += snprintf(buf + sf, mxln - sf, + "2GHZ_SHORT_SLOT_INCAPABLE\n"); + if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE) + sf += snprintf(buf + sf, mxln - sf, + "2GHZ_SHORT_PREAMBLE_INCAPABLE\n"); + if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) + sf += snprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n"); + if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) + sf += snprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n"); + if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) + sf += snprintf(buf + sf, mxln - sf, "NEED_DTIM_PERIOD\n"); + if (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT) + sf += snprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n"); + if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) + sf += snprintf(buf + sf, mxln - sf, "AMPDU_AGGREGATION\n"); + if (local->hw.flags & IEEE80211_HW_SUPPORTS_PS) + sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PS\n"); + if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) + sf += snprintf(buf + sf, mxln - sf, "PS_NULLFUNC_STACK\n"); + if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) + sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n"); + if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE) + sf += snprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n"); + if (local->hw.flags & IEEE80211_HW_BEACON_FILTER) + sf += snprintf(buf + sf, mxln - sf, "BEACON_FILTER\n"); + if (local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS) + sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n"); + if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) + sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_SMPS\n"); + if (local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD) + sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n"); + if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) + sf += snprintf(buf + sf, mxln - sf, "REPORTS_TX_ACK_STATUS\n"); + if (local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) + sf += snprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n"); + if (local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) + sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_CQM_RSSI\n"); + if (local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK) + sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n"); + if (local->hw.flags & IEEE80211_HW_AP_LINK_PS) + sf += snprintf(buf + sf, mxln - sf, "AP_LINK_PS\n"); + + rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); + kfree(buf); + return rv; +} static ssize_t queues_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -315,11 +379,9 @@ static ssize_t queues_read(struct file *file, char __user *user_buf, return simple_read_from_buffer(user_buf, count, ppos, buf, res); } -static const struct file_operations queues_ops = { - .read = queues_read, - .open = mac80211_open_file_generic, - .llseek = default_llseek, -}; +DEBUGFS_READONLY_FILE_OPS(hwflags); +DEBUGFS_READONLY_FILE_OPS(channel_type); +DEBUGFS_READONLY_FILE_OPS(queues); /* statistics stuff */ @@ -395,6 +457,7 @@ void debugfs_hw_add(struct ieee80211_local *local) DEBUGFS_ADD(uapsd_queues); DEBUGFS_ADD(uapsd_max_sp_len); DEBUGFS_ADD(channel_type); + DEBUGFS_ADD(hwflags); DEBUGFS_ADD(user_power); DEBUGFS_ADD(power); diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index c04a139..a01d213 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -92,6 +92,31 @@ static ssize_t sta_inactive_ms_read(struct file *file, char __user *userbuf, } STA_OPS(inactive_ms); + +static ssize_t sta_connected_time_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct sta_info *sta = file->private_data; + struct timespec uptime; + struct tm result; + long connected_time_secs; + char buf[100]; + int res; + do_posix_clock_monotonic_gettime(&uptime); + connected_time_secs = uptime.tv_sec - sta->last_connected; + time_to_tm(connected_time_secs, 0, &result); + result.tm_year -= 70; + result.tm_mday -= 1; + res = scnprintf(buf, sizeof(buf), + "years - %ld\nmonths - %d\ndays - %d\nclock - %d:%d:%d\n\n", + result.tm_year, result.tm_mon, result.tm_mday, + result.tm_hour, result.tm_min, result.tm_sec); + return simple_read_from_buffer(userbuf, count, ppos, buf, res); +} +STA_OPS(connected_time); + + + static ssize_t sta_last_seq_ctrl_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { @@ -324,6 +349,7 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) DEBUGFS_ADD(flags); DEBUGFS_ADD(num_ps_buf_frames); DEBUGFS_ADD(inactive_ms); + DEBUGFS_ADD(connected_time); DEBUGFS_ADD(last_seq_ctrl); DEBUGFS_ADD(agg_status); DEBUGFS_ADD(dev); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 9c0d62b..eebf7a6 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -41,6 +41,33 @@ static inline void drv_stop(struct ieee80211_local *local) local->started = false; } +#ifdef CONFIG_PM +static inline int drv_suspend(struct ieee80211_local *local, + struct cfg80211_wowlan *wowlan) +{ + int ret; + + might_sleep(); + + trace_drv_suspend(local); + ret = local->ops->suspend(&local->hw, wowlan); + trace_drv_return_int(local, ret); + return ret; +} + +static inline int drv_resume(struct ieee80211_local *local) +{ + int ret; + + might_sleep(); + + trace_drv_resume(local); + ret = local->ops->resume(&local->hw); + trace_drv_return_int(local, ret); + return ret; +} +#endif + static inline int drv_add_interface(struct ieee80211_local *local, struct ieee80211_vif *vif) { @@ -185,12 +212,39 @@ static inline int drv_hw_scan(struct ieee80211_local *local, might_sleep(); - trace_drv_hw_scan(local, sdata, req); + trace_drv_hw_scan(local, sdata); ret = local->ops->hw_scan(&local->hw, &sdata->vif, req); trace_drv_return_int(local, ret); return ret; } +static inline int +drv_sched_scan_start(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies) +{ + int ret; + + might_sleep(); + + trace_drv_sched_scan_start(local, sdata); + ret = local->ops->sched_scan_start(&local->hw, &sdata->vif, + req, ies); + trace_drv_return_int(local, ret); + return ret; +} + +static inline void drv_sched_scan_stop(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + might_sleep(); + + trace_drv_sched_scan_stop(local, sdata); + local->ops->sched_scan_stop(&local->hw, &sdata->vif); + trace_drv_return_void(local); +} + static inline void drv_sw_scan_start(struct ieee80211_local *local) { might_sleep(); @@ -552,4 +606,35 @@ static inline void drv_get_ringparam(struct ieee80211_local *local, trace_drv_return_void(local); } +static inline bool drv_tx_frames_pending(struct ieee80211_local *local) +{ + bool ret = false; + + might_sleep(); + + trace_drv_tx_frames_pending(local); + if (local->ops->tx_frames_pending) + ret = local->ops->tx_frames_pending(&local->hw); + trace_drv_return_bool(local, ret); + + return ret; +} + +static inline int drv_set_bitrate_mask(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const struct cfg80211_bitrate_mask *mask) +{ + int ret = -EOPNOTSUPP; + + might_sleep(); + + trace_drv_set_bitrate_mask(local, sdata, mask); + if (local->ops->set_bitrate_mask) + ret = local->ops->set_bitrate_mask(&local->hw, + &sdata->vif, mask); + trace_drv_return_int(local, ret); + + return ret; +} + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 45aab80..ed9edcb 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -55,6 +55,70 @@ DECLARE_EVENT_CLASS(local_only_evt, TP_printk(LOCAL_PR_FMT, LOCAL_PR_ARG) ); +DECLARE_EVENT_CLASS(local_sdata_addr_evt, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __array(char, addr, 6) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + memcpy(__entry->addr, sdata->vif.addr, 6); + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT " addr:%pM", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr + ) +); + +DECLARE_EVENT_CLASS(local_u32_evt, + TP_PROTO(struct ieee80211_local *local, u32 value), + TP_ARGS(local, value), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u32, value) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->value = value; + ), + + TP_printk( + LOCAL_PR_FMT " value:%d", + LOCAL_PR_ARG, __entry->value + ) +); + +DECLARE_EVENT_CLASS(local_sdata_evt, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT, + LOCAL_PR_ARG, VIF_PR_ARG + ) +); + DEFINE_EVENT(local_only_evt, drv_return_void, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) @@ -74,6 +138,21 @@ TRACE_EVENT(drv_return_int, TP_printk(LOCAL_PR_FMT " - %d", LOCAL_PR_ARG, __entry->ret) ); +TRACE_EVENT(drv_return_bool, + TP_PROTO(struct ieee80211_local *local, bool ret), + TP_ARGS(local, ret), + TP_STRUCT__entry( + LOCAL_ENTRY + __field(bool, ret) + ), + TP_fast_assign( + LOCAL_ASSIGN; + __entry->ret = ret; + ), + TP_printk(LOCAL_PR_FMT " - %s", LOCAL_PR_ARG, (__entry->ret) ? + "true" : "false") +); + TRACE_EVENT(drv_return_u64, TP_PROTO(struct ieee80211_local *local, u64 ret), TP_ARGS(local, ret), @@ -93,33 +172,25 @@ DEFINE_EVENT(local_only_evt, drv_start, TP_ARGS(local) ); +DEFINE_EVENT(local_only_evt, drv_suspend, + TP_PROTO(struct ieee80211_local *local), + TP_ARGS(local) +); + +DEFINE_EVENT(local_only_evt, drv_resume, + TP_PROTO(struct ieee80211_local *local), + TP_ARGS(local) +); + DEFINE_EVENT(local_only_evt, drv_stop, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) ); -TRACE_EVENT(drv_add_interface, +DEFINE_EVENT(local_sdata_addr_evt, drv_add_interface, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), - - TP_ARGS(local, sdata), - - TP_STRUCT__entry( - LOCAL_ENTRY - VIF_ENTRY - __array(char, addr, 6) - ), - - TP_fast_assign( - LOCAL_ASSIGN; - VIF_ASSIGN; - memcpy(__entry->addr, sdata->vif.addr, 6); - ), - - TP_printk( - LOCAL_PR_FMT VIF_PR_FMT " addr:%pM", - LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr - ) + TP_ARGS(local, sdata) ); TRACE_EVENT(drv_change_interface, @@ -150,27 +221,10 @@ TRACE_EVENT(drv_change_interface, ) ); -TRACE_EVENT(drv_remove_interface, - TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), - - TP_ARGS(local, sdata), - - TP_STRUCT__entry( - LOCAL_ENTRY - VIF_ENTRY - __array(char, addr, 6) - ), - - TP_fast_assign( - LOCAL_ASSIGN; - VIF_ASSIGN; - memcpy(__entry->addr, sdata->vif.addr, 6); - ), - - TP_printk( - LOCAL_PR_FMT VIF_PR_FMT " addr:%pM", - LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr - ) +DEFINE_EVENT(local_sdata_addr_evt, drv_remove_interface, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) ); TRACE_EVENT(drv_config, @@ -400,27 +454,22 @@ TRACE_EVENT(drv_update_tkip_key, ) ); -TRACE_EVENT(drv_hw_scan, +DEFINE_EVENT(local_sdata_evt, drv_hw_scan, TP_PROTO(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, - struct cfg80211_scan_request *req), - - TP_ARGS(local, sdata, req), - - TP_STRUCT__entry( - LOCAL_ENTRY - VIF_ENTRY - ), + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) +); - TP_fast_assign( - LOCAL_ASSIGN; - VIF_ASSIGN; - ), +DEFINE_EVENT(local_sdata_evt, drv_sched_scan_start, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) +); - TP_printk( - LOCAL_PR_FMT VIF_PR_FMT, - LOCAL_PR_ARG,VIF_PR_ARG - ) +DEFINE_EVENT(local_sdata_evt, drv_sched_scan_stop, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) ); DEFINE_EVENT(local_only_evt, drv_sw_scan_start, @@ -489,46 +538,14 @@ TRACE_EVENT(drv_get_tkip_seq, ) ); -TRACE_EVENT(drv_set_frag_threshold, +DEFINE_EVENT(local_u32_evt, drv_set_frag_threshold, TP_PROTO(struct ieee80211_local *local, u32 value), - - TP_ARGS(local, value), - - TP_STRUCT__entry( - LOCAL_ENTRY - __field(u32, value) - ), - - TP_fast_assign( - LOCAL_ASSIGN; - __entry->value = value; - ), - - TP_printk( - LOCAL_PR_FMT " value:%d", - LOCAL_PR_ARG, __entry->value - ) + TP_ARGS(local, value) ); -TRACE_EVENT(drv_set_rts_threshold, +DEFINE_EVENT(local_u32_evt, drv_set_rts_threshold, TP_PROTO(struct ieee80211_local *local, u32 value), - - TP_ARGS(local, value), - - TP_STRUCT__entry( - LOCAL_ENTRY - __field(u32, value) - ), - - TP_fast_assign( - LOCAL_ASSIGN; - __entry->value = value; - ), - - TP_printk( - LOCAL_PR_FMT " value:%d", - LOCAL_PR_ARG, __entry->value - ) + TP_ARGS(local, value) ); TRACE_EVENT(drv_set_coverage_class, @@ -964,11 +981,43 @@ TRACE_EVENT(drv_get_ringparam, ) ); +DEFINE_EVENT(local_only_evt, drv_tx_frames_pending, + TP_PROTO(struct ieee80211_local *local), + TP_ARGS(local) +); + DEFINE_EVENT(local_only_evt, drv_offchannel_tx_cancel_wait, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) ); +TRACE_EVENT(drv_set_bitrate_mask, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const struct cfg80211_bitrate_mask *mask), + + TP_ARGS(local, sdata, mask), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __field(u32, legacy_2g) + __field(u32, legacy_5g) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + __entry->legacy_2g = mask->control[IEEE80211_BAND_2GHZ].legacy; + __entry->legacy_5g = mask->control[IEEE80211_BAND_5GHZ].legacy; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT " 2G Mask:0x%x 5G Mask:0x%x", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->legacy_2g, __entry->legacy_5g + ) +); + /* * Tracing for API calls that drivers call. */ @@ -1147,6 +1196,42 @@ TRACE_EVENT(api_scan_completed, ) ); +TRACE_EVENT(api_sched_scan_results, + TP_PROTO(struct ieee80211_local *local), + + TP_ARGS(local), + + TP_STRUCT__entry( + LOCAL_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + ), + + TP_printk( + LOCAL_PR_FMT, LOCAL_PR_ARG + ) +); + +TRACE_EVENT(api_sched_scan_stopped, + TP_PROTO(struct ieee80211_local *local), + + TP_ARGS(local), + + TP_STRUCT__entry( + LOCAL_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + ), + + TP_printk( + LOCAL_PR_FMT, LOCAL_PR_ARG + ) +); + TRACE_EVENT(api_sta_block_awake, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sta *sta, bool block), diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 3e81af1..b81860c 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -40,7 +40,7 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) { - u16 auth_alg, auth_transaction, status_code; + u16 auth_alg, auth_transaction; lockdep_assert_held(&sdata->u.ibss.mtx); @@ -49,7 +49,6 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); - status_code = le16_to_cpu(mgmt->u.auth.status_code); /* * IEEE 802.11 standard does not require authentication in IBSS @@ -527,8 +526,6 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata) static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; - struct ieee80211_local *local = sdata->local; - struct ieee80211_supported_band *sband; u8 bssid[ETH_ALEN]; u16 capability; int i; @@ -551,8 +548,6 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID %pM\n", sdata->name, bssid); - sband = local->hw.wiphy->bands[ifibss->channel->band]; - capability = WLAN_CAPABILITY_IBSS; if (ifibss->privacy) @@ -661,7 +656,6 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, struct sk_buff *req) { - struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(req); struct ieee80211_mgmt *mgmt = (void *)req->data; struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_local *local = sdata->local; @@ -685,7 +679,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, mgmt->bssid, tx_last_beacon); #endif /* CONFIG_MAC80211_IBSS_DEBUG */ - if (!tx_last_beacon && !(rx_status->rx_flags & IEEE80211_RX_RA_MATCH)) + if (!tx_last_beacon && is_multicast_ether_addr(mgmt->da)) return; if (memcmp(mgmt->bssid, ifibss->bssid, ETH_ALEN) != 0 && diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a404017..6f55a78 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -488,8 +488,13 @@ struct ieee80211_if_mesh { struct mesh_config mshcfg; u32 mesh_seqnum; bool accepting_plinks; - const u8 *vendor_ie; - u8 vendor_ie_len; + const u8 *ie; + u8 ie_len; + enum { + IEEE80211_MESH_SEC_NONE = 0x0, + IEEE80211_MESH_SEC_AUTHED = 0x1, + IEEE80211_MESH_SEC_SECURED = 0x2, + } security; }; #ifdef CONFIG_MAC80211_MESH @@ -763,8 +768,14 @@ struct ieee80211_local { /* device is started */ bool started; + /* wowlan is enabled -- don't reconfig on resume */ + bool wowlan; + int tx_headroom; /* required headroom for hardware/radiotap */ + /* count for keys needing tailroom space allocation */ + int crypto_tx_tailroom_needed_cnt; + /* Tasklet and skb queue to process calls from IRQ mode. All frames * added to skb_queue will be processed, but frames in * skb_queue_unreliable may be dropped if the total length of these @@ -809,8 +820,8 @@ struct ieee80211_local { struct rate_control_ref *rate_ctrl; - struct crypto_blkcipher *wep_tx_tfm; - struct crypto_blkcipher *wep_rx_tfm; + struct crypto_cipher *wep_tx_tfm; + struct crypto_cipher *wep_rx_tfm; u32 wep_iv; /* see iface.c */ @@ -836,6 +847,9 @@ struct ieee80211_local { int scan_channel_idx; int scan_ies_len; + bool sched_scanning; + struct ieee80211_sched_scan_ies sched_scan_ies; + unsigned long leave_oper_channel_time; enum mac80211_scan_state next_scan_state; struct delayed_work scan_work; @@ -1143,6 +1157,12 @@ ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq, void ieee80211_rx_bss_put(struct ieee80211_local *local, struct ieee80211_bss *bss); +/* scheduled scan handling */ +int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, + struct cfg80211_sched_scan_request *req); +int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata, + bool driver_initiated); + /* off-channel helpers */ bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local); void ieee80211_offchannel_enable_all_ps(struct ieee80211_local *local, @@ -1246,7 +1266,8 @@ int ieee80211_reconfig(struct ieee80211_local *local); void ieee80211_stop_device(struct ieee80211_local *local); #ifdef CONFIG_PM -int __ieee80211_suspend(struct ieee80211_hw *hw); +int __ieee80211_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan); static inline int __ieee80211_resume(struct ieee80211_hw *hw) { @@ -1259,7 +1280,8 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw) return ieee80211_reconfig(hw_to_local(hw)); } #else -static inline int __ieee80211_suspend(struct ieee80211_hw *hw) +static inline int __ieee80211_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) { return 0; } diff --git a/net/mac80211/key.c b/net/mac80211/key.c index af3c564..b510721 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -101,6 +101,11 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) if (!ret) { key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; + + if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) || + (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV))) + key->local->crypto_tx_tailroom_needed_cnt--; + return 0; } @@ -156,6 +161,10 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; + + if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) || + (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV))) + key->local->crypto_tx_tailroom_needed_cnt++; } void ieee80211_key_removed(struct ieee80211_key_conf *key_conf) @@ -388,8 +397,10 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key) ieee80211_aes_key_free(key->u.ccmp.tfm); if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC) ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm); - if (key->local) + if (key->local) { ieee80211_debugfs_key_remove(key); + key->local->crypto_tx_tailroom_needed_cnt--; + } kfree(key); } @@ -451,6 +462,8 @@ int ieee80211_key_link(struct ieee80211_key *key, ieee80211_debugfs_key_add(key); + key->local->crypto_tx_tailroom_needed_cnt++; + ret = ieee80211_key_enable_hw_accel(key); mutex_unlock(&sdata->local->key_mtx); @@ -492,8 +505,12 @@ void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata) mutex_lock(&sdata->local->key_mtx); - list_for_each_entry(key, &sdata->key_list, list) + sdata->local->crypto_tx_tailroom_needed_cnt = 0; + + list_for_each_entry(key, &sdata->key_list, list) { + sdata->local->crypto_tx_tailroom_needed_cnt++; ieee80211_key_enable_hw_accel(key); + } mutex_unlock(&sdata->local->key_mtx); } diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 562d298..30e6a68 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -33,12 +33,6 @@ #include "cfg.h" #include "debugfs.h" - -static bool ieee80211_disable_40mhz_24ghz; -module_param(ieee80211_disable_40mhz_24ghz, bool, 0644); -MODULE_PARM_DESC(ieee80211_disable_40mhz_24ghz, - "Disable 40MHz support in the 2.4GHz band"); - static struct lock_class_key ieee80211_rx_skb_queue_class; void ieee80211_configure_filter(struct ieee80211_local *local) @@ -364,7 +358,8 @@ static void ieee80211_restart_work(struct work_struct *work) flush_workqueue(local->workqueue); mutex_lock(&local->mtx); - WARN(test_bit(SCAN_HW_SCANNING, &local->scanning), + WARN(test_bit(SCAN_HW_SCANNING, &local->scanning) || + local->sched_scanning, "%s called with hardware scan in progress\n", __func__); mutex_unlock(&local->mtx); @@ -545,7 +540,9 @@ ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = { }, [NL80211_IFTYPE_MESH_POINT] = { .tx = 0xffff, - .rx = BIT(IEEE80211_STYPE_ACTION >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | + BIT(IEEE80211_STYPE_DEAUTH >> 4), }, }; @@ -584,8 +581,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, wiphy->flags |= WIPHY_FLAG_NETNS_OK | WIPHY_FLAG_4ADDR_AP | - WIPHY_FLAG_4ADDR_STATION | - WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS; + WIPHY_FLAG_4ADDR_STATION; if (!ops->set_key) wiphy->flags |= WIPHY_FLAG_IBSS_RSN; @@ -701,6 +697,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) WLAN_CIPHER_SUITE_AES_CMAC }; + if ((hw->wiphy->wowlan.flags || hw->wiphy->wowlan.n_patterns) +#ifdef CONFIG_PM + && (!local->ops->suspend || !local->ops->resume) +#endif + ) + return -EINVAL; + if (hw->max_report_rates == 0) hw->max_report_rates = hw->max_rates; @@ -726,18 +729,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) } channels += sband->n_channels; - /* - * Since ieee80211_disable_40mhz_24ghz is global, we can - * modify the sband's ht data even if the driver uses a - * global structure for that. - */ - if (ieee80211_disable_40mhz_24ghz && - band == IEEE80211_BAND_2GHZ && - sband->ht_cap.ht_supported) { - sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; - sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40; - } - if (max_bitrates < sband->n_bitrates) max_bitrates = sband->n_bitrates; supp_ht = supp_ht || sband->ht_cap.ht_supported; @@ -760,6 +751,11 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) local->hw.wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MESH_POINT); #endif + /* if the underlying driver supports mesh, mac80211 will (at least) + * provide routing of mesh authentication frames to userspace */ + if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_MESH_POINT)) + local->hw.wiphy->flags |= WIPHY_FLAG_MESH_AUTH; + /* mac80211 supports control port protocol changing */ local->hw.wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL; @@ -838,6 +834,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (!local->ops->remain_on_channel) local->hw.wiphy->max_remain_on_channel_duration = 5000; + if (local->ops->sched_scan_start) + local->hw.wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; + result = wiphy_register(local->hw.wiphy); if (result < 0) goto fail_wiphy_register; @@ -879,10 +878,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) local->dynamic_ps_forced_timeout = -1; - result = sta_info_start(local); - if (result < 0) - goto fail_sta_info; - result = ieee80211_wep_init(local); if (result < 0) wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n", @@ -945,7 +940,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) rtnl_unlock(); ieee80211_wep_free(local); sta_info_stop(local); - fail_sta_info: destroy_workqueue(local->workqueue); fail_workqueue: wiphy_unregister(local->hw.wiphy); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 2a57cc0..75378e8 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -279,9 +279,9 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; *pos++ = 0x00; - if (sdata->u.mesh.vendor_ie) { - int len = sdata->u.mesh.vendor_ie_len; - const u8 *data = sdata->u.mesh.vendor_ie; + if (sdata->u.mesh.ie) { + int len = sdata->u.mesh.ie_len; + const u8 *data = sdata->u.mesh.ie; if (skb_tailroom(skb) > len) memcpy(skb_put(skb, len), data, len); } @@ -573,6 +573,10 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, &elems); + /* ignore beacons from secure mesh peers if our security is off */ + if (elems.rsn_len && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) + return; + if (elems.ds_params && elems.ds_params_len == 1) freq = ieee80211_channel_to_frequency(elems.ds_params[0], band); else @@ -586,9 +590,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, if (elems.mesh_id && elems.mesh_config && mesh_matches_local(&elems, sdata)) { supp_rates = ieee80211_sta_get_rates(local, &elems, band); - - mesh_neighbour_update(mgmt->sa, supp_rates, sdata, - mesh_peer_accepts_plinks(&elems)); + mesh_neighbour_update(mgmt->sa, supp_rates, sdata, &elems); } } @@ -598,7 +600,7 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata, struct ieee80211_rx_status *rx_status) { switch (mgmt->u.action.category) { - case WLAN_CATEGORY_MESH_PLINK: + case WLAN_CATEGORY_MESH_ACTION: mesh_rx_plink_frame(sdata, mgmt, len, rx_status); break; case WLAN_CATEGORY_MESH_PATH_SEL: @@ -611,12 +613,9 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_rx_status *rx_status; - struct ieee80211_if_mesh *ifmsh; struct ieee80211_mgmt *mgmt; u16 stype; - ifmsh = &sdata->u.mesh; - rx_status = IEEE80211_SKB_RXCB(skb); mgmt = (struct ieee80211_mgmt *) skb->data; stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE; diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index b99e230..10acf1c 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -226,7 +226,8 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata); /* Mesh plinks */ void mesh_neighbour_update(u8 *hw_addr, u32 rates, - struct ieee80211_sub_if_data *sdata, bool add); + struct ieee80211_sub_if_data *sdata, + struct ieee802_11_elems *ie); bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie); void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata); void mesh_plink_broken(struct sta_info *sta); diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 5bf64d7..849fecd 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -391,7 +391,6 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, (mpath->flags & MESH_PATH_SN_VALID)) { if (SN_GT(mpath->sn, orig_sn) || (mpath->sn == orig_sn && - action == MPATH_PREQ && new_metric >= mpath->metric)) { process = false; fresh_info = false; @@ -633,7 +632,6 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata, struct mesh_path *mpath; u8 ttl; u8 *ta, *target_addr; - u8 target_flags; u32 target_sn; u16 target_rcode; @@ -644,7 +642,6 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata, return; } ttl--; - target_flags = PERR_IE_TARGET_FLAGS(perr_elem); target_addr = PERR_IE_TARGET_ADDR(perr_elem); target_sn = PERR_IE_TARGET_SN(perr_elem); target_rcode = PERR_IE_TARGET_RCODE(perr_elem); @@ -675,12 +672,10 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct mesh_path *mpath; - u8 *ta; u8 ttl, flags, hopcount; u8 *orig_addr; u32 orig_sn, metric; - ta = mgmt->sa; ttl = rann->rann_ttl; if (ttl <= 1) { ifmsh->mshstats.dropped_frames_ttl++; diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 8d65b47..c7cb3cc 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -55,52 +55,46 @@ void mesh_table_free(struct mesh_table *tbl, bool free_leafs) mesh_hash = tbl->hash_buckets; for (i = 0; i <= tbl->hash_mask; i++) { - spin_lock(&tbl->hashwlock[i]); + spin_lock_bh(&tbl->hashwlock[i]); hlist_for_each_safe(p, q, &mesh_hash[i]) { tbl->free_node(p, free_leafs); atomic_dec(&tbl->entries); } - spin_unlock(&tbl->hashwlock[i]); + spin_unlock_bh(&tbl->hashwlock[i]); } __mesh_table_free(tbl); } -static struct mesh_table *mesh_table_grow(struct mesh_table *tbl) +static int mesh_table_grow(struct mesh_table *oldtbl, + struct mesh_table *newtbl) { - struct mesh_table *newtbl; struct hlist_head *oldhash; struct hlist_node *p, *q; int i; - if (atomic_read(&tbl->entries) - < tbl->mean_chain_len * (tbl->hash_mask + 1)) - goto endgrow; + if (atomic_read(&oldtbl->entries) + < oldtbl->mean_chain_len * (oldtbl->hash_mask + 1)) + return -EAGAIN; - newtbl = mesh_table_alloc(tbl->size_order + 1); - if (!newtbl) - goto endgrow; - - newtbl->free_node = tbl->free_node; - newtbl->mean_chain_len = tbl->mean_chain_len; - newtbl->copy_node = tbl->copy_node; - atomic_set(&newtbl->entries, atomic_read(&tbl->entries)); + newtbl->free_node = oldtbl->free_node; + newtbl->mean_chain_len = oldtbl->mean_chain_len; + newtbl->copy_node = oldtbl->copy_node; + atomic_set(&newtbl->entries, atomic_read(&oldtbl->entries)); - oldhash = tbl->hash_buckets; - for (i = 0; i <= tbl->hash_mask; i++) + oldhash = oldtbl->hash_buckets; + for (i = 0; i <= oldtbl->hash_mask; i++) hlist_for_each(p, &oldhash[i]) - if (tbl->copy_node(p, newtbl) < 0) + if (oldtbl->copy_node(p, newtbl) < 0) goto errcopy; - return newtbl; + return 0; errcopy: for (i = 0; i <= newtbl->hash_mask; i++) { hlist_for_each_safe(p, q, &newtbl->hash_buckets[i]) - tbl->free_node(p, 0); + oldtbl->free_node(p, 0); } - __mesh_table_free(newtbl); -endgrow: - return NULL; + return -ENOMEM; } @@ -280,7 +274,7 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) if (!new_node) goto err_node_alloc; - read_lock(&pathtbl_resize_lock); + read_lock_bh(&pathtbl_resize_lock); memcpy(new_mpath->dst, dst, ETH_ALEN); new_mpath->sdata = sdata; new_mpath->flags = 0; @@ -295,7 +289,7 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) hash_idx = mesh_table_hash(dst, sdata, mesh_paths); bucket = &mesh_paths->hash_buckets[hash_idx]; - spin_lock(&mesh_paths->hashwlock[hash_idx]); + spin_lock_bh(&mesh_paths->hashwlock[hash_idx]); err = -EEXIST; hlist_for_each_entry(node, n, bucket, list) { @@ -311,8 +305,8 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) mesh_paths_generation++; - spin_unlock(&mesh_paths->hashwlock[hash_idx]); - read_unlock(&pathtbl_resize_lock); + spin_unlock_bh(&mesh_paths->hashwlock[hash_idx]); + read_unlock_bh(&pathtbl_resize_lock); if (grow) { set_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags); ieee80211_queue_work(&local->hw, &sdata->work); @@ -320,8 +314,8 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) return 0; err_exists: - spin_unlock(&mesh_paths->hashwlock[hash_idx]); - read_unlock(&pathtbl_resize_lock); + spin_unlock_bh(&mesh_paths->hashwlock[hash_idx]); + read_unlock_bh(&pathtbl_resize_lock); kfree(new_node); err_node_alloc: kfree(new_mpath); @@ -334,15 +328,21 @@ void mesh_mpath_table_grow(void) { struct mesh_table *oldtbl, *newtbl; - write_lock(&pathtbl_resize_lock); + rcu_read_lock(); + newtbl = mesh_table_alloc(rcu_dereference(mesh_paths)->size_order + 1); + if (!newtbl) + return; + write_lock_bh(&pathtbl_resize_lock); oldtbl = mesh_paths; - newtbl = mesh_table_grow(mesh_paths); - if (!newtbl) { - write_unlock(&pathtbl_resize_lock); + if (mesh_table_grow(mesh_paths, newtbl) < 0) { + rcu_read_unlock(); + __mesh_table_free(newtbl); + write_unlock_bh(&pathtbl_resize_lock); return; } + rcu_read_unlock(); rcu_assign_pointer(mesh_paths, newtbl); - write_unlock(&pathtbl_resize_lock); + write_unlock_bh(&pathtbl_resize_lock); synchronize_rcu(); mesh_table_free(oldtbl, false); @@ -352,15 +352,21 @@ void mesh_mpp_table_grow(void) { struct mesh_table *oldtbl, *newtbl; - write_lock(&pathtbl_resize_lock); + rcu_read_lock(); + newtbl = mesh_table_alloc(rcu_dereference(mpp_paths)->size_order + 1); + if (!newtbl) + return; + write_lock_bh(&pathtbl_resize_lock); oldtbl = mpp_paths; - newtbl = mesh_table_grow(mpp_paths); - if (!newtbl) { - write_unlock(&pathtbl_resize_lock); + if (mesh_table_grow(mpp_paths, newtbl) < 0) { + rcu_read_unlock(); + __mesh_table_free(newtbl); + write_unlock_bh(&pathtbl_resize_lock); return; } + rcu_read_unlock(); rcu_assign_pointer(mpp_paths, newtbl); - write_unlock(&pathtbl_resize_lock); + write_unlock_bh(&pathtbl_resize_lock); synchronize_rcu(); mesh_table_free(oldtbl, false); @@ -394,7 +400,7 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) if (!new_node) goto err_node_alloc; - read_lock(&pathtbl_resize_lock); + read_lock_bh(&pathtbl_resize_lock); memcpy(new_mpath->dst, dst, ETH_ALEN); memcpy(new_mpath->mpp, mpp, ETH_ALEN); new_mpath->sdata = sdata; @@ -407,7 +413,7 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) hash_idx = mesh_table_hash(dst, sdata, mpp_paths); bucket = &mpp_paths->hash_buckets[hash_idx]; - spin_lock(&mpp_paths->hashwlock[hash_idx]); + spin_lock_bh(&mpp_paths->hashwlock[hash_idx]); err = -EEXIST; hlist_for_each_entry(node, n, bucket, list) { @@ -421,8 +427,8 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) mpp_paths->mean_chain_len * (mpp_paths->hash_mask + 1)) grow = 1; - spin_unlock(&mpp_paths->hashwlock[hash_idx]); - read_unlock(&pathtbl_resize_lock); + spin_unlock_bh(&mpp_paths->hashwlock[hash_idx]); + read_unlock_bh(&pathtbl_resize_lock); if (grow) { set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags); ieee80211_queue_work(&local->hw, &sdata->work); @@ -430,8 +436,8 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) return 0; err_exists: - spin_unlock(&mpp_paths->hashwlock[hash_idx]); - read_unlock(&pathtbl_resize_lock); + spin_unlock_bh(&mpp_paths->hashwlock[hash_idx]); + read_unlock_bh(&pathtbl_resize_lock); kfree(new_node); err_node_alloc: kfree(new_mpath); @@ -544,11 +550,11 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) int hash_idx; int err = 0; - read_lock(&pathtbl_resize_lock); + read_lock_bh(&pathtbl_resize_lock); hash_idx = mesh_table_hash(addr, sdata, mesh_paths); bucket = &mesh_paths->hash_buckets[hash_idx]; - spin_lock(&mesh_paths->hashwlock[hash_idx]); + spin_lock_bh(&mesh_paths->hashwlock[hash_idx]); hlist_for_each_entry(node, n, bucket, list) { mpath = node->mpath; if (mpath->sdata == sdata && @@ -566,8 +572,8 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) err = -ENXIO; enddel: mesh_paths_generation++; - spin_unlock(&mesh_paths->hashwlock[hash_idx]); - read_unlock(&pathtbl_resize_lock); + spin_unlock_bh(&mesh_paths->hashwlock[hash_idx]); + read_unlock_bh(&pathtbl_resize_lock); return err; } @@ -719,7 +725,7 @@ void mesh_path_expire(struct ieee80211_sub_if_data *sdata) struct hlist_node *p; int i; - read_lock(&pathtbl_resize_lock); + read_lock_bh(&pathtbl_resize_lock); for_each_mesh_entry(mesh_paths, p, node, i) { if (node->mpath->sdata != sdata) continue; @@ -734,7 +740,7 @@ void mesh_path_expire(struct ieee80211_sub_if_data *sdata) } else spin_unlock_bh(&mpath->state_lock); } - read_unlock(&pathtbl_resize_lock); + read_unlock_bh(&pathtbl_resize_lock); } void mesh_pathtbl_unregister(void) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 44b5393..2c37bee 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -43,7 +43,7 @@ #define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks) enum plink_frame_type { - PLINK_OPEN = 0, + PLINK_OPEN = 1, PLINK_CONFIRM, PLINK_CLOSE }; @@ -105,7 +105,7 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, if (!sta) return NULL; - sta->flags = WLAN_STA_AUTHORIZED; + sta->flags = WLAN_STA_AUTHORIZED | WLAN_STA_AUTH; sta->sta.supp_rates[local->hw.conf.channel->band] = rates; rate_control_rate_init(sta); @@ -161,7 +161,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, __le16 reason) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 + - sdata->u.mesh.vendor_ie_len); + sdata->u.mesh.ie_len); struct ieee80211_mgmt *mgmt; bool include_plid = false; static const u8 meshpeeringproto[] = { 0x00, 0x0F, 0xAC, 0x2A }; @@ -181,8 +181,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, IEEE80211_STYPE_ACTION); memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); - /* BSSID is left zeroed, wildcard value */ - mgmt->u.action.category = WLAN_CATEGORY_MESH_PLINK; + memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); + mgmt->u.action.category = WLAN_CATEGORY_MESH_ACTION; mgmt->u.action.u.plink_action.action_code = action; if (action == PLINK_CLOSE) @@ -237,8 +237,9 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, return 0; } -void mesh_neighbour_update(u8 *hw_addr, u32 rates, struct ieee80211_sub_if_data *sdata, - bool peer_accepting_plinks) +void mesh_neighbour_update(u8 *hw_addr, u32 rates, + struct ieee80211_sub_if_data *sdata, + struct ieee802_11_elems *elems) { struct ieee80211_local *local = sdata->local; struct sta_info *sta; @@ -248,8 +249,14 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates, struct ieee80211_sub_if_data sta = sta_info_get(sdata, hw_addr); if (!sta) { rcu_read_unlock(); - - sta = mesh_plink_alloc(sdata, hw_addr, rates); + /* Userspace handles peer allocation when security is enabled + * */ + if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) + cfg80211_notify_new_peer_candidate(sdata->dev, hw_addr, + elems->ie_start, elems->total_len, + GFP_KERNEL); + else + sta = mesh_plink_alloc(sdata, hw_addr, rates); if (!sta) return; if (sta_info_insert_rcu(sta)) { @@ -260,7 +267,8 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates, struct ieee80211_sub_if_data sta->last_rx = jiffies; sta->sta.supp_rates[local->hw.conf.channel->band] = rates; - if (peer_accepting_plinks && sta->plink_state == PLINK_LISTEN && + if (mesh_peer_accepts_plinks(elems) && + sta->plink_state == PLINK_LISTEN && sdata->u.mesh.accepting_plinks && sdata->u.mesh.mshcfg.auto_open_plinks) mesh_plink_open(sta); @@ -372,6 +380,9 @@ int mesh_plink_open(struct sta_info *sta) __le16 llid; struct ieee80211_sub_if_data *sdata = sta->sdata; + if (!test_sta_flags(sta, WLAN_STA_AUTH)) + return -EPERM; + spin_lock_bh(&sta->lock); get_random_bytes(&llid, 2); sta->llid = llid; @@ -449,6 +460,11 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m mpl_dbg("Mesh plink: missing necessary peer link ie\n"); return; } + if (elems.rsn_len && + sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { + mpl_dbg("Mesh plink: can't establish link with secure peer\n"); + return; + } ftype = mgmt->u.action.u.plink_action.action_code; ie_len = elems.peer_link_len; @@ -480,6 +496,12 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m return; } + if (sta && !test_sta_flags(sta, WLAN_STA_AUTH)) { + mpl_dbg("Mesh plink: Action frame from non-authed peer\n"); + rcu_read_unlock(); + return; + } + if (sta && sta->plink_state == PLINK_BLOCKED) { rcu_read_unlock(); return; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 64d92d5..4f6b267 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -90,20 +90,11 @@ enum rx_mgmt_action { /* no action required */ RX_MGMT_NONE, - /* caller must call cfg80211_send_rx_auth() */ - RX_MGMT_CFG80211_AUTH, - - /* caller must call cfg80211_send_rx_assoc() */ - RX_MGMT_CFG80211_ASSOC, - /* caller must call cfg80211_send_deauth() */ RX_MGMT_CFG80211_DEAUTH, /* caller must call cfg80211_send_disassoc() */ RX_MGMT_CFG80211_DISASSOC, - - /* caller must tell cfg80211 about internal error */ - RX_MGMT_CFG80211_ASSOC_ERROR, }; /* utils */ @@ -759,6 +750,8 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) dynamic_ps_enable_work); struct ieee80211_sub_if_data *sdata = local->ps_sdata; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + unsigned long flags; + int q; /* can only happen when PS was just disabled anyway */ if (!sdata) @@ -767,18 +760,37 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) if (local->hw.conf.flags & IEEE80211_CONF_PS) return; + /* + * transmission can be stopped by others which leads to + * dynamic_ps_timer expiry. Postpond the ps timer if it + * is not the actual idle state. + */ + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + for (q = 0; q < local->hw.queues; q++) { + if (local->queue_stop_reasons[q]) { + spin_unlock_irqrestore(&local->queue_stop_reason_lock, + flags); + mod_timer(&local->dynamic_ps_timer, jiffies + + msecs_to_jiffies( + local->hw.conf.dynamic_ps_timeout)); + return; + } + } + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) && (!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED))) { netif_tx_stop_all_queues(sdata->dev); - /* - * Flush all the frames queued in the driver before - * going to power save - */ - drv_flush(local, false); - ieee80211_send_nullfunc(local, sdata, 1); - /* Flush once again to get the tx status of nullfunc frame */ - drv_flush(local, false); + if (drv_tx_frames_pending(local)) + mod_timer(&local->dynamic_ps_timer, jiffies + + msecs_to_jiffies( + local->hw.conf.dynamic_ps_timeout)); + else { + ieee80211_send_nullfunc(local, sdata, 1); + /* Flush to get the tx status of nullfunc frame */ + drv_flush(local, false); + } } if (!((local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) && @@ -789,7 +801,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } - netif_tx_start_all_queues(sdata->dev); + netif_tx_wake_all_queues(sdata->dev); } void ieee80211_dynamic_ps_timer(unsigned long data) diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index e373551..730778a 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -6,7 +6,7 @@ #include "driver-ops.h" #include "led.h" -int __ieee80211_suspend(struct ieee80211_hw *hw) +int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; @@ -14,12 +14,23 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) ieee80211_scan_cancel(local); + if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { + mutex_lock(&local->sta_mtx); + list_for_each_entry(sta, &local->sta_list, list) { + set_sta_flags(sta, WLAN_STA_BLOCK_BA); + ieee80211_sta_tear_down_BA_sessions(sta, true); + } + mutex_unlock(&local->sta_mtx); + } + ieee80211_stop_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_SUSPEND); /* flush out all packets */ synchronize_net(); + drv_flush(local, false); + local->quiescing = true; /* make quiescing visible to timers everywhere */ mb(); @@ -36,6 +47,16 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) cancel_work_sync(&local->dynamic_ps_enable_work); del_timer_sync(&local->dynamic_ps_timer); + local->wowlan = wowlan && local->open_count; + if (local->wowlan) { + int err = drv_suspend(local, wowlan); + if (err) { + local->quiescing = false; + return err; + } + goto suspend; + } + /* disable keys */ list_for_each_entry(sdata, &local->interfaces, list) ieee80211_disable_keys(sdata); @@ -43,11 +64,6 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) /* tear down aggregation sessions and remove STAs */ mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { - if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { - set_sta_flags(sta, WLAN_STA_BLOCK_BA); - ieee80211_sta_tear_down_BA_sessions(sta, true); - } - if (sta->uploaded) { sdata = sta->sdata; if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) @@ -98,6 +114,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) if (local->open_count) ieee80211_stop_device(local); + suspend: local->suspended = true; /* need suspended to be visible before quiescing is false */ barrier(); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 2afeac9..1b9413f 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -143,7 +143,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, if (status->flag & RX_FLAG_HT) { /* * MCS information is a separate field in radiotap, - * added below. + * added below. The byte here is needed as padding + * for the channel though, so initialise it to 0. */ *pos = 0; } else { @@ -403,11 +404,13 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx) struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); struct sk_buff *skb = rx->skb; - if (likely(!(status->rx_flags & IEEE80211_RX_IN_SCAN))) + if (likely(!(status->rx_flags & IEEE80211_RX_IN_SCAN) && + !local->sched_scanning)) return RX_CONTINUE; if (test_bit(SCAN_HW_SCANNING, &local->scanning) || - test_bit(SCAN_SW_SCANNING, &local->scanning)) + test_bit(SCAN_SW_SCANNING, &local->scanning) || + local->sched_scanning) return ieee80211_scan_rx(rx->sdata, skb); /* scanning finished during invoking of handlers */ @@ -494,15 +497,19 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) return RX_DROP_MONITOR; if (ieee80211_is_action(hdr->frame_control)) { + u8 category; mgmt = (struct ieee80211_mgmt *)hdr; - if (mgmt->u.action.category != WLAN_CATEGORY_MESH_PLINK) + category = mgmt->u.action.category; + if (category != WLAN_CATEGORY_MESH_ACTION && + category != WLAN_CATEGORY_SELF_PROTECTED) return RX_DROP_MONITOR; return RX_CONTINUE; } if (ieee80211_is_probe_req(hdr->frame_control) || ieee80211_is_probe_resp(hdr->frame_control) || - ieee80211_is_beacon(hdr->frame_control)) + ieee80211_is_beacon(hdr->frame_control) || + ieee80211_is_auth(hdr->frame_control)) return RX_CONTINUE; return RX_DROP_MONITOR; @@ -650,7 +657,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_hw *hw, set_release_timer: mod_timer(&tid_agg_rx->reorder_timer, - tid_agg_rx->reorder_time[j] + + tid_agg_rx->reorder_time[j] + 1 + HT_RX_REORDER_BUF_TIMEOUT); } else { del_timer(&tid_agg_rx->reorder_timer); @@ -707,6 +714,8 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw, /* * If the current MPDU is in the right order and nothing else * is stored we can process it directly, no need to buffer it. + * If it is first but there's something stored, we may be able + * to release frames after this one. */ if (mpdu_seq_num == tid_agg_rx->head_seq_num && tid_agg_rx->stored_mpdu_num == 0) { @@ -1583,7 +1592,7 @@ ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx) } static int -__ieee80211_data_to_8023(struct ieee80211_rx_data *rx) +__ieee80211_data_to_8023(struct ieee80211_rx_data *rx, bool *port_control) { struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; @@ -1591,6 +1600,7 @@ __ieee80211_data_to_8023(struct ieee80211_rx_data *rx) struct ethhdr *ehdr; int ret; + *port_control = false; if (ieee80211_has_a4(hdr->frame_control) && sdata->vif.type == NL80211_IFTYPE_AP_VLAN && !sdata->u.vlan.sta) return -1; @@ -1609,11 +1619,13 @@ __ieee80211_data_to_8023(struct ieee80211_rx_data *rx) return -1; ret = ieee80211_data_to_8023(rx->skb, sdata->vif.addr, sdata->vif.type); - if (ret < 0 || !check_port_control) + if (ret < 0) return ret; ehdr = (struct ethhdr *) rx->skb->data; - if (ehdr->h_proto != rx->sdata->control_port_protocol) + if (ehdr->h_proto == rx->sdata->control_port_protocol) + *port_control = true; + else if (check_port_control) return -1; return 0; @@ -1914,6 +1926,7 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx) struct net_device *dev = sdata->dev; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; __le16 fc = hdr->frame_control; + bool port_control; int err; if (unlikely(!ieee80211_is_data(hdr->frame_control))) @@ -1930,13 +1943,21 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx) sdata->vif.type == NL80211_IFTYPE_AP) return RX_DROP_MONITOR; - err = __ieee80211_data_to_8023(rx); + err = __ieee80211_data_to_8023(rx, &port_control); if (unlikely(err)) return RX_DROP_UNUSABLE; if (!ieee80211_frame_allowed(rx, fc)) return RX_DROP_MONITOR; + if (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && + unlikely(port_control) && sdata->bss) { + sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, + u.ap); + dev = sdata->dev; + rx->sdata = sdata; + } + rx->skb->dev = dev; dev->stats.rx_packets++; @@ -2189,7 +2210,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) goto handled; } break; - case WLAN_CATEGORY_MESH_PLINK: + case WLAN_CATEGORY_MESH_ACTION: if (!ieee80211_vif_is_mesh(&sdata->vif)) break; goto queue; @@ -2352,47 +2373,6 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) return RX_QUEUED; } -static void ieee80211_rx_michael_mic_report(struct ieee80211_hdr *hdr, - struct ieee80211_rx_data *rx) -{ - int keyidx; - unsigned int hdrlen; - - hdrlen = ieee80211_hdrlen(hdr->frame_control); - if (rx->skb->len >= hdrlen + 4) - keyidx = rx->skb->data[hdrlen + 3] >> 6; - else - keyidx = -1; - - if (!rx->sta) { - /* - * Some hardware seem to generate incorrect Michael MIC - * reports; ignore them to avoid triggering countermeasures. - */ - return; - } - - if (!ieee80211_has_protected(hdr->frame_control)) - return; - - if (rx->sdata->vif.type == NL80211_IFTYPE_AP && keyidx) { - /* - * APs with pairwise keys should never receive Michael MIC - * errors for non-zero keyidx because these are reserved for - * group keys and only the AP is sending real multicast - * frames in the BSS. - */ - return; - } - - if (!ieee80211_is_data(hdr->frame_control) && - !ieee80211_is_auth(hdr->frame_control)) - return; - - mac80211_ev_michael_mic_failure(rx->sdata, keyidx, hdr, NULL, - GFP_ATOMIC); -} - /* TODO: use IEEE80211_RX_FRAGMENTED */ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx, struct ieee80211_rate *rate) @@ -2736,12 +2716,6 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx, if (!prepares) return false; - if (status->flag & RX_FLAG_MMIC_ERROR) { - if (status->rx_flags & IEEE80211_RX_RA_MATCH) - ieee80211_rx_michael_mic_report(hdr, rx); - return false; - } - if (!consume) { skb = skb_copy(skb, GFP_ATOMIC); if (!skb) { diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 489b6ad..ea44a8e 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -15,6 +15,7 @@ #include <linux/if_arp.h> #include <linux/rtnetlink.h> #include <linux/pm_qos_params.h> +#include <linux/slab.h> #include <net/sch_generic.h> #include <linux/slab.h> #include <net/mac80211.h> @@ -170,7 +171,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) return RX_CONTINUE; if (skb->len < 24) - return RX_DROP_MONITOR; + return RX_CONTINUE; presp = ieee80211_is_probe_resp(fc); if (presp) { @@ -850,3 +851,101 @@ void ieee80211_scan_cancel(struct ieee80211_local *local) } mutex_unlock(&local->mtx); } + +int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, + struct cfg80211_sched_scan_request *req) +{ + struct ieee80211_local *local = sdata->local; + int ret, i; + + mutex_lock(&sdata->local->mtx); + + if (local->sched_scanning) { + ret = -EBUSY; + goto out; + } + + if (!local->ops->sched_scan_start) { + ret = -ENOTSUPP; + goto out; + } + + for (i = 0; i < IEEE80211_NUM_BANDS; i++) { + local->sched_scan_ies.ie[i] = kzalloc(2 + + IEEE80211_MAX_SSID_LEN + + local->scan_ies_len, + GFP_KERNEL); + if (!local->sched_scan_ies.ie[i]) { + ret = -ENOMEM; + goto out_free; + } + + local->sched_scan_ies.len[i] = + ieee80211_build_preq_ies(local, + local->sched_scan_ies.ie[i], + req->ie, req->ie_len, i, + (u32) -1, 0); + } + + ret = drv_sched_scan_start(local, sdata, req, + &local->sched_scan_ies); + if (ret == 0) { + local->sched_scanning = true; + goto out; + } + +out_free: + while (i > 0) + kfree(local->sched_scan_ies.ie[--i]); +out: + mutex_unlock(&sdata->local->mtx); + return ret; +} + +int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata, + bool driver_initiated) +{ + struct ieee80211_local *local = sdata->local; + int ret = 0, i; + + mutex_lock(&sdata->local->mtx); + + if (!local->ops->sched_scan_stop) { + ret = -ENOTSUPP; + goto out; + } + + if (local->sched_scanning) { + for (i = 0; i < IEEE80211_NUM_BANDS; i++) + kfree(local->sched_scan_ies.ie[i]); + + if (!driver_initiated) + drv_sched_scan_stop(local, sdata); + local->sched_scanning = false; + } + +out: + mutex_unlock(&sdata->local->mtx); + + return ret; +} + +void ieee80211_sched_scan_results(struct ieee80211_hw *hw) +{ + struct ieee80211_local *local = hw_to_local(hw); + + trace_api_sched_scan_results(local); + + cfg80211_sched_scan_results(hw->wiphy); +} +EXPORT_SYMBOL(ieee80211_sched_scan_results); + +void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw) +{ + struct ieee80211_local *local = hw_to_local(hw); + + trace_api_sched_scan_stopped(local); + + cfg80211_sched_scan_stopped(hw->wiphy); +} +EXPORT_SYMBOL(ieee80211_sched_scan_stopped); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index d0311a3..f05244d 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -228,6 +228,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct sta_info *sta; + struct timespec uptime; int i; sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp); @@ -245,6 +246,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, sta->sdata = sdata; sta->last_rx = jiffies; + do_posix_clock_monotonic_gettime(&uptime); + sta->last_connected = uptime.tv_sec; ewma_init(&sta->avg_signal, 1024, 8); if (sta_prepare_rate_control(local, sta, gfp)) { @@ -584,7 +587,6 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, { unsigned long flags; struct sk_buff *skb; - struct ieee80211_sub_if_data *sdata; if (skb_queue_empty(&sta->ps_tx_buf)) return false; @@ -601,7 +603,6 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, if (!skb) break; - sdata = sta->sdata; local->total_ps_buffered--; #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG printk(KERN_DEBUG "Buffered frame expired (STA %pM)\n", @@ -609,7 +610,8 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, #endif dev_kfree_skb(skb); - if (skb_queue_empty(&sta->ps_tx_buf)) + if (skb_queue_empty(&sta->ps_tx_buf) && + !test_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF)) sta_info_clear_tim_bit(sta); } @@ -698,6 +700,8 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ cancel_work_sync(&sta->drv_unblock_wk); + cfg80211_del_sta(sdata->dev, sta->sta.addr, GFP_KERNEL); + rate_control_remove_sta_debugfs(sta); ieee80211_sta_debugfs_remove(sta); @@ -766,9 +770,8 @@ static void sta_info_cleanup(unsigned long data) if (!timer_needed) return; - local->sta_cleanup.expires = - round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL); - add_timer(&local->sta_cleanup); + mod_timer(&local->sta_cleanup, + round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL)); } void sta_info_init(struct ieee80211_local *local) @@ -781,14 +784,6 @@ void sta_info_init(struct ieee80211_local *local) setup_timer(&local->sta_cleanup, sta_info_cleanup, (unsigned long)local); - local->sta_cleanup.expires = - round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL); -} - -int sta_info_start(struct ieee80211_local *local) -{ - add_timer(&local->sta_cleanup); - return 0; } void sta_info_stop(struct ieee80211_local *local) @@ -900,6 +895,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) struct ieee80211_local *local = sdata->local; int sent, buffered; + clear_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF); if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta); @@ -992,3 +988,12 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, ieee80211_queue_work(hw, &sta->drv_unblock_wk); } EXPORT_SYMBOL(ieee80211_sta_block_awake); + +void ieee80211_sta_set_tim(struct ieee80211_sta *pubsta) +{ + struct sta_info *sta = container_of(pubsta, struct sta_info, sta); + + set_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF); + sta_info_set_tim_bit(sta); +} +EXPORT_SYMBOL(ieee80211_sta_set_tim); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 5768114..f00b4dc 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -43,6 +43,8 @@ * be in the queues * @WLAN_STA_PSPOLL: Station sent PS-poll while driver was keeping * station in power-save mode, reply when the driver unblocks. + * @WLAN_STA_PS_DRIVER_BUF: Station has frames pending in driver internal + * buffers. Automatically cleared on station wake-up. */ enum ieee80211_sta_info_flags { WLAN_STA_AUTH = 1<<0, @@ -58,6 +60,7 @@ enum ieee80211_sta_info_flags { WLAN_STA_BLOCK_BA = 1<<11, WLAN_STA_PS_DRIVER = 1<<12, WLAN_STA_PSPOLL = 1<<13, + WLAN_STA_PS_DRIVER_BUF = 1<<14, }; #define STA_TID_NUM 16 @@ -171,29 +174,6 @@ struct sta_ampdu_mlme { /** - * enum plink_state - state of a mesh peer link finite state machine - * - * @PLINK_LISTEN: initial state, considered the implicit state of non existant - * mesh peer links - * @PLINK_OPN_SNT: mesh plink open frame has been sent to this mesh peer - * @PLINK_OPN_RCVD: mesh plink open frame has been received from this mesh peer - * @PLINK_CNF_RCVD: mesh plink confirm frame has been received from this mesh - * peer - * @PLINK_ESTAB: mesh peer link is established - * @PLINK_HOLDING: mesh peer link is being closed or cancelled - * @PLINK_BLOCKED: all frames transmitted from this mesh plink are discarded - */ -enum plink_state { - PLINK_LISTEN, - PLINK_OPN_SNT, - PLINK_OPN_RCVD, - PLINK_CNF_RCVD, - PLINK_ESTAB, - PLINK_HOLDING, - PLINK_BLOCKED -}; - -/** * struct sta_info - STA information * * This structure collects information about a station that @@ -226,6 +206,7 @@ enum plink_state { * @rx_bytes: Number of bytes received from this STA * @wep_weak_iv_count: number of weak WEP IVs received from this station * @last_rx: time (in jiffies) when last frame was received from this STA + * @last_connected: time (in seconds) when a station got connected * @num_duplicates: number of duplicate frames received from this STA * @rx_fragments: number of received MPDUs * @rx_dropped: number of dropped MPDUs from this STA @@ -295,6 +276,7 @@ struct sta_info { unsigned long rx_packets, rx_bytes; unsigned long wep_weak_iv_count; unsigned long last_rx; + long last_connected; unsigned long num_duplicates; unsigned long rx_fragments; unsigned long rx_dropped; @@ -497,7 +479,6 @@ void sta_info_set_tim_bit(struct sta_info *sta); void sta_info_clear_tim_bit(struct sta_info *sta); void sta_info_init(struct ieee80211_local *local); -int sta_info_start(struct ieee80211_local *local); void sta_info_stop(struct ieee80211_local *local); int sta_info_flush(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/status.c b/net/mac80211/status.c index b936dd2..1658efa 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -189,16 +189,19 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) bool acked; for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { - /* the HW cannot have attempted that rate */ - if (i >= hw->max_report_rates) { + if (info->status.rates[i].idx < 0) { + break; + } else if (i >= hw->max_report_rates) { + /* the HW cannot have attempted that rate */ info->status.rates[i].idx = -1; info->status.rates[i].count = 0; - } else if (info->status.rates[i].idx >= 0) { - rates_idx = i; + break; } retry_count += info->status.rates[i].count; } + rates_idx = i - 1; + if (retry_count < 0) retry_count = 0; @@ -443,3 +446,11 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) dev_kfree_skb(skb); } EXPORT_SYMBOL(ieee80211_tx_status); + +void ieee80211_report_low_ack(struct ieee80211_sta *pubsta, u32 num_packets) +{ + struct sta_info *sta = container_of(pubsta, struct sta_info, sta); + cfg80211_cqm_pktloss_notify(sta->sdata->dev, sta->sta.addr, + num_packets, GFP_ATOMIC); +} +EXPORT_SYMBOL(ieee80211_report_low_ack); diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c index e840c9c..757e4eb 100644 --- a/net/mac80211/tkip.c +++ b/net/mac80211/tkip.c @@ -202,7 +202,7 @@ EXPORT_SYMBOL(ieee80211_get_tkip_key); * @payload_len is the length of payload (_not_ including IV/ICV length). * @ta is the transmitter addresses. */ -int ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm, +int ieee80211_tkip_encrypt_data(struct crypto_cipher *tfm, struct ieee80211_key *key, u8 *pos, size_t payload_len, u8 *ta) { @@ -223,7 +223,7 @@ int ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm, * beginning of the buffer containing IEEE 802.11 header payload, i.e., * including IV, Ext. IV, real data, Michael MIC, ICV. @payload_len is the * length of payload, including IV, Ext. IV, MIC, ICV. */ -int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm, +int ieee80211_tkip_decrypt_data(struct crypto_cipher *tfm, struct ieee80211_key *key, u8 *payload, size_t payload_len, u8 *ta, u8 *ra, int only_iv, int queue, diff --git a/net/mac80211/tkip.h b/net/mac80211/tkip.h index 7e83dee..1cab9c8 100644 --- a/net/mac80211/tkip.h +++ b/net/mac80211/tkip.h @@ -15,7 +15,7 @@ u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key, u16 iv16); -int ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm, +int ieee80211_tkip_encrypt_data(struct crypto_cipher *tfm, struct ieee80211_key *key, u8 *pos, size_t payload_len, u8 *ta); enum { @@ -24,7 +24,7 @@ enum { TKIP_DECRYPT_INVALID_KEYIDX = -2, TKIP_DECRYPT_REPLAY = -3, }; -int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm, +int ieee80211_tkip_decrypt_data(struct crypto_cipher *tfm, struct ieee80211_key *key, u8 *payload, size_t payload_len, u8 *ta, u8 *ra, int only_iv, int queue, diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index ce4596e..c9f1211 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1036,14 +1036,11 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx, struct ieee80211_radiotap_iterator iterator; struct ieee80211_radiotap_header *rthdr = (struct ieee80211_radiotap_header *) skb->data; - struct ieee80211_supported_band *sband; bool hw_frag; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len, NULL); - sband = tx->local->hw.wiphy->bands[tx->channel->band]; - info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; tx->flags &= ~IEEE80211_TX_FRAGMENTED; @@ -1442,11 +1439,8 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, struct ieee80211_tx_data tx; ieee80211_tx_result res_prepare; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - u16 queue; bool result = true; - queue = skb_get_queue_mapping(skb); - if (unlikely(skb->len < 10)) { dev_kfree_skb(skb); return true; @@ -1482,12 +1476,7 @@ static int ieee80211_skb_resize(struct ieee80211_local *local, { int tail_need = 0; - /* - * This could be optimised, devices that do full hardware - * crypto (including TKIP MMIC) need no tailroom... But we - * have no drivers for such devices currently. - */ - if (may_encrypt) { + if (may_encrypt && local->crypto_tx_tailroom_needed_cnt) { tail_need = IEEE80211_ENCRYPT_TAILROOM; tail_need -= skb_tailroom(skb); tail_need = max_t(int, tail_need, 0); @@ -1762,6 +1751,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, ret = NETDEV_TX_OK; goto fail; } + rcu_read_lock(); if (!is_multicast_ether_addr(skb->data)) mppath = mpp_path_lookup(skb->data, sdata); @@ -1776,13 +1766,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, !(mppath && compare_ether_addr(mppath->mpp, skb->data))) { hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc, skb->data, skb->data + ETH_ALEN); + rcu_read_unlock(); meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, sdata, NULL, NULL); } else { int is_mesh_mcast = 1; const u8 *mesh_da; - rcu_read_lock(); if (is_multicast_ether_addr(skb->data)) /* DA TA mSA AE:SA */ mesh_da = skb->data; @@ -2262,7 +2252,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, /* headroom, head length, tail length and maximum TIM length */ skb = dev_alloc_skb(local->tx_headroom + 400 + - sdata->u.mesh.vendor_ie_len); + sdata->u.mesh.ie_len); if (!skb) goto out; @@ -2485,7 +2475,6 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, { struct ieee80211_local *local = hw_to_local(hw); struct sk_buff *skb = NULL; - struct sta_info *sta; struct ieee80211_tx_data tx; struct ieee80211_sub_if_data *sdata; struct ieee80211_if_ap *bss = NULL; @@ -2527,7 +2516,6 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, info = IEEE80211_SKB_CB(skb); - sta = tx.sta; tx.flags |= IEEE80211_TX_PS_BUFFERED; tx.channel = local->hw.conf.channel; info->band = tx.channel->band; @@ -2547,8 +2535,9 @@ void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) skb_set_network_header(skb, 0); skb_set_transport_header(skb, 0); - /* send all internal mgmt frames on VO */ - skb_set_queue_mapping(skb, 0); + /* Send all internal mgmt frames on VO. Accordingly set TID to 7. */ + skb_set_queue_mapping(skb, IEEE80211_AC_VO); + skb->priority = 7; /* * The other path calling ieee80211_xmit is from the tasklet, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 556647a..d3fe2d2 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1125,9 +1125,27 @@ int ieee80211_reconfig(struct ieee80211_local *local) struct sta_info *sta; int res; +#ifdef CONFIG_PM if (local->suspended) local->resuming = true; + if (local->wowlan) { + local->wowlan = false; + res = drv_resume(local); + if (res < 0) { + local->resuming = false; + return res; + } + if (res == 0) + goto wake_up; + WARN_ON(res > 1); + /* + * res is 1, which means the driver requested + * to go through a regular reset on wakeup. + */ + } +#endif + /* restart hardware */ if (local->open_count) { /* @@ -1258,6 +1276,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) if (ieee80211_sdata_running(sdata)) ieee80211_enable_keys(sdata); + wake_up: ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_SUSPEND); @@ -1290,7 +1309,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) } } - add_timer(&local->sta_cleanup); + mod_timer(&local->sta_cleanup, jiffies + 1); mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c index 2ff6d1e..a1c6bfd 100644 --- a/net/mac80211/wep.c +++ b/net/mac80211/wep.c @@ -30,17 +30,15 @@ int ieee80211_wep_init(struct ieee80211_local *local) /* start WEP IV from a random value */ get_random_bytes(&local->wep_iv, WEP_IV_LEN); - local->wep_tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, - CRYPTO_ALG_ASYNC); + local->wep_tx_tfm = crypto_alloc_cipher("arc4", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(local->wep_tx_tfm)) { local->wep_rx_tfm = ERR_PTR(-EINVAL); return PTR_ERR(local->wep_tx_tfm); } - local->wep_rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, - CRYPTO_ALG_ASYNC); + local->wep_rx_tfm = crypto_alloc_cipher("arc4", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(local->wep_rx_tfm)) { - crypto_free_blkcipher(local->wep_tx_tfm); + crypto_free_cipher(local->wep_tx_tfm); local->wep_tx_tfm = ERR_PTR(-EINVAL); return PTR_ERR(local->wep_rx_tfm); } @@ -51,9 +49,9 @@ int ieee80211_wep_init(struct ieee80211_local *local) void ieee80211_wep_free(struct ieee80211_local *local) { if (!IS_ERR(local->wep_tx_tfm)) - crypto_free_blkcipher(local->wep_tx_tfm); + crypto_free_cipher(local->wep_tx_tfm); if (!IS_ERR(local->wep_rx_tfm)) - crypto_free_blkcipher(local->wep_rx_tfm); + crypto_free_cipher(local->wep_rx_tfm); } static inline bool ieee80211_wep_weak_iv(u32 iv, int keylen) @@ -127,12 +125,11 @@ static void ieee80211_wep_remove_iv(struct ieee80211_local *local, /* Perform WEP encryption using given key. data buffer must have tailroom * for 4-byte ICV. data_len must not include this ICV. Note: this function * does _not_ add IV. data = RC4(data | CRC32(data)) */ -int ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, +int ieee80211_wep_encrypt_data(struct crypto_cipher *tfm, u8 *rc4key, size_t klen, u8 *data, size_t data_len) { - struct blkcipher_desc desc = { .tfm = tfm }; - struct scatterlist sg; __le32 icv; + int i; if (IS_ERR(tfm)) return -1; @@ -140,9 +137,9 @@ int ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, icv = cpu_to_le32(~crc32_le(~0, data, data_len)); put_unaligned(icv, (__le32 *)(data + data_len)); - crypto_blkcipher_setkey(tfm, rc4key, klen); - sg_init_one(&sg, data, data_len + WEP_ICV_LEN); - crypto_blkcipher_encrypt(&desc, &sg, &sg, sg.length); + crypto_cipher_setkey(tfm, rc4key, klen); + for (i = 0; i < data_len + WEP_ICV_LEN; i++) + crypto_cipher_encrypt_one(tfm, data + i, data + i); return 0; } @@ -186,19 +183,18 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local, /* Perform WEP decryption using given key. data buffer includes encrypted * payload, including 4-byte ICV, but _not_ IV. data_len must not include ICV. * Return 0 on success and -1 on ICV mismatch. */ -int ieee80211_wep_decrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, +int ieee80211_wep_decrypt_data(struct crypto_cipher *tfm, u8 *rc4key, size_t klen, u8 *data, size_t data_len) { - struct blkcipher_desc desc = { .tfm = tfm }; - struct scatterlist sg; __le32 crc; + int i; if (IS_ERR(tfm)) return -1; - crypto_blkcipher_setkey(tfm, rc4key, klen); - sg_init_one(&sg, data, data_len + WEP_ICV_LEN); - crypto_blkcipher_decrypt(&desc, &sg, &sg, sg.length); + crypto_cipher_setkey(tfm, rc4key, klen); + for (i = 0; i < data_len + WEP_ICV_LEN; i++) + crypto_cipher_decrypt_one(tfm, data + i, data + i); crc = cpu_to_le32(~crc32_le(~0, data, data_len)); if (memcmp(&crc, data + data_len, WEP_ICV_LEN) != 0) diff --git a/net/mac80211/wep.h b/net/mac80211/wep.h index 58654ee..01e5484 100644 --- a/net/mac80211/wep.h +++ b/net/mac80211/wep.h @@ -18,12 +18,12 @@ int ieee80211_wep_init(struct ieee80211_local *local); void ieee80211_wep_free(struct ieee80211_local *local); -int ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, +int ieee80211_wep_encrypt_data(struct crypto_cipher *tfm, u8 *rc4key, size_t klen, u8 *data, size_t data_len); int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb, const u8 *key, int keylen, int keyidx); -int ieee80211_wep_decrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, +int ieee80211_wep_decrypt_data(struct crypto_cipher *tfm, u8 *rc4key, size_t klen, u8 *data, size_t data_len); bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key); diff --git a/net/mac80211/work.c b/net/mac80211/work.c index e73c8ca..a94b312 100644 --- a/net/mac80211/work.c +++ b/net/mac80211/work.c @@ -198,9 +198,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb; struct ieee80211_mgmt *mgmt; u8 *pos, qos_info; - const u8 *ies; size_t offset = 0, noffset; - int i, len, count, rates_len, supp_rates_len; + int i, count, rates_len, supp_rates_len; u16 capab; struct ieee80211_supported_band *sband; u32 rates = 0; @@ -285,7 +284,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, } /* SSID */ - ies = pos = skb_put(skb, 2 + wk->assoc.ssid_len); + pos = skb_put(skb, 2 + wk->assoc.ssid_len); *pos++ = WLAN_EID_SSID; *pos++ = wk->assoc.ssid_len; memcpy(pos, wk->assoc.ssid, wk->assoc.ssid_len); @@ -295,7 +294,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, if (supp_rates_len > 8) supp_rates_len = 8; - len = sband->n_bitrates; pos = skb_put(skb, supp_rates_len + 2); *pos++ = WLAN_EID_SUPP_RATES; *pos++ = supp_rates_len; diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index f1765de..9dc3b5f 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -87,42 +87,76 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - /* No way to verify the MIC if the hardware stripped it */ - if (status->flag & RX_FLAG_MMIC_STRIPPED) + /* + * it makes no sense to check for MIC errors on anything other + * than data frames. + */ + if (!ieee80211_is_data_present(hdr->frame_control)) + return RX_CONTINUE; + + /* + * No way to verify the MIC if the hardware stripped it or + * the IV with the key index. In this case we have solely rely + * on the driver to set RX_FLAG_MMIC_ERROR in the event of a + * MIC failure report. + */ + if (status->flag & (RX_FLAG_MMIC_STRIPPED | RX_FLAG_IV_STRIPPED)) { + if (status->flag & RX_FLAG_MMIC_ERROR) + goto mic_fail; + + if (!(status->flag & RX_FLAG_IV_STRIPPED)) + goto update_iv; + return RX_CONTINUE; + } + /* + * Some hardware seems to generate Michael MIC failure reports; even + * though, the frame was not encrypted with TKIP and therefore has no + * MIC. Ignore the flag them to avoid triggering countermeasures. + */ if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_TKIP || - !ieee80211_has_protected(hdr->frame_control) || - !ieee80211_is_data_present(hdr->frame_control)) + !(status->flag & RX_FLAG_DECRYPTED)) return RX_CONTINUE; + if (rx->sdata->vif.type == NL80211_IFTYPE_AP && rx->key->conf.keyidx) { + /* + * APs with pairwise keys should never receive Michael MIC + * errors for non-zero keyidx because these are reserved for + * group keys and only the AP is sending real multicast + * frames in the BSS. ( + */ + return RX_DROP_UNUSABLE; + } + + if (status->flag & RX_FLAG_MMIC_ERROR) + goto mic_fail; + hdrlen = ieee80211_hdrlen(hdr->frame_control); if (skb->len < hdrlen + MICHAEL_MIC_LEN) return RX_DROP_UNUSABLE; data = skb->data + hdrlen; data_len = skb->len - hdrlen - MICHAEL_MIC_LEN; - key = &rx->key->conf.key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]; michael_mic(key, hdr, data, data_len, mic); - if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0) { - if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) - return RX_DROP_UNUSABLE; - - mac80211_ev_michael_mic_failure(rx->sdata, rx->key->conf.keyidx, - (void *) skb->data, NULL, - GFP_ATOMIC); - return RX_DROP_UNUSABLE; - } + if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0) + goto mic_fail; /* remove Michael MIC from payload */ skb_trim(skb, skb->len - MICHAEL_MIC_LEN); +update_iv: /* update IV in key information to be able to detect replays */ rx->key->u.tkip.rx[rx->queue].iv32 = rx->tkip_iv32; rx->key->u.tkip.rx[rx->queue].iv16 = rx->tkip_iv16; return RX_CONTINUE; + +mic_fail: + mac80211_ev_michael_mic_failure(rx->sdata, rx->key->conf.keyidx, + (void *) skb->data, NULL, GFP_ATOMIC); + return RX_DROP_UNUSABLE; } |