diff options
| author | John W. Linville <linville@tuxdriver.com> | 2011-08-29 14:52:20 -0400 |
|---|---|---|
| committer | John W. Linville <linville@tuxdriver.com> | 2011-08-29 14:52:20 -0400 |
| commit | ba6e5eb107b4b26444cb67ce6fb8eb0973a97964 (patch) | |
| tree | 9377baf652e0cd8360372020b0386e238d07a30d /net/mac80211 | |
| parent | f3116f62cb56ef5efd172371fab688bb27529f69 (diff) | |
| parent | a508a6ea234571e0e7d1e9f2455fc1eca54d1fef (diff) | |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem
Diffstat (limited to 'net/mac80211')
| -rw-r--r-- | net/mac80211/Kconfig | 13 | ||||
| -rw-r--r-- | net/mac80211/agg-tx.c | 2 | ||||
| -rw-r--r-- | net/mac80211/cfg.c | 19 | ||||
| -rw-r--r-- | net/mac80211/debugfs_netdev.c | 8 | ||||
| -rw-r--r-- | net/mac80211/ieee80211_i.h | 1 | ||||
| -rw-r--r-- | net/mac80211/mesh.c | 259 | ||||
| -rw-r--r-- | net/mac80211/mesh.h | 36 | ||||
| -rw-r--r-- | net/mac80211/mesh_hwmp.c | 130 | ||||
| -rw-r--r-- | net/mac80211/mesh_pathtbl.c | 305 | ||||
| -rw-r--r-- | net/mac80211/mesh_plink.c | 241 | ||||
| -rw-r--r-- | net/mac80211/mlme.c | 59 | ||||
| -rw-r--r-- | net/mac80211/rx.c | 48 | ||||
| -rw-r--r-- | net/mac80211/sta_info.c | 330 | ||||
| -rw-r--r-- | net/mac80211/sta_info.h | 30 | ||||
| -rw-r--r-- | net/mac80211/status.c | 18 | ||||
| -rw-r--r-- | net/mac80211/tx.c | 14 |
16 files changed, 1148 insertions, 365 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index f5fdfcbf552a..d1886b59bec4 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig | |||
| @@ -199,6 +199,19 @@ config MAC80211_VERBOSE_MPL_DEBUG | |||
| 199 | 199 | ||
| 200 | Do not select this option. | 200 | Do not select this option. |
| 201 | 201 | ||
| 202 | config MAC80211_VERBOSE_MPATH_DEBUG | ||
| 203 | bool "Verbose mesh path debugging" | ||
| 204 | depends on MAC80211_DEBUG_MENU | ||
| 205 | depends on MAC80211_MESH | ||
| 206 | ---help--- | ||
| 207 | Selecting this option causes mac80211 to print out very | ||
| 208 | verbose mesh path selection debugging messages (when mac80211 | ||
| 209 | is taking part in a mesh network). | ||
| 210 | It should not be selected on production systems as those | ||
| 211 | messages are remotely triggerable. | ||
| 212 | |||
| 213 | Do not select this option. | ||
| 214 | |||
| 202 | config MAC80211_VERBOSE_MHWMP_DEBUG | 215 | config MAC80211_VERBOSE_MHWMP_DEBUG |
| 203 | bool "Verbose mesh HWMP routing debugging" | 216 | bool "Verbose mesh HWMP routing debugging" |
| 204 | depends on MAC80211_DEBUG_MENU | 217 | depends on MAC80211_DEBUG_MENU |
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index b7075f33dc06..018108d1a2fd 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c | |||
| @@ -128,7 +128,7 @@ void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u1 | |||
| 128 | memcpy(bar->ta, sdata->vif.addr, ETH_ALEN); | 128 | memcpy(bar->ta, sdata->vif.addr, ETH_ALEN); |
| 129 | bar_control |= (u16)IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL; | 129 | bar_control |= (u16)IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL; |
| 130 | bar_control |= (u16)IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA; | 130 | bar_control |= (u16)IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA; |
| 131 | bar_control |= (u16)(tid << 12); | 131 | bar_control |= (u16)(tid << IEEE80211_BAR_CTRL_TID_INFO_SHIFT); |
| 132 | bar->control = cpu_to_le16(bar_control); | 132 | bar->control = cpu_to_le16(bar_control); |
| 133 | bar->start_seq_num = cpu_to_le16(ssn); | 133 | bar->start_seq_num = cpu_to_le16(ssn); |
| 134 | 134 | ||
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index a589addf6ce1..4baa03b1c251 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
| @@ -697,6 +697,9 @@ static void sta_apply_parameters(struct ieee80211_local *local, | |||
| 697 | } | 697 | } |
| 698 | spin_unlock_irqrestore(&sta->flaglock, flags); | 698 | spin_unlock_irqrestore(&sta->flaglock, flags); |
| 699 | 699 | ||
| 700 | sta->sta.uapsd_queues = params->uapsd_queues; | ||
| 701 | sta->sta.max_sp = params->max_sp; | ||
| 702 | |||
| 700 | /* | 703 | /* |
| 701 | * cfg80211 validates this (1-2007) and allows setting the AID | 704 | * cfg80211 validates this (1-2007) and allows setting the AID |
| 702 | * only when creating a new station entry | 705 | * only when creating a new station entry |
| @@ -1137,6 +1140,22 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy, | |||
| 1137 | conf->dot11MeshHWMPRootMode = nconf->dot11MeshHWMPRootMode; | 1140 | conf->dot11MeshHWMPRootMode = nconf->dot11MeshHWMPRootMode; |
| 1138 | ieee80211_mesh_root_setup(ifmsh); | 1141 | ieee80211_mesh_root_setup(ifmsh); |
| 1139 | } | 1142 | } |
| 1143 | if (_chg_mesh_attr(NL80211_MESHCONF_GATE_ANNOUNCEMENTS, mask)) { | ||
| 1144 | /* our current gate announcement implementation rides on root | ||
| 1145 | * announcements, so require this ifmsh to also be a root node | ||
| 1146 | * */ | ||
| 1147 | if (nconf->dot11MeshGateAnnouncementProtocol && | ||
| 1148 | !conf->dot11MeshHWMPRootMode) { | ||
| 1149 | conf->dot11MeshHWMPRootMode = 1; | ||
| 1150 | ieee80211_mesh_root_setup(ifmsh); | ||
| 1151 | } | ||
| 1152 | conf->dot11MeshGateAnnouncementProtocol = | ||
| 1153 | nconf->dot11MeshGateAnnouncementProtocol; | ||
| 1154 | } | ||
| 1155 | if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_RANN_INTERVAL, mask)) { | ||
| 1156 | conf->dot11MeshHWMPRannInterval = | ||
| 1157 | nconf->dot11MeshHWMPRannInterval; | ||
| 1158 | } | ||
| 1140 | return 0; | 1159 | return 0; |
| 1141 | } | 1160 | } |
| 1142 | 1161 | ||
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 9ea7c0d0103f..6e8eab7919e2 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c | |||
| @@ -372,6 +372,10 @@ IEEE80211_IF_FILE(min_discovery_timeout, | |||
| 372 | u.mesh.mshcfg.min_discovery_timeout, DEC); | 372 | u.mesh.mshcfg.min_discovery_timeout, DEC); |
| 373 | IEEE80211_IF_FILE(dot11MeshHWMPRootMode, | 373 | IEEE80211_IF_FILE(dot11MeshHWMPRootMode, |
| 374 | u.mesh.mshcfg.dot11MeshHWMPRootMode, DEC); | 374 | u.mesh.mshcfg.dot11MeshHWMPRootMode, DEC); |
| 375 | IEEE80211_IF_FILE(dot11MeshGateAnnouncementProtocol, | ||
| 376 | u.mesh.mshcfg.dot11MeshGateAnnouncementProtocol, DEC); | ||
| 377 | IEEE80211_IF_FILE(dot11MeshHWMPRannInterval, | ||
| 378 | u.mesh.mshcfg.dot11MeshHWMPRannInterval, DEC); | ||
| 375 | #endif | 379 | #endif |
| 376 | 380 | ||
| 377 | 381 | ||
| @@ -485,7 +489,9 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata) | |||
| 485 | MESHPARAMS_ADD(dot11MeshHWMPmaxPREQretries); | 489 | MESHPARAMS_ADD(dot11MeshHWMPmaxPREQretries); |
| 486 | MESHPARAMS_ADD(path_refresh_time); | 490 | MESHPARAMS_ADD(path_refresh_time); |
| 487 | MESHPARAMS_ADD(min_discovery_timeout); | 491 | MESHPARAMS_ADD(min_discovery_timeout); |
| 488 | 492 | MESHPARAMS_ADD(dot11MeshHWMPRootMode); | |
| 493 | MESHPARAMS_ADD(dot11MeshHWMPRannInterval); | ||
| 494 | MESHPARAMS_ADD(dot11MeshGateAnnouncementProtocol); | ||
| 489 | #undef MESHPARAMS_ADD | 495 | #undef MESHPARAMS_ADD |
| 490 | } | 496 | } |
| 491 | #endif | 497 | #endif |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index ea7419050846..c204cee1189c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
| @@ -514,6 +514,7 @@ struct ieee80211_if_mesh { | |||
| 514 | struct mesh_config mshcfg; | 514 | struct mesh_config mshcfg; |
| 515 | u32 mesh_seqnum; | 515 | u32 mesh_seqnum; |
| 516 | bool accepting_plinks; | 516 | bool accepting_plinks; |
| 517 | int num_gates; | ||
| 517 | const u8 *ie; | 518 | const u8 *ie; |
| 518 | u8 ie_len; | 519 | u8 ie_len; |
| 519 | enum { | 520 | enum { |
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 29e9980c8e60..28ab510e621a 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c | |||
| @@ -13,10 +13,6 @@ | |||
| 13 | #include "ieee80211_i.h" | 13 | #include "ieee80211_i.h" |
| 14 | #include "mesh.h" | 14 | #include "mesh.h" |
| 15 | 15 | ||
| 16 | #define IEEE80211_MESH_PEER_INACTIVITY_LIMIT (1800 * HZ) | ||
| 17 | #define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ) | ||
| 18 | #define IEEE80211_MESH_RANN_INTERVAL (1 * HZ) | ||
| 19 | |||
| 20 | #define MESHCONF_CAPAB_ACCEPT_PLINKS 0x01 | 16 | #define MESHCONF_CAPAB_ACCEPT_PLINKS 0x01 |
| 21 | #define MESHCONF_CAPAB_FORWARDING 0x08 | 17 | #define MESHCONF_CAPAB_FORWARDING 0x08 |
| 22 | 18 | ||
| @@ -27,6 +23,17 @@ | |||
| 27 | int mesh_allocated; | 23 | int mesh_allocated; |
| 28 | static struct kmem_cache *rm_cache; | 24 | static struct kmem_cache *rm_cache; |
| 29 | 25 | ||
| 26 | #ifdef CONFIG_MAC80211_MESH | ||
| 27 | bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt) | ||
| 28 | { | ||
| 29 | return (mgmt->u.action.u.mesh_action.action_code == | ||
| 30 | WLAN_MESH_ACTION_HWMP_PATH_SELECTION); | ||
| 31 | } | ||
| 32 | #else | ||
| 33 | bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt) | ||
| 34 | { return false; } | ||
| 35 | #endif | ||
| 36 | |||
| 30 | void ieee80211s_init(void) | 37 | void ieee80211s_init(void) |
| 31 | { | 38 | { |
| 32 | mesh_pathtbl_init(); | 39 | mesh_pathtbl_init(); |
| @@ -204,36 +211,185 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr, | |||
| 204 | return 0; | 211 | return 0; |
| 205 | } | 212 | } |
| 206 | 213 | ||
| 207 | void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) | 214 | int |
| 215 | mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) | ||
| 216 | { | ||
| 217 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | ||
| 218 | u8 *pos, neighbors; | ||
| 219 | u8 meshconf_len = sizeof(struct ieee80211_meshconf_ie); | ||
| 220 | |||
| 221 | if (skb_tailroom(skb) < 2 + meshconf_len) | ||
| 222 | return -ENOMEM; | ||
| 223 | |||
| 224 | pos = skb_put(skb, 2 + meshconf_len); | ||
| 225 | *pos++ = WLAN_EID_MESH_CONFIG; | ||
| 226 | *pos++ = meshconf_len; | ||
| 227 | |||
| 228 | /* Active path selection protocol ID */ | ||
| 229 | *pos++ = ifmsh->mesh_pp_id; | ||
| 230 | /* Active path selection metric ID */ | ||
| 231 | *pos++ = ifmsh->mesh_pm_id; | ||
| 232 | /* Congestion control mode identifier */ | ||
| 233 | *pos++ = ifmsh->mesh_cc_id; | ||
| 234 | /* Synchronization protocol identifier */ | ||
| 235 | *pos++ = ifmsh->mesh_sp_id; | ||
| 236 | /* Authentication Protocol identifier */ | ||
| 237 | *pos++ = ifmsh->mesh_auth_id; | ||
| 238 | /* Mesh Formation Info - number of neighbors */ | ||
| 239 | neighbors = atomic_read(&ifmsh->mshstats.estab_plinks); | ||
| 240 | /* Number of neighbor mesh STAs or 15 whichever is smaller */ | ||
| 241 | neighbors = (neighbors > 15) ? 15 : neighbors; | ||
| 242 | *pos++ = neighbors << 1; | ||
| 243 | /* Mesh capability */ | ||
| 244 | ifmsh->accepting_plinks = mesh_plink_availables(sdata); | ||
| 245 | *pos = MESHCONF_CAPAB_FORWARDING; | ||
| 246 | *pos++ |= ifmsh->accepting_plinks ? | ||
| 247 | MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; | ||
| 248 | *pos++ = 0x00; | ||
| 249 | |||
| 250 | return 0; | ||
| 251 | } | ||
| 252 | |||
| 253 | int | ||
| 254 | mesh_add_meshid_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) | ||
| 255 | { | ||
| 256 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | ||
| 257 | u8 *pos; | ||
| 258 | |||
| 259 | if (skb_tailroom(skb) < 2 + ifmsh->mesh_id_len) | ||
| 260 | return -ENOMEM; | ||
| 261 | |||
| 262 | pos = skb_put(skb, 2 + ifmsh->mesh_id_len); | ||
| 263 | *pos++ = WLAN_EID_MESH_ID; | ||
| 264 | *pos++ = ifmsh->mesh_id_len; | ||
| 265 | if (ifmsh->mesh_id_len) | ||
| 266 | memcpy(pos, ifmsh->mesh_id, ifmsh->mesh_id_len); | ||
| 267 | |||
| 268 | return 0; | ||
| 269 | } | ||
| 270 | |||
| 271 | int | ||
| 272 | mesh_add_vendor_ies(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) | ||
| 273 | { | ||
| 274 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | ||
| 275 | u8 offset, len; | ||
| 276 | const u8 *data; | ||
| 277 | |||
| 278 | if (!ifmsh->ie || !ifmsh->ie_len) | ||
| 279 | return 0; | ||
| 280 | |||
| 281 | /* fast-forward to vendor IEs */ | ||
| 282 | offset = ieee80211_ie_split_vendor(ifmsh->ie, ifmsh->ie_len, 0); | ||
| 283 | |||
| 284 | if (offset) { | ||
| 285 | len = ifmsh->ie_len - offset; | ||
| 286 | data = ifmsh->ie + offset; | ||
| 287 | if (skb_tailroom(skb) < len) | ||
| 288 | return -ENOMEM; | ||
| 289 | memcpy(skb_put(skb, len), data, len); | ||
| 290 | } | ||
| 291 | |||
| 292 | return 0; | ||
| 293 | } | ||
| 294 | |||
| 295 | int | ||
| 296 | mesh_add_rsn_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) | ||
| 297 | { | ||
| 298 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | ||
| 299 | u8 len = 0; | ||
| 300 | const u8 *data; | ||
| 301 | |||
| 302 | if (!ifmsh->ie || !ifmsh->ie_len) | ||
| 303 | return 0; | ||
| 304 | |||
| 305 | /* find RSN IE */ | ||
| 306 | data = ifmsh->ie; | ||
| 307 | while (data < ifmsh->ie + ifmsh->ie_len) { | ||
| 308 | if (*data == WLAN_EID_RSN) { | ||
| 309 | len = data[1] + 2; | ||
| 310 | break; | ||
| 311 | } | ||
| 312 | data++; | ||
| 313 | } | ||
| 314 | |||
| 315 | if (len) { | ||
| 316 | if (skb_tailroom(skb) < len) | ||
| 317 | return -ENOMEM; | ||
| 318 | memcpy(skb_put(skb, len), data, len); | ||
| 319 | } | ||
| 320 | |||
| 321 | return 0; | ||
| 322 | } | ||
| 323 | |||
| 324 | int | ||
| 325 | mesh_add_srates_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) | ||
| 208 | { | 326 | { |
| 209 | struct ieee80211_local *local = sdata->local; | 327 | struct ieee80211_local *local = sdata->local; |
| 210 | struct ieee80211_supported_band *sband; | 328 | struct ieee80211_supported_band *sband; |
| 211 | u8 *pos; | 329 | int rate; |
| 212 | int len, i, rate; | 330 | u8 i, rates, *pos; |
| 213 | u8 neighbors; | ||
| 214 | 331 | ||
| 215 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | 332 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; |
| 216 | len = sband->n_bitrates; | 333 | rates = sband->n_bitrates; |
| 217 | if (len > 8) | 334 | if (rates > 8) |
| 218 | len = 8; | 335 | rates = 8; |
| 219 | pos = skb_put(skb, len + 2); | 336 | |
| 337 | if (skb_tailroom(skb) < rates + 2) | ||
| 338 | return -ENOMEM; | ||
| 339 | |||
| 340 | pos = skb_put(skb, rates + 2); | ||
| 220 | *pos++ = WLAN_EID_SUPP_RATES; | 341 | *pos++ = WLAN_EID_SUPP_RATES; |
| 221 | *pos++ = len; | 342 | *pos++ = rates; |
| 222 | for (i = 0; i < len; i++) { | 343 | for (i = 0; i < rates; i++) { |
| 223 | rate = sband->bitrates[i].bitrate; | 344 | rate = sband->bitrates[i].bitrate; |
| 224 | *pos++ = (u8) (rate / 5); | 345 | *pos++ = (u8) (rate / 5); |
| 225 | } | 346 | } |
| 226 | 347 | ||
| 227 | if (sband->n_bitrates > len) { | 348 | return 0; |
| 228 | pos = skb_put(skb, sband->n_bitrates - len + 2); | 349 | } |
| 350 | |||
| 351 | int | ||
| 352 | mesh_add_ext_srates_ie(struct sk_buff *skb, | ||
| 353 | struct ieee80211_sub_if_data *sdata) | ||
| 354 | { | ||
| 355 | struct ieee80211_local *local = sdata->local; | ||
| 356 | struct ieee80211_supported_band *sband; | ||
| 357 | int rate; | ||
| 358 | u8 i, exrates, *pos; | ||
| 359 | |||
| 360 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | ||
| 361 | exrates = sband->n_bitrates; | ||
| 362 | if (exrates > 8) | ||
| 363 | exrates -= 8; | ||
| 364 | else | ||
| 365 | exrates = 0; | ||
| 366 | |||
| 367 | if (skb_tailroom(skb) < exrates + 2) | ||
| 368 | return -ENOMEM; | ||
| 369 | |||
| 370 | if (exrates) { | ||
| 371 | pos = skb_put(skb, exrates + 2); | ||
| 229 | *pos++ = WLAN_EID_EXT_SUPP_RATES; | 372 | *pos++ = WLAN_EID_EXT_SUPP_RATES; |
| 230 | *pos++ = sband->n_bitrates - len; | 373 | *pos++ = exrates; |
| 231 | for (i = len; i < sband->n_bitrates; i++) { | 374 | for (i = 8; i < sband->n_bitrates; i++) { |
| 232 | rate = sband->bitrates[i].bitrate; | 375 | rate = sband->bitrates[i].bitrate; |
| 233 | *pos++ = (u8) (rate / 5); | 376 | *pos++ = (u8) (rate / 5); |
| 234 | } | 377 | } |
| 235 | } | 378 | } |
| 379 | return 0; | ||
| 380 | } | ||
| 236 | 381 | ||
| 382 | int mesh_add_ds_params_ie(struct sk_buff *skb, | ||
| 383 | struct ieee80211_sub_if_data *sdata) | ||
| 384 | { | ||
| 385 | struct ieee80211_local *local = sdata->local; | ||
| 386 | struct ieee80211_supported_band *sband; | ||
| 387 | u8 *pos; | ||
| 388 | |||
| 389 | if (skb_tailroom(skb) < 3) | ||
| 390 | return -ENOMEM; | ||
| 391 | |||
| 392 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | ||
| 237 | if (sband->band == IEEE80211_BAND_2GHZ) { | 393 | if (sband->band == IEEE80211_BAND_2GHZ) { |
| 238 | pos = skb_put(skb, 2 + 1); | 394 | pos = skb_put(skb, 2 + 1); |
| 239 | *pos++ = WLAN_EID_DS_PARAMS; | 395 | *pos++ = WLAN_EID_DS_PARAMS; |
| @@ -241,53 +397,9 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) | |||
| 241 | *pos++ = ieee80211_frequency_to_channel(local->hw.conf.channel->center_freq); | 397 | *pos++ = ieee80211_frequency_to_channel(local->hw.conf.channel->center_freq); |
| 242 | } | 398 | } |
| 243 | 399 | ||
| 244 | pos = skb_put(skb, 2 + sdata->u.mesh.mesh_id_len); | 400 | return 0; |
| 245 | *pos++ = WLAN_EID_MESH_ID; | ||
| 246 | *pos++ = sdata->u.mesh.mesh_id_len; | ||
| 247 | if (sdata->u.mesh.mesh_id_len) | ||
| 248 | memcpy(pos, sdata->u.mesh.mesh_id, sdata->u.mesh.mesh_id_len); | ||
| 249 | |||
| 250 | pos = skb_put(skb, 2 + sizeof(struct ieee80211_meshconf_ie)); | ||
| 251 | *pos++ = WLAN_EID_MESH_CONFIG; | ||
| 252 | *pos++ = sizeof(struct ieee80211_meshconf_ie); | ||
| 253 | |||
| 254 | /* Active path selection protocol ID */ | ||
| 255 | *pos++ = sdata->u.mesh.mesh_pp_id; | ||
| 256 | |||
| 257 | /* Active path selection metric ID */ | ||
| 258 | *pos++ = sdata->u.mesh.mesh_pm_id; | ||
| 259 | |||
| 260 | /* Congestion control mode identifier */ | ||
| 261 | *pos++ = sdata->u.mesh.mesh_cc_id; | ||
| 262 | |||
| 263 | /* Synchronization protocol identifier */ | ||
| 264 | *pos++ = sdata->u.mesh.mesh_sp_id; | ||
| 265 | |||
| 266 | /* Authentication Protocol identifier */ | ||
| 267 | *pos++ = sdata->u.mesh.mesh_auth_id; | ||
| 268 | |||
| 269 | /* Mesh Formation Info - number of neighbors */ | ||
| 270 | neighbors = atomic_read(&sdata->u.mesh.mshstats.estab_plinks); | ||
| 271 | /* Number of neighbor mesh STAs or 15 whichever is smaller */ | ||
| 272 | neighbors = (neighbors > 15) ? 15 : neighbors; | ||
| 273 | *pos++ = neighbors << 1; | ||
| 274 | |||
| 275 | /* Mesh capability */ | ||
| 276 | sdata->u.mesh.accepting_plinks = mesh_plink_availables(sdata); | ||
| 277 | *pos = MESHCONF_CAPAB_FORWARDING; | ||
| 278 | *pos++ |= sdata->u.mesh.accepting_plinks ? | ||
| 279 | MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; | ||
| 280 | *pos++ = 0x00; | ||
| 281 | |||
| 282 | if (sdata->u.mesh.ie) { | ||
| 283 | int len = sdata->u.mesh.ie_len; | ||
| 284 | const u8 *data = sdata->u.mesh.ie; | ||
| 285 | if (skb_tailroom(skb) > len) | ||
| 286 | memcpy(skb_put(skb, len), data, len); | ||
| 287 | } | ||
| 288 | } | 401 | } |
| 289 | 402 | ||
| 290 | |||
| 291 | static void ieee80211_mesh_path_timer(unsigned long data) | 403 | static void ieee80211_mesh_path_timer(unsigned long data) |
| 292 | { | 404 | { |
| 293 | struct ieee80211_sub_if_data *sdata = | 405 | struct ieee80211_sub_if_data *sdata = |
| @@ -425,7 +537,8 @@ static void ieee80211_mesh_rootpath(struct ieee80211_sub_if_data *sdata) | |||
| 425 | 537 | ||
| 426 | mesh_path_tx_root_frame(sdata); | 538 | mesh_path_tx_root_frame(sdata); |
| 427 | mod_timer(&ifmsh->mesh_path_root_timer, | 539 | mod_timer(&ifmsh->mesh_path_root_timer, |
| 428 | round_jiffies(jiffies + IEEE80211_MESH_RANN_INTERVAL)); | 540 | round_jiffies(TU_TO_EXP_TIME( |
| 541 | ifmsh->mshcfg.dot11MeshHWMPRannInterval))); | ||
| 429 | } | 542 | } |
| 430 | 543 | ||
| 431 | #ifdef CONFIG_PM | 544 | #ifdef CONFIG_PM |
| @@ -433,7 +546,7 @@ void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata) | |||
| 433 | { | 546 | { |
| 434 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | 547 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; |
| 435 | 548 | ||
| 436 | /* use atomic bitops in case both timers fire at the same time */ | 549 | /* use atomic bitops in case all timers fire at the same time */ |
| 437 | 550 | ||
| 438 | if (del_timer_sync(&ifmsh->housekeeping_timer)) | 551 | if (del_timer_sync(&ifmsh->housekeeping_timer)) |
| 439 | set_bit(TMR_RUNNING_HK, &ifmsh->timers_running); | 552 | set_bit(TMR_RUNNING_HK, &ifmsh->timers_running); |
| @@ -557,11 +670,18 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata, | |||
| 557 | struct ieee80211_rx_status *rx_status) | 670 | struct ieee80211_rx_status *rx_status) |
| 558 | { | 671 | { |
| 559 | switch (mgmt->u.action.category) { | 672 | switch (mgmt->u.action.category) { |
| 560 | case WLAN_CATEGORY_MESH_ACTION: | 673 | case WLAN_CATEGORY_SELF_PROTECTED: |
| 561 | mesh_rx_plink_frame(sdata, mgmt, len, rx_status); | 674 | switch (mgmt->u.action.u.self_prot.action_code) { |
| 675 | case WLAN_SP_MESH_PEERING_OPEN: | ||
| 676 | case WLAN_SP_MESH_PEERING_CLOSE: | ||
| 677 | case WLAN_SP_MESH_PEERING_CONFIRM: | ||
| 678 | mesh_rx_plink_frame(sdata, mgmt, len, rx_status); | ||
| 679 | break; | ||
| 680 | } | ||
| 562 | break; | 681 | break; |
| 563 | case WLAN_CATEGORY_MESH_PATH_SEL: | 682 | case WLAN_CATEGORY_MESH_ACTION: |
| 564 | mesh_rx_path_sel_frame(sdata, mgmt, len); | 683 | if (mesh_action_is_path_sel(mgmt)) |
| 684 | mesh_rx_path_sel_frame(sdata, mgmt, len); | ||
| 565 | break; | 685 | break; |
| 566 | } | 686 | } |
| 567 | } | 687 | } |
| @@ -633,6 +753,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) | |||
| 633 | ifmsh->accepting_plinks = true; | 753 | ifmsh->accepting_plinks = true; |
| 634 | ifmsh->preq_id = 0; | 754 | ifmsh->preq_id = 0; |
| 635 | ifmsh->sn = 0; | 755 | ifmsh->sn = 0; |
| 756 | ifmsh->num_gates = 0; | ||
| 636 | atomic_set(&ifmsh->mpaths, 0); | 757 | atomic_set(&ifmsh->mpaths, 0); |
| 637 | mesh_rmc_init(sdata); | 758 | mesh_rmc_init(sdata); |
| 638 | ifmsh->last_preq = jiffies; | 759 | ifmsh->last_preq = jiffies; |
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 249e733362e7..20272072171f 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h | |||
| @@ -81,6 +81,7 @@ enum mesh_deferred_task_flags { | |||
| 81 | * @discovery_retries: number of discovery retries | 81 | * @discovery_retries: number of discovery retries |
| 82 | * @flags: mesh path flags, as specified on &enum mesh_path_flags | 82 | * @flags: mesh path flags, as specified on &enum mesh_path_flags |
| 83 | * @state_lock: mesh path state lock | 83 | * @state_lock: mesh path state lock |
| 84 | * @is_gate: the destination station of this path is a mesh gate | ||
| 84 | * | 85 | * |
| 85 | * | 86 | * |
| 86 | * The combination of dst and sdata is unique in the mesh path table. Since the | 87 | * The combination of dst and sdata is unique in the mesh path table. Since the |
| @@ -104,6 +105,7 @@ struct mesh_path { | |||
| 104 | u8 discovery_retries; | 105 | u8 discovery_retries; |
| 105 | enum mesh_path_flags flags; | 106 | enum mesh_path_flags flags; |
| 106 | spinlock_t state_lock; | 107 | spinlock_t state_lock; |
| 108 | bool is_gate; | ||
| 107 | }; | 109 | }; |
| 108 | 110 | ||
| 109 | /** | 111 | /** |
| @@ -120,6 +122,9 @@ struct mesh_path { | |||
| 120 | * buckets | 122 | * buckets |
| 121 | * @mean_chain_len: maximum average length for the hash buckets' list, if it is | 123 | * @mean_chain_len: maximum average length for the hash buckets' list, if it is |
| 122 | * reached, the table will grow | 124 | * reached, the table will grow |
| 125 | * @known_gates: list of known mesh gates and their mpaths by the station. The | ||
| 126 | * gate's mpath may or may not be resolved and active. | ||
| 127 | * | ||
| 123 | * rcu_head: RCU head to free the table | 128 | * rcu_head: RCU head to free the table |
| 124 | */ | 129 | */ |
| 125 | struct mesh_table { | 130 | struct mesh_table { |
| @@ -133,6 +138,8 @@ struct mesh_table { | |||
| 133 | int (*copy_node) (struct hlist_node *p, struct mesh_table *newtbl); | 138 | int (*copy_node) (struct hlist_node *p, struct mesh_table *newtbl); |
| 134 | int size_order; | 139 | int size_order; |
| 135 | int mean_chain_len; | 140 | int mean_chain_len; |
| 141 | struct hlist_head *known_gates; | ||
| 142 | spinlock_t gates_lock; | ||
| 136 | 143 | ||
| 137 | struct rcu_head rcu_head; | 144 | struct rcu_head rcu_head; |
| 138 | }; | 145 | }; |
| @@ -166,6 +173,8 @@ struct mesh_rmc { | |||
| 166 | u32 idx_mask; | 173 | u32 idx_mask; |
| 167 | }; | 174 | }; |
| 168 | 175 | ||
| 176 | #define IEEE80211_MESH_PEER_INACTIVITY_LIMIT (1800 * HZ) | ||
| 177 | #define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ) | ||
| 169 | 178 | ||
| 170 | #define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units */ | 179 | #define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units */ |
| 171 | 180 | ||
| @@ -177,14 +186,6 @@ struct mesh_rmc { | |||
| 177 | /* Maximum number of paths per interface */ | 186 | /* Maximum number of paths per interface */ |
| 178 | #define MESH_MAX_MPATHS 1024 | 187 | #define MESH_MAX_MPATHS 1024 |
| 179 | 188 | ||
| 180 | /* Pending ANA approval */ | ||
| 181 | #define MESH_PATH_SEL_ACTION 0 | ||
| 182 | |||
| 183 | /* PERR reason codes */ | ||
| 184 | #define PEER_RCODE_UNSPECIFIED 11 | ||
| 185 | #define PERR_RCODE_NO_ROUTE 12 | ||
| 186 | #define PERR_RCODE_DEST_UNREACH 13 | ||
| 187 | |||
| 188 | /* Public interfaces */ | 189 | /* Public interfaces */ |
| 189 | /* Various */ | 190 | /* Various */ |
| 190 | int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, | 191 | int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, |
| @@ -199,6 +200,20 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, | |||
| 199 | void mesh_ids_set_default(struct ieee80211_if_mesh *mesh); | 200 | void mesh_ids_set_default(struct ieee80211_if_mesh *mesh); |
| 200 | void mesh_mgmt_ies_add(struct sk_buff *skb, | 201 | void mesh_mgmt_ies_add(struct sk_buff *skb, |
| 201 | struct ieee80211_sub_if_data *sdata); | 202 | struct ieee80211_sub_if_data *sdata); |
| 203 | int mesh_add_meshconf_ie(struct sk_buff *skb, | ||
| 204 | struct ieee80211_sub_if_data *sdata); | ||
| 205 | int mesh_add_meshid_ie(struct sk_buff *skb, | ||
| 206 | struct ieee80211_sub_if_data *sdata); | ||
| 207 | int mesh_add_rsn_ie(struct sk_buff *skb, | ||
| 208 | struct ieee80211_sub_if_data *sdata); | ||
| 209 | int mesh_add_vendor_ies(struct sk_buff *skb, | ||
| 210 | struct ieee80211_sub_if_data *sdata); | ||
| 211 | int mesh_add_srates_ie(struct sk_buff *skb, | ||
| 212 | struct ieee80211_sub_if_data *sdata); | ||
| 213 | int mesh_add_ext_srates_ie(struct sk_buff *skb, | ||
| 214 | struct ieee80211_sub_if_data *sdata); | ||
| 215 | int mesh_add_ds_params_ie(struct sk_buff *skb, | ||
| 216 | struct ieee80211_sub_if_data *sdata); | ||
| 202 | void mesh_rmc_free(struct ieee80211_sub_if_data *sdata); | 217 | void mesh_rmc_free(struct ieee80211_sub_if_data *sdata); |
| 203 | int mesh_rmc_init(struct ieee80211_sub_if_data *sdata); | 218 | int mesh_rmc_init(struct ieee80211_sub_if_data *sdata); |
| 204 | void ieee80211s_init(void); | 219 | void ieee80211s_init(void); |
| @@ -227,6 +242,10 @@ void mesh_path_flush(struct ieee80211_sub_if_data *sdata); | |||
| 227 | void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, | 242 | void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, |
| 228 | struct ieee80211_mgmt *mgmt, size_t len); | 243 | struct ieee80211_mgmt *mgmt, size_t len); |
| 229 | int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata); | 244 | int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata); |
| 245 | |||
| 246 | int mesh_path_add_gate(struct mesh_path *mpath); | ||
| 247 | int mesh_path_send_to_gates(struct mesh_path *mpath); | ||
| 248 | int mesh_gate_num(struct ieee80211_sub_if_data *sdata); | ||
| 230 | /* Mesh plinks */ | 249 | /* Mesh plinks */ |
| 231 | void mesh_neighbour_update(u8 *hw_addr, u32 rates, | 250 | void mesh_neighbour_update(u8 *hw_addr, u32 rates, |
| 232 | struct ieee80211_sub_if_data *sdata, | 251 | struct ieee80211_sub_if_data *sdata, |
| @@ -262,6 +281,7 @@ void mesh_path_quiesce(struct ieee80211_sub_if_data *sdata); | |||
| 262 | void mesh_path_restart(struct ieee80211_sub_if_data *sdata); | 281 | void mesh_path_restart(struct ieee80211_sub_if_data *sdata); |
| 263 | void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata); | 282 | void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata); |
| 264 | 283 | ||
| 284 | bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt); | ||
| 265 | extern int mesh_paths_generation; | 285 | extern int mesh_paths_generation; |
| 266 | 286 | ||
| 267 | #ifdef CONFIG_MAC80211_MESH | 287 | #ifdef CONFIG_MAC80211_MESH |
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 3d8e55ae6ab6..fd4f76a3e139 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c | |||
| @@ -11,7 +11,8 @@ | |||
| 11 | #include "mesh.h" | 11 | #include "mesh.h" |
| 12 | 12 | ||
| 13 | #ifdef CONFIG_MAC80211_VERBOSE_MHWMP_DEBUG | 13 | #ifdef CONFIG_MAC80211_VERBOSE_MHWMP_DEBUG |
| 14 | #define mhwmp_dbg(fmt, args...) printk(KERN_DEBUG "Mesh HWMP: " fmt, ##args) | 14 | #define mhwmp_dbg(fmt, args...) \ |
| 15 | printk(KERN_DEBUG "Mesh HWMP (%s): " fmt "\n", sdata->name, ##args) | ||
| 15 | #else | 16 | #else |
| 16 | #define mhwmp_dbg(fmt, args...) do { (void)(0); } while (0) | 17 | #define mhwmp_dbg(fmt, args...) do { (void)(0); } while (0) |
| 17 | #endif | 18 | #endif |
| @@ -68,12 +69,12 @@ static inline u32 u16_field_get(u8 *preq_elem, int offset, bool ae) | |||
| 68 | #define PREP_IE_FLAGS(x) PREQ_IE_FLAGS(x) | 69 | #define PREP_IE_FLAGS(x) PREQ_IE_FLAGS(x) |
| 69 | #define PREP_IE_HOPCOUNT(x) PREQ_IE_HOPCOUNT(x) | 70 | #define PREP_IE_HOPCOUNT(x) PREQ_IE_HOPCOUNT(x) |
| 70 | #define PREP_IE_TTL(x) PREQ_IE_TTL(x) | 71 | #define PREP_IE_TTL(x) PREQ_IE_TTL(x) |
| 71 | #define PREP_IE_ORIG_ADDR(x) (x + 3) | 72 | #define PREP_IE_ORIG_ADDR(x) (AE_F_SET(x) ? x + 27 : x + 21) |
| 72 | #define PREP_IE_ORIG_SN(x) u32_field_get(x, 9, 0) | 73 | #define PREP_IE_ORIG_SN(x) u32_field_get(x, 27, AE_F_SET(x)) |
| 73 | #define PREP_IE_LIFETIME(x) u32_field_get(x, 13, AE_F_SET(x)) | 74 | #define PREP_IE_LIFETIME(x) u32_field_get(x, 13, AE_F_SET(x)) |
| 74 | #define PREP_IE_METRIC(x) u32_field_get(x, 17, AE_F_SET(x)) | 75 | #define PREP_IE_METRIC(x) u32_field_get(x, 17, AE_F_SET(x)) |
| 75 | #define PREP_IE_TARGET_ADDR(x) (AE_F_SET(x) ? x + 27 : x + 21) | 76 | #define PREP_IE_TARGET_ADDR(x) (x + 3) |
| 76 | #define PREP_IE_TARGET_SN(x) u32_field_get(x, 27, AE_F_SET(x)) | 77 | #define PREP_IE_TARGET_SN(x) u32_field_get(x, 9, 0) |
| 77 | 78 | ||
| 78 | #define PERR_IE_TTL(x) (*(x)) | 79 | #define PERR_IE_TTL(x) (*(x)) |
| 79 | #define PERR_IE_TARGET_FLAGS(x) (*(x + 2)) | 80 | #define PERR_IE_TARGET_FLAGS(x) (*(x + 2)) |
| @@ -132,24 +133,25 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags, | |||
| 132 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); | 133 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); |
| 133 | /* BSSID == SA */ | 134 | /* BSSID == SA */ |
| 134 | memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); | 135 | memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); |
| 135 | mgmt->u.action.category = WLAN_CATEGORY_MESH_PATH_SEL; | 136 | mgmt->u.action.category = WLAN_CATEGORY_MESH_ACTION; |
| 136 | mgmt->u.action.u.mesh_action.action_code = MESH_PATH_SEL_ACTION; | 137 | mgmt->u.action.u.mesh_action.action_code = |
| 138 | WLAN_MESH_ACTION_HWMP_PATH_SELECTION; | ||
| 137 | 139 | ||
| 138 | switch (action) { | 140 | switch (action) { |
| 139 | case MPATH_PREQ: | 141 | case MPATH_PREQ: |
| 140 | mhwmp_dbg("sending PREQ to %pM\n", target); | 142 | mhwmp_dbg("sending PREQ to %pM", target); |
| 141 | ie_len = 37; | 143 | ie_len = 37; |
| 142 | pos = skb_put(skb, 2 + ie_len); | 144 | pos = skb_put(skb, 2 + ie_len); |
| 143 | *pos++ = WLAN_EID_PREQ; | 145 | *pos++ = WLAN_EID_PREQ; |
| 144 | break; | 146 | break; |
| 145 | case MPATH_PREP: | 147 | case MPATH_PREP: |
| 146 | mhwmp_dbg("sending PREP to %pM\n", target); | 148 | mhwmp_dbg("sending PREP to %pM", target); |
| 147 | ie_len = 31; | 149 | ie_len = 31; |
| 148 | pos = skb_put(skb, 2 + ie_len); | 150 | pos = skb_put(skb, 2 + ie_len); |
| 149 | *pos++ = WLAN_EID_PREP; | 151 | *pos++ = WLAN_EID_PREP; |
| 150 | break; | 152 | break; |
| 151 | case MPATH_RANN: | 153 | case MPATH_RANN: |
| 152 | mhwmp_dbg("sending RANN from %pM\n", orig_addr); | 154 | mhwmp_dbg("sending RANN from %pM", orig_addr); |
| 153 | ie_len = sizeof(struct ieee80211_rann_ie); | 155 | ie_len = sizeof(struct ieee80211_rann_ie); |
| 154 | pos = skb_put(skb, 2 + ie_len); | 156 | pos = skb_put(skb, 2 + ie_len); |
| 155 | *pos++ = WLAN_EID_RANN; | 157 | *pos++ = WLAN_EID_RANN; |
| @@ -163,29 +165,37 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags, | |||
| 163 | *pos++ = flags; | 165 | *pos++ = flags; |
| 164 | *pos++ = hop_count; | 166 | *pos++ = hop_count; |
| 165 | *pos++ = ttl; | 167 | *pos++ = ttl; |
| 166 | if (action == MPATH_PREQ) { | 168 | if (action == MPATH_PREP) { |
| 167 | memcpy(pos, &preq_id, 4); | 169 | memcpy(pos, target, ETH_ALEN); |
| 170 | pos += ETH_ALEN; | ||
| 171 | memcpy(pos, &target_sn, 4); | ||
| 168 | pos += 4; | 172 | pos += 4; |
| 169 | } | 173 | } else { |
| 170 | memcpy(pos, orig_addr, ETH_ALEN); | 174 | if (action == MPATH_PREQ) { |
| 171 | pos += ETH_ALEN; | 175 | memcpy(pos, &preq_id, 4); |
| 172 | memcpy(pos, &orig_sn, 4); | 176 | pos += 4; |
| 173 | pos += 4; | 177 | } |
| 174 | if (action != MPATH_RANN) { | 178 | memcpy(pos, orig_addr, ETH_ALEN); |
| 175 | memcpy(pos, &lifetime, 4); | 179 | pos += ETH_ALEN; |
| 180 | memcpy(pos, &orig_sn, 4); | ||
| 176 | pos += 4; | 181 | pos += 4; |
| 177 | } | 182 | } |
| 183 | memcpy(pos, &lifetime, 4); /* interval for RANN */ | ||
| 184 | pos += 4; | ||
| 178 | memcpy(pos, &metric, 4); | 185 | memcpy(pos, &metric, 4); |
| 179 | pos += 4; | 186 | pos += 4; |
| 180 | if (action == MPATH_PREQ) { | 187 | if (action == MPATH_PREQ) { |
| 181 | /* destination count */ | 188 | *pos++ = 1; /* destination count */ |
| 182 | *pos++ = 1; | ||
| 183 | *pos++ = target_flags; | 189 | *pos++ = target_flags; |
| 184 | } | ||
| 185 | if (action != MPATH_RANN) { | ||
| 186 | memcpy(pos, target, ETH_ALEN); | 190 | memcpy(pos, target, ETH_ALEN); |
| 187 | pos += ETH_ALEN; | 191 | pos += ETH_ALEN; |
| 188 | memcpy(pos, &target_sn, 4); | 192 | memcpy(pos, &target_sn, 4); |
| 193 | pos += 4; | ||
| 194 | } else if (action == MPATH_PREP) { | ||
| 195 | memcpy(pos, orig_addr, ETH_ALEN); | ||
| 196 | pos += ETH_ALEN; | ||
| 197 | memcpy(pos, &orig_sn, 4); | ||
| 198 | pos += 4; | ||
| 189 | } | 199 | } |
| 190 | 200 | ||
| 191 | ieee80211_tx_skb(sdata, skb); | 201 | ieee80211_tx_skb(sdata, skb); |
| @@ -224,9 +234,11 @@ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, | |||
| 224 | 234 | ||
| 225 | memcpy(mgmt->da, ra, ETH_ALEN); | 235 | memcpy(mgmt->da, ra, ETH_ALEN); |
| 226 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); | 236 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); |
| 227 | /* BSSID is left zeroed, wildcard value */ | 237 | /* BSSID == SA */ |
| 228 | mgmt->u.action.category = WLAN_CATEGORY_MESH_PATH_SEL; | 238 | memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); |
| 229 | mgmt->u.action.u.mesh_action.action_code = MESH_PATH_SEL_ACTION; | 239 | mgmt->u.action.category = WLAN_CATEGORY_MESH_ACTION; |
| 240 | mgmt->u.action.u.mesh_action.action_code = | ||
| 241 | WLAN_MESH_ACTION_HWMP_PATH_SELECTION; | ||
| 230 | ie_len = 15; | 242 | ie_len = 15; |
| 231 | pos = skb_put(skb, 2 + ie_len); | 243 | pos = skb_put(skb, 2 + ie_len); |
| 232 | *pos++ = WLAN_EID_PERR; | 244 | *pos++ = WLAN_EID_PERR; |
| @@ -483,10 +495,10 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, | |||
| 483 | orig_sn = PREQ_IE_ORIG_SN(preq_elem); | 495 | orig_sn = PREQ_IE_ORIG_SN(preq_elem); |
| 484 | target_flags = PREQ_IE_TARGET_F(preq_elem); | 496 | target_flags = PREQ_IE_TARGET_F(preq_elem); |
| 485 | 497 | ||
| 486 | mhwmp_dbg("received PREQ from %pM\n", orig_addr); | 498 | mhwmp_dbg("received PREQ from %pM", orig_addr); |
| 487 | 499 | ||
| 488 | if (memcmp(target_addr, sdata->vif.addr, ETH_ALEN) == 0) { | 500 | if (memcmp(target_addr, sdata->vif.addr, ETH_ALEN) == 0) { |
| 489 | mhwmp_dbg("PREQ is for us\n"); | 501 | mhwmp_dbg("PREQ is for us"); |
| 490 | forward = false; | 502 | forward = false; |
| 491 | reply = true; | 503 | reply = true; |
| 492 | metric = 0; | 504 | metric = 0; |
| @@ -522,7 +534,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, | |||
| 522 | lifetime = PREQ_IE_LIFETIME(preq_elem); | 534 | lifetime = PREQ_IE_LIFETIME(preq_elem); |
| 523 | ttl = ifmsh->mshcfg.element_ttl; | 535 | ttl = ifmsh->mshcfg.element_ttl; |
| 524 | if (ttl != 0) { | 536 | if (ttl != 0) { |
| 525 | mhwmp_dbg("replying to the PREQ\n"); | 537 | mhwmp_dbg("replying to the PREQ"); |
| 526 | mesh_path_sel_frame_tx(MPATH_PREP, 0, target_addr, | 538 | mesh_path_sel_frame_tx(MPATH_PREP, 0, target_addr, |
| 527 | cpu_to_le32(target_sn), 0, orig_addr, | 539 | cpu_to_le32(target_sn), 0, orig_addr, |
| 528 | cpu_to_le32(orig_sn), mgmt->sa, 0, ttl, | 540 | cpu_to_le32(orig_sn), mgmt->sa, 0, ttl, |
| @@ -542,7 +554,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, | |||
| 542 | ifmsh->mshstats.dropped_frames_ttl++; | 554 | ifmsh->mshstats.dropped_frames_ttl++; |
| 543 | return; | 555 | return; |
| 544 | } | 556 | } |
| 545 | mhwmp_dbg("forwarding the PREQ from %pM\n", orig_addr); | 557 | mhwmp_dbg("forwarding the PREQ from %pM", orig_addr); |
| 546 | --ttl; | 558 | --ttl; |
| 547 | flags = PREQ_IE_FLAGS(preq_elem); | 559 | flags = PREQ_IE_FLAGS(preq_elem); |
| 548 | preq_id = PREQ_IE_PREQ_ID(preq_elem); | 560 | preq_id = PREQ_IE_PREQ_ID(preq_elem); |
| @@ -577,7 +589,7 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, | |||
| 577 | u8 next_hop[ETH_ALEN]; | 589 | u8 next_hop[ETH_ALEN]; |
| 578 | u32 target_sn, orig_sn, lifetime; | 590 | u32 target_sn, orig_sn, lifetime; |
| 579 | 591 | ||
| 580 | mhwmp_dbg("received PREP from %pM\n", PREP_IE_ORIG_ADDR(prep_elem)); | 592 | mhwmp_dbg("received PREP from %pM", PREP_IE_ORIG_ADDR(prep_elem)); |
| 581 | 593 | ||
| 582 | /* Note that we divert from the draft nomenclature and denominate | 594 | /* Note that we divert from the draft nomenclature and denominate |
| 583 | * destination to what the draft refers to as origininator. So in this | 595 | * destination to what the draft refers to as origininator. So in this |
| @@ -683,6 +695,8 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, | |||
| 683 | u8 ttl, flags, hopcount; | 695 | u8 ttl, flags, hopcount; |
| 684 | u8 *orig_addr; | 696 | u8 *orig_addr; |
| 685 | u32 orig_sn, metric; | 697 | u32 orig_sn, metric; |
| 698 | u32 interval = ifmsh->mshcfg.dot11MeshHWMPRannInterval; | ||
| 699 | bool root_is_gate; | ||
| 686 | 700 | ||
| 687 | ttl = rann->rann_ttl; | 701 | ttl = rann->rann_ttl; |
| 688 | if (ttl <= 1) { | 702 | if (ttl <= 1) { |
| @@ -691,12 +705,19 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, | |||
| 691 | } | 705 | } |
| 692 | ttl--; | 706 | ttl--; |
| 693 | flags = rann->rann_flags; | 707 | flags = rann->rann_flags; |
| 708 | root_is_gate = !!(flags & RANN_FLAG_IS_GATE); | ||
| 694 | orig_addr = rann->rann_addr; | 709 | orig_addr = rann->rann_addr; |
| 695 | orig_sn = rann->rann_seq; | 710 | orig_sn = rann->rann_seq; |
| 696 | hopcount = rann->rann_hopcount; | 711 | hopcount = rann->rann_hopcount; |
| 697 | hopcount++; | 712 | hopcount++; |
| 698 | metric = rann->rann_metric; | 713 | metric = rann->rann_metric; |
| 699 | mhwmp_dbg("received RANN from %pM\n", orig_addr); | 714 | |
| 715 | /* Ignore our own RANNs */ | ||
| 716 | if (memcmp(orig_addr, sdata->vif.addr, ETH_ALEN) == 0) | ||
| 717 | return; | ||
| 718 | |||
| 719 | mhwmp_dbg("received RANN from %pM (is_gate=%d)", orig_addr, | ||
| 720 | root_is_gate); | ||
| 700 | 721 | ||
| 701 | rcu_read_lock(); | 722 | rcu_read_lock(); |
| 702 | mpath = mesh_path_lookup(orig_addr, sdata); | 723 | mpath = mesh_path_lookup(orig_addr, sdata); |
| @@ -708,18 +729,28 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, | |||
| 708 | sdata->u.mesh.mshstats.dropped_frames_no_route++; | 729 | sdata->u.mesh.mshstats.dropped_frames_no_route++; |
| 709 | return; | 730 | return; |
| 710 | } | 731 | } |
| 711 | mesh_queue_preq(mpath, | ||
| 712 | PREQ_Q_F_START | PREQ_Q_F_REFRESH); | ||
| 713 | } | 732 | } |
| 733 | |||
| 734 | if ((!(mpath->flags & (MESH_PATH_ACTIVE | MESH_PATH_RESOLVING)) || | ||
| 735 | time_after(jiffies, mpath->exp_time - 1*HZ)) && | ||
| 736 | !(mpath->flags & MESH_PATH_FIXED)) { | ||
| 737 | mhwmp_dbg("%s time to refresh root mpath %pM", sdata->name, | ||
| 738 | orig_addr); | ||
| 739 | mesh_queue_preq(mpath, PREQ_Q_F_START | PREQ_Q_F_REFRESH); | ||
| 740 | } | ||
| 741 | |||
| 714 | if (mpath->sn < orig_sn) { | 742 | if (mpath->sn < orig_sn) { |
| 715 | mesh_path_sel_frame_tx(MPATH_RANN, flags, orig_addr, | 743 | mesh_path_sel_frame_tx(MPATH_RANN, flags, orig_addr, |
| 716 | cpu_to_le32(orig_sn), | 744 | cpu_to_le32(orig_sn), |
| 717 | 0, NULL, 0, broadcast_addr, | 745 | 0, NULL, 0, broadcast_addr, |
| 718 | hopcount, ttl, 0, | 746 | hopcount, ttl, cpu_to_le32(interval), |
| 719 | cpu_to_le32(metric + mpath->metric), | 747 | cpu_to_le32(metric + mpath->metric), |
| 720 | 0, sdata); | 748 | 0, sdata); |
| 721 | mpath->sn = orig_sn; | 749 | mpath->sn = orig_sn; |
| 722 | } | 750 | } |
| 751 | if (root_is_gate) | ||
| 752 | mesh_path_add_gate(mpath); | ||
| 753 | |||
| 723 | rcu_read_unlock(); | 754 | rcu_read_unlock(); |
| 724 | } | 755 | } |
| 725 | 756 | ||
| @@ -787,7 +818,7 @@ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags) | |||
| 787 | 818 | ||
| 788 | preq_node = kmalloc(sizeof(struct mesh_preq_queue), GFP_ATOMIC); | 819 | preq_node = kmalloc(sizeof(struct mesh_preq_queue), GFP_ATOMIC); |
| 789 | if (!preq_node) { | 820 | if (!preq_node) { |
| 790 | mhwmp_dbg("could not allocate PREQ node\n"); | 821 | mhwmp_dbg("could not allocate PREQ node"); |
| 791 | return; | 822 | return; |
| 792 | } | 823 | } |
| 793 | 824 | ||
| @@ -796,7 +827,7 @@ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags) | |||
| 796 | spin_unlock_bh(&ifmsh->mesh_preq_queue_lock); | 827 | spin_unlock_bh(&ifmsh->mesh_preq_queue_lock); |
| 797 | kfree(preq_node); | 828 | kfree(preq_node); |
| 798 | if (printk_ratelimit()) | 829 | if (printk_ratelimit()) |
| 799 | mhwmp_dbg("PREQ node queue full\n"); | 830 | mhwmp_dbg("PREQ node queue full"); |
| 800 | return; | 831 | return; |
| 801 | } | 832 | } |
| 802 | 833 | ||
| @@ -981,35 +1012,46 @@ void mesh_path_timer(unsigned long data) | |||
| 981 | { | 1012 | { |
| 982 | struct mesh_path *mpath = (void *) data; | 1013 | struct mesh_path *mpath = (void *) data; |
| 983 | struct ieee80211_sub_if_data *sdata = mpath->sdata; | 1014 | struct ieee80211_sub_if_data *sdata = mpath->sdata; |
| 1015 | int ret; | ||
| 984 | 1016 | ||
| 985 | if (sdata->local->quiescing) | 1017 | if (sdata->local->quiescing) |
| 986 | return; | 1018 | return; |
| 987 | 1019 | ||
| 988 | spin_lock_bh(&mpath->state_lock); | 1020 | spin_lock_bh(&mpath->state_lock); |
| 989 | if (mpath->flags & MESH_PATH_RESOLVED || | 1021 | if (mpath->flags & MESH_PATH_RESOLVED || |
| 990 | (!(mpath->flags & MESH_PATH_RESOLVING))) | 1022 | (!(mpath->flags & MESH_PATH_RESOLVING))) { |
| 991 | mpath->flags &= ~(MESH_PATH_RESOLVING | MESH_PATH_RESOLVED); | 1023 | mpath->flags &= ~(MESH_PATH_RESOLVING | MESH_PATH_RESOLVED); |
| 992 | else if (mpath->discovery_retries < max_preq_retries(sdata)) { | 1024 | spin_unlock_bh(&mpath->state_lock); |
| 1025 | } else if (mpath->discovery_retries < max_preq_retries(sdata)) { | ||
| 993 | ++mpath->discovery_retries; | 1026 | ++mpath->discovery_retries; |
| 994 | mpath->discovery_timeout *= 2; | 1027 | mpath->discovery_timeout *= 2; |
| 1028 | spin_unlock_bh(&mpath->state_lock); | ||
| 995 | mesh_queue_preq(mpath, 0); | 1029 | mesh_queue_preq(mpath, 0); |
| 996 | } else { | 1030 | } else { |
| 997 | mpath->flags = 0; | 1031 | mpath->flags = 0; |
| 998 | mpath->exp_time = jiffies; | 1032 | mpath->exp_time = jiffies; |
| 999 | mesh_path_flush_pending(mpath); | 1033 | spin_unlock_bh(&mpath->state_lock); |
| 1034 | if (!mpath->is_gate && mesh_gate_num(sdata) > 0) { | ||
| 1035 | ret = mesh_path_send_to_gates(mpath); | ||
| 1036 | if (ret) | ||
| 1037 | mhwmp_dbg("no gate was reachable"); | ||
| 1038 | } else | ||
| 1039 | mesh_path_flush_pending(mpath); | ||
| 1000 | } | 1040 | } |
| 1001 | |||
| 1002 | spin_unlock_bh(&mpath->state_lock); | ||
| 1003 | } | 1041 | } |
| 1004 | 1042 | ||
| 1005 | void | 1043 | void |
| 1006 | mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata) | 1044 | mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata) |
| 1007 | { | 1045 | { |
| 1008 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | 1046 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; |
| 1047 | u32 interval = ifmsh->mshcfg.dot11MeshHWMPRannInterval; | ||
| 1048 | u8 flags; | ||
| 1009 | 1049 | ||
| 1010 | mesh_path_sel_frame_tx(MPATH_RANN, 0, sdata->vif.addr, | 1050 | flags = (ifmsh->mshcfg.dot11MeshGateAnnouncementProtocol) |
| 1051 | ? RANN_FLAG_IS_GATE : 0; | ||
| 1052 | mesh_path_sel_frame_tx(MPATH_RANN, flags, sdata->vif.addr, | ||
| 1011 | cpu_to_le32(++ifmsh->sn), | 1053 | cpu_to_le32(++ifmsh->sn), |
| 1012 | 0, NULL, 0, broadcast_addr, | 1054 | 0, NULL, 0, broadcast_addr, |
| 1013 | 0, sdata->u.mesh.mshcfg.element_ttl, | 1055 | 0, sdata->u.mesh.mshcfg.element_ttl, |
| 1014 | 0, 0, 0, sdata); | 1056 | cpu_to_le32(interval), 0, 0, sdata); |
| 1015 | } | 1057 | } |
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index dc7ae8d31aaf..7fde32159fdc 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c | |||
| @@ -17,6 +17,12 @@ | |||
| 17 | #include "ieee80211_i.h" | 17 | #include "ieee80211_i.h" |
| 18 | #include "mesh.h" | 18 | #include "mesh.h" |
| 19 | 19 | ||
| 20 | #ifdef CONFIG_MAC80211_VERBOSE_MPATH_DEBUG | ||
| 21 | #define mpath_dbg(fmt, args...) printk(KERN_DEBUG fmt, ##args) | ||
| 22 | #else | ||
| 23 | #define mpath_dbg(fmt, args...) do { (void)(0); } while (0) | ||
| 24 | #endif | ||
| 25 | |||
| 20 | /* There will be initially 2^INIT_PATHS_SIZE_ORDER buckets */ | 26 | /* There will be initially 2^INIT_PATHS_SIZE_ORDER buckets */ |
| 21 | #define INIT_PATHS_SIZE_ORDER 2 | 27 | #define INIT_PATHS_SIZE_ORDER 2 |
| 22 | 28 | ||
| @@ -60,6 +66,8 @@ static inline struct mesh_table *resize_dereference_mpp_paths(void) | |||
| 60 | lockdep_is_held(&pathtbl_resize_lock)); | 66 | lockdep_is_held(&pathtbl_resize_lock)); |
| 61 | } | 67 | } |
| 62 | 68 | ||
| 69 | static int mesh_gate_add(struct mesh_table *tbl, struct mesh_path *mpath); | ||
| 70 | |||
| 63 | /* | 71 | /* |
| 64 | * CAREFUL -- "tbl" must not be an expression, | 72 | * CAREFUL -- "tbl" must not be an expression, |
| 65 | * in particular not an rcu_dereference(), since | 73 | * in particular not an rcu_dereference(), since |
| @@ -103,6 +111,7 @@ static struct mesh_table *mesh_table_alloc(int size_order) | |||
| 103 | sizeof(newtbl->hash_rnd)); | 111 | sizeof(newtbl->hash_rnd)); |
| 104 | for (i = 0; i <= newtbl->hash_mask; i++) | 112 | for (i = 0; i <= newtbl->hash_mask; i++) |
| 105 | spin_lock_init(&newtbl->hashwlock[i]); | 113 | spin_lock_init(&newtbl->hashwlock[i]); |
| 114 | spin_lock_init(&newtbl->gates_lock); | ||
| 106 | 115 | ||
| 107 | return newtbl; | 116 | return newtbl; |
| 108 | } | 117 | } |
| @@ -118,6 +127,7 @@ static void mesh_table_free(struct mesh_table *tbl, bool free_leafs) | |||
| 118 | { | 127 | { |
| 119 | struct hlist_head *mesh_hash; | 128 | struct hlist_head *mesh_hash; |
| 120 | struct hlist_node *p, *q; | 129 | struct hlist_node *p, *q; |
| 130 | struct mpath_node *gate; | ||
| 121 | int i; | 131 | int i; |
| 122 | 132 | ||
| 123 | mesh_hash = tbl->hash_buckets; | 133 | mesh_hash = tbl->hash_buckets; |
| @@ -129,6 +139,17 @@ static void mesh_table_free(struct mesh_table *tbl, bool free_leafs) | |||
| 129 | } | 139 | } |
| 130 | spin_unlock_bh(&tbl->hashwlock[i]); | 140 | spin_unlock_bh(&tbl->hashwlock[i]); |
| 131 | } | 141 | } |
| 142 | if (free_leafs) { | ||
| 143 | spin_lock_bh(&tbl->gates_lock); | ||
| 144 | hlist_for_each_entry_safe(gate, p, q, | ||
| 145 | tbl->known_gates, list) { | ||
| 146 | hlist_del(&gate->list); | ||
| 147 | kfree(gate); | ||
| 148 | } | ||
| 149 | kfree(tbl->known_gates); | ||
| 150 | spin_unlock_bh(&tbl->gates_lock); | ||
| 151 | } | ||
| 152 | |||
| 132 | __mesh_table_free(tbl); | 153 | __mesh_table_free(tbl); |
| 133 | } | 154 | } |
| 134 | 155 | ||
| @@ -146,6 +167,7 @@ static int mesh_table_grow(struct mesh_table *oldtbl, | |||
| 146 | newtbl->free_node = oldtbl->free_node; | 167 | newtbl->free_node = oldtbl->free_node; |
| 147 | newtbl->mean_chain_len = oldtbl->mean_chain_len; | 168 | newtbl->mean_chain_len = oldtbl->mean_chain_len; |
| 148 | newtbl->copy_node = oldtbl->copy_node; | 169 | newtbl->copy_node = oldtbl->copy_node; |
| 170 | newtbl->known_gates = oldtbl->known_gates; | ||
| 149 | atomic_set(&newtbl->entries, atomic_read(&oldtbl->entries)); | 171 | atomic_set(&newtbl->entries, atomic_read(&oldtbl->entries)); |
| 150 | 172 | ||
| 151 | oldhash = oldtbl->hash_buckets; | 173 | oldhash = oldtbl->hash_buckets; |
| @@ -205,6 +227,111 @@ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta) | |||
| 205 | spin_unlock_irqrestore(&mpath->frame_queue.lock, flags); | 227 | spin_unlock_irqrestore(&mpath->frame_queue.lock, flags); |
| 206 | } | 228 | } |
| 207 | 229 | ||
| 230 | static void prepare_for_gate(struct sk_buff *skb, char *dst_addr, | ||
| 231 | struct mesh_path *gate_mpath) | ||
| 232 | { | ||
| 233 | struct ieee80211_hdr *hdr; | ||
| 234 | struct ieee80211s_hdr *mshdr; | ||
| 235 | int mesh_hdrlen, hdrlen; | ||
| 236 | char *next_hop; | ||
| 237 | |||
| 238 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 239 | hdrlen = ieee80211_hdrlen(hdr->frame_control); | ||
| 240 | mshdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); | ||
| 241 | |||
| 242 | if (!(mshdr->flags & MESH_FLAGS_AE)) { | ||
| 243 | /* size of the fixed part of the mesh header */ | ||
| 244 | mesh_hdrlen = 6; | ||
| 245 | |||
| 246 | /* make room for the two extended addresses */ | ||
| 247 | skb_push(skb, 2 * ETH_ALEN); | ||
| 248 | memmove(skb->data, hdr, hdrlen + mesh_hdrlen); | ||
| 249 | |||
| 250 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 251 | |||
| 252 | /* we preserve the previous mesh header and only add | ||
| 253 | * the new addreses */ | ||
| 254 | mshdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); | ||
| 255 | mshdr->flags = MESH_FLAGS_AE_A5_A6; | ||
| 256 | memcpy(mshdr->eaddr1, hdr->addr3, ETH_ALEN); | ||
| 257 | memcpy(mshdr->eaddr2, hdr->addr4, ETH_ALEN); | ||
| 258 | } | ||
| 259 | |||
| 260 | /* update next hop */ | ||
| 261 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 262 | rcu_read_lock(); | ||
| 263 | next_hop = rcu_dereference(gate_mpath->next_hop)->sta.addr; | ||
| 264 | memcpy(hdr->addr1, next_hop, ETH_ALEN); | ||
| 265 | rcu_read_unlock(); | ||
| 266 | memcpy(hdr->addr3, dst_addr, ETH_ALEN); | ||
| 267 | } | ||
| 268 | |||
| 269 | /** | ||
| 270 | * | ||
| 271 | * mesh_path_move_to_queue - Move or copy frames from one mpath queue to another | ||
| 272 | * | ||
| 273 | * This function is used to transfer or copy frames from an unresolved mpath to | ||
| 274 | * a gate mpath. The function also adds the Address Extension field and | ||
| 275 | * updates the next hop. | ||
| 276 | * | ||
| 277 | * If a frame already has an Address Extension field, only the next hop and | ||
| 278 | * destination addresses are updated. | ||
| 279 | * | ||
| 280 | * The gate mpath must be an active mpath with a valid mpath->next_hop. | ||
| 281 | * | ||
| 282 | * @mpath: An active mpath the frames will be sent to (i.e. the gate) | ||
| 283 | * @from_mpath: The failed mpath | ||
| 284 | * @copy: When true, copy all the frames to the new mpath queue. When false, | ||
| 285 | * move them. | ||
| 286 | */ | ||
| 287 | static void mesh_path_move_to_queue(struct mesh_path *gate_mpath, | ||
| 288 | struct mesh_path *from_mpath, | ||
| 289 | bool copy) | ||
| 290 | { | ||
| 291 | struct sk_buff *skb, *cp_skb = NULL; | ||
| 292 | struct sk_buff_head gateq, failq; | ||
| 293 | unsigned long flags; | ||
| 294 | int num_skbs; | ||
| 295 | |||
| 296 | BUG_ON(gate_mpath == from_mpath); | ||
| 297 | BUG_ON(!gate_mpath->next_hop); | ||
| 298 | |||
| 299 | __skb_queue_head_init(&gateq); | ||
| 300 | __skb_queue_head_init(&failq); | ||
| 301 | |||
| 302 | spin_lock_irqsave(&from_mpath->frame_queue.lock, flags); | ||
| 303 | skb_queue_splice_init(&from_mpath->frame_queue, &failq); | ||
| 304 | spin_unlock_irqrestore(&from_mpath->frame_queue.lock, flags); | ||
| 305 | |||
| 306 | num_skbs = skb_queue_len(&failq); | ||
| 307 | |||
| 308 | while (num_skbs--) { | ||
| 309 | skb = __skb_dequeue(&failq); | ||
| 310 | if (copy) | ||
| 311 | cp_skb = skb_copy(skb, GFP_ATOMIC); | ||
| 312 | |||
| 313 | prepare_for_gate(skb, gate_mpath->dst, gate_mpath); | ||
| 314 | __skb_queue_tail(&gateq, skb); | ||
| 315 | |||
| 316 | if (copy && cp_skb) | ||
| 317 | __skb_queue_tail(&failq, cp_skb); | ||
| 318 | } | ||
| 319 | |||
| 320 | spin_lock_irqsave(&gate_mpath->frame_queue.lock, flags); | ||
| 321 | skb_queue_splice(&gateq, &gate_mpath->frame_queue); | ||
| 322 | mpath_dbg("Mpath queue for gate %pM has %d frames\n", | ||
| 323 | gate_mpath->dst, | ||
| 324 | skb_queue_len(&gate_mpath->frame_queue)); | ||
| 325 | spin_unlock_irqrestore(&gate_mpath->frame_queue.lock, flags); | ||
| 326 | |||
| 327 | if (!copy) | ||
| 328 | return; | ||
| 329 | |||
| 330 | spin_lock_irqsave(&from_mpath->frame_queue.lock, flags); | ||
| 331 | skb_queue_splice(&failq, &from_mpath->frame_queue); | ||
| 332 | spin_unlock_irqrestore(&from_mpath->frame_queue.lock, flags); | ||
| 333 | } | ||
| 334 | |||
| 208 | 335 | ||
| 209 | /** | 336 | /** |
| 210 | * mesh_path_lookup - look up a path in the mesh path table | 337 | * mesh_path_lookup - look up a path in the mesh path table |
| @@ -304,6 +431,109 @@ struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data | |||
| 304 | return NULL; | 431 | return NULL; |
| 305 | } | 432 | } |
| 306 | 433 | ||
| 434 | static void mesh_gate_node_reclaim(struct rcu_head *rp) | ||
| 435 | { | ||
| 436 | struct mpath_node *node = container_of(rp, struct mpath_node, rcu); | ||
| 437 | kfree(node); | ||
| 438 | } | ||
| 439 | |||
| 440 | /** | ||
| 441 | * mesh_gate_add - mark mpath as path to a mesh gate and add to known_gates | ||
| 442 | * @mesh_tbl: table which contains known_gates list | ||
| 443 | * @mpath: mpath to known mesh gate | ||
| 444 | * | ||
| 445 | * Returns: 0 on success | ||
| 446 | * | ||
| 447 | */ | ||
| 448 | static int mesh_gate_add(struct mesh_table *tbl, struct mesh_path *mpath) | ||
| 449 | { | ||
| 450 | struct mpath_node *gate, *new_gate; | ||
| 451 | struct hlist_node *n; | ||
| 452 | int err; | ||
| 453 | |||
| 454 | rcu_read_lock(); | ||
| 455 | tbl = rcu_dereference(tbl); | ||
| 456 | |||
| 457 | hlist_for_each_entry_rcu(gate, n, tbl->known_gates, list) | ||
| 458 | if (gate->mpath == mpath) { | ||
| 459 | err = -EEXIST; | ||
| 460 | goto err_rcu; | ||
| 461 | } | ||
| 462 | |||
| 463 | new_gate = kzalloc(sizeof(struct mpath_node), GFP_ATOMIC); | ||
| 464 | if (!new_gate) { | ||
| 465 | err = -ENOMEM; | ||
| 466 | goto err_rcu; | ||
| 467 | } | ||
| 468 | |||
| 469 | mpath->is_gate = true; | ||
| 470 | mpath->sdata->u.mesh.num_gates++; | ||
| 471 | new_gate->mpath = mpath; | ||
| 472 | spin_lock_bh(&tbl->gates_lock); | ||
| 473 | hlist_add_head_rcu(&new_gate->list, tbl->known_gates); | ||
| 474 | spin_unlock_bh(&tbl->gates_lock); | ||
| 475 | rcu_read_unlock(); | ||
| 476 | mpath_dbg("Mesh path (%s): Recorded new gate: %pM. %d known gates\n", | ||
| 477 | mpath->sdata->name, mpath->dst, | ||
| 478 | mpath->sdata->u.mesh.num_gates); | ||
| 479 | return 0; | ||
| 480 | err_rcu: | ||
| 481 | rcu_read_unlock(); | ||
| 482 | return err; | ||
| 483 | } | ||
| 484 | |||
| 485 | /** | ||
| 486 | * mesh_gate_del - remove a mesh gate from the list of known gates | ||
| 487 | * @tbl: table which holds our list of known gates | ||
| 488 | * @mpath: gate mpath | ||
| 489 | * | ||
| 490 | * Returns: 0 on success | ||
| 491 | * | ||
| 492 | * Locking: must be called inside rcu_read_lock() section | ||
| 493 | */ | ||
| 494 | static int mesh_gate_del(struct mesh_table *tbl, struct mesh_path *mpath) | ||
| 495 | { | ||
| 496 | struct mpath_node *gate; | ||
| 497 | struct hlist_node *p, *q; | ||
| 498 | |||
| 499 | tbl = rcu_dereference(tbl); | ||
| 500 | |||
| 501 | hlist_for_each_entry_safe(gate, p, q, tbl->known_gates, list) | ||
| 502 | if (gate->mpath == mpath) { | ||
| 503 | spin_lock_bh(&tbl->gates_lock); | ||
| 504 | hlist_del_rcu(&gate->list); | ||
| 505 | call_rcu(&gate->rcu, mesh_gate_node_reclaim); | ||
| 506 | spin_unlock_bh(&tbl->gates_lock); | ||
| 507 | mpath->sdata->u.mesh.num_gates--; | ||
| 508 | mpath->is_gate = false; | ||
| 509 | mpath_dbg("Mesh path (%s): Deleted gate: %pM. " | ||
| 510 | "%d known gates\n", mpath->sdata->name, | ||
| 511 | mpath->dst, mpath->sdata->u.mesh.num_gates); | ||
| 512 | break; | ||
| 513 | } | ||
| 514 | |||
| 515 | return 0; | ||
| 516 | } | ||
| 517 | |||
| 518 | /** | ||
| 519 | * | ||
| 520 | * mesh_path_add_gate - add the given mpath to a mesh gate to our path table | ||
| 521 | * @mpath: gate path to add to table | ||
| 522 | */ | ||
| 523 | int mesh_path_add_gate(struct mesh_path *mpath) | ||
| 524 | { | ||
| 525 | return mesh_gate_add(mesh_paths, mpath); | ||
| 526 | } | ||
| 527 | |||
| 528 | /** | ||
| 529 | * mesh_gate_num - number of gates known to this interface | ||
| 530 | * @sdata: subif data | ||
| 531 | */ | ||
| 532 | int mesh_gate_num(struct ieee80211_sub_if_data *sdata) | ||
| 533 | { | ||
| 534 | return sdata->u.mesh.num_gates; | ||
| 535 | } | ||
| 536 | |||
| 307 | /** | 537 | /** |
| 308 | * mesh_path_add - allocate and add a new path to the mesh path table | 538 | * mesh_path_add - allocate and add a new path to the mesh path table |
| 309 | * @addr: destination address of the path (ETH_ALEN length) | 539 | * @addr: destination address of the path (ETH_ALEN length) |
| @@ -481,6 +711,7 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) | |||
| 481 | new_mpath->flags = 0; | 711 | new_mpath->flags = 0; |
| 482 | skb_queue_head_init(&new_mpath->frame_queue); | 712 | skb_queue_head_init(&new_mpath->frame_queue); |
| 483 | new_node->mpath = new_mpath; | 713 | new_node->mpath = new_mpath; |
| 714 | init_timer(&new_mpath->timer); | ||
| 484 | new_mpath->exp_time = jiffies; | 715 | new_mpath->exp_time = jiffies; |
| 485 | spin_lock_init(&new_mpath->state_lock); | 716 | spin_lock_init(&new_mpath->state_lock); |
| 486 | 717 | ||
| @@ -539,6 +770,7 @@ void mesh_plink_broken(struct sta_info *sta) | |||
| 539 | struct hlist_node *p; | 770 | struct hlist_node *p; |
| 540 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 771 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
| 541 | int i; | 772 | int i; |
| 773 | __le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_DEST_UNREACHABLE); | ||
| 542 | 774 | ||
| 543 | rcu_read_lock(); | 775 | rcu_read_lock(); |
| 544 | tbl = rcu_dereference(mesh_paths); | 776 | tbl = rcu_dereference(mesh_paths); |
| @@ -553,8 +785,7 @@ void mesh_plink_broken(struct sta_info *sta) | |||
| 553 | spin_unlock_bh(&mpath->state_lock); | 785 | spin_unlock_bh(&mpath->state_lock); |
| 554 | mesh_path_error_tx(sdata->u.mesh.mshcfg.element_ttl, | 786 | mesh_path_error_tx(sdata->u.mesh.mshcfg.element_ttl, |
| 555 | mpath->dst, cpu_to_le32(mpath->sn), | 787 | mpath->dst, cpu_to_le32(mpath->sn), |
| 556 | cpu_to_le16(PERR_RCODE_DEST_UNREACH), | 788 | reason, bcast, sdata); |
| 557 | bcast, sdata); | ||
| 558 | } else | 789 | } else |
| 559 | spin_unlock_bh(&mpath->state_lock); | 790 | spin_unlock_bh(&mpath->state_lock); |
| 560 | } | 791 | } |
| @@ -647,12 +878,14 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) | |||
| 647 | mpath = node->mpath; | 878 | mpath = node->mpath; |
| 648 | if (mpath->sdata == sdata && | 879 | if (mpath->sdata == sdata && |
| 649 | memcmp(addr, mpath->dst, ETH_ALEN) == 0) { | 880 | memcmp(addr, mpath->dst, ETH_ALEN) == 0) { |
| 650 | spin_lock(&mpath->state_lock); | 881 | spin_lock_bh(&mpath->state_lock); |
| 882 | if (mpath->is_gate) | ||
| 883 | mesh_gate_del(tbl, mpath); | ||
| 651 | mpath->flags |= MESH_PATH_RESOLVING; | 884 | mpath->flags |= MESH_PATH_RESOLVING; |
| 652 | hlist_del_rcu(&node->list); | 885 | hlist_del_rcu(&node->list); |
| 653 | call_rcu(&node->rcu, mesh_path_node_reclaim); | 886 | call_rcu(&node->rcu, mesh_path_node_reclaim); |
| 654 | atomic_dec(&tbl->entries); | 887 | atomic_dec(&tbl->entries); |
| 655 | spin_unlock(&mpath->state_lock); | 888 | spin_unlock_bh(&mpath->state_lock); |
| 656 | goto enddel; | 889 | goto enddel; |
| 657 | } | 890 | } |
| 658 | } | 891 | } |
| @@ -681,6 +914,58 @@ void mesh_path_tx_pending(struct mesh_path *mpath) | |||
| 681 | } | 914 | } |
| 682 | 915 | ||
| 683 | /** | 916 | /** |
| 917 | * mesh_path_send_to_gates - sends pending frames to all known mesh gates | ||
| 918 | * | ||
| 919 | * @mpath: mesh path whose queue will be emptied | ||
| 920 | * | ||
| 921 | * If there is only one gate, the frames are transferred from the failed mpath | ||
| 922 | * queue to that gate's queue. If there are more than one gates, the frames | ||
| 923 | * are copied from each gate to the next. After frames are copied, the | ||
| 924 | * mpath queues are emptied onto the transmission queue. | ||
| 925 | */ | ||
| 926 | int mesh_path_send_to_gates(struct mesh_path *mpath) | ||
| 927 | { | ||
| 928 | struct ieee80211_sub_if_data *sdata = mpath->sdata; | ||
| 929 | struct hlist_node *n; | ||
| 930 | struct mesh_table *tbl; | ||
| 931 | struct mesh_path *from_mpath = mpath; | ||
| 932 | struct mpath_node *gate = NULL; | ||
| 933 | bool copy = false; | ||
| 934 | struct hlist_head *known_gates; | ||
| 935 | |||
| 936 | rcu_read_lock(); | ||
| 937 | tbl = rcu_dereference(mesh_paths); | ||
| 938 | known_gates = tbl->known_gates; | ||
| 939 | rcu_read_unlock(); | ||
| 940 | |||
| 941 | if (!known_gates) | ||
| 942 | return -EHOSTUNREACH; | ||
| 943 | |||
| 944 | hlist_for_each_entry_rcu(gate, n, known_gates, list) { | ||
| 945 | if (gate->mpath->sdata != sdata) | ||
| 946 | continue; | ||
| 947 | |||
| 948 | if (gate->mpath->flags & MESH_PATH_ACTIVE) { | ||
| 949 | mpath_dbg("Forwarding to %pM\n", gate->mpath->dst); | ||
| 950 | mesh_path_move_to_queue(gate->mpath, from_mpath, copy); | ||
| 951 | from_mpath = gate->mpath; | ||
| 952 | copy = true; | ||
| 953 | } else { | ||
| 954 | mpath_dbg("Not forwarding %p\n", gate->mpath); | ||
| 955 | mpath_dbg("flags %x\n", gate->mpath->flags); | ||
| 956 | } | ||
| 957 | } | ||
| 958 | |||
| 959 | hlist_for_each_entry_rcu(gate, n, known_gates, list) | ||
| 960 | if (gate->mpath->sdata == sdata) { | ||
| 961 | mpath_dbg("Sending to %pM\n", gate->mpath->dst); | ||
| 962 | mesh_path_tx_pending(gate->mpath); | ||
| 963 | } | ||
| 964 | |||
| 965 | return (from_mpath == mpath) ? -EHOSTUNREACH : 0; | ||
| 966 | } | ||
| 967 | |||
| 968 | /** | ||
| 684 | * mesh_path_discard_frame - discard a frame whose path could not be resolved | 969 | * mesh_path_discard_frame - discard a frame whose path could not be resolved |
| 685 | * | 970 | * |
| 686 | * @skb: frame to discard | 971 | * @skb: frame to discard |
| @@ -699,6 +984,7 @@ void mesh_path_discard_frame(struct sk_buff *skb, | |||
| 699 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | 984 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; |
| 700 | struct mesh_path *mpath; | 985 | struct mesh_path *mpath; |
| 701 | u32 sn = 0; | 986 | u32 sn = 0; |
| 987 | __le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_NOFORWARD); | ||
| 702 | 988 | ||
| 703 | if (memcmp(hdr->addr4, sdata->vif.addr, ETH_ALEN) != 0) { | 989 | if (memcmp(hdr->addr4, sdata->vif.addr, ETH_ALEN) != 0) { |
| 704 | u8 *ra, *da; | 990 | u8 *ra, *da; |
| @@ -709,8 +995,7 @@ void mesh_path_discard_frame(struct sk_buff *skb, | |||
| 709 | if (mpath) | 995 | if (mpath) |
| 710 | sn = ++mpath->sn; | 996 | sn = ++mpath->sn; |
| 711 | mesh_path_error_tx(sdata->u.mesh.mshcfg.element_ttl, skb->data, | 997 | mesh_path_error_tx(sdata->u.mesh.mshcfg.element_ttl, skb->data, |
| 712 | cpu_to_le32(sn), | 998 | cpu_to_le32(sn), reason, ra, sdata); |
| 713 | cpu_to_le16(PERR_RCODE_NO_ROUTE), ra, sdata); | ||
| 714 | } | 999 | } |
| 715 | 1000 | ||
| 716 | kfree_skb(skb); | 1001 | kfree_skb(skb); |
| @@ -728,8 +1013,7 @@ void mesh_path_flush_pending(struct mesh_path *mpath) | |||
| 728 | { | 1013 | { |
| 729 | struct sk_buff *skb; | 1014 | struct sk_buff *skb; |
| 730 | 1015 | ||
| 731 | while ((skb = skb_dequeue(&mpath->frame_queue)) && | 1016 | while ((skb = skb_dequeue(&mpath->frame_queue)) != NULL) |
| 732 | (mpath->flags & MESH_PATH_ACTIVE)) | ||
| 733 | mesh_path_discard_frame(skb, mpath->sdata); | 1017 | mesh_path_discard_frame(skb, mpath->sdata); |
| 734 | } | 1018 | } |
| 735 | 1019 | ||
| @@ -797,6 +1081,9 @@ int mesh_pathtbl_init(void) | |||
| 797 | tbl_path->free_node = &mesh_path_node_free; | 1081 | tbl_path->free_node = &mesh_path_node_free; |
| 798 | tbl_path->copy_node = &mesh_path_node_copy; | 1082 | tbl_path->copy_node = &mesh_path_node_copy; |
| 799 | tbl_path->mean_chain_len = MEAN_CHAIN_LEN; | 1083 | tbl_path->mean_chain_len = MEAN_CHAIN_LEN; |
| 1084 | tbl_path->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC); | ||
| 1085 | INIT_HLIST_HEAD(tbl_path->known_gates); | ||
| 1086 | |||
| 800 | 1087 | ||
| 801 | tbl_mpp = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); | 1088 | tbl_mpp = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); |
| 802 | if (!tbl_mpp) { | 1089 | if (!tbl_mpp) { |
| @@ -806,6 +1093,8 @@ int mesh_pathtbl_init(void) | |||
| 806 | tbl_mpp->free_node = &mesh_path_node_free; | 1093 | tbl_mpp->free_node = &mesh_path_node_free; |
| 807 | tbl_mpp->copy_node = &mesh_path_node_copy; | 1094 | tbl_mpp->copy_node = &mesh_path_node_copy; |
| 808 | tbl_mpp->mean_chain_len = MEAN_CHAIN_LEN; | 1095 | tbl_mpp->mean_chain_len = MEAN_CHAIN_LEN; |
| 1096 | tbl_mpp->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC); | ||
| 1097 | INIT_HLIST_HEAD(tbl_mpp->known_gates); | ||
| 809 | 1098 | ||
| 810 | /* Need no locking since this is during init */ | 1099 | /* Need no locking since this is during init */ |
| 811 | RCU_INIT_POINTER(mesh_paths, tbl_path); | 1100 | RCU_INIT_POINTER(mesh_paths, tbl_path); |
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index f4adc0917888..1a00d0f701c3 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c | |||
| @@ -19,35 +19,18 @@ | |||
| 19 | #define mpl_dbg(fmt, args...) do { (void)(0); } while (0) | 19 | #define mpl_dbg(fmt, args...) do { (void)(0); } while (0) |
| 20 | #endif | 20 | #endif |
| 21 | 21 | ||
| 22 | #define PLINK_GET_LLID(p) (p + 4) | 22 | #define PLINK_GET_LLID(p) (p + 2) |
| 23 | #define PLINK_GET_PLID(p) (p + 6) | 23 | #define PLINK_GET_PLID(p) (p + 4) |
| 24 | 24 | ||
| 25 | #define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \ | 25 | #define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \ |
| 26 | jiffies + HZ * t / 1000)) | 26 | jiffies + HZ * t / 1000)) |
| 27 | 27 | ||
| 28 | /* Peer link cancel reasons, all subject to ANA approval */ | ||
| 29 | #define MESH_LINK_CANCELLED 2 | ||
| 30 | #define MESH_MAX_NEIGHBORS 3 | ||
| 31 | #define MESH_CAPABILITY_POLICY_VIOLATION 4 | ||
| 32 | #define MESH_CLOSE_RCVD 5 | ||
| 33 | #define MESH_MAX_RETRIES 6 | ||
| 34 | #define MESH_CONFIRM_TIMEOUT 7 | ||
| 35 | #define MESH_SECURITY_ROLE_NEGOTIATION_DIFFERS 8 | ||
| 36 | #define MESH_SECURITY_AUTHENTICATION_IMPOSSIBLE 9 | ||
| 37 | #define MESH_SECURITY_FAILED_VERIFICATION 10 | ||
| 38 | |||
| 39 | #define dot11MeshMaxRetries(s) (s->u.mesh.mshcfg.dot11MeshMaxRetries) | 28 | #define dot11MeshMaxRetries(s) (s->u.mesh.mshcfg.dot11MeshMaxRetries) |
| 40 | #define dot11MeshRetryTimeout(s) (s->u.mesh.mshcfg.dot11MeshRetryTimeout) | 29 | #define dot11MeshRetryTimeout(s) (s->u.mesh.mshcfg.dot11MeshRetryTimeout) |
| 41 | #define dot11MeshConfirmTimeout(s) (s->u.mesh.mshcfg.dot11MeshConfirmTimeout) | 30 | #define dot11MeshConfirmTimeout(s) (s->u.mesh.mshcfg.dot11MeshConfirmTimeout) |
| 42 | #define dot11MeshHoldingTimeout(s) (s->u.mesh.mshcfg.dot11MeshHoldingTimeout) | 31 | #define dot11MeshHoldingTimeout(s) (s->u.mesh.mshcfg.dot11MeshHoldingTimeout) |
| 43 | #define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks) | 32 | #define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks) |
| 44 | 33 | ||
| 45 | enum plink_frame_type { | ||
| 46 | PLINK_OPEN = 1, | ||
| 47 | PLINK_CONFIRM, | ||
| 48 | PLINK_CLOSE | ||
| 49 | }; | ||
| 50 | |||
| 51 | enum plink_event { | 34 | enum plink_event { |
| 52 | PLINK_UNDEFINED, | 35 | PLINK_UNDEFINED, |
| 53 | OPN_ACPT, | 36 | OPN_ACPT, |
| @@ -157,16 +140,16 @@ void mesh_plink_deactivate(struct sta_info *sta) | |||
| 157 | } | 140 | } |
| 158 | 141 | ||
| 159 | static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, | 142 | static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, |
| 160 | enum plink_frame_type action, u8 *da, __le16 llid, __le16 plid, | 143 | enum ieee80211_self_protected_actioncode action, |
| 161 | __le16 reason) { | 144 | u8 *da, __le16 llid, __le16 plid, __le16 reason) { |
| 162 | struct ieee80211_local *local = sdata->local; | 145 | struct ieee80211_local *local = sdata->local; |
| 163 | struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 + | 146 | struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 + |
| 164 | sdata->u.mesh.ie_len); | 147 | sdata->u.mesh.ie_len); |
| 165 | struct ieee80211_mgmt *mgmt; | 148 | struct ieee80211_mgmt *mgmt; |
| 166 | bool include_plid = false; | 149 | bool include_plid = false; |
| 167 | static const u8 meshpeeringproto[] = { 0x00, 0x0F, 0xAC, 0x2A }; | 150 | int ie_len = 4; |
| 151 | u16 peering_proto = 0; | ||
| 168 | u8 *pos; | 152 | u8 *pos; |
| 169 | int ie_len; | ||
| 170 | 153 | ||
| 171 | if (!skb) | 154 | if (!skb) |
| 172 | return -1; | 155 | return -1; |
| @@ -175,63 +158,75 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, | |||
| 175 | * common action part (1) | 158 | * common action part (1) |
| 176 | */ | 159 | */ |
| 177 | mgmt = (struct ieee80211_mgmt *) | 160 | mgmt = (struct ieee80211_mgmt *) |
| 178 | skb_put(skb, 25 + sizeof(mgmt->u.action.u.plink_action)); | 161 | skb_put(skb, 25 + sizeof(mgmt->u.action.u.self_prot)); |
| 179 | memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.plink_action)); | 162 | memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.self_prot)); |
| 180 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | 163 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | |
| 181 | IEEE80211_STYPE_ACTION); | 164 | IEEE80211_STYPE_ACTION); |
| 182 | memcpy(mgmt->da, da, ETH_ALEN); | 165 | memcpy(mgmt->da, da, ETH_ALEN); |
| 183 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); | 166 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); |
| 184 | memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); | 167 | memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); |
| 185 | mgmt->u.action.category = WLAN_CATEGORY_MESH_ACTION; | 168 | mgmt->u.action.category = WLAN_CATEGORY_SELF_PROTECTED; |
| 186 | mgmt->u.action.u.plink_action.action_code = action; | 169 | mgmt->u.action.u.self_prot.action_code = action; |
| 187 | 170 | ||
| 188 | if (action == PLINK_CLOSE) | 171 | if (action != WLAN_SP_MESH_PEERING_CLOSE) { |
| 189 | mgmt->u.action.u.plink_action.aux = reason; | 172 | /* capability info */ |
| 190 | else { | 173 | pos = skb_put(skb, 2); |
| 191 | mgmt->u.action.u.plink_action.aux = cpu_to_le16(0x0); | 174 | memset(pos, 0, 2); |
| 192 | if (action == PLINK_CONFIRM) { | 175 | if (action == WLAN_SP_MESH_PEERING_CONFIRM) { |
| 193 | pos = skb_put(skb, 4); | 176 | /* AID */ |
| 194 | /* two-byte status code followed by two-byte AID */ | 177 | pos = skb_put(skb, 2); |
| 195 | memset(pos, 0, 2); | ||
| 196 | memcpy(pos + 2, &plid, 2); | 178 | memcpy(pos + 2, &plid, 2); |
| 197 | } | 179 | } |
| 198 | mesh_mgmt_ies_add(skb, sdata); | 180 | if (mesh_add_srates_ie(skb, sdata) || |
| 181 | mesh_add_ext_srates_ie(skb, sdata) || | ||
| 182 | mesh_add_rsn_ie(skb, sdata) || | ||
| 183 | mesh_add_meshid_ie(skb, sdata) || | ||
| 184 | mesh_add_meshconf_ie(skb, sdata)) | ||
| 185 | return -1; | ||
| 186 | } else { /* WLAN_SP_MESH_PEERING_CLOSE */ | ||
| 187 | if (mesh_add_meshid_ie(skb, sdata)) | ||
| 188 | return -1; | ||
| 199 | } | 189 | } |
| 200 | 190 | ||
| 201 | /* Add Peer Link Management element */ | 191 | /* Add Mesh Peering Management element */ |
| 202 | switch (action) { | 192 | switch (action) { |
| 203 | case PLINK_OPEN: | 193 | case WLAN_SP_MESH_PEERING_OPEN: |
| 204 | ie_len = 6; | ||
| 205 | break; | 194 | break; |
| 206 | case PLINK_CONFIRM: | 195 | case WLAN_SP_MESH_PEERING_CONFIRM: |
| 207 | ie_len = 8; | 196 | ie_len += 2; |
| 208 | include_plid = true; | 197 | include_plid = true; |
| 209 | break; | 198 | break; |
| 210 | case PLINK_CLOSE: | 199 | case WLAN_SP_MESH_PEERING_CLOSE: |
| 211 | default: | 200 | if (plid) { |
| 212 | if (!plid) | 201 | ie_len += 2; |
| 213 | ie_len = 8; | ||
| 214 | else { | ||
| 215 | ie_len = 10; | ||
| 216 | include_plid = true; | 202 | include_plid = true; |
| 217 | } | 203 | } |
| 204 | ie_len += 2; /* reason code */ | ||
| 218 | break; | 205 | break; |
| 206 | default: | ||
| 207 | return -EINVAL; | ||
| 219 | } | 208 | } |
| 220 | 209 | ||
| 210 | if (WARN_ON(skb_tailroom(skb) < 2 + ie_len)) | ||
| 211 | return -ENOMEM; | ||
| 212 | |||
| 221 | pos = skb_put(skb, 2 + ie_len); | 213 | pos = skb_put(skb, 2 + ie_len); |
| 222 | *pos++ = WLAN_EID_PEER_LINK; | 214 | *pos++ = WLAN_EID_PEER_MGMT; |
| 223 | *pos++ = ie_len; | 215 | *pos++ = ie_len; |
| 224 | memcpy(pos, meshpeeringproto, sizeof(meshpeeringproto)); | 216 | memcpy(pos, &peering_proto, 2); |
| 225 | pos += 4; | 217 | pos += 2; |
| 226 | memcpy(pos, &llid, 2); | 218 | memcpy(pos, &llid, 2); |
| 219 | pos += 2; | ||
| 227 | if (include_plid) { | 220 | if (include_plid) { |
| 228 | pos += 2; | ||
| 229 | memcpy(pos, &plid, 2); | 221 | memcpy(pos, &plid, 2); |
| 230 | } | ||
| 231 | if (action == PLINK_CLOSE) { | ||
| 232 | pos += 2; | 222 | pos += 2; |
| 223 | } | ||
| 224 | if (action == WLAN_SP_MESH_PEERING_CLOSE) { | ||
| 233 | memcpy(pos, &reason, 2); | 225 | memcpy(pos, &reason, 2); |
| 226 | pos += 2; | ||
| 234 | } | 227 | } |
| 228 | if (mesh_add_vendor_ies(skb, sdata)) | ||
| 229 | return -1; | ||
| 235 | 230 | ||
| 236 | ieee80211_tx_skb(sdata, skb); | 231 | ieee80211_tx_skb(sdata, skb); |
| 237 | return 0; | 232 | return 0; |
| @@ -322,21 +317,21 @@ static void mesh_plink_timer(unsigned long data) | |||
| 322 | ++sta->plink_retries; | 317 | ++sta->plink_retries; |
| 323 | mod_plink_timer(sta, sta->plink_timeout); | 318 | mod_plink_timer(sta, sta->plink_timeout); |
| 324 | spin_unlock_bh(&sta->lock); | 319 | spin_unlock_bh(&sta->lock); |
| 325 | mesh_plink_frame_tx(sdata, PLINK_OPEN, sta->sta.addr, llid, | 320 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, |
| 326 | 0, 0); | 321 | sta->sta.addr, llid, 0, 0); |
| 327 | break; | 322 | break; |
| 328 | } | 323 | } |
| 329 | reason = cpu_to_le16(MESH_MAX_RETRIES); | 324 | reason = cpu_to_le16(WLAN_REASON_MESH_MAX_RETRIES); |
| 330 | /* fall through on else */ | 325 | /* fall through on else */ |
| 331 | case NL80211_PLINK_CNF_RCVD: | 326 | case NL80211_PLINK_CNF_RCVD: |
| 332 | /* confirm timer */ | 327 | /* confirm timer */ |
| 333 | if (!reason) | 328 | if (!reason) |
| 334 | reason = cpu_to_le16(MESH_CONFIRM_TIMEOUT); | 329 | reason = cpu_to_le16(WLAN_REASON_MESH_CONFIRM_TIMEOUT); |
| 335 | sta->plink_state = NL80211_PLINK_HOLDING; | 330 | sta->plink_state = NL80211_PLINK_HOLDING; |
| 336 | mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); | 331 | mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); |
| 337 | spin_unlock_bh(&sta->lock); | 332 | spin_unlock_bh(&sta->lock); |
| 338 | mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, plid, | 333 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, |
| 339 | reason); | 334 | sta->sta.addr, llid, plid, reason); |
| 340 | break; | 335 | break; |
| 341 | case NL80211_PLINK_HOLDING: | 336 | case NL80211_PLINK_HOLDING: |
| 342 | /* holding timer */ | 337 | /* holding timer */ |
| @@ -396,7 +391,7 @@ int mesh_plink_open(struct sta_info *sta) | |||
| 396 | mpl_dbg("Mesh plink: starting establishment with %pM\n", | 391 | mpl_dbg("Mesh plink: starting establishment with %pM\n", |
| 397 | sta->sta.addr); | 392 | sta->sta.addr); |
| 398 | 393 | ||
| 399 | return mesh_plink_frame_tx(sdata, PLINK_OPEN, | 394 | return mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, |
| 400 | sta->sta.addr, llid, 0, 0); | 395 | sta->sta.addr, llid, 0, 0); |
| 401 | } | 396 | } |
| 402 | 397 | ||
| @@ -422,7 +417,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
| 422 | struct ieee802_11_elems elems; | 417 | struct ieee802_11_elems elems; |
| 423 | struct sta_info *sta; | 418 | struct sta_info *sta; |
| 424 | enum plink_event event; | 419 | enum plink_event event; |
| 425 | enum plink_frame_type ftype; | 420 | enum ieee80211_self_protected_actioncode ftype; |
| 426 | size_t baselen; | 421 | size_t baselen; |
| 427 | bool deactivated, matches_local = true; | 422 | bool deactivated, matches_local = true; |
| 428 | u8 ie_len; | 423 | u8 ie_len; |
| @@ -449,14 +444,15 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
| 449 | return; | 444 | return; |
| 450 | } | 445 | } |
| 451 | 446 | ||
| 452 | baseaddr = mgmt->u.action.u.plink_action.variable; | 447 | baseaddr = mgmt->u.action.u.self_prot.variable; |
| 453 | baselen = (u8 *) mgmt->u.action.u.plink_action.variable - (u8 *) mgmt; | 448 | baselen = (u8 *) mgmt->u.action.u.self_prot.variable - (u8 *) mgmt; |
| 454 | if (mgmt->u.action.u.plink_action.action_code == PLINK_CONFIRM) { | 449 | if (mgmt->u.action.u.self_prot.action_code == |
| 450 | WLAN_SP_MESH_PEERING_CONFIRM) { | ||
| 455 | baseaddr += 4; | 451 | baseaddr += 4; |
| 456 | baselen += 4; | 452 | baselen += 4; |
| 457 | } | 453 | } |
| 458 | ieee802_11_parse_elems(baseaddr, len - baselen, &elems); | 454 | ieee802_11_parse_elems(baseaddr, len - baselen, &elems); |
| 459 | if (!elems.peer_link) { | 455 | if (!elems.peering) { |
| 460 | mpl_dbg("Mesh plink: missing necessary peer link ie\n"); | 456 | mpl_dbg("Mesh plink: missing necessary peer link ie\n"); |
| 461 | return; | 457 | return; |
| 462 | } | 458 | } |
| @@ -466,31 +462,34 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
| 466 | return; | 462 | return; |
| 467 | } | 463 | } |
| 468 | 464 | ||
| 469 | ftype = mgmt->u.action.u.plink_action.action_code; | 465 | ftype = mgmt->u.action.u.self_prot.action_code; |
| 470 | ie_len = elems.peer_link_len; | 466 | ie_len = elems.peering_len; |
| 471 | if ((ftype == PLINK_OPEN && ie_len != 6) || | 467 | if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) || |
| 472 | (ftype == PLINK_CONFIRM && ie_len != 8) || | 468 | (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) || |
| 473 | (ftype == PLINK_CLOSE && ie_len != 8 && ie_len != 10)) { | 469 | (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6 |
| 470 | && ie_len != 8)) { | ||
| 474 | mpl_dbg("Mesh plink: incorrect plink ie length %d %d\n", | 471 | mpl_dbg("Mesh plink: incorrect plink ie length %d %d\n", |
| 475 | ftype, ie_len); | 472 | ftype, ie_len); |
| 476 | return; | 473 | return; |
| 477 | } | 474 | } |
| 478 | 475 | ||
| 479 | if (ftype != PLINK_CLOSE && (!elems.mesh_id || !elems.mesh_config)) { | 476 | if (ftype != WLAN_SP_MESH_PEERING_CLOSE && |
| 477 | (!elems.mesh_id || !elems.mesh_config)) { | ||
| 480 | mpl_dbg("Mesh plink: missing necessary ie\n"); | 478 | mpl_dbg("Mesh plink: missing necessary ie\n"); |
| 481 | return; | 479 | return; |
| 482 | } | 480 | } |
| 483 | /* Note the lines below are correct, the llid in the frame is the plid | 481 | /* Note the lines below are correct, the llid in the frame is the plid |
| 484 | * from the point of view of this host. | 482 | * from the point of view of this host. |
| 485 | */ | 483 | */ |
| 486 | memcpy(&plid, PLINK_GET_LLID(elems.peer_link), 2); | 484 | memcpy(&plid, PLINK_GET_LLID(elems.peering), 2); |
| 487 | if (ftype == PLINK_CONFIRM || (ftype == PLINK_CLOSE && ie_len == 10)) | 485 | if (ftype == WLAN_SP_MESH_PEERING_CONFIRM || |
| 488 | memcpy(&llid, PLINK_GET_PLID(elems.peer_link), 2); | 486 | (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) |
| 487 | memcpy(&llid, PLINK_GET_PLID(elems.peering), 2); | ||
| 489 | 488 | ||
| 490 | rcu_read_lock(); | 489 | rcu_read_lock(); |
| 491 | 490 | ||
| 492 | sta = sta_info_get(sdata, mgmt->sa); | 491 | sta = sta_info_get(sdata, mgmt->sa); |
| 493 | if (!sta && ftype != PLINK_OPEN) { | 492 | if (!sta && ftype != WLAN_SP_MESH_PEERING_OPEN) { |
| 494 | mpl_dbg("Mesh plink: cls or cnf from unknown peer\n"); | 493 | mpl_dbg("Mesh plink: cls or cnf from unknown peer\n"); |
| 495 | rcu_read_unlock(); | 494 | rcu_read_unlock(); |
| 496 | return; | 495 | return; |
| @@ -509,30 +508,30 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
| 509 | 508 | ||
| 510 | /* Now we will figure out the appropriate event... */ | 509 | /* Now we will figure out the appropriate event... */ |
| 511 | event = PLINK_UNDEFINED; | 510 | event = PLINK_UNDEFINED; |
| 512 | if (ftype != PLINK_CLOSE && (!mesh_matches_local(&elems, sdata))) { | 511 | if (ftype != WLAN_SP_MESH_PEERING_CLOSE && |
| 512 | (!mesh_matches_local(&elems, sdata))) { | ||
| 513 | matches_local = false; | 513 | matches_local = false; |
| 514 | switch (ftype) { | 514 | switch (ftype) { |
| 515 | case PLINK_OPEN: | 515 | case WLAN_SP_MESH_PEERING_OPEN: |
| 516 | event = OPN_RJCT; | 516 | event = OPN_RJCT; |
| 517 | break; | 517 | break; |
| 518 | case PLINK_CONFIRM: | 518 | case WLAN_SP_MESH_PEERING_CONFIRM: |
| 519 | event = CNF_RJCT; | 519 | event = CNF_RJCT; |
| 520 | break; | 520 | break; |
| 521 | case PLINK_CLOSE: | 521 | default: |
| 522 | /* avoid warning */ | ||
| 523 | break; | 522 | break; |
| 524 | } | 523 | } |
| 525 | } | 524 | } |
| 526 | 525 | ||
| 527 | if (!sta && !matches_local) { | 526 | if (!sta && !matches_local) { |
| 528 | rcu_read_unlock(); | 527 | rcu_read_unlock(); |
| 529 | reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); | 528 | reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); |
| 530 | llid = 0; | 529 | llid = 0; |
| 531 | mesh_plink_frame_tx(sdata, PLINK_CLOSE, mgmt->sa, llid, | 530 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, |
| 532 | plid, reason); | 531 | mgmt->sa, llid, plid, reason); |
| 533 | return; | 532 | return; |
| 534 | } else if (!sta) { | 533 | } else if (!sta) { |
| 535 | /* ftype == PLINK_OPEN */ | 534 | /* ftype == WLAN_SP_MESH_PEERING_OPEN */ |
| 536 | u32 rates; | 535 | u32 rates; |
| 537 | 536 | ||
| 538 | rcu_read_unlock(); | 537 | rcu_read_unlock(); |
| @@ -557,21 +556,21 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
| 557 | } else if (matches_local) { | 556 | } else if (matches_local) { |
| 558 | spin_lock_bh(&sta->lock); | 557 | spin_lock_bh(&sta->lock); |
| 559 | switch (ftype) { | 558 | switch (ftype) { |
| 560 | case PLINK_OPEN: | 559 | case WLAN_SP_MESH_PEERING_OPEN: |
| 561 | if (!mesh_plink_free_count(sdata) || | 560 | if (!mesh_plink_free_count(sdata) || |
| 562 | (sta->plid && sta->plid != plid)) | 561 | (sta->plid && sta->plid != plid)) |
| 563 | event = OPN_IGNR; | 562 | event = OPN_IGNR; |
| 564 | else | 563 | else |
| 565 | event = OPN_ACPT; | 564 | event = OPN_ACPT; |
| 566 | break; | 565 | break; |
| 567 | case PLINK_CONFIRM: | 566 | case WLAN_SP_MESH_PEERING_CONFIRM: |
| 568 | if (!mesh_plink_free_count(sdata) || | 567 | if (!mesh_plink_free_count(sdata) || |
| 569 | (sta->llid != llid || sta->plid != plid)) | 568 | (sta->llid != llid || sta->plid != plid)) |
| 570 | event = CNF_IGNR; | 569 | event = CNF_IGNR; |
| 571 | else | 570 | else |
| 572 | event = CNF_ACPT; | 571 | event = CNF_ACPT; |
| 573 | break; | 572 | break; |
| 574 | case PLINK_CLOSE: | 573 | case WLAN_SP_MESH_PEERING_CLOSE: |
| 575 | if (sta->plink_state == NL80211_PLINK_ESTAB) | 574 | if (sta->plink_state == NL80211_PLINK_ESTAB) |
| 576 | /* Do not check for llid or plid. This does not | 575 | /* Do not check for llid or plid. This does not |
| 577 | * follow the standard but since multiple plinks | 576 | * follow the standard but since multiple plinks |
| @@ -620,10 +619,12 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
| 620 | sta->llid = llid; | 619 | sta->llid = llid; |
| 621 | mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); | 620 | mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); |
| 622 | spin_unlock_bh(&sta->lock); | 621 | spin_unlock_bh(&sta->lock); |
| 623 | mesh_plink_frame_tx(sdata, PLINK_OPEN, sta->sta.addr, llid, | 622 | mesh_plink_frame_tx(sdata, |
| 624 | 0, 0); | 623 | WLAN_SP_MESH_PEERING_OPEN, |
| 625 | mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, | 624 | sta->sta.addr, llid, 0, 0); |
| 626 | llid, plid, 0); | 625 | mesh_plink_frame_tx(sdata, |
| 626 | WLAN_SP_MESH_PEERING_CONFIRM, | ||
| 627 | sta->sta.addr, llid, plid, 0); | ||
| 627 | break; | 628 | break; |
| 628 | default: | 629 | default: |
| 629 | spin_unlock_bh(&sta->lock); | 630 | spin_unlock_bh(&sta->lock); |
| @@ -635,10 +636,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
| 635 | switch (event) { | 636 | switch (event) { |
| 636 | case OPN_RJCT: | 637 | case OPN_RJCT: |
| 637 | case CNF_RJCT: | 638 | case CNF_RJCT: |
| 638 | reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); | 639 | reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); |
| 639 | case CLS_ACPT: | 640 | case CLS_ACPT: |
| 640 | if (!reason) | 641 | if (!reason) |
| 641 | reason = cpu_to_le16(MESH_CLOSE_RCVD); | 642 | reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); |
| 642 | sta->reason = reason; | 643 | sta->reason = reason; |
| 643 | sta->plink_state = NL80211_PLINK_HOLDING; | 644 | sta->plink_state = NL80211_PLINK_HOLDING; |
| 644 | if (!mod_plink_timer(sta, | 645 | if (!mod_plink_timer(sta, |
| @@ -647,8 +648,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
| 647 | 648 | ||
| 648 | llid = sta->llid; | 649 | llid = sta->llid; |
| 649 | spin_unlock_bh(&sta->lock); | 650 | spin_unlock_bh(&sta->lock); |
| 650 | mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, | 651 | mesh_plink_frame_tx(sdata, |
| 651 | plid, reason); | 652 | WLAN_SP_MESH_PEERING_CLOSE, |
| 653 | sta->sta.addr, llid, plid, reason); | ||
| 652 | break; | 654 | break; |
| 653 | case OPN_ACPT: | 655 | case OPN_ACPT: |
| 654 | /* retry timer is left untouched */ | 656 | /* retry timer is left untouched */ |
| @@ -656,8 +658,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
| 656 | sta->plid = plid; | 658 | sta->plid = plid; |
| 657 | llid = sta->llid; | 659 | llid = sta->llid; |
| 658 | spin_unlock_bh(&sta->lock); | 660 | spin_unlock_bh(&sta->lock); |
| 659 | mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, | 661 | mesh_plink_frame_tx(sdata, |
| 660 | plid, 0); | 662 | WLAN_SP_MESH_PEERING_CONFIRM, |
| 663 | sta->sta.addr, llid, plid, 0); | ||
| 661 | break; | 664 | break; |
| 662 | case CNF_ACPT: | 665 | case CNF_ACPT: |
| 663 | sta->plink_state = NL80211_PLINK_CNF_RCVD; | 666 | sta->plink_state = NL80211_PLINK_CNF_RCVD; |
| @@ -677,10 +680,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
| 677 | switch (event) { | 680 | switch (event) { |
| 678 | case OPN_RJCT: | 681 | case OPN_RJCT: |
| 679 | case CNF_RJCT: | 682 | case CNF_RJCT: |
| 680 | reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); | 683 | reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); |
| 681 | case CLS_ACPT: | 684 | case CLS_ACPT: |
| 682 | if (!reason) | 685 | if (!reason) |
| 683 | reason = cpu_to_le16(MESH_CLOSE_RCVD); | 686 | reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); |
| 684 | sta->reason = reason; | 687 | sta->reason = reason; |
| 685 | sta->plink_state = NL80211_PLINK_HOLDING; | 688 | sta->plink_state = NL80211_PLINK_HOLDING; |
| 686 | if (!mod_plink_timer(sta, | 689 | if (!mod_plink_timer(sta, |
| @@ -689,14 +692,15 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
| 689 | 692 | ||
| 690 | llid = sta->llid; | 693 | llid = sta->llid; |
| 691 | spin_unlock_bh(&sta->lock); | 694 | spin_unlock_bh(&sta->lock); |
| 692 | mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, | 695 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, |
| 693 | plid, reason); | 696 | sta->sta.addr, llid, plid, reason); |
| 694 | break; | 697 | break; |
| 695 | case OPN_ACPT: | 698 | case OPN_ACPT: |
| 696 | llid = sta->llid; | 699 | llid = sta->llid; |
| 697 | spin_unlock_bh(&sta->lock); | 700 | spin_unlock_bh(&sta->lock); |
| 698 | mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, | 701 | mesh_plink_frame_tx(sdata, |
| 699 | plid, 0); | 702 | WLAN_SP_MESH_PEERING_CONFIRM, |
| 703 | sta->sta.addr, llid, plid, 0); | ||
| 700 | break; | 704 | break; |
| 701 | case CNF_ACPT: | 705 | case CNF_ACPT: |
| 702 | del_timer(&sta->plink_timer); | 706 | del_timer(&sta->plink_timer); |
| @@ -717,10 +721,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
| 717 | switch (event) { | 721 | switch (event) { |
| 718 | case OPN_RJCT: | 722 | case OPN_RJCT: |
| 719 | case CNF_RJCT: | 723 | case CNF_RJCT: |
| 720 | reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); | 724 | reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); |
| 721 | case CLS_ACPT: | 725 | case CLS_ACPT: |
| 722 | if (!reason) | 726 | if (!reason) |
| 723 | reason = cpu_to_le16(MESH_CLOSE_RCVD); | 727 | reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); |
| 724 | sta->reason = reason; | 728 | sta->reason = reason; |
| 725 | sta->plink_state = NL80211_PLINK_HOLDING; | 729 | sta->plink_state = NL80211_PLINK_HOLDING; |
| 726 | if (!mod_plink_timer(sta, | 730 | if (!mod_plink_timer(sta, |
| @@ -729,8 +733,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
| 729 | 733 | ||
| 730 | llid = sta->llid; | 734 | llid = sta->llid; |
| 731 | spin_unlock_bh(&sta->lock); | 735 | spin_unlock_bh(&sta->lock); |
| 732 | mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, | 736 | mesh_plink_frame_tx(sdata, |
| 733 | plid, reason); | 737 | WLAN_SP_MESH_PEERING_CLOSE, |
| 738 | sta->sta.addr, llid, plid, reason); | ||
| 734 | break; | 739 | break; |
| 735 | case OPN_ACPT: | 740 | case OPN_ACPT: |
| 736 | del_timer(&sta->plink_timer); | 741 | del_timer(&sta->plink_timer); |
| @@ -740,8 +745,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
| 740 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); | 745 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); |
| 741 | mpl_dbg("Mesh plink with %pM ESTABLISHED\n", | 746 | mpl_dbg("Mesh plink with %pM ESTABLISHED\n", |
| 742 | sta->sta.addr); | 747 | sta->sta.addr); |
| 743 | mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, | 748 | mesh_plink_frame_tx(sdata, |
| 744 | plid, 0); | 749 | WLAN_SP_MESH_PEERING_CONFIRM, |
| 750 | sta->sta.addr, llid, plid, 0); | ||
| 745 | break; | 751 | break; |
| 746 | default: | 752 | default: |
| 747 | spin_unlock_bh(&sta->lock); | 753 | spin_unlock_bh(&sta->lock); |
| @@ -752,7 +758,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
| 752 | case NL80211_PLINK_ESTAB: | 758 | case NL80211_PLINK_ESTAB: |
| 753 | switch (event) { | 759 | switch (event) { |
| 754 | case CLS_ACPT: | 760 | case CLS_ACPT: |
| 755 | reason = cpu_to_le16(MESH_CLOSE_RCVD); | 761 | reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); |
| 756 | sta->reason = reason; | 762 | sta->reason = reason; |
| 757 | deactivated = __mesh_plink_deactivate(sta); | 763 | deactivated = __mesh_plink_deactivate(sta); |
| 758 | sta->plink_state = NL80211_PLINK_HOLDING; | 764 | sta->plink_state = NL80211_PLINK_HOLDING; |
| @@ -761,14 +767,15 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
| 761 | spin_unlock_bh(&sta->lock); | 767 | spin_unlock_bh(&sta->lock); |
| 762 | if (deactivated) | 768 | if (deactivated) |
| 763 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); | 769 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); |
| 764 | mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, | 770 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, |
| 765 | plid, reason); | 771 | sta->sta.addr, llid, plid, reason); |
| 766 | break; | 772 | break; |
| 767 | case OPN_ACPT: | 773 | case OPN_ACPT: |
| 768 | llid = sta->llid; | 774 | llid = sta->llid; |
| 769 | spin_unlock_bh(&sta->lock); | 775 | spin_unlock_bh(&sta->lock); |
| 770 | mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, | 776 | mesh_plink_frame_tx(sdata, |
| 771 | plid, 0); | 777 | WLAN_SP_MESH_PEERING_CONFIRM, |
| 778 | sta->sta.addr, llid, plid, 0); | ||
| 772 | break; | 779 | break; |
| 773 | default: | 780 | default: |
| 774 | spin_unlock_bh(&sta->lock); | 781 | spin_unlock_bh(&sta->lock); |
| @@ -790,8 +797,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
| 790 | llid = sta->llid; | 797 | llid = sta->llid; |
| 791 | reason = sta->reason; | 798 | reason = sta->reason; |
| 792 | spin_unlock_bh(&sta->lock); | 799 | spin_unlock_bh(&sta->lock); |
| 793 | mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, | 800 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, |
| 794 | llid, plid, reason); | 801 | sta->sta.addr, llid, plid, reason); |
| 795 | break; | 802 | break; |
| 796 | default: | 803 | default: |
| 797 | spin_unlock_bh(&sta->lock); | 804 | spin_unlock_bh(&sta->lock); |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d6470c7fd6ce..60a6f273cd30 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
| @@ -1482,10 +1482,14 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, | |||
| 1482 | 1482 | ||
| 1483 | ifmgd->aid = aid; | 1483 | ifmgd->aid = aid; |
| 1484 | 1484 | ||
| 1485 | sta = sta_info_alloc(sdata, cbss->bssid, GFP_KERNEL); | 1485 | mutex_lock(&sdata->local->sta_mtx); |
| 1486 | if (!sta) { | 1486 | /* |
| 1487 | printk(KERN_DEBUG "%s: failed to alloc STA entry for" | 1487 | * station info was already allocated and inserted before |
| 1488 | " the AP\n", sdata->name); | 1488 | * the association and should be available to us |
| 1489 | */ | ||
| 1490 | sta = sta_info_get_rx(sdata, cbss->bssid); | ||
| 1491 | if (WARN_ON(!sta)) { | ||
| 1492 | mutex_unlock(&sdata->local->sta_mtx); | ||
| 1489 | return false; | 1493 | return false; |
| 1490 | } | 1494 | } |
| 1491 | 1495 | ||
| @@ -1556,7 +1560,8 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, | |||
| 1556 | if (elems.wmm_param) | 1560 | if (elems.wmm_param) |
| 1557 | set_sta_flags(sta, WLAN_STA_WME); | 1561 | set_sta_flags(sta, WLAN_STA_WME); |
| 1558 | 1562 | ||
| 1559 | err = sta_info_insert(sta); | 1563 | /* sta_info_reinsert will also unlock the mutex lock */ |
| 1564 | err = sta_info_reinsert(sta); | ||
| 1560 | sta = NULL; | 1565 | sta = NULL; |
| 1561 | if (err) { | 1566 | if (err) { |
| 1562 | printk(KERN_DEBUG "%s: failed to insert STA entry for" | 1567 | printk(KERN_DEBUG "%s: failed to insert STA entry for" |
| @@ -2429,6 +2434,32 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, | |||
| 2429 | return 0; | 2434 | return 0; |
| 2430 | } | 2435 | } |
| 2431 | 2436 | ||
| 2437 | /* create and insert a dummy station entry */ | ||
| 2438 | static int ieee80211_pre_assoc(struct ieee80211_sub_if_data *sdata, | ||
| 2439 | u8 *bssid) { | ||
| 2440 | struct sta_info *sta; | ||
| 2441 | int err; | ||
| 2442 | |||
| 2443 | sta = sta_info_alloc(sdata, bssid, GFP_KERNEL); | ||
| 2444 | if (!sta) { | ||
| 2445 | printk(KERN_DEBUG "%s: failed to alloc STA entry for" | ||
| 2446 | " the AP\n", sdata->name); | ||
| 2447 | return -ENOMEM; | ||
| 2448 | } | ||
| 2449 | |||
| 2450 | sta->dummy = true; | ||
| 2451 | |||
| 2452 | err = sta_info_insert(sta); | ||
| 2453 | sta = NULL; | ||
| 2454 | if (err) { | ||
| 2455 | printk(KERN_DEBUG "%s: failed to insert Dummy STA entry for" | ||
| 2456 | " the AP (error %d)\n", sdata->name, err); | ||
| 2457 | return err; | ||
| 2458 | } | ||
| 2459 | |||
| 2460 | return 0; | ||
| 2461 | } | ||
| 2462 | |||
| 2432 | static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk, | 2463 | static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk, |
| 2433 | struct sk_buff *skb) | 2464 | struct sk_buff *skb) |
| 2434 | { | 2465 | { |
| @@ -2436,9 +2467,11 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk, | |||
| 2436 | struct ieee80211_mgmt *mgmt; | 2467 | struct ieee80211_mgmt *mgmt; |
| 2437 | struct ieee80211_rx_status *rx_status; | 2468 | struct ieee80211_rx_status *rx_status; |
| 2438 | struct ieee802_11_elems elems; | 2469 | struct ieee802_11_elems elems; |
| 2470 | struct cfg80211_bss *cbss = wk->assoc.bss; | ||
| 2439 | u16 status; | 2471 | u16 status; |
| 2440 | 2472 | ||
| 2441 | if (!skb) { | 2473 | if (!skb) { |
| 2474 | sta_info_destroy_addr(wk->sdata, cbss->bssid); | ||
| 2442 | cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta); | 2475 | cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta); |
| 2443 | goto destroy; | 2476 | goto destroy; |
| 2444 | } | 2477 | } |
| @@ -2468,12 +2501,16 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk, | |||
| 2468 | if (!ieee80211_assoc_success(wk, mgmt, skb->len)) { | 2501 | if (!ieee80211_assoc_success(wk, mgmt, skb->len)) { |
| 2469 | mutex_unlock(&wk->sdata->u.mgd.mtx); | 2502 | mutex_unlock(&wk->sdata->u.mgd.mtx); |
| 2470 | /* oops -- internal error -- send timeout for now */ | 2503 | /* oops -- internal error -- send timeout for now */ |
| 2504 | sta_info_destroy_addr(wk->sdata, cbss->bssid); | ||
| 2471 | cfg80211_send_assoc_timeout(wk->sdata->dev, | 2505 | cfg80211_send_assoc_timeout(wk->sdata->dev, |
| 2472 | wk->filter_ta); | 2506 | wk->filter_ta); |
| 2473 | return WORK_DONE_DESTROY; | 2507 | return WORK_DONE_DESTROY; |
| 2474 | } | 2508 | } |
| 2475 | 2509 | ||
| 2476 | mutex_unlock(&wk->sdata->u.mgd.mtx); | 2510 | mutex_unlock(&wk->sdata->u.mgd.mtx); |
| 2511 | } else { | ||
| 2512 | /* assoc failed - destroy the dummy station entry */ | ||
| 2513 | sta_info_destroy_addr(wk->sdata, cbss->bssid); | ||
| 2477 | } | 2514 | } |
| 2478 | 2515 | ||
| 2479 | cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len); | 2516 | cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len); |
| @@ -2492,7 +2529,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
| 2492 | struct ieee80211_bss *bss = (void *)req->bss->priv; | 2529 | struct ieee80211_bss *bss = (void *)req->bss->priv; |
| 2493 | struct ieee80211_work *wk; | 2530 | struct ieee80211_work *wk; |
| 2494 | const u8 *ssid; | 2531 | const u8 *ssid; |
| 2495 | int i; | 2532 | int i, err; |
| 2496 | 2533 | ||
| 2497 | mutex_lock(&ifmgd->mtx); | 2534 | mutex_lock(&ifmgd->mtx); |
| 2498 | if (ifmgd->associated) { | 2535 | if (ifmgd->associated) { |
| @@ -2517,6 +2554,16 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
| 2517 | if (!wk) | 2554 | if (!wk) |
| 2518 | return -ENOMEM; | 2555 | return -ENOMEM; |
| 2519 | 2556 | ||
| 2557 | /* | ||
| 2558 | * create a dummy station info entry in order | ||
| 2559 | * to start accepting incoming EAPOL packets from the station | ||
| 2560 | */ | ||
| 2561 | err = ieee80211_pre_assoc(sdata, req->bss->bssid); | ||
| 2562 | if (err) { | ||
| 2563 | kfree(wk); | ||
| 2564 | return err; | ||
| 2565 | } | ||
| 2566 | |||
| 2520 | ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N; | 2567 | ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N; |
| 2521 | ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED; | 2568 | ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED; |
| 2522 | 2569 | ||
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index fe2c2a717793..f45fd2fedc24 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c | |||
| @@ -850,8 +850,21 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx) | |||
| 850 | ieee80211_is_pspoll(hdr->frame_control)) && | 850 | ieee80211_is_pspoll(hdr->frame_control)) && |
| 851 | rx->sdata->vif.type != NL80211_IFTYPE_ADHOC && | 851 | rx->sdata->vif.type != NL80211_IFTYPE_ADHOC && |
| 852 | rx->sdata->vif.type != NL80211_IFTYPE_WDS && | 852 | rx->sdata->vif.type != NL80211_IFTYPE_WDS && |
| 853 | (!rx->sta || !test_sta_flags(rx->sta, WLAN_STA_ASSOC)))) | 853 | (!rx->sta || !test_sta_flags(rx->sta, WLAN_STA_ASSOC)))) { |
| 854 | if (rx->sta && rx->sta->dummy && | ||
| 855 | ieee80211_is_data_present(hdr->frame_control)) { | ||
| 856 | u16 ethertype; | ||
| 857 | u8 *payload; | ||
| 858 | |||
| 859 | payload = rx->skb->data + | ||
| 860 | ieee80211_hdrlen(hdr->frame_control); | ||
| 861 | ethertype = (payload[6] << 8) | payload[7]; | ||
| 862 | if (cpu_to_be16(ethertype) == | ||
| 863 | rx->sdata->control_port_protocol) | ||
| 864 | return RX_CONTINUE; | ||
| 865 | } | ||
| 854 | return RX_DROP_MONITOR; | 866 | return RX_DROP_MONITOR; |
| 867 | } | ||
| 855 | 868 | ||
| 856 | return RX_CONTINUE; | 869 | return RX_CONTINUE; |
| 857 | } | 870 | } |
| @@ -2220,12 +2233,29 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) | |||
| 2220 | goto handled; | 2233 | goto handled; |
| 2221 | } | 2234 | } |
| 2222 | break; | 2235 | break; |
| 2236 | case WLAN_CATEGORY_SELF_PROTECTED: | ||
| 2237 | switch (mgmt->u.action.u.self_prot.action_code) { | ||
| 2238 | case WLAN_SP_MESH_PEERING_OPEN: | ||
| 2239 | case WLAN_SP_MESH_PEERING_CLOSE: | ||
| 2240 | case WLAN_SP_MESH_PEERING_CONFIRM: | ||
| 2241 | if (!ieee80211_vif_is_mesh(&sdata->vif)) | ||
| 2242 | goto invalid; | ||
| 2243 | if (sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE) | ||
| 2244 | /* userspace handles this frame */ | ||
| 2245 | break; | ||
| 2246 | goto queue; | ||
| 2247 | case WLAN_SP_MGK_INFORM: | ||
| 2248 | case WLAN_SP_MGK_ACK: | ||
| 2249 | if (!ieee80211_vif_is_mesh(&sdata->vif)) | ||
| 2250 | goto invalid; | ||
| 2251 | break; | ||
| 2252 | } | ||
| 2253 | break; | ||
| 2223 | case WLAN_CATEGORY_MESH_ACTION: | 2254 | case WLAN_CATEGORY_MESH_ACTION: |
| 2224 | if (!ieee80211_vif_is_mesh(&sdata->vif)) | 2255 | if (!ieee80211_vif_is_mesh(&sdata->vif)) |
| 2225 | break; | 2256 | break; |
| 2226 | goto queue; | 2257 | if (mesh_action_is_path_sel(mgmt) && |
| 2227 | case WLAN_CATEGORY_MESH_PATH_SEL: | 2258 | (!mesh_path_sel_is_hwmp(sdata))) |
| 2228 | if (!mesh_path_sel_is_hwmp(sdata)) | ||
| 2229 | break; | 2259 | break; |
| 2230 | goto queue; | 2260 | goto queue; |
| 2231 | } | 2261 | } |
| @@ -2686,7 +2716,9 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx, | |||
| 2686 | } else if (!ieee80211_bssid_match(bssid, | 2716 | } else if (!ieee80211_bssid_match(bssid, |
| 2687 | sdata->vif.addr)) { | 2717 | sdata->vif.addr)) { |
| 2688 | if (!(status->rx_flags & IEEE80211_RX_IN_SCAN) && | 2718 | if (!(status->rx_flags & IEEE80211_RX_IN_SCAN) && |
| 2689 | !ieee80211_is_beacon(hdr->frame_control)) | 2719 | !ieee80211_is_beacon(hdr->frame_control) && |
| 2720 | !(ieee80211_is_action(hdr->frame_control) && | ||
| 2721 | sdata->vif.p2p)) | ||
| 2690 | return 0; | 2722 | return 0; |
| 2691 | status->rx_flags &= ~IEEE80211_RX_RA_MATCH; | 2723 | status->rx_flags &= ~IEEE80211_RX_RA_MATCH; |
| 2692 | } | 2724 | } |
| @@ -2791,7 +2823,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, | |||
| 2791 | if (ieee80211_is_data(fc)) { | 2823 | if (ieee80211_is_data(fc)) { |
| 2792 | prev_sta = NULL; | 2824 | prev_sta = NULL; |
| 2793 | 2825 | ||
| 2794 | for_each_sta_info(local, hdr->addr2, sta, tmp) { | 2826 | for_each_sta_info_rx(local, hdr->addr2, sta, tmp) { |
| 2795 | if (!prev_sta) { | 2827 | if (!prev_sta) { |
| 2796 | prev_sta = sta; | 2828 | prev_sta = sta; |
| 2797 | continue; | 2829 | continue; |
| @@ -2835,7 +2867,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, | |||
| 2835 | continue; | 2867 | continue; |
| 2836 | } | 2868 | } |
| 2837 | 2869 | ||
| 2838 | rx.sta = sta_info_get_bss(prev, hdr->addr2); | 2870 | rx.sta = sta_info_get_bss_rx(prev, hdr->addr2); |
| 2839 | rx.sdata = prev; | 2871 | rx.sdata = prev; |
| 2840 | ieee80211_prepare_and_rx_handle(&rx, skb, false); | 2872 | ieee80211_prepare_and_rx_handle(&rx, skb, false); |
| 2841 | 2873 | ||
| @@ -2843,7 +2875,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, | |||
| 2843 | } | 2875 | } |
| 2844 | 2876 | ||
| 2845 | if (prev) { | 2877 | if (prev) { |
| 2846 | rx.sta = sta_info_get_bss(prev, hdr->addr2); | 2878 | rx.sta = sta_info_get_bss_rx(prev, hdr->addr2); |
| 2847 | rx.sdata = prev; | 2879 | rx.sdata = prev; |
| 2848 | 2880 | ||
| 2849 | if (ieee80211_prepare_and_rx_handle(&rx, skb, true)) | 2881 | if (ieee80211_prepare_and_rx_handle(&rx, skb, true)) |
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 0bdbf3b8f28b..6bc17fb80ee9 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
| @@ -100,6 +100,27 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, | |||
| 100 | lockdep_is_held(&local->sta_lock) || | 100 | lockdep_is_held(&local->sta_lock) || |
| 101 | lockdep_is_held(&local->sta_mtx)); | 101 | lockdep_is_held(&local->sta_mtx)); |
| 102 | while (sta) { | 102 | while (sta) { |
| 103 | if (sta->sdata == sdata && !sta->dummy && | ||
| 104 | memcmp(sta->sta.addr, addr, ETH_ALEN) == 0) | ||
| 105 | break; | ||
| 106 | sta = rcu_dereference_check(sta->hnext, | ||
| 107 | lockdep_is_held(&local->sta_lock) || | ||
| 108 | lockdep_is_held(&local->sta_mtx)); | ||
| 109 | } | ||
| 110 | return sta; | ||
| 111 | } | ||
| 112 | |||
| 113 | /* get a station info entry even if it is a dummy station*/ | ||
| 114 | struct sta_info *sta_info_get_rx(struct ieee80211_sub_if_data *sdata, | ||
| 115 | const u8 *addr) | ||
| 116 | { | ||
| 117 | struct ieee80211_local *local = sdata->local; | ||
| 118 | struct sta_info *sta; | ||
| 119 | |||
| 120 | sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], | ||
| 121 | lockdep_is_held(&local->sta_lock) || | ||
| 122 | lockdep_is_held(&local->sta_mtx)); | ||
| 123 | while (sta) { | ||
| 103 | if (sta->sdata == sdata && | 124 | if (sta->sdata == sdata && |
| 104 | memcmp(sta->sta.addr, addr, ETH_ALEN) == 0) | 125 | memcmp(sta->sta.addr, addr, ETH_ALEN) == 0) |
| 105 | break; | 126 | break; |
| @@ -126,6 +147,32 @@ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, | |||
| 126 | while (sta) { | 147 | while (sta) { |
| 127 | if ((sta->sdata == sdata || | 148 | if ((sta->sdata == sdata || |
| 128 | (sta->sdata->bss && sta->sdata->bss == sdata->bss)) && | 149 | (sta->sdata->bss && sta->sdata->bss == sdata->bss)) && |
| 150 | !sta->dummy && | ||
| 151 | memcmp(sta->sta.addr, addr, ETH_ALEN) == 0) | ||
| 152 | break; | ||
| 153 | sta = rcu_dereference_check(sta->hnext, | ||
| 154 | lockdep_is_held(&local->sta_lock) || | ||
| 155 | lockdep_is_held(&local->sta_mtx)); | ||
| 156 | } | ||
| 157 | return sta; | ||
| 158 | } | ||
| 159 | |||
| 160 | /* | ||
| 161 | * Get sta info either from the specified interface | ||
| 162 | * or from one of its vlans (including dummy stations) | ||
| 163 | */ | ||
| 164 | struct sta_info *sta_info_get_bss_rx(struct ieee80211_sub_if_data *sdata, | ||
| 165 | const u8 *addr) | ||
| 166 | { | ||
| 167 | struct ieee80211_local *local = sdata->local; | ||
| 168 | struct sta_info *sta; | ||
| 169 | |||
| 170 | sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], | ||
| 171 | lockdep_is_held(&local->sta_lock) || | ||
| 172 | lockdep_is_held(&local->sta_mtx)); | ||
| 173 | while (sta) { | ||
| 174 | if ((sta->sdata == sdata || | ||
| 175 | (sta->sdata->bss && sta->sdata->bss == sdata->bss)) && | ||
| 129 | memcmp(sta->sta.addr, addr, ETH_ALEN) == 0) | 176 | memcmp(sta->sta.addr, addr, ETH_ALEN) == 0) |
| 130 | break; | 177 | break; |
| 131 | sta = rcu_dereference_check(sta->hnext, | 178 | sta = rcu_dereference_check(sta->hnext, |
| @@ -280,7 +327,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, | |||
| 280 | return sta; | 327 | return sta; |
| 281 | } | 328 | } |
| 282 | 329 | ||
| 283 | static int sta_info_finish_insert(struct sta_info *sta, bool async) | 330 | static int sta_info_finish_insert(struct sta_info *sta, |
| 331 | bool async, bool dummy_reinsert) | ||
| 284 | { | 332 | { |
| 285 | struct ieee80211_local *local = sta->local; | 333 | struct ieee80211_local *local = sta->local; |
| 286 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 334 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
| @@ -290,51 +338,58 @@ static int sta_info_finish_insert(struct sta_info *sta, bool async) | |||
| 290 | 338 | ||
| 291 | lockdep_assert_held(&local->sta_mtx); | 339 | lockdep_assert_held(&local->sta_mtx); |
| 292 | 340 | ||
| 293 | /* notify driver */ | 341 | if (!sta->dummy || dummy_reinsert) { |
| 294 | if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) | 342 | /* notify driver */ |
| 295 | sdata = container_of(sdata->bss, | 343 | if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) |
| 296 | struct ieee80211_sub_if_data, | 344 | sdata = container_of(sdata->bss, |
| 297 | u.ap); | 345 | struct ieee80211_sub_if_data, |
| 298 | err = drv_sta_add(local, sdata, &sta->sta); | 346 | u.ap); |
| 299 | if (err) { | 347 | err = drv_sta_add(local, sdata, &sta->sta); |
| 300 | if (!async) | 348 | if (err) { |
| 301 | return err; | 349 | if (!async) |
| 302 | printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to driver (%d)" | 350 | return err; |
| 303 | " - keeping it anyway.\n", | 351 | printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to " |
| 304 | sdata->name, sta->sta.addr, err); | 352 | "driver (%d) - keeping it anyway.\n", |
| 305 | } else { | 353 | sdata->name, sta->sta.addr, err); |
| 306 | sta->uploaded = true; | 354 | } else { |
| 355 | sta->uploaded = true; | ||
| 307 | #ifdef CONFIG_MAC80211_VERBOSE_DEBUG | 356 | #ifdef CONFIG_MAC80211_VERBOSE_DEBUG |
| 308 | if (async) | 357 | if (async) |
| 309 | wiphy_debug(local->hw.wiphy, | 358 | wiphy_debug(local->hw.wiphy, |
| 310 | "Finished adding IBSS STA %pM\n", | 359 | "Finished adding IBSS STA %pM\n", |
| 311 | sta->sta.addr); | 360 | sta->sta.addr); |
| 312 | #endif | 361 | #endif |
| 362 | } | ||
| 363 | |||
| 364 | sdata = sta->sdata; | ||
| 313 | } | 365 | } |
| 314 | 366 | ||
| 315 | sdata = sta->sdata; | 367 | if (!dummy_reinsert) { |
| 368 | if (!async) { | ||
| 369 | local->num_sta++; | ||
| 370 | local->sta_generation++; | ||
| 371 | smp_mb(); | ||
| 316 | 372 | ||
| 317 | if (!async) { | 373 | /* make the station visible */ |
| 318 | local->num_sta++; | 374 | spin_lock_irqsave(&local->sta_lock, flags); |
| 319 | local->sta_generation++; | 375 | sta_info_hash_add(local, sta); |
| 320 | smp_mb(); | 376 | spin_unlock_irqrestore(&local->sta_lock, flags); |
| 377 | } | ||
| 321 | 378 | ||
| 322 | /* make the station visible */ | 379 | list_add(&sta->list, &local->sta_list); |
| 323 | spin_lock_irqsave(&local->sta_lock, flags); | 380 | } else { |
| 324 | sta_info_hash_add(local, sta); | 381 | sta->dummy = false; |
| 325 | spin_unlock_irqrestore(&local->sta_lock, flags); | ||
| 326 | } | 382 | } |
| 327 | 383 | ||
| 328 | list_add(&sta->list, &local->sta_list); | 384 | if (!sta->dummy) { |
| 329 | 385 | ieee80211_sta_debugfs_add(sta); | |
| 330 | ieee80211_sta_debugfs_add(sta); | 386 | rate_control_add_sta_debugfs(sta); |
| 331 | rate_control_add_sta_debugfs(sta); | ||
| 332 | |||
| 333 | memset(&sinfo, 0, sizeof(sinfo)); | ||
| 334 | sinfo.filled = 0; | ||
| 335 | sinfo.generation = local->sta_generation; | ||
| 336 | cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL); | ||
| 337 | 387 | ||
| 388 | memset(&sinfo, 0, sizeof(sinfo)); | ||
| 389 | sinfo.filled = 0; | ||
| 390 | sinfo.generation = local->sta_generation; | ||
| 391 | cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL); | ||
| 392 | } | ||
| 338 | 393 | ||
| 339 | return 0; | 394 | return 0; |
| 340 | } | 395 | } |
| @@ -351,7 +406,7 @@ static void sta_info_finish_pending(struct ieee80211_local *local) | |||
| 351 | list_del(&sta->list); | 406 | list_del(&sta->list); |
| 352 | spin_unlock_irqrestore(&local->sta_lock, flags); | 407 | spin_unlock_irqrestore(&local->sta_lock, flags); |
| 353 | 408 | ||
| 354 | sta_info_finish_insert(sta, true); | 409 | sta_info_finish_insert(sta, true, false); |
| 355 | 410 | ||
| 356 | spin_lock_irqsave(&local->sta_lock, flags); | 411 | spin_lock_irqsave(&local->sta_lock, flags); |
| 357 | } | 412 | } |
| @@ -368,106 +423,117 @@ static void sta_info_finish_work(struct work_struct *work) | |||
| 368 | mutex_unlock(&local->sta_mtx); | 423 | mutex_unlock(&local->sta_mtx); |
| 369 | } | 424 | } |
| 370 | 425 | ||
| 371 | int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU) | 426 | static int sta_info_insert_check(struct sta_info *sta) |
| 372 | { | 427 | { |
| 373 | struct ieee80211_local *local = sta->local; | ||
| 374 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 428 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
| 375 | unsigned long flags; | ||
| 376 | int err = 0; | ||
| 377 | 429 | ||
| 378 | /* | 430 | /* |
| 379 | * Can't be a WARN_ON because it can be triggered through a race: | 431 | * Can't be a WARN_ON because it can be triggered through a race: |
| 380 | * something inserts a STA (on one CPU) without holding the RTNL | 432 | * something inserts a STA (on one CPU) without holding the RTNL |
| 381 | * and another CPU turns off the net device. | 433 | * and another CPU turns off the net device. |
| 382 | */ | 434 | */ |
| 383 | if (unlikely(!ieee80211_sdata_running(sdata))) { | 435 | if (unlikely(!ieee80211_sdata_running(sdata))) |
| 384 | err = -ENETDOWN; | 436 | return -ENETDOWN; |
| 385 | rcu_read_lock(); | ||
| 386 | goto out_free; | ||
| 387 | } | ||
| 388 | 437 | ||
| 389 | if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->vif.addr) == 0 || | 438 | if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->vif.addr) == 0 || |
| 390 | is_multicast_ether_addr(sta->sta.addr))) { | 439 | is_multicast_ether_addr(sta->sta.addr))) |
| 391 | err = -EINVAL; | 440 | return -EINVAL; |
| 441 | |||
| 442 | return 0; | ||
| 443 | } | ||
| 444 | |||
| 445 | static int sta_info_insert_ibss(struct sta_info *sta) __acquires(RCU) | ||
| 446 | { | ||
| 447 | struct ieee80211_local *local = sta->local; | ||
| 448 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
| 449 | unsigned long flags; | ||
| 450 | |||
| 451 | spin_lock_irqsave(&local->sta_lock, flags); | ||
| 452 | /* check if STA exists already */ | ||
| 453 | if (sta_info_get_bss_rx(sdata, sta->sta.addr)) { | ||
| 454 | spin_unlock_irqrestore(&local->sta_lock, flags); | ||
| 392 | rcu_read_lock(); | 455 | rcu_read_lock(); |
| 393 | goto out_free; | 456 | return -EEXIST; |
| 394 | } | 457 | } |
| 395 | 458 | ||
| 396 | /* | 459 | local->num_sta++; |
| 397 | * In ad-hoc mode, we sometimes need to insert stations | 460 | local->sta_generation++; |
| 398 | * from tasklet context from the RX path. To avoid races, | 461 | smp_mb(); |
| 399 | * always do so in that case -- see the comment below. | 462 | sta_info_hash_add(local, sta); |
| 400 | */ | ||
| 401 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { | ||
| 402 | spin_lock_irqsave(&local->sta_lock, flags); | ||
| 403 | /* check if STA exists already */ | ||
| 404 | if (sta_info_get_bss(sdata, sta->sta.addr)) { | ||
| 405 | spin_unlock_irqrestore(&local->sta_lock, flags); | ||
| 406 | rcu_read_lock(); | ||
| 407 | err = -EEXIST; | ||
| 408 | goto out_free; | ||
| 409 | } | ||
| 410 | |||
| 411 | local->num_sta++; | ||
| 412 | local->sta_generation++; | ||
| 413 | smp_mb(); | ||
| 414 | sta_info_hash_add(local, sta); | ||
| 415 | 463 | ||
| 416 | list_add_tail(&sta->list, &local->sta_pending_list); | 464 | list_add_tail(&sta->list, &local->sta_pending_list); |
| 417 | 465 | ||
| 418 | rcu_read_lock(); | 466 | rcu_read_lock(); |
| 419 | spin_unlock_irqrestore(&local->sta_lock, flags); | 467 | spin_unlock_irqrestore(&local->sta_lock, flags); |
| 420 | 468 | ||
| 421 | #ifdef CONFIG_MAC80211_VERBOSE_DEBUG | 469 | #ifdef CONFIG_MAC80211_VERBOSE_DEBUG |
| 422 | wiphy_debug(local->hw.wiphy, "Added IBSS STA %pM\n", | 470 | wiphy_debug(local->hw.wiphy, "Added IBSS STA %pM\n", |
| 423 | sta->sta.addr); | 471 | sta->sta.addr); |
| 424 | #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ | 472 | #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ |
| 425 | 473 | ||
| 426 | ieee80211_queue_work(&local->hw, &local->sta_finish_work); | 474 | ieee80211_queue_work(&local->hw, &local->sta_finish_work); |
| 427 | 475 | ||
| 428 | return 0; | 476 | return 0; |
| 429 | } | 477 | } |
| 478 | |||
| 479 | /* | ||
| 480 | * should be called with sta_mtx locked | ||
| 481 | * this function replaces the mutex lock | ||
| 482 | * with a RCU lock | ||
| 483 | */ | ||
| 484 | static int sta_info_insert_non_ibss(struct sta_info *sta) __acquires(RCU) | ||
| 485 | { | ||
| 486 | struct ieee80211_local *local = sta->local; | ||
| 487 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
| 488 | unsigned long flags; | ||
| 489 | struct sta_info *exist_sta; | ||
| 490 | bool dummy_reinsert = false; | ||
| 491 | int err = 0; | ||
| 492 | |||
| 493 | lockdep_assert_held(&local->sta_mtx); | ||
| 430 | 494 | ||
| 431 | /* | 495 | /* |
| 432 | * On first glance, this will look racy, because the code | 496 | * On first glance, this will look racy, because the code |
| 433 | * below this point, which inserts a station with sleeping, | 497 | * in this function, which inserts a station with sleeping, |
| 434 | * unlocks the sta_lock between checking existence in the | 498 | * unlocks the sta_lock between checking existence in the |
| 435 | * hash table and inserting into it. | 499 | * hash table and inserting into it. |
| 436 | * | 500 | * |
| 437 | * However, it is not racy against itself because it keeps | 501 | * However, it is not racy against itself because it keeps |
| 438 | * the mutex locked. It still seems to race against the | 502 | * the mutex locked. |
| 439 | * above code that atomically inserts the station... That, | ||
| 440 | * however, is not true because the above code can only | ||
| 441 | * be invoked for IBSS interfaces, and the below code will | ||
| 442 | * not be -- and the two do not race against each other as | ||
| 443 | * the hash table also keys off the interface. | ||
| 444 | */ | 503 | */ |
| 445 | 504 | ||
| 446 | might_sleep(); | ||
| 447 | |||
| 448 | mutex_lock(&local->sta_mtx); | ||
| 449 | |||
| 450 | spin_lock_irqsave(&local->sta_lock, flags); | 505 | spin_lock_irqsave(&local->sta_lock, flags); |
| 451 | /* check if STA exists already */ | 506 | /* |
| 452 | if (sta_info_get_bss(sdata, sta->sta.addr)) { | 507 | * check if STA exists already. |
| 453 | spin_unlock_irqrestore(&local->sta_lock, flags); | 508 | * only accept a scenario of a second call to sta_info_insert_non_ibss |
| 454 | mutex_unlock(&local->sta_mtx); | 509 | * with a dummy station entry that was inserted earlier |
| 455 | rcu_read_lock(); | 510 | * in that case - assume that the dummy station flag should |
| 456 | err = -EEXIST; | 511 | * be removed. |
| 457 | goto out_free; | 512 | */ |
| 513 | exist_sta = sta_info_get_bss_rx(sdata, sta->sta.addr); | ||
| 514 | if (exist_sta) { | ||
| 515 | if (exist_sta == sta && sta->dummy) { | ||
| 516 | dummy_reinsert = true; | ||
| 517 | } else { | ||
| 518 | spin_unlock_irqrestore(&local->sta_lock, flags); | ||
| 519 | mutex_unlock(&local->sta_mtx); | ||
| 520 | rcu_read_lock(); | ||
| 521 | return -EEXIST; | ||
| 522 | } | ||
| 458 | } | 523 | } |
| 459 | 524 | ||
| 460 | spin_unlock_irqrestore(&local->sta_lock, flags); | 525 | spin_unlock_irqrestore(&local->sta_lock, flags); |
| 461 | 526 | ||
| 462 | err = sta_info_finish_insert(sta, false); | 527 | err = sta_info_finish_insert(sta, false, dummy_reinsert); |
| 463 | if (err) { | 528 | if (err) { |
| 464 | mutex_unlock(&local->sta_mtx); | 529 | mutex_unlock(&local->sta_mtx); |
| 465 | rcu_read_lock(); | 530 | rcu_read_lock(); |
| 466 | goto out_free; | 531 | return err; |
| 467 | } | 532 | } |
| 468 | 533 | ||
| 469 | #ifdef CONFIG_MAC80211_VERBOSE_DEBUG | 534 | #ifdef CONFIG_MAC80211_VERBOSE_DEBUG |
| 470 | wiphy_debug(local->hw.wiphy, "Inserted STA %pM\n", sta->sta.addr); | 535 | wiphy_debug(local->hw.wiphy, "Inserted %sSTA %pM\n", |
| 536 | sta->dummy ? "dummy " : "", sta->sta.addr); | ||
| 471 | #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ | 537 | #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ |
| 472 | 538 | ||
| 473 | /* move reference to rcu-protected */ | 539 | /* move reference to rcu-protected */ |
| @@ -478,6 +544,51 @@ int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU) | |||
| 478 | mesh_accept_plinks_update(sdata); | 544 | mesh_accept_plinks_update(sdata); |
| 479 | 545 | ||
| 480 | return 0; | 546 | return 0; |
| 547 | } | ||
| 548 | |||
| 549 | int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU) | ||
| 550 | { | ||
| 551 | struct ieee80211_local *local = sta->local; | ||
| 552 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
| 553 | int err = 0; | ||
| 554 | |||
| 555 | err = sta_info_insert_check(sta); | ||
| 556 | if (err) { | ||
| 557 | rcu_read_lock(); | ||
| 558 | goto out_free; | ||
| 559 | } | ||
| 560 | |||
| 561 | /* | ||
| 562 | * In ad-hoc mode, we sometimes need to insert stations | ||
| 563 | * from tasklet context from the RX path. To avoid races, | ||
| 564 | * always do so in that case -- see the comment below. | ||
| 565 | */ | ||
| 566 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { | ||
| 567 | err = sta_info_insert_ibss(sta); | ||
| 568 | if (err) | ||
| 569 | goto out_free; | ||
| 570 | |||
| 571 | return 0; | ||
| 572 | } | ||
| 573 | |||
| 574 | /* | ||
| 575 | * It might seem that the function called below is in race against | ||
| 576 | * the function call above that atomically inserts the station... That, | ||
| 577 | * however, is not true because the above code can only | ||
| 578 | * be invoked for IBSS interfaces, and the below code will | ||
| 579 | * not be -- and the two do not race against each other as | ||
| 580 | * the hash table also keys off the interface. | ||
| 581 | */ | ||
| 582 | |||
| 583 | might_sleep(); | ||
| 584 | |||
| 585 | mutex_lock(&local->sta_mtx); | ||
| 586 | |||
| 587 | err = sta_info_insert_non_ibss(sta); | ||
| 588 | if (err) | ||
| 589 | goto out_free; | ||
| 590 | |||
| 591 | return 0; | ||
| 481 | out_free: | 592 | out_free: |
| 482 | BUG_ON(!err); | 593 | BUG_ON(!err); |
| 483 | __sta_info_free(local, sta); | 594 | __sta_info_free(local, sta); |
| @@ -493,6 +604,25 @@ int sta_info_insert(struct sta_info *sta) | |||
| 493 | return err; | 604 | return err; |
| 494 | } | 605 | } |
| 495 | 606 | ||
| 607 | /* Caller must hold sta->local->sta_mtx */ | ||
| 608 | int sta_info_reinsert(struct sta_info *sta) | ||
| 609 | { | ||
| 610 | struct ieee80211_local *local = sta->local; | ||
| 611 | int err = 0; | ||
| 612 | |||
| 613 | err = sta_info_insert_check(sta); | ||
| 614 | if (err) { | ||
| 615 | mutex_unlock(&local->sta_mtx); | ||
| 616 | return err; | ||
| 617 | } | ||
| 618 | |||
| 619 | might_sleep(); | ||
| 620 | |||
| 621 | err = sta_info_insert_non_ibss(sta); | ||
| 622 | rcu_read_unlock(); | ||
| 623 | return err; | ||
| 624 | } | ||
| 625 | |||
| 496 | static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid) | 626 | static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid) |
| 497 | { | 627 | { |
| 498 | /* | 628 | /* |
| @@ -733,7 +863,7 @@ int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata, const u8 *addr) | |||
| 733 | int ret; | 863 | int ret; |
| 734 | 864 | ||
| 735 | mutex_lock(&sdata->local->sta_mtx); | 865 | mutex_lock(&sdata->local->sta_mtx); |
| 736 | sta = sta_info_get(sdata, addr); | 866 | sta = sta_info_get_rx(sdata, addr); |
| 737 | ret = __sta_info_destroy(sta); | 867 | ret = __sta_info_destroy(sta); |
| 738 | mutex_unlock(&sdata->local->sta_mtx); | 868 | mutex_unlock(&sdata->local->sta_mtx); |
| 739 | 869 | ||
| @@ -747,7 +877,7 @@ int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata, | |||
| 747 | int ret; | 877 | int ret; |
| 748 | 878 | ||
| 749 | mutex_lock(&sdata->local->sta_mtx); | 879 | mutex_lock(&sdata->local->sta_mtx); |
| 750 | sta = sta_info_get_bss(sdata, addr); | 880 | sta = sta_info_get_bss_rx(sdata, addr); |
| 751 | ret = __sta_info_destroy(sta); | 881 | ret = __sta_info_destroy(sta); |
| 752 | mutex_unlock(&sdata->local->sta_mtx); | 882 | mutex_unlock(&sdata->local->sta_mtx); |
| 753 | 883 | ||
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 28beb78e601e..e9eb565506da 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h | |||
| @@ -238,10 +238,12 @@ struct sta_ampdu_mlme { | |||
| 238 | * @plink_timer: peer link watch timer | 238 | * @plink_timer: peer link watch timer |
| 239 | * @plink_timer_was_running: used by suspend/resume to restore timers | 239 | * @plink_timer_was_running: used by suspend/resume to restore timers |
| 240 | * @debugfs: debug filesystem info | 240 | * @debugfs: debug filesystem info |
| 241 | * @sta: station information we share with the driver | ||
| 242 | * @dead: set to true when sta is unlinked | 241 | * @dead: set to true when sta is unlinked |
| 243 | * @uploaded: set to true when sta is uploaded to the driver | 242 | * @uploaded: set to true when sta is uploaded to the driver |
| 244 | * @lost_packets: number of consecutive lost packets | 243 | * @lost_packets: number of consecutive lost packets |
| 244 | * @dummy: indicate a dummy station created for receiving | ||
| 245 | * EAP frames before association | ||
| 246 | * @sta: station information we share with the driver | ||
| 245 | */ | 247 | */ |
| 246 | struct sta_info { | 248 | struct sta_info { |
| 247 | /* General information, mostly static */ | 249 | /* General information, mostly static */ |
| @@ -336,6 +338,9 @@ struct sta_info { | |||
| 336 | 338 | ||
| 337 | unsigned int lost_packets; | 339 | unsigned int lost_packets; |
| 338 | 340 | ||
| 341 | /* should be right in front of sta to be in the same cache line */ | ||
| 342 | bool dummy; | ||
| 343 | |||
| 339 | /* keep last! */ | 344 | /* keep last! */ |
| 340 | struct ieee80211_sta sta; | 345 | struct ieee80211_sta sta; |
| 341 | }; | 346 | }; |
| @@ -436,9 +441,15 @@ rcu_dereference_protected_tid_tx(struct sta_info *sta, int tid) | |||
| 436 | struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, | 441 | struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, |
| 437 | const u8 *addr); | 442 | const u8 *addr); |
| 438 | 443 | ||
| 444 | struct sta_info *sta_info_get_rx(struct ieee80211_sub_if_data *sdata, | ||
| 445 | const u8 *addr); | ||
| 446 | |||
| 439 | struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, | 447 | struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, |
| 440 | const u8 *addr); | 448 | const u8 *addr); |
| 441 | 449 | ||
| 450 | struct sta_info *sta_info_get_bss_rx(struct ieee80211_sub_if_data *sdata, | ||
| 451 | const u8 *addr); | ||
| 452 | |||
| 442 | static inline | 453 | static inline |
| 443 | void for_each_sta_info_type_check(struct ieee80211_local *local, | 454 | void for_each_sta_info_type_check(struct ieee80211_local *local, |
| 444 | const u8 *addr, | 455 | const u8 *addr, |
| @@ -459,6 +470,22 @@ void for_each_sta_info_type_check(struct ieee80211_local *local, | |||
| 459 | _sta = nxt, \ | 470 | _sta = nxt, \ |
| 460 | nxt = _sta ? rcu_dereference(_sta->hnext) : NULL \ | 471 | nxt = _sta ? rcu_dereference(_sta->hnext) : NULL \ |
| 461 | ) \ | 472 | ) \ |
| 473 | /* run code only if address matches and it's not a dummy sta */ \ | ||
| 474 | if (memcmp(_sta->sta.addr, (_addr), ETH_ALEN) == 0 && \ | ||
| 475 | !_sta->dummy) | ||
| 476 | |||
| 477 | #define for_each_sta_info_rx(local, _addr, _sta, nxt) \ | ||
| 478 | for ( /* initialise loop */ \ | ||
| 479 | _sta = rcu_dereference(local->sta_hash[STA_HASH(_addr)]),\ | ||
| 480 | nxt = _sta ? rcu_dereference(_sta->hnext) : NULL; \ | ||
| 481 | /* typecheck */ \ | ||
| 482 | for_each_sta_info_type_check(local, (_addr), _sta, nxt),\ | ||
| 483 | /* continue condition */ \ | ||
| 484 | _sta; \ | ||
| 485 | /* advance loop */ \ | ||
| 486 | _sta = nxt, \ | ||
| 487 | nxt = _sta ? rcu_dereference(_sta->hnext) : NULL \ | ||
| 488 | ) \ | ||
| 462 | /* compare address and run code only if it matches */ \ | 489 | /* compare address and run code only if it matches */ \ |
| 463 | if (memcmp(_sta->sta.addr, (_addr), ETH_ALEN) == 0) | 490 | if (memcmp(_sta->sta.addr, (_addr), ETH_ALEN) == 0) |
| 464 | 491 | ||
| @@ -484,6 +511,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, | |||
| 484 | int sta_info_insert(struct sta_info *sta); | 511 | int sta_info_insert(struct sta_info *sta); |
| 485 | int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU); | 512 | int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU); |
| 486 | int sta_info_insert_atomic(struct sta_info *sta); | 513 | int sta_info_insert_atomic(struct sta_info *sta); |
| 514 | int sta_info_reinsert(struct sta_info *sta); | ||
| 487 | 515 | ||
| 488 | int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata, | 516 | int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata, |
| 489 | const u8 *addr); | 517 | const u8 *addr); |
diff --git a/net/mac80211/status.c b/net/mac80211/status.c index a89cca3491b4..e51bd2a1a073 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c | |||
| @@ -187,6 +187,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
| 187 | int rates_idx = -1; | 187 | int rates_idx = -1; |
| 188 | bool send_to_cooked; | 188 | bool send_to_cooked; |
| 189 | bool acked; | 189 | bool acked; |
| 190 | struct ieee80211_bar *bar; | ||
| 191 | u16 tid; | ||
| 190 | 192 | ||
| 191 | for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { | 193 | for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { |
| 192 | if (info->status.rates[i].idx < 0) { | 194 | if (info->status.rates[i].idx < 0) { |
| @@ -243,6 +245,22 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
| 243 | tid, ssn); | 245 | tid, ssn); |
| 244 | } | 246 | } |
| 245 | 247 | ||
| 248 | if (!acked && ieee80211_is_back_req(fc)) { | ||
| 249 | /* | ||
| 250 | * BAR failed, let's tear down the BA session as a | ||
| 251 | * last resort as some STAs (Intel 5100 on Windows) | ||
| 252 | * can get stuck when the BA window isn't flushed | ||
| 253 | * correctly. | ||
| 254 | */ | ||
| 255 | bar = (struct ieee80211_bar *) skb->data; | ||
| 256 | if (!(bar->control & IEEE80211_BAR_CTRL_MULTI_TID)) { | ||
| 257 | tid = (bar->control & | ||
| 258 | IEEE80211_BAR_CTRL_TID_INFO_MASK) >> | ||
| 259 | IEEE80211_BAR_CTRL_TID_INFO_SHIFT; | ||
| 260 | ieee80211_stop_tx_ba_session(&sta->sta, tid); | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 246 | if (info->flags & IEEE80211_TX_STAT_TX_FILTERED) { | 264 | if (info->flags & IEEE80211_TX_STAT_TX_FILTERED) { |
| 247 | ieee80211_handle_filtered_frame(local, sta, skb); | 265 | ieee80211_handle_filtered_frame(local, sta, skb); |
| 248 | rcu_read_unlock(); | 266 | rcu_read_unlock(); |
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 69fd494f32f9..01072639666f 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
| @@ -2295,13 +2295,23 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, | |||
| 2295 | memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); | 2295 | memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); |
| 2296 | mgmt->u.beacon.beacon_int = | 2296 | mgmt->u.beacon.beacon_int = |
| 2297 | cpu_to_le16(sdata->vif.bss_conf.beacon_int); | 2297 | cpu_to_le16(sdata->vif.bss_conf.beacon_int); |
| 2298 | mgmt->u.beacon.capab_info = 0x0; /* 0x0 for MPs */ | 2298 | mgmt->u.beacon.capab_info |= cpu_to_le16( |
| 2299 | sdata->u.mesh.security ? WLAN_CAPABILITY_PRIVACY : 0); | ||
| 2299 | 2300 | ||
| 2300 | pos = skb_put(skb, 2); | 2301 | pos = skb_put(skb, 2); |
| 2301 | *pos++ = WLAN_EID_SSID; | 2302 | *pos++ = WLAN_EID_SSID; |
| 2302 | *pos++ = 0x0; | 2303 | *pos++ = 0x0; |
| 2303 | 2304 | ||
| 2304 | mesh_mgmt_ies_add(skb, sdata); | 2305 | if (mesh_add_srates_ie(skb, sdata) || |
| 2306 | mesh_add_ds_params_ie(skb, sdata) || | ||
| 2307 | mesh_add_ext_srates_ie(skb, sdata) || | ||
| 2308 | mesh_add_rsn_ie(skb, sdata) || | ||
| 2309 | mesh_add_meshid_ie(skb, sdata) || | ||
| 2310 | mesh_add_meshconf_ie(skb, sdata) || | ||
| 2311 | mesh_add_vendor_ies(skb, sdata)) { | ||
| 2312 | pr_err("o11s: couldn't add ies!\n"); | ||
| 2313 | goto out; | ||
| 2314 | } | ||
| 2305 | } else { | 2315 | } else { |
| 2306 | WARN_ON(1); | 2316 | WARN_ON(1); |
| 2307 | goto out; | 2317 | goto out; |
