diff options
Diffstat (limited to 'net/mac80211/mesh.c')
-rw-r--r-- | net/mac80211/mesh.c | 213 |
1 files changed, 137 insertions, 76 deletions
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 29e9980c8e60..a7078fdba8ca 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(); |
@@ -193,10 +200,9 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr, | |||
193 | } | 200 | } |
194 | 201 | ||
195 | p = kmem_cache_alloc(rm_cache, GFP_ATOMIC); | 202 | p = kmem_cache_alloc(rm_cache, GFP_ATOMIC); |
196 | if (!p) { | 203 | if (!p) |
197 | printk(KERN_DEBUG "o11s: could not allocate RMC entry\n"); | ||
198 | return 0; | 204 | return 0; |
199 | } | 205 | |
200 | p->seqnum = seqnum; | 206 | p->seqnum = seqnum; |
201 | p->exp_time = jiffies + RMC_TIMEOUT; | 207 | p->exp_time = jiffies + RMC_TIMEOUT; |
202 | memcpy(p->sa, sa, ETH_ALEN); | 208 | memcpy(p->sa, sa, ETH_ALEN); |
@@ -204,89 +210,136 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr, | |||
204 | return 0; | 210 | return 0; |
205 | } | 211 | } |
206 | 212 | ||
207 | void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) | 213 | int |
214 | mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) | ||
208 | { | 215 | { |
209 | struct ieee80211_local *local = sdata->local; | 216 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; |
210 | struct ieee80211_supported_band *sband; | 217 | u8 *pos, neighbors; |
211 | u8 *pos; | 218 | u8 meshconf_len = sizeof(struct ieee80211_meshconf_ie); |
212 | int len, i, rate; | ||
213 | u8 neighbors; | ||
214 | |||
215 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | ||
216 | len = sband->n_bitrates; | ||
217 | if (len > 8) | ||
218 | len = 8; | ||
219 | pos = skb_put(skb, len + 2); | ||
220 | *pos++ = WLAN_EID_SUPP_RATES; | ||
221 | *pos++ = len; | ||
222 | for (i = 0; i < len; i++) { | ||
223 | rate = sband->bitrates[i].bitrate; | ||
224 | *pos++ = (u8) (rate / 5); | ||
225 | } | ||
226 | |||
227 | if (sband->n_bitrates > len) { | ||
228 | pos = skb_put(skb, sband->n_bitrates - len + 2); | ||
229 | *pos++ = WLAN_EID_EXT_SUPP_RATES; | ||
230 | *pos++ = sband->n_bitrates - len; | ||
231 | for (i = len; i < sband->n_bitrates; i++) { | ||
232 | rate = sband->bitrates[i].bitrate; | ||
233 | *pos++ = (u8) (rate / 5); | ||
234 | } | ||
235 | } | ||
236 | |||
237 | if (sband->band == IEEE80211_BAND_2GHZ) { | ||
238 | pos = skb_put(skb, 2 + 1); | ||
239 | *pos++ = WLAN_EID_DS_PARAMS; | ||
240 | *pos++ = 1; | ||
241 | *pos++ = ieee80211_frequency_to_channel(local->hw.conf.channel->center_freq); | ||
242 | } | ||
243 | 219 | ||
244 | pos = skb_put(skb, 2 + sdata->u.mesh.mesh_id_len); | 220 | if (skb_tailroom(skb) < 2 + meshconf_len) |
245 | *pos++ = WLAN_EID_MESH_ID; | 221 | return -ENOMEM; |
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 | 222 | ||
250 | pos = skb_put(skb, 2 + sizeof(struct ieee80211_meshconf_ie)); | 223 | pos = skb_put(skb, 2 + meshconf_len); |
251 | *pos++ = WLAN_EID_MESH_CONFIG; | 224 | *pos++ = WLAN_EID_MESH_CONFIG; |
252 | *pos++ = sizeof(struct ieee80211_meshconf_ie); | 225 | *pos++ = meshconf_len; |
253 | 226 | ||
254 | /* Active path selection protocol ID */ | 227 | /* Active path selection protocol ID */ |
255 | *pos++ = sdata->u.mesh.mesh_pp_id; | 228 | *pos++ = ifmsh->mesh_pp_id; |
256 | |||
257 | /* Active path selection metric ID */ | 229 | /* Active path selection metric ID */ |
258 | *pos++ = sdata->u.mesh.mesh_pm_id; | 230 | *pos++ = ifmsh->mesh_pm_id; |
259 | |||
260 | /* Congestion control mode identifier */ | 231 | /* Congestion control mode identifier */ |
261 | *pos++ = sdata->u.mesh.mesh_cc_id; | 232 | *pos++ = ifmsh->mesh_cc_id; |
262 | |||
263 | /* Synchronization protocol identifier */ | 233 | /* Synchronization protocol identifier */ |
264 | *pos++ = sdata->u.mesh.mesh_sp_id; | 234 | *pos++ = ifmsh->mesh_sp_id; |
265 | |||
266 | /* Authentication Protocol identifier */ | 235 | /* Authentication Protocol identifier */ |
267 | *pos++ = sdata->u.mesh.mesh_auth_id; | 236 | *pos++ = ifmsh->mesh_auth_id; |
268 | |||
269 | /* Mesh Formation Info - number of neighbors */ | 237 | /* Mesh Formation Info - number of neighbors */ |
270 | neighbors = atomic_read(&sdata->u.mesh.mshstats.estab_plinks); | 238 | neighbors = atomic_read(&ifmsh->mshstats.estab_plinks); |
271 | /* Number of neighbor mesh STAs or 15 whichever is smaller */ | 239 | /* Number of neighbor mesh STAs or 15 whichever is smaller */ |
272 | neighbors = (neighbors > 15) ? 15 : neighbors; | 240 | neighbors = (neighbors > 15) ? 15 : neighbors; |
273 | *pos++ = neighbors << 1; | 241 | *pos++ = neighbors << 1; |
274 | |||
275 | /* Mesh capability */ | 242 | /* Mesh capability */ |
276 | sdata->u.mesh.accepting_plinks = mesh_plink_availables(sdata); | 243 | ifmsh->accepting_plinks = mesh_plink_availables(sdata); |
277 | *pos = MESHCONF_CAPAB_FORWARDING; | 244 | *pos = MESHCONF_CAPAB_FORWARDING; |
278 | *pos++ |= sdata->u.mesh.accepting_plinks ? | 245 | *pos++ |= ifmsh->accepting_plinks ? |
279 | MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; | 246 | MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; |
280 | *pos++ = 0x00; | 247 | *pos++ = 0x00; |
281 | 248 | ||
282 | if (sdata->u.mesh.ie) { | 249 | return 0; |
283 | int len = sdata->u.mesh.ie_len; | 250 | } |
284 | const u8 *data = sdata->u.mesh.ie; | 251 | |
285 | if (skb_tailroom(skb) > len) | 252 | int |
286 | memcpy(skb_put(skb, len), data, len); | 253 | mesh_add_meshid_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) |
254 | { | ||
255 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | ||
256 | u8 *pos; | ||
257 | |||
258 | if (skb_tailroom(skb) < 2 + ifmsh->mesh_id_len) | ||
259 | return -ENOMEM; | ||
260 | |||
261 | pos = skb_put(skb, 2 + ifmsh->mesh_id_len); | ||
262 | *pos++ = WLAN_EID_MESH_ID; | ||
263 | *pos++ = ifmsh->mesh_id_len; | ||
264 | if (ifmsh->mesh_id_len) | ||
265 | memcpy(pos, ifmsh->mesh_id, ifmsh->mesh_id_len); | ||
266 | |||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | int | ||
271 | mesh_add_vendor_ies(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) | ||
272 | { | ||
273 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | ||
274 | u8 offset, len; | ||
275 | const u8 *data; | ||
276 | |||
277 | if (!ifmsh->ie || !ifmsh->ie_len) | ||
278 | return 0; | ||
279 | |||
280 | /* fast-forward to vendor IEs */ | ||
281 | offset = ieee80211_ie_split_vendor(ifmsh->ie, ifmsh->ie_len, 0); | ||
282 | |||
283 | if (offset) { | ||
284 | len = ifmsh->ie_len - offset; | ||
285 | data = ifmsh->ie + offset; | ||
286 | if (skb_tailroom(skb) < len) | ||
287 | return -ENOMEM; | ||
288 | memcpy(skb_put(skb, len), data, len); | ||
287 | } | 289 | } |
290 | |||
291 | return 0; | ||
288 | } | 292 | } |
289 | 293 | ||
294 | int | ||
295 | mesh_add_rsn_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) | ||
296 | { | ||
297 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | ||
298 | u8 len = 0; | ||
299 | const u8 *data; | ||
300 | |||
301 | if (!ifmsh->ie || !ifmsh->ie_len) | ||
302 | return 0; | ||
303 | |||
304 | /* find RSN IE */ | ||
305 | data = ifmsh->ie; | ||
306 | while (data < ifmsh->ie + ifmsh->ie_len) { | ||
307 | if (*data == WLAN_EID_RSN) { | ||
308 | len = data[1] + 2; | ||
309 | break; | ||
310 | } | ||
311 | data++; | ||
312 | } | ||
313 | |||
314 | if (len) { | ||
315 | if (skb_tailroom(skb) < len) | ||
316 | return -ENOMEM; | ||
317 | memcpy(skb_put(skb, len), data, len); | ||
318 | } | ||
319 | |||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | int mesh_add_ds_params_ie(struct sk_buff *skb, | ||
324 | struct ieee80211_sub_if_data *sdata) | ||
325 | { | ||
326 | struct ieee80211_local *local = sdata->local; | ||
327 | struct ieee80211_supported_band *sband; | ||
328 | u8 *pos; | ||
329 | |||
330 | if (skb_tailroom(skb) < 3) | ||
331 | return -ENOMEM; | ||
332 | |||
333 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | ||
334 | if (sband->band == IEEE80211_BAND_2GHZ) { | ||
335 | pos = skb_put(skb, 2 + 1); | ||
336 | *pos++ = WLAN_EID_DS_PARAMS; | ||
337 | *pos++ = 1; | ||
338 | *pos++ = ieee80211_frequency_to_channel(local->hw.conf.channel->center_freq); | ||
339 | } | ||
340 | |||
341 | return 0; | ||
342 | } | ||
290 | 343 | ||
291 | static void ieee80211_mesh_path_timer(unsigned long data) | 344 | static void ieee80211_mesh_path_timer(unsigned long data) |
292 | { | 345 | { |
@@ -352,8 +405,7 @@ int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, | |||
352 | memcpy(hdr->addr3, meshsa, ETH_ALEN); | 405 | memcpy(hdr->addr3, meshsa, ETH_ALEN); |
353 | return 24; | 406 | return 24; |
354 | } else { | 407 | } else { |
355 | *fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | | 408 | *fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); |
356 | IEEE80211_FCTL_TODS); | ||
357 | /* RA TA DA SA */ | 409 | /* RA TA DA SA */ |
358 | memset(hdr->addr1, 0, ETH_ALEN); /* RA is resolved later */ | 410 | memset(hdr->addr1, 0, ETH_ALEN); /* RA is resolved later */ |
359 | memcpy(hdr->addr2, meshsa, ETH_ALEN); | 411 | memcpy(hdr->addr2, meshsa, ETH_ALEN); |
@@ -425,7 +477,8 @@ static void ieee80211_mesh_rootpath(struct ieee80211_sub_if_data *sdata) | |||
425 | 477 | ||
426 | mesh_path_tx_root_frame(sdata); | 478 | mesh_path_tx_root_frame(sdata); |
427 | mod_timer(&ifmsh->mesh_path_root_timer, | 479 | mod_timer(&ifmsh->mesh_path_root_timer, |
428 | round_jiffies(jiffies + IEEE80211_MESH_RANN_INTERVAL)); | 480 | round_jiffies(TU_TO_EXP_TIME( |
481 | ifmsh->mshcfg.dot11MeshHWMPRannInterval))); | ||
429 | } | 482 | } |
430 | 483 | ||
431 | #ifdef CONFIG_PM | 484 | #ifdef CONFIG_PM |
@@ -433,7 +486,7 @@ void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata) | |||
433 | { | 486 | { |
434 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | 487 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; |
435 | 488 | ||
436 | /* use atomic bitops in case both timers fire at the same time */ | 489 | /* use atomic bitops in case all timers fire at the same time */ |
437 | 490 | ||
438 | if (del_timer_sync(&ifmsh->housekeeping_timer)) | 491 | if (del_timer_sync(&ifmsh->housekeeping_timer)) |
439 | set_bit(TMR_RUNNING_HK, &ifmsh->timers_running); | 492 | set_bit(TMR_RUNNING_HK, &ifmsh->timers_running); |
@@ -557,11 +610,18 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata, | |||
557 | struct ieee80211_rx_status *rx_status) | 610 | struct ieee80211_rx_status *rx_status) |
558 | { | 611 | { |
559 | switch (mgmt->u.action.category) { | 612 | switch (mgmt->u.action.category) { |
560 | case WLAN_CATEGORY_MESH_ACTION: | 613 | case WLAN_CATEGORY_SELF_PROTECTED: |
561 | mesh_rx_plink_frame(sdata, mgmt, len, rx_status); | 614 | switch (mgmt->u.action.u.self_prot.action_code) { |
615 | case WLAN_SP_MESH_PEERING_OPEN: | ||
616 | case WLAN_SP_MESH_PEERING_CLOSE: | ||
617 | case WLAN_SP_MESH_PEERING_CONFIRM: | ||
618 | mesh_rx_plink_frame(sdata, mgmt, len, rx_status); | ||
619 | break; | ||
620 | } | ||
562 | break; | 621 | break; |
563 | case WLAN_CATEGORY_MESH_PATH_SEL: | 622 | case WLAN_CATEGORY_MESH_ACTION: |
564 | mesh_rx_path_sel_frame(sdata, mgmt, len); | 623 | if (mesh_action_is_path_sel(mgmt)) |
624 | mesh_rx_path_sel_frame(sdata, mgmt, len); | ||
565 | break; | 625 | break; |
566 | } | 626 | } |
567 | } | 627 | } |
@@ -633,6 +693,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) | |||
633 | ifmsh->accepting_plinks = true; | 693 | ifmsh->accepting_plinks = true; |
634 | ifmsh->preq_id = 0; | 694 | ifmsh->preq_id = 0; |
635 | ifmsh->sn = 0; | 695 | ifmsh->sn = 0; |
696 | ifmsh->num_gates = 0; | ||
636 | atomic_set(&ifmsh->mpaths, 0); | 697 | atomic_set(&ifmsh->mpaths, 0); |
637 | mesh_rmc_init(sdata); | 698 | mesh_rmc_init(sdata); |
638 | ifmsh->last_preq = jiffies; | 699 | ifmsh->last_preq = jiffies; |