Commit 3c5772a5 authored by Javier Cardona's avatar Javier Cardona Committed by John W. Linville

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: default avatarAndrey Yurovsky <andrey@cozybit.com>
Signed-off-by: default avatarJavier Cardona <javier@cozybit.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent a9e3091b
...@@ -398,22 +398,76 @@ endgrow: ...@@ -398,22 +398,76 @@ endgrow:
return NULL; return NULL;
} }
/**
* 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 * ieee80211_new_mesh_header - create a new mesh header
* @meshhdr: uninitialized mesh header * @meshhdr: uninitialized mesh header
* @sdata: mesh interface to be used * @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. * Return the header length.
*/ */
int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr, 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; meshhdr->ttl = sdata->u.mesh.mshcfg.dot11MeshTTL;
put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &meshhdr->seqnum); put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &meshhdr->seqnum);
sdata->u.mesh.mesh_seqnum++; sdata->u.mesh.mesh_seqnum++;
if (addr4) {
return 6; 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, static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata,
......
...@@ -193,8 +193,11 @@ struct mesh_rmc { ...@@ -193,8 +193,11 @@ struct mesh_rmc {
/* Public interfaces */ /* Public interfaces */
/* Various */ /* 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, 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, int mesh_rmc_check(u8 *addr, struct ieee80211s_hdr *mesh_hdr,
struct ieee80211_sub_if_data *sdata); struct ieee80211_sub_if_data *sdata);
bool mesh_matches_local(struct ieee802_11_elems *ie, bool mesh_matches_local(struct ieee802_11_elems *ie,
......
...@@ -489,13 +489,22 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) ...@@ -489,13 +489,22 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
{ {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control); unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control);
char *dev_addr = rx->dev->dev_addr;
if (ieee80211_is_data(hdr->frame_control)) { if (ieee80211_is_data(hdr->frame_control)) {
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)) if (!ieee80211_has_a4(hdr->frame_control))
return RX_DROP_MONITOR; return RX_DROP_MONITOR;
if (memcmp(hdr->addr4, rx->dev->dev_addr, ETH_ALEN) == 0) if (memcmp(hdr->addr4, dev_addr, ETH_ALEN) == 0)
return RX_DROP_MONITOR; return RX_DROP_MONITOR;
} }
}
/* If there is not an established peer link and this is not a peer link /* If there is not an established peer link and this is not a peer link
* establisment frame, beacon or probe, drop the frame. * establisment frame, beacon or probe, drop the frame.
...@@ -527,7 +536,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) ...@@ -527,7 +536,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
if (ieee80211_is_data(hdr->frame_control) && if (ieee80211_is_data(hdr->frame_control) &&
is_multicast_ether_addr(hdr->addr1) && 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; return RX_DROP_MONITOR;
#undef msh_h_get #undef msh_h_get
...@@ -1495,7 +1504,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) ...@@ -1495,7 +1504,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
/* illegal frame */ /* illegal frame */
return RX_DROP_MONITOR; 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; struct mesh_path *mppath;
rcu_read_lock(); rcu_read_lock();
...@@ -1512,7 +1522,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) ...@@ -1512,7 +1522,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
rcu_read_unlock(); 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; return RX_CONTINUE;
mesh_hdr->ttl--; mesh_hdr->ttl--;
...@@ -1532,22 +1544,21 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) ...@@ -1532,22 +1544,21 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
rx->dev->name); rx->dev->name);
fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data; 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); memcpy(fwd_hdr->addr2, rx->dev->dev_addr, ETH_ALEN);
info = IEEE80211_SKB_CB(fwd_skb); info = IEEE80211_SKB_CB(fwd_skb);
memset(info, 0, sizeof(*info)); memset(info, 0, sizeof(*info));
info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
info->control.vif = &rx->sdata->vif; info->control.vif = &rx->sdata->vif;
ieee80211_select_queue(local, fwd_skb); ieee80211_select_queue(local, fwd_skb);
if (is_multicast_ether_addr(fwd_hdr->addr3)) if (!is_multicast_ether_addr(fwd_hdr->addr1)) {
memcpy(fwd_hdr->addr1, fwd_hdr->addr3, 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); ETH_ALEN);
else { err = mesh_nexthop_lookup(fwd_skb, sdata);
int err = mesh_nexthop_lookup(fwd_skb, sdata);
/* Failed to immediately resolve next hop: /* Failed to immediately resolve next hop:
* fwded frame was dropped or will be added * fwded frame was dropped or will be added
* later to the pending skb queue. */ * later to the pending skb queue. */
...@@ -1560,7 +1571,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) ...@@ -1560,7 +1571,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
} }
} }
if (is_multicast_ether_addr(hdr->addr3) || if (is_multicast_ether_addr(hdr->addr1) ||
rx->dev->flags & IFF_PROMISC) rx->dev->flags & IFF_PROMISC)
return RX_CONTINUE; return RX_CONTINUE;
else else
......
...@@ -1414,9 +1414,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, ...@@ -1414,9 +1414,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
if (ieee80211_vif_is_mesh(&sdata->vif) && if (ieee80211_vif_is_mesh(&sdata->vif) &&
ieee80211_is_data(hdr->frame_control)) { ieee80211_is_data(hdr->frame_control)) {
if (is_multicast_ether_addr(hdr->addr3)) if (!is_multicast_ether_addr(hdr->addr1))
memcpy(hdr->addr1, hdr->addr3, ETH_ALEN);
else
if (mesh_nexthop_lookup(skb, sdata)) { if (mesh_nexthop_lookup(skb, sdata)) {
dev_put(sdata->dev); dev_put(sdata->dev);
return; return;
...@@ -1619,52 +1617,58 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, ...@@ -1619,52 +1617,58 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
break; break;
#ifdef CONFIG_MAC80211_MESH #ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_MESH_POINT:
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
if (!sdata->u.mesh.mshcfg.dot11MeshTTL) { if (!sdata->u.mesh.mshcfg.dot11MeshTTL) {
/* Do not send frames with mesh_ttl == 0 */ /* Do not send frames with mesh_ttl == 0 */
sdata->u.mesh.mshstats.dropped_frames_ttl++; sdata->u.mesh.mshstats.dropped_frames_ttl++;
ret = NETDEV_TX_OK; ret = NETDEV_TX_OK;
goto fail; goto fail;
} }
memset(&mesh_hdr, 0, sizeof(mesh_hdr));
if (compare_ether_addr(dev->dev_addr, if (compare_ether_addr(dev->dev_addr,
skb->data + ETH_ALEN) == 0) { skb->data + ETH_ALEN) == 0) {
/* RA TA DA SA */ hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
memset(hdr.addr1, 0, ETH_ALEN); skb->data, skb->data + ETH_ALEN);
memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr,
memcpy(hdr.addr3, skb->data, ETH_ALEN); sdata, NULL, NULL, NULL);
memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, sdata);
} else { } else {
/* packet from other interface */ /* packet from other interface */
struct mesh_path *mppath; struct mesh_path *mppath;
int is_mesh_mcast = 1;
char *mesh_da;
memset(hdr.addr1, 0, ETH_ALEN); rcu_read_lock();
memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
memcpy(hdr.addr4, dev->dev_addr, ETH_ALEN);
if (is_multicast_ether_addr(skb->data)) if (is_multicast_ether_addr(skb->data))
memcpy(hdr.addr3, skb->data, ETH_ALEN); /* DA TA mSA AE:SA */
mesh_da = skb->data;
else { else {
rcu_read_lock();
mppath = mpp_path_lookup(skb->data, sdata); mppath = mpp_path_lookup(skb->data, sdata);
if (mppath) if (mppath) {
memcpy(hdr.addr3, mppath->mpp, ETH_ALEN); /* RA TA mDA mSA AE:DA SA */
else mesh_da = mppath->mpp;
memset(hdr.addr3, 0xff, ETH_ALEN); is_mesh_mcast = 0;
rcu_read_unlock(); } 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; break;
#endif #endif
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_STATION:
......
...@@ -274,11 +274,11 @@ static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) ...@@ -274,11 +274,11 @@ static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
switch (ae) { switch (ae) {
case 0: case 0:
return 6; return 6;
case 1: case MESH_FLAGS_AE_A4:
return 12; return 12;
case 2: case MESH_FLAGS_AE_A5_A6:
return 18; return 18;
case 3: case (MESH_FLAGS_AE_A4 | MESH_FLAGS_AE_A5_A6):
return 24; return 24;
default: default:
return 6; return 6;
...@@ -333,10 +333,18 @@ int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr, ...@@ -333,10 +333,18 @@ int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr,
} }
break; break;
case cpu_to_le16(IEEE80211_FCTL_FROMDS): case cpu_to_le16(IEEE80211_FCTL_FROMDS):
if (iftype != NL80211_IFTYPE_STATION || if ((iftype != NL80211_IFTYPE_STATION &&
iftype != NL80211_IFTYPE_MESH_POINT) ||
(is_multicast_ether_addr(dst) && (is_multicast_ether_addr(dst) &&
!compare_ether_addr(src, addr))) !compare_ether_addr(src, addr)))
return -1; return -1;
if (iftype == NL80211_IFTYPE_MESH_POINT) {
struct ieee80211s_hdr *meshdr =
(struct ieee80211s_hdr *) (skb->data + hdrlen);
hdrlen += ieee80211_get_mesh_hdrlen(meshdr);
if (meshdr->flags & MESH_FLAGS_AE_A4)
memcpy(src, meshdr->eaddr1, ETH_ALEN);
}
break; break;
case cpu_to_le16(0): case cpu_to_le16(0):
if (iftype != NL80211_IFTYPE_ADHOC) if (iftype != NL80211_IFTYPE_ADHOC)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment