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>
This commit is contained in:
parent
a9e3091bf0
commit
3c5772a527
|
@ -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 {
|
||||||
|
|
||||||
/* 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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
/* 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)
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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);
|
|
||||||
memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
|
|
||||||
memcpy(hdr.addr4, dev->dev_addr, ETH_ALEN);
|
|
||||||
|
|
||||||
if (is_multicast_ether_addr(skb->data))
|
|
||||||
memcpy(hdr.addr3, skb->data, ETH_ALEN);
|
|
||||||
else {
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
|
if (is_multicast_ether_addr(skb->data))
|
||||||
|
/* DA TA mSA AE:SA */
|
||||||
|
mesh_da = skb->data;
|
||||||
|
else {
|
||||||
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;
|
||||||
|
} 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();
|
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)
|
||||||
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,
|
||||||
}
|
}
|
||||||
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)
|
||||||
|
|
Loading…
Reference in New Issue