static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, enum ieee80211_self_protected_actioncode action, u8 *da, __le16 llid, __le16 plid, __le16 reason) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 + sdata->u.mesh.ie_len); struct ieee80211_mgmt *mgmt; bool include_plid = false; int ie_len = 4; u16 peering_proto = 0; u8 *pos; if (!skb) return -1; skb_reserve(skb, local->hw.extra_tx_headroom); /* 25 is the size of the common mgmt part (24) plus the size of the * common action part (1) */ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 25 + sizeof(mgmt->u.action.u.self_prot)); memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.self_prot)); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); mgmt->u.action.category = WLAN_CATEGORY_SELF_PROTECTED; mgmt->u.action.u.self_prot.action_code = action; if (action != WLAN_SP_MESH_PEERING_CLOSE) { /* capability info */ pos = skb_put(skb, 2); memset(pos, 0, 2); if (action == WLAN_SP_MESH_PEERING_CONFIRM) { /* AID */ pos = skb_put(skb, 2); memcpy(pos + 2, &plid, 2); } 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; } else { /* WLAN_SP_MESH_PEERING_CLOSE */ if (mesh_add_meshid_ie(skb, sdata)) return -1; } /* Add Mesh Peering Management element */ switch (action) { case WLAN_SP_MESH_PEERING_OPEN: break; case WLAN_SP_MESH_PEERING_CONFIRM: ie_len += 2; include_plid = true; break; case WLAN_SP_MESH_PEERING_CLOSE: if (plid) { ie_len += 2; include_plid = true; } ie_len += 2; /* reason code */ break; default: return -EINVAL; } if (WARN_ON(skb_tailroom(skb) < 2 + ie_len)) return -ENOMEM; pos = skb_put(skb, 2 + ie_len); *pos++ = WLAN_EID_PEER_MGMT; *pos++ = ie_len; memcpy(pos, &peering_proto, 2); pos += 2; memcpy(pos, &llid, 2); pos += 2; if (include_plid) { memcpy(pos, &plid, 2); pos += 2; } if (action == WLAN_SP_MESH_PEERING_CLOSE) { memcpy(pos, &reason, 2); pos += 2; } if (mesh_add_vendor_ies(skb, sdata)) return -1; ieee80211_tx_skb(sdata, skb); return 0; }
static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, enum ieee80211_self_protected_actioncode action, u8 *da, __le16 llid, __le16 plid, __le16 reason) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; bool include_plid = false; u16 peering_proto = 0; u8 *pos, ie_len = 4; int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.self_prot) + sizeof(mgmt->u.action.u.self_prot); skb = dev_alloc_skb(local->tx_headroom + hdr_len + 2 + /* */ 2 + /* */ 2 + 8 + /* */ 2 + (IEEE80211_MAX_SUPP_RATES - 8) + 2 + sdata->u.mesh.mesh_id_len + 2 + sizeof(struct ieee80211_meshconf_ie) + 2 + sizeof(struct ieee80211_ht_cap) + 2 + sizeof(struct ieee80211_ht_info) + 2 + 8 + /* */ sdata->u.mesh.ie_len); if (!skb) return -1; skb_reserve(skb, local->tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); memset(mgmt, 0, hdr_len); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); mgmt->u.action.category = WLAN_CATEGORY_SELF_PROTECTED; mgmt->u.action.u.self_prot.action_code = action; if (action != WLAN_SP_MESH_PEERING_CLOSE) { /* */ pos = skb_put(skb, 2); memset(pos, 0, 2); if (action == WLAN_SP_MESH_PEERING_CONFIRM) { /* */ pos = skb_put(skb, 2); memcpy(pos + 2, &plid, 2); } if (ieee80211_add_srates_ie(&sdata->vif, skb) || ieee80211_add_ext_srates_ie(&sdata->vif, skb) || mesh_add_rsn_ie(skb, sdata) || mesh_add_meshid_ie(skb, sdata) || mesh_add_meshconf_ie(skb, sdata)) return -1; } else { /* */ if (mesh_add_meshid_ie(skb, sdata)) return -1; } /* */ switch (action) { case WLAN_SP_MESH_PEERING_OPEN: break; case WLAN_SP_MESH_PEERING_CONFIRM: ie_len += 2; include_plid = true; break; case WLAN_SP_MESH_PEERING_CLOSE: if (plid) { ie_len += 2; include_plid = true; } ie_len += 2; /* */ break; default: return -EINVAL; } if (WARN_ON(skb_tailroom(skb) < 2 + ie_len)) return -ENOMEM; pos = skb_put(skb, 2 + ie_len); *pos++ = WLAN_EID_PEER_MGMT; *pos++ = ie_len; memcpy(pos, &peering_proto, 2); pos += 2; memcpy(pos, &llid, 2); pos += 2; if (include_plid) { memcpy(pos, &plid, 2); pos += 2; } if (action == WLAN_SP_MESH_PEERING_CLOSE) { memcpy(pos, &reason, 2); pos += 2; } if (action != WLAN_SP_MESH_PEERING_CLOSE) { if (mesh_add_ht_cap_ie(skb, sdata) || mesh_add_ht_info_ie(skb, sdata)) return -1; } if (mesh_add_vendor_ies(skb, sdata)) return -1; ieee80211_tx_skb(sdata, skb); return 0; }