mac80211: fix mesh beacon format

Correct ordering of IEs in the mesh beacon while removing unneeded IEs
from mesh peering frames. Set privacy bit in capability info if security
is enabled. Add utility functions to aid in construction
of IEs and reduce code duplication.

Signed-off-by: Thomas Pedersen <thomas@cozybit.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Thomas Pedersen 2011-08-11 19:35:10 -07:00 committed by John W. Linville
parent f6a3e99da8
commit 082ebb0c25
4 changed files with 196 additions and 62 deletions

View File

@ -204,36 +204,185 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr,
return 0;
}
void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
int
mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
u8 *pos, neighbors;
u8 meshconf_len = sizeof(struct ieee80211_meshconf_ie);
if (skb_tailroom(skb) < 2 + meshconf_len)
return -ENOMEM;
pos = skb_put(skb, 2 + meshconf_len);
*pos++ = WLAN_EID_MESH_CONFIG;
*pos++ = meshconf_len;
/* Active path selection protocol ID */
*pos++ = ifmsh->mesh_pp_id;
/* Active path selection metric ID */
*pos++ = ifmsh->mesh_pm_id;
/* Congestion control mode identifier */
*pos++ = ifmsh->mesh_cc_id;
/* Synchronization protocol identifier */
*pos++ = ifmsh->mesh_sp_id;
/* Authentication Protocol identifier */
*pos++ = ifmsh->mesh_auth_id;
/* Mesh Formation Info - number of neighbors */
neighbors = atomic_read(&ifmsh->mshstats.estab_plinks);
/* Number of neighbor mesh STAs or 15 whichever is smaller */
neighbors = (neighbors > 15) ? 15 : neighbors;
*pos++ = neighbors << 1;
/* Mesh capability */
ifmsh->accepting_plinks = mesh_plink_availables(sdata);
*pos = MESHCONF_CAPAB_FORWARDING;
*pos++ |= ifmsh->accepting_plinks ?
MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
*pos++ = 0x00;
return 0;
}
int
mesh_add_meshid_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
u8 *pos;
if (skb_tailroom(skb) < 2 + ifmsh->mesh_id_len)
return -ENOMEM;
pos = skb_put(skb, 2 + ifmsh->mesh_id_len);
*pos++ = WLAN_EID_MESH_ID;
*pos++ = ifmsh->mesh_id_len;
if (ifmsh->mesh_id_len)
memcpy(pos, ifmsh->mesh_id, ifmsh->mesh_id_len);
return 0;
}
int
mesh_add_vendor_ies(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
u8 offset, len;
const u8 *data;
if (!ifmsh->ie || !ifmsh->ie_len)
return 0;
/* fast-forward to vendor IEs */
offset = ieee80211_ie_split_vendor(ifmsh->ie, ifmsh->ie_len, 0);
if (offset) {
len = ifmsh->ie_len - offset;
data = ifmsh->ie + offset;
if (skb_tailroom(skb) < len)
return -ENOMEM;
memcpy(skb_put(skb, len), data, len);
}
return 0;
}
int
mesh_add_rsn_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
u8 len = 0;
const u8 *data;
if (!ifmsh->ie || !ifmsh->ie_len)
return 0;
/* find RSN IE */
data = ifmsh->ie;
while (data < ifmsh->ie + ifmsh->ie_len) {
if (*data == WLAN_EID_RSN) {
len = data[1] + 2;
break;
}
data++;
}
if (len) {
if (skb_tailroom(skb) < len)
return -ENOMEM;
memcpy(skb_put(skb, len), data, len);
}
return 0;
}
int
mesh_add_srates_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
u8 *pos;
int len, i, rate;
u8 neighbors;
int rate;
u8 i, rates, *pos;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
len = sband->n_bitrates;
if (len > 8)
len = 8;
pos = skb_put(skb, len + 2);
rates = sband->n_bitrates;
if (rates > 8)
rates = 8;
if (skb_tailroom(skb) < rates + 2)
return -ENOMEM;
pos = skb_put(skb, rates + 2);
*pos++ = WLAN_EID_SUPP_RATES;
*pos++ = len;
for (i = 0; i < len; i++) {
*pos++ = rates;
for (i = 0; i < rates; i++) {
rate = sband->bitrates[i].bitrate;
*pos++ = (u8) (rate / 5);
}
if (sband->n_bitrates > len) {
pos = skb_put(skb, sband->n_bitrates - len + 2);
return 0;
}
int
mesh_add_ext_srates_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
int rate;
u8 i, exrates, *pos;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
exrates = sband->n_bitrates;
if (exrates > 8)
exrates -= 8;
else
exrates = 0;
if (skb_tailroom(skb) < exrates + 2)
return -ENOMEM;
if (exrates) {
pos = skb_put(skb, exrates + 2);
*pos++ = WLAN_EID_EXT_SUPP_RATES;
*pos++ = sband->n_bitrates - len;
for (i = len; i < sband->n_bitrates; i++) {
*pos++ = exrates;
for (i = 8; i < sband->n_bitrates; i++) {
rate = sband->bitrates[i].bitrate;
*pos++ = (u8) (rate / 5);
}
}
return 0;
}
int mesh_add_ds_params_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
u8 *pos;
if (skb_tailroom(skb) < 3)
return -ENOMEM;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
if (sband->band == IEEE80211_BAND_2GHZ) {
pos = skb_put(skb, 2 + 1);
*pos++ = WLAN_EID_DS_PARAMS;
@ -241,53 +390,9 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
*pos++ = ieee80211_frequency_to_channel(local->hw.conf.channel->center_freq);
}
pos = skb_put(skb, 2 + sdata->u.mesh.mesh_id_len);
*pos++ = WLAN_EID_MESH_ID;
*pos++ = sdata->u.mesh.mesh_id_len;
if (sdata->u.mesh.mesh_id_len)
memcpy(pos, sdata->u.mesh.mesh_id, sdata->u.mesh.mesh_id_len);
pos = skb_put(skb, 2 + sizeof(struct ieee80211_meshconf_ie));
*pos++ = WLAN_EID_MESH_CONFIG;
*pos++ = sizeof(struct ieee80211_meshconf_ie);
/* Active path selection protocol ID */
*pos++ = sdata->u.mesh.mesh_pp_id;
/* Active path selection metric ID */
*pos++ = sdata->u.mesh.mesh_pm_id;
/* Congestion control mode identifier */
*pos++ = sdata->u.mesh.mesh_cc_id;
/* Synchronization protocol identifier */
*pos++ = sdata->u.mesh.mesh_sp_id;
/* Authentication Protocol identifier */
*pos++ = sdata->u.mesh.mesh_auth_id;
/* Mesh Formation Info - number of neighbors */
neighbors = atomic_read(&sdata->u.mesh.mshstats.estab_plinks);
/* Number of neighbor mesh STAs or 15 whichever is smaller */
neighbors = (neighbors > 15) ? 15 : neighbors;
*pos++ = neighbors << 1;
/* Mesh capability */
sdata->u.mesh.accepting_plinks = mesh_plink_availables(sdata);
*pos = MESHCONF_CAPAB_FORWARDING;
*pos++ |= sdata->u.mesh.accepting_plinks ?
MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
*pos++ = 0x00;
if (sdata->u.mesh.ie) {
int len = sdata->u.mesh.ie_len;
const u8 *data = sdata->u.mesh.ie;
if (skb_tailroom(skb) > len)
memcpy(skb_put(skb, len), data, len);
}
return 0;
}
static void ieee80211_mesh_path_timer(unsigned long data)
{
struct ieee80211_sub_if_data *sdata =

View File

@ -199,6 +199,20 @@ bool mesh_matches_local(struct ieee802_11_elems *ie,
void mesh_ids_set_default(struct ieee80211_if_mesh *mesh);
void mesh_mgmt_ies_add(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
int mesh_add_meshconf_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
int mesh_add_meshid_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
int mesh_add_rsn_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
int mesh_add_vendor_ies(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
int mesh_add_srates_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
int mesh_add_ext_srates_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
int mesh_add_ds_params_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
void mesh_rmc_free(struct ieee80211_sub_if_data *sdata);
int mesh_rmc_init(struct ieee80211_sub_if_data *sdata);
void ieee80211s_init(void);

View File

@ -195,7 +195,12 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
memset(pos, 0, 2);
memcpy(pos + 2, &plid, 2);
}
mesh_mgmt_ies_add(skb, sdata);
if (mesh_add_srates_ie(skb, sdata) ||
mesh_add_ext_srates_ie(skb, sdata) ||
mesh_add_rsn_ie(skb, sdata) ||
mesh_add_meshid_ie(skb, sdata) ||
mesh_add_meshconf_ie(skb, sdata))
return -1;
}
/* Add Peer Link Management element */

View File

@ -2295,13 +2295,23 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
mgmt->u.beacon.beacon_int =
cpu_to_le16(sdata->vif.bss_conf.beacon_int);
mgmt->u.beacon.capab_info = 0x0; /* 0x0 for MPs */
mgmt->u.beacon.capab_info |= cpu_to_le16(
sdata->u.mesh.security ? WLAN_CAPABILITY_PRIVACY : 0);
pos = skb_put(skb, 2);
*pos++ = WLAN_EID_SSID;
*pos++ = 0x0;
mesh_mgmt_ies_add(skb, sdata);
if (mesh_add_srates_ie(skb, sdata) ||
mesh_add_ds_params_ie(skb, sdata) ||
mesh_add_ext_srates_ie(skb, sdata) ||
mesh_add_rsn_ie(skb, sdata) ||
mesh_add_meshid_ie(skb, sdata) ||
mesh_add_meshconf_ie(skb, sdata) ||
mesh_add_vendor_ies(skb, sdata)) {
pr_err("o11s: couldn't add ies!\n");
goto out;
}
} else {
WARN_ON(1);
goto out;