mac80211: Use 3-address format for mesh broadcast frames.
The 11s task group recently changed the frame mesh multicast/broadcast frame
format to use 3-address. This was done to avoid interactions with widely
deployed lazy-WDS access points.
This patch changes the format of group addressed frames, both mesh-originated
and proxied, to use the data format defined in draft D2.08 and forward. The
address fields used for group addressed frames is:
In 802.11 header
ToDS:0 FromDS:1
addr1: DA (broadcast/multicast address)
addr2: TA
addr3: Mesh SA
In address extension header:
addr4: SA (only present if frame was proxied)
Note that this change breaks backward compatibility with earlier mesh stack
versions.
Signed-off-by: Andrey Yurovsky <andrey@cozybit.com>
Signed-off-by: Javier Cardona <javier@cozybit.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 8c068e2..10d9338 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -399,21 +399,75 @@
}
/**
+ * ieee80211_fill_mesh_addresses - fill addresses of a locally originated mesh frame
+ * @hdr: 802.11 frame header
+ * @fc: frame control field
+ * @meshda: destination address in the mesh
+ * @meshsa: source address address in the mesh. Same as TA, as frame is
+ * locally originated.
+ *
+ * Return the length of the 802.11 (does not include a mesh control header)
+ */
+int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, char
+ *meshda, char *meshsa) {
+ if (is_multicast_ether_addr(meshda)) {
+ *fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
+ /* DA TA SA */
+ memcpy(hdr->addr1, meshda, ETH_ALEN);
+ memcpy(hdr->addr2, meshsa, ETH_ALEN);
+ memcpy(hdr->addr3, meshsa, ETH_ALEN);
+ return 24;
+ } else {
+ *fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS |
+ IEEE80211_FCTL_TODS);
+ /* RA TA DA SA */
+ memset(hdr->addr1, 0, ETH_ALEN); /* RA is resolved later */
+ memcpy(hdr->addr2, meshsa, ETH_ALEN);
+ memcpy(hdr->addr3, meshda, ETH_ALEN);
+ memcpy(hdr->addr4, meshsa, ETH_ALEN);
+ return 30;
+ }
+}
+
+/**
* ieee80211_new_mesh_header - create a new mesh header
* @meshhdr: uninitialized mesh header
* @sdata: mesh interface to be used
+ * @addr4: addr4 of the mesh frame (1st in ae header)
+ * may be NULL
+ * @addr5: addr5 of the mesh frame (1st or 2nd in ae header)
+ * may be NULL unless addr6 is present
+ * @addr6: addr6 of the mesh frame (2nd or 3rd in ae header)
+ * may be NULL unless addr5 is present
*
* Return the header length.
*/
int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
- struct ieee80211_sub_if_data *sdata)
+ struct ieee80211_sub_if_data *sdata, char *addr4,
+ char *addr5, char *addr6)
{
- meshhdr->flags = 0;
+ int aelen = 0;
+ memset(meshhdr, 0, sizeof(meshhdr));
meshhdr->ttl = sdata->u.mesh.mshcfg.dot11MeshTTL;
put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &meshhdr->seqnum);
sdata->u.mesh.mesh_seqnum++;
-
- return 6;
+ if (addr4) {
+ meshhdr->flags |= MESH_FLAGS_AE_A4;
+ aelen += ETH_ALEN;
+ memcpy(meshhdr->eaddr1, addr4, ETH_ALEN);
+ }
+ if (addr5 && addr6) {
+ meshhdr->flags |= MESH_FLAGS_AE_A5_A6;
+ aelen += 2 * ETH_ALEN;
+ if (!addr4) {
+ memcpy(meshhdr->eaddr1, addr5, ETH_ALEN);
+ memcpy(meshhdr->eaddr2, addr6, ETH_ALEN);
+ } else {
+ memcpy(meshhdr->eaddr2, addr5, ETH_ALEN);
+ memcpy(meshhdr->eaddr3, addr6, ETH_ALEN);
+ }
+ }
+ return 6 + aelen;
}
static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 6aaf1ec..2ebd74c 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -193,8 +193,11 @@
/* Public interfaces */
/* Various */
+int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc,
+ char *da, char *sa);
int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
- struct ieee80211_sub_if_data *sdata);
+ struct ieee80211_sub_if_data *sdata, char *addr4,
+ char *addr5, char *addr6);
int mesh_rmc_check(u8 *addr, struct ieee80211s_hdr *mesh_hdr,
struct ieee80211_sub_if_data *sdata);
bool mesh_matches_local(struct ieee802_11_elems *ie,
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 25a669c..4cd9e45 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -489,12 +489,21 @@
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ char *dev_addr = rx->dev->dev_addr;
if (ieee80211_is_data(hdr->frame_control)) {
- if (!ieee80211_has_a4(hdr->frame_control))
- return RX_DROP_MONITOR;
- if (memcmp(hdr->addr4, rx->dev->dev_addr, ETH_ALEN) == 0)
- return RX_DROP_MONITOR;
+ if (is_multicast_ether_addr(hdr->addr1)) {
+ if (ieee80211_has_tods(hdr->frame_control) ||
+ !ieee80211_has_fromds(hdr->frame_control))
+ return RX_DROP_MONITOR;
+ if (memcmp(hdr->addr3, dev_addr, ETH_ALEN) == 0)
+ return RX_DROP_MONITOR;
+ } else {
+ if (!ieee80211_has_a4(hdr->frame_control))
+ return RX_DROP_MONITOR;
+ if (memcmp(hdr->addr4, dev_addr, ETH_ALEN) == 0)
+ return RX_DROP_MONITOR;
+ }
}
/* If there is not an established peer link and this is not a peer link
@@ -527,7 +536,7 @@
if (ieee80211_is_data(hdr->frame_control) &&
is_multicast_ether_addr(hdr->addr1) &&
- mesh_rmc_check(hdr->addr4, msh_h_get(hdr, hdrlen), rx->sdata))
+ mesh_rmc_check(hdr->addr3, msh_h_get(hdr, hdrlen), rx->sdata))
return RX_DROP_MONITOR;
#undef msh_h_get
@@ -1495,7 +1504,8 @@
/* illegal frame */
return RX_DROP_MONITOR;
- if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6){
+ if (!is_multicast_ether_addr(hdr->addr1) &&
+ (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6)) {
struct mesh_path *mppath;
rcu_read_lock();
@@ -1512,7 +1522,9 @@
rcu_read_unlock();
}
- if (compare_ether_addr(rx->dev->dev_addr, hdr->addr3) == 0)
+ /* Frame has reached destination. Don't forward */
+ if (!is_multicast_ether_addr(hdr->addr1) &&
+ compare_ether_addr(rx->dev->dev_addr, hdr->addr3) == 0)
return RX_CONTINUE;
mesh_hdr->ttl--;
@@ -1532,22 +1544,21 @@
rx->dev->name);
fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data;
- /*
- * Save TA to addr1 to send TA a path error if a
- * suitable next hop is not found
- */
- memcpy(fwd_hdr->addr1, fwd_hdr->addr2, ETH_ALEN);
memcpy(fwd_hdr->addr2, rx->dev->dev_addr, ETH_ALEN);
info = IEEE80211_SKB_CB(fwd_skb);
memset(info, 0, sizeof(*info));
info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
info->control.vif = &rx->sdata->vif;
ieee80211_select_queue(local, fwd_skb);
- if (is_multicast_ether_addr(fwd_hdr->addr3))
- memcpy(fwd_hdr->addr1, fwd_hdr->addr3,
+ if (!is_multicast_ether_addr(fwd_hdr->addr1)) {
+ int err;
+ /*
+ * Save TA to addr1 to send TA a path error if a
+ * suitable next hop is not found
+ */
+ memcpy(fwd_hdr->addr1, fwd_hdr->addr2,
ETH_ALEN);
- else {
- int err = mesh_nexthop_lookup(fwd_skb, sdata);
+ err = mesh_nexthop_lookup(fwd_skb, sdata);
/* Failed to immediately resolve next hop:
* fwded frame was dropped or will be added
* later to the pending skb queue. */
@@ -1560,7 +1571,7 @@
}
}
- if (is_multicast_ether_addr(hdr->addr3) ||
+ if (is_multicast_ether_addr(hdr->addr1) ||
rx->dev->flags & IFF_PROMISC)
return RX_CONTINUE;
else
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 3244298..ee8aa76 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1414,9 +1414,7 @@
if (ieee80211_vif_is_mesh(&sdata->vif) &&
ieee80211_is_data(hdr->frame_control)) {
- if (is_multicast_ether_addr(hdr->addr3))
- memcpy(hdr->addr1, hdr->addr3, ETH_ALEN);
- else
+ if (!is_multicast_ether_addr(hdr->addr1))
if (mesh_nexthop_lookup(skb, sdata)) {
dev_put(sdata->dev);
return;
@@ -1619,52 +1617,58 @@
break;
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
- fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
if (!sdata->u.mesh.mshcfg.dot11MeshTTL) {
/* Do not send frames with mesh_ttl == 0 */
sdata->u.mesh.mshstats.dropped_frames_ttl++;
ret = NETDEV_TX_OK;
goto fail;
}
- memset(&mesh_hdr, 0, sizeof(mesh_hdr));
if (compare_ether_addr(dev->dev_addr,
skb->data + ETH_ALEN) == 0) {
- /* RA TA DA SA */
- memset(hdr.addr1, 0, ETH_ALEN);
- memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
- memcpy(hdr.addr3, skb->data, ETH_ALEN);
- memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
- meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, sdata);
+ hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
+ skb->data, skb->data + ETH_ALEN);
+ meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr,
+ sdata, NULL, NULL, NULL);
} else {
/* packet from other interface */
struct mesh_path *mppath;
+ int is_mesh_mcast = 1;
+ char *mesh_da;
- memset(hdr.addr1, 0, ETH_ALEN);
- memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
- memcpy(hdr.addr4, dev->dev_addr, ETH_ALEN);
-
+ rcu_read_lock();
if (is_multicast_ether_addr(skb->data))
- memcpy(hdr.addr3, skb->data, ETH_ALEN);
+ /* DA TA mSA AE:SA */
+ mesh_da = skb->data;
else {
- rcu_read_lock();
mppath = mpp_path_lookup(skb->data, sdata);
- if (mppath)
- memcpy(hdr.addr3, mppath->mpp, ETH_ALEN);
- else
- memset(hdr.addr3, 0xff, ETH_ALEN);
- rcu_read_unlock();
+ if (mppath) {
+ /* RA TA mDA mSA AE:DA SA */
+ mesh_da = mppath->mpp;
+ is_mesh_mcast = 0;
+ } else
+ /* DA TA mSA AE:SA */
+ mesh_da = dev->broadcast;
}
+ hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
+ mesh_da, dev->dev_addr);
+ rcu_read_unlock();
+ if (is_mesh_mcast)
+ meshhdrlen =
+ ieee80211_new_mesh_header(&mesh_hdr,
+ sdata,
+ skb->data + ETH_ALEN,
+ NULL,
+ NULL);
+ else
+ meshhdrlen =
+ ieee80211_new_mesh_header(&mesh_hdr,
+ sdata,
+ NULL,
+ skb->data,
+ skb->data + ETH_ALEN);
- mesh_hdr.flags |= MESH_FLAGS_AE_A5_A6;
- mesh_hdr.ttl = sdata->u.mesh.mshcfg.dot11MeshTTL;
- put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &mesh_hdr.seqnum);
- memcpy(mesh_hdr.eaddr1, skb->data, ETH_ALEN);
- memcpy(mesh_hdr.eaddr2, skb->data + ETH_ALEN, ETH_ALEN);
- sdata->u.mesh.mesh_seqnum++;
- meshhdrlen = 18;
}
- hdrlen = 30;
break;
#endif
case NL80211_IFTYPE_STATION: