diff options
-rw-r--r-- | include/linux/ieee80211.h | 5 | ||||
-rw-r--r-- | net/mac80211/mesh.h | 4 | ||||
-rw-r--r-- | net/mac80211/mesh_pathtbl.c | 127 | ||||
-rw-r--r-- | net/mac80211/rx.c | 32 | ||||
-rw-r--r-- | net/mac80211/tx.c | 44 |
5 files changed, 201 insertions, 11 deletions
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index abc1abc63bf0..14126bc36641 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h | |||
@@ -471,6 +471,11 @@ struct ieee80211s_hdr { | |||
471 | u8 eaddr3[6]; | 471 | u8 eaddr3[6]; |
472 | } __attribute__ ((packed)); | 472 | } __attribute__ ((packed)); |
473 | 473 | ||
474 | /* Mesh flags */ | ||
475 | #define MESH_FLAGS_AE_A4 0x1 | ||
476 | #define MESH_FLAGS_AE_A5_A6 0x2 | ||
477 | #define MESH_FLAGS_PS_DEEP 0x4 | ||
478 | |||
474 | /** | 479 | /** |
475 | * struct ieee80211_quiet_ie | 480 | * struct ieee80211_quiet_ie |
476 | * | 481 | * |
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 8ee414a0447c..e10471c6ba42 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h | |||
@@ -71,6 +71,7 @@ enum mesh_path_flags { | |||
71 | */ | 71 | */ |
72 | struct mesh_path { | 72 | struct mesh_path { |
73 | u8 dst[ETH_ALEN]; | 73 | u8 dst[ETH_ALEN]; |
74 | u8 mpp[ETH_ALEN]; /* used for MPP or MAP */ | ||
74 | struct ieee80211_sub_if_data *sdata; | 75 | struct ieee80211_sub_if_data *sdata; |
75 | struct sta_info *next_hop; | 76 | struct sta_info *next_hop; |
76 | struct timer_list timer; | 77 | struct timer_list timer; |
@@ -226,6 +227,9 @@ int mesh_nexthop_lookup(struct sk_buff *skb, | |||
226 | void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata); | 227 | void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata); |
227 | struct mesh_path *mesh_path_lookup(u8 *dst, | 228 | struct mesh_path *mesh_path_lookup(u8 *dst, |
228 | struct ieee80211_sub_if_data *sdata); | 229 | struct ieee80211_sub_if_data *sdata); |
230 | struct mesh_path *mpp_path_lookup(u8 *dst, | ||
231 | struct ieee80211_sub_if_data *sdata); | ||
232 | int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata); | ||
229 | struct mesh_path *mesh_path_lookup_by_idx(int idx, | 233 | struct mesh_path *mesh_path_lookup_by_idx(int idx, |
230 | struct ieee80211_sub_if_data *sdata); | 234 | struct ieee80211_sub_if_data *sdata); |
231 | void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop); | 235 | void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop); |
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index e4fa2905fadc..3c72557df45a 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c | |||
@@ -36,6 +36,7 @@ struct mpath_node { | |||
36 | }; | 36 | }; |
37 | 37 | ||
38 | static struct mesh_table *mesh_paths; | 38 | static struct mesh_table *mesh_paths; |
39 | static struct mesh_table *mpp_paths; /* Store paths for MPP&MAP */ | ||
39 | 40 | ||
40 | /* This lock will have the grow table function as writer and add / delete nodes | 41 | /* This lock will have the grow table function as writer and add / delete nodes |
41 | * as readers. When reading the table (i.e. doing lookups) we are well protected | 42 | * as readers. When reading the table (i.e. doing lookups) we are well protected |
@@ -94,6 +95,34 @@ struct mesh_path *mesh_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) | |||
94 | return NULL; | 95 | return NULL; |
95 | } | 96 | } |
96 | 97 | ||
98 | struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) | ||
99 | { | ||
100 | struct mesh_path *mpath; | ||
101 | struct hlist_node *n; | ||
102 | struct hlist_head *bucket; | ||
103 | struct mesh_table *tbl; | ||
104 | struct mpath_node *node; | ||
105 | |||
106 | tbl = rcu_dereference(mpp_paths); | ||
107 | |||
108 | bucket = &tbl->hash_buckets[mesh_table_hash(dst, sdata, tbl)]; | ||
109 | hlist_for_each_entry_rcu(node, n, bucket, list) { | ||
110 | mpath = node->mpath; | ||
111 | if (mpath->sdata == sdata && | ||
112 | memcmp(dst, mpath->dst, ETH_ALEN) == 0) { | ||
113 | if (MPATH_EXPIRED(mpath)) { | ||
114 | spin_lock_bh(&mpath->state_lock); | ||
115 | if (MPATH_EXPIRED(mpath)) | ||
116 | mpath->flags &= ~MESH_PATH_ACTIVE; | ||
117 | spin_unlock_bh(&mpath->state_lock); | ||
118 | } | ||
119 | return mpath; | ||
120 | } | ||
121 | } | ||
122 | return NULL; | ||
123 | } | ||
124 | |||
125 | |||
97 | /** | 126 | /** |
98 | * mesh_path_lookup_by_idx - look up a path in the mesh path table by its index | 127 | * mesh_path_lookup_by_idx - look up a path in the mesh path table by its index |
99 | * @idx: index | 128 | * @idx: index |
@@ -226,6 +255,91 @@ err_path_alloc: | |||
226 | } | 255 | } |
227 | 256 | ||
228 | 257 | ||
258 | int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) | ||
259 | { | ||
260 | struct mesh_path *mpath, *new_mpath; | ||
261 | struct mpath_node *node, *new_node; | ||
262 | struct hlist_head *bucket; | ||
263 | struct hlist_node *n; | ||
264 | int grow = 0; | ||
265 | int err = 0; | ||
266 | u32 hash_idx; | ||
267 | |||
268 | |||
269 | if (memcmp(dst, sdata->dev->dev_addr, ETH_ALEN) == 0) | ||
270 | /* never add ourselves as neighbours */ | ||
271 | return -ENOTSUPP; | ||
272 | |||
273 | if (is_multicast_ether_addr(dst)) | ||
274 | return -ENOTSUPP; | ||
275 | |||
276 | err = -ENOMEM; | ||
277 | new_mpath = kzalloc(sizeof(struct mesh_path), GFP_KERNEL); | ||
278 | if (!new_mpath) | ||
279 | goto err_path_alloc; | ||
280 | |||
281 | new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL); | ||
282 | if (!new_node) | ||
283 | goto err_node_alloc; | ||
284 | |||
285 | read_lock(&pathtbl_resize_lock); | ||
286 | memcpy(new_mpath->dst, dst, ETH_ALEN); | ||
287 | memcpy(new_mpath->mpp, mpp, ETH_ALEN); | ||
288 | new_mpath->sdata = sdata; | ||
289 | new_mpath->flags = 0; | ||
290 | skb_queue_head_init(&new_mpath->frame_queue); | ||
291 | new_node->mpath = new_mpath; | ||
292 | new_mpath->exp_time = jiffies; | ||
293 | spin_lock_init(&new_mpath->state_lock); | ||
294 | |||
295 | hash_idx = mesh_table_hash(dst, sdata, mpp_paths); | ||
296 | bucket = &mpp_paths->hash_buckets[hash_idx]; | ||
297 | |||
298 | spin_lock(&mpp_paths->hashwlock[hash_idx]); | ||
299 | |||
300 | err = -EEXIST; | ||
301 | hlist_for_each_entry(node, n, bucket, list) { | ||
302 | mpath = node->mpath; | ||
303 | if (mpath->sdata == sdata && memcmp(dst, mpath->dst, ETH_ALEN) == 0) | ||
304 | goto err_exists; | ||
305 | } | ||
306 | |||
307 | hlist_add_head_rcu(&new_node->list, bucket); | ||
308 | if (atomic_inc_return(&mpp_paths->entries) >= | ||
309 | mpp_paths->mean_chain_len * (mpp_paths->hash_mask + 1)) | ||
310 | grow = 1; | ||
311 | |||
312 | spin_unlock(&mpp_paths->hashwlock[hash_idx]); | ||
313 | read_unlock(&pathtbl_resize_lock); | ||
314 | if (grow) { | ||
315 | struct mesh_table *oldtbl, *newtbl; | ||
316 | |||
317 | write_lock(&pathtbl_resize_lock); | ||
318 | oldtbl = mpp_paths; | ||
319 | newtbl = mesh_table_grow(mpp_paths); | ||
320 | if (!newtbl) { | ||
321 | write_unlock(&pathtbl_resize_lock); | ||
322 | return 0; | ||
323 | } | ||
324 | rcu_assign_pointer(mpp_paths, newtbl); | ||
325 | write_unlock(&pathtbl_resize_lock); | ||
326 | |||
327 | synchronize_rcu(); | ||
328 | mesh_table_free(oldtbl, false); | ||
329 | } | ||
330 | return 0; | ||
331 | |||
332 | err_exists: | ||
333 | spin_unlock(&mpp_paths->hashwlock[hash_idx]); | ||
334 | read_unlock(&pathtbl_resize_lock); | ||
335 | kfree(new_node); | ||
336 | err_node_alloc: | ||
337 | kfree(new_mpath); | ||
338 | err_path_alloc: | ||
339 | return err; | ||
340 | } | ||
341 | |||
342 | |||
229 | /** | 343 | /** |
230 | * mesh_plink_broken - deactivates paths and sends perr when a link breaks | 344 | * mesh_plink_broken - deactivates paths and sends perr when a link breaks |
231 | * | 345 | * |
@@ -475,11 +589,21 @@ static int mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl) | |||
475 | int mesh_pathtbl_init(void) | 589 | int mesh_pathtbl_init(void) |
476 | { | 590 | { |
477 | mesh_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); | 591 | mesh_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); |
592 | if (!mesh_paths) | ||
593 | return -ENOMEM; | ||
478 | mesh_paths->free_node = &mesh_path_node_free; | 594 | mesh_paths->free_node = &mesh_path_node_free; |
479 | mesh_paths->copy_node = &mesh_path_node_copy; | 595 | mesh_paths->copy_node = &mesh_path_node_copy; |
480 | mesh_paths->mean_chain_len = MEAN_CHAIN_LEN; | 596 | mesh_paths->mean_chain_len = MEAN_CHAIN_LEN; |
481 | if (!mesh_paths) | 597 | |
598 | mpp_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); | ||
599 | if (!mpp_paths) { | ||
600 | mesh_table_free(mesh_paths, true); | ||
482 | return -ENOMEM; | 601 | return -ENOMEM; |
602 | } | ||
603 | mpp_paths->free_node = &mesh_path_node_free; | ||
604 | mpp_paths->copy_node = &mesh_path_node_copy; | ||
605 | mpp_paths->mean_chain_len = MEAN_CHAIN_LEN; | ||
606 | |||
483 | return 0; | 607 | return 0; |
484 | } | 608 | } |
485 | 609 | ||
@@ -511,4 +635,5 @@ void mesh_path_expire(struct ieee80211_sub_if_data *sdata) | |||
511 | void mesh_pathtbl_unregister(void) | 635 | void mesh_pathtbl_unregister(void) |
512 | { | 636 | { |
513 | mesh_table_free(mesh_paths, true); | 637 | mesh_table_free(mesh_paths, true); |
638 | mesh_table_free(mpp_paths, true); | ||
514 | } | 639 | } |
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 3ab9670f1809..2efa4dd47b5d 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c | |||
@@ -1107,10 +1107,6 @@ ieee80211_data_to_8023(struct ieee80211_rx_data *rx) | |||
1107 | 1107 | ||
1108 | hdrlen = ieee80211_hdrlen(hdr->frame_control); | 1108 | hdrlen = ieee80211_hdrlen(hdr->frame_control); |
1109 | 1109 | ||
1110 | if (ieee80211_vif_is_mesh(&sdata->vif)) | ||
1111 | hdrlen += ieee80211_get_mesh_hdrlen( | ||
1112 | (struct ieee80211s_hdr *) (skb->data + hdrlen)); | ||
1113 | |||
1114 | /* convert IEEE 802.11 header + possible LLC headers into Ethernet | 1110 | /* convert IEEE 802.11 header + possible LLC headers into Ethernet |
1115 | * header | 1111 | * header |
1116 | * IEEE 802.11 address fields: | 1112 | * IEEE 802.11 address fields: |
@@ -1134,6 +1130,15 @@ ieee80211_data_to_8023(struct ieee80211_rx_data *rx) | |||
1134 | if (unlikely(sdata->vif.type != NL80211_IFTYPE_WDS && | 1130 | if (unlikely(sdata->vif.type != NL80211_IFTYPE_WDS && |
1135 | sdata->vif.type != NL80211_IFTYPE_MESH_POINT)) | 1131 | sdata->vif.type != NL80211_IFTYPE_MESH_POINT)) |
1136 | return -1; | 1132 | return -1; |
1133 | if (ieee80211_vif_is_mesh(&sdata->vif)) { | ||
1134 | struct ieee80211s_hdr *meshdr = (struct ieee80211s_hdr *) | ||
1135 | (skb->data + hdrlen); | ||
1136 | hdrlen += ieee80211_get_mesh_hdrlen(meshdr); | ||
1137 | if (meshdr->flags & MESH_FLAGS_AE_A5_A6) { | ||
1138 | memcpy(dst, meshdr->eaddr1, ETH_ALEN); | ||
1139 | memcpy(src, meshdr->eaddr2, ETH_ALEN); | ||
1140 | } | ||
1141 | } | ||
1137 | break; | 1142 | break; |
1138 | case __constant_cpu_to_le16(IEEE80211_FCTL_FROMDS): | 1143 | case __constant_cpu_to_le16(IEEE80211_FCTL_FROMDS): |
1139 | if (sdata->vif.type != NL80211_IFTYPE_STATION || | 1144 | if (sdata->vif.type != NL80211_IFTYPE_STATION || |
@@ -1393,6 +1398,25 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) | |||
1393 | /* illegal frame */ | 1398 | /* illegal frame */ |
1394 | return RX_DROP_MONITOR; | 1399 | return RX_DROP_MONITOR; |
1395 | 1400 | ||
1401 | if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6){ | ||
1402 | struct ieee80211_sub_if_data *sdata; | ||
1403 | struct mesh_path *mppath; | ||
1404 | |||
1405 | sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev); | ||
1406 | rcu_read_lock(); | ||
1407 | mppath = mpp_path_lookup(mesh_hdr->eaddr2, sdata); | ||
1408 | if (!mppath) { | ||
1409 | mpp_path_add(mesh_hdr->eaddr2, hdr->addr4, sdata); | ||
1410 | } else { | ||
1411 | spin_lock_bh(&mppath->state_lock); | ||
1412 | mppath->exp_time = jiffies; | ||
1413 | if (compare_ether_addr(mppath->mpp, hdr->addr4) != 0) | ||
1414 | memcpy(mppath->mpp, hdr->addr4, ETH_ALEN); | ||
1415 | spin_unlock_bh(&mppath->state_lock); | ||
1416 | } | ||
1417 | rcu_read_unlock(); | ||
1418 | } | ||
1419 | |||
1396 | if (compare_ether_addr(rx->dev->dev_addr, hdr->addr3) == 0) | 1420 | if (compare_ether_addr(rx->dev->dev_addr, hdr->addr3) == 0) |
1397 | return RX_CONTINUE; | 1421 | return RX_CONTINUE; |
1398 | 1422 | ||
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 00d798cc9e04..00d96e63dce9 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -1498,18 +1498,50 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, | |||
1498 | #ifdef CONFIG_MAC80211_MESH | 1498 | #ifdef CONFIG_MAC80211_MESH |
1499 | case NL80211_IFTYPE_MESH_POINT: | 1499 | case NL80211_IFTYPE_MESH_POINT: |
1500 | fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); | 1500 | fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); |
1501 | /* RA TA DA SA */ | ||
1502 | memset(hdr.addr1, 0, ETH_ALEN); | ||
1503 | memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); | ||
1504 | memcpy(hdr.addr3, skb->data, ETH_ALEN); | ||
1505 | memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); | ||
1506 | if (!sdata->u.mesh.mshcfg.dot11MeshTTL) { | 1501 | if (!sdata->u.mesh.mshcfg.dot11MeshTTL) { |
1507 | /* Do not send frames with mesh_ttl == 0 */ | 1502 | /* Do not send frames with mesh_ttl == 0 */ |
1508 | sdata->u.mesh.mshstats.dropped_frames_ttl++; | 1503 | sdata->u.mesh.mshstats.dropped_frames_ttl++; |
1509 | ret = 0; | 1504 | ret = 0; |
1510 | goto fail; | 1505 | goto fail; |
1511 | } | 1506 | } |
1512 | meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, sdata); | 1507 | memset(&mesh_hdr, 0, sizeof(mesh_hdr)); |
1508 | |||
1509 | if (compare_ether_addr(dev->dev_addr, | ||
1510 | skb->data + ETH_ALEN) == 0) { | ||
1511 | /* RA TA DA SA */ | ||
1512 | memset(hdr.addr1, 0, ETH_ALEN); | ||
1513 | memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); | ||
1514 | memcpy(hdr.addr3, skb->data, ETH_ALEN); | ||
1515 | memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); | ||
1516 | meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, sdata); | ||
1517 | } else { | ||
1518 | /* packet from other interface */ | ||
1519 | struct mesh_path *mppath; | ||
1520 | |||
1521 | memset(hdr.addr1, 0, ETH_ALEN); | ||
1522 | memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); | ||
1523 | memcpy(hdr.addr4, dev->dev_addr, ETH_ALEN); | ||
1524 | |||
1525 | if (is_multicast_ether_addr(skb->data)) | ||
1526 | memcpy(hdr.addr3, skb->data, ETH_ALEN); | ||
1527 | else { | ||
1528 | rcu_read_lock(); | ||
1529 | mppath = mpp_path_lookup(skb->data, sdata); | ||
1530 | if (mppath) | ||
1531 | memcpy(hdr.addr3, mppath->mpp, ETH_ALEN); | ||
1532 | else | ||
1533 | memset(hdr.addr3, 0xff, ETH_ALEN); | ||
1534 | rcu_read_unlock(); | ||
1535 | } | ||
1536 | |||
1537 | mesh_hdr.flags |= MESH_FLAGS_AE_A5_A6; | ||
1538 | mesh_hdr.ttl = sdata->u.mesh.mshcfg.dot11MeshTTL; | ||
1539 | put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &mesh_hdr.seqnum); | ||
1540 | memcpy(mesh_hdr.eaddr1, skb->data, ETH_ALEN); | ||
1541 | memcpy(mesh_hdr.eaddr2, skb->data + ETH_ALEN, ETH_ALEN); | ||
1542 | sdata->u.mesh.mesh_seqnum++; | ||
1543 | meshhdrlen = 18; | ||
1544 | } | ||
1513 | hdrlen = 30; | 1545 | hdrlen = 30; |
1514 | break; | 1546 | break; |
1515 | #endif | 1547 | #endif |