mac80211: factor out 802.11 header building code

Factor out the 802.11 header building code from the xmit function
to be able to use it separately in a later commit.

While at it, fix up some documentation.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Arik Nemtsov <arik@wizery.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2014-11-09 18:50:10 +02:00
parent 73c4e195e6
commit 4c9451ed94

View File

@ -1784,24 +1784,26 @@ static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local,
} }
/** /**
* __ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type * ieee80211_build_hdr - build 802.11 header in the given frame
* subinterfaces (wlan#, WDS, and VLAN interfaces) * @sdata: virtual interface to build the header for
* @skb: packet to be sent * @skb: the skb to build the header in
* @dev: incoming interface
* @info_flags: skb flags to set * @info_flags: skb flags to set
* *
* On failure skb will be freed. * This function takes the skb with 802.3 header and reformats the header to
* the appropriate IEEE 802.11 header based on which interface the packet is
* being transmitted on.
* *
* This function takes in an Ethernet header and encapsulates it with suitable * Note that this function also takes care of the TX status request and
* IEEE 802.11 header based on which interface the packet is coming in. The * potential unsharing of the SKB - this needs to be interleaved with the
* encapsulated packet will then be passed to master interface, wlan#.11, for * header building.
* transmission (through low-level driver). *
* The function requires the read-side RCU lock held
*
* Returns: the (possibly reallocated) skb or an ERR_PTR() code
*/ */
void __ieee80211_subif_start_xmit(struct sk_buff *skb, static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
struct net_device *dev, struct sk_buff *skb, u32 info_flags)
u32 info_flags)
{ {
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info; struct ieee80211_tx_info *info;
int head_need; int head_need;
@ -1821,20 +1823,13 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_sub_if_data *ap_sdata; struct ieee80211_sub_if_data *ap_sdata;
enum ieee80211_band band; enum ieee80211_band band;
int ret;
if (unlikely(skb->len < ETH_HLEN))
goto fail;
/* convert Ethernet header to proper 802.11 header (based on /* convert Ethernet header to proper 802.11 header (based on
* operation mode) */ * operation mode) */
ethertype = (skb->data[12] << 8) | skb->data[13]; ethertype = (skb->data[12] << 8) | skb->data[13];
fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA);
rcu_read_lock();
/* Measure frame arrival for Tx latency statistics calculation */
ieee80211_tx_latency_start_msrmnt(local, skb);
switch (sdata->vif.type) { switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_AP_VLAN:
sta = rcu_dereference(sdata->u.vlan.sta); sta = rcu_dereference(sdata->u.vlan.sta);
@ -1852,8 +1847,10 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
u.ap); u.ap);
chanctx_conf = rcu_dereference(ap_sdata->vif.chanctx_conf); chanctx_conf = rcu_dereference(ap_sdata->vif.chanctx_conf);
if (!chanctx_conf) if (!chanctx_conf) {
goto fail_rcu; ret = -ENOTCONN;
goto free;
}
band = chanctx_conf->def.chan->band; band = chanctx_conf->def.chan->band;
if (sta) if (sta)
break; break;
@ -1861,8 +1858,10 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
if (sdata->vif.type == NL80211_IFTYPE_AP) if (sdata->vif.type == NL80211_IFTYPE_AP)
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (!chanctx_conf) if (!chanctx_conf) {
goto fail_rcu; ret = -ENOTCONN;
goto free;
}
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
/* DA BSSID SA */ /* DA BSSID SA */
memcpy(hdr.addr1, skb->data, ETH_ALEN); memcpy(hdr.addr1, skb->data, ETH_ALEN);
@ -1949,8 +1948,10 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
} }
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (!chanctx_conf) if (!chanctx_conf) {
goto fail_rcu; ret = -ENOTCONN;
goto free;
}
band = chanctx_conf->def.chan->band; band = chanctx_conf->def.chan->band;
break; break;
#endif #endif
@ -1980,8 +1981,10 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
* of a link teardown after a TDLS sta is removed due to being * of a link teardown after a TDLS sta is removed due to being
* unreachable. * unreachable.
*/ */
if (tdls_peer && !tdls_auth && !tdls_setup_frame) if (tdls_peer && !tdls_auth && !tdls_setup_frame) {
goto fail_rcu; ret = -EINVAL;
goto free;
}
/* send direct packets to authorized TDLS peers */ /* send direct packets to authorized TDLS peers */
if (tdls_peer && tdls_auth) { if (tdls_peer && tdls_auth) {
@ -2009,8 +2012,10 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
hdrlen = 24; hdrlen = 24;
} }
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (!chanctx_conf) if (!chanctx_conf) {
goto fail_rcu; ret = -ENOTCONN;
goto free;
}
band = chanctx_conf->def.chan->band; band = chanctx_conf->def.chan->band;
break; break;
case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_OCB:
@ -2020,8 +2025,10 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
eth_broadcast_addr(hdr.addr3); eth_broadcast_addr(hdr.addr3);
hdrlen = 24; hdrlen = 24;
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (!chanctx_conf) if (!chanctx_conf) {
goto fail_rcu; ret = -ENOTCONN;
goto free;
}
band = chanctx_conf->def.chan->band; band = chanctx_conf->def.chan->band;
break; break;
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
@ -2031,12 +2038,15 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
memcpy(hdr.addr3, sdata->u.ibss.bssid, ETH_ALEN); memcpy(hdr.addr3, sdata->u.ibss.bssid, ETH_ALEN);
hdrlen = 24; hdrlen = 24;
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (!chanctx_conf) if (!chanctx_conf) {
goto fail_rcu; ret = -ENOTCONN;
goto free;
}
band = chanctx_conf->def.chan->band; band = chanctx_conf->def.chan->band;
break; break;
default: default:
goto fail_rcu; ret = -EINVAL;
goto free;
} }
/* /*
@ -2074,12 +2084,13 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
!ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) { !ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
net_info_ratelimited("%s: dropped frame to %pM (unauthorized port)\n", net_info_ratelimited("%s: dropped frame to %pM (unauthorized port)\n",
dev->name, hdr.addr1); sdata->name, hdr.addr1);
#endif #endif
I802_DEBUG_INC(local->tx_handlers_drop_unauth_port); I802_DEBUG_INC(local->tx_handlers_drop_unauth_port);
goto fail_rcu; ret = -EPERM;
goto free;
} }
if (unlikely(!multicast && skb->sk && if (unlikely(!multicast && skb->sk &&
@ -2116,8 +2127,10 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
skb = skb_clone(skb, GFP_ATOMIC); skb = skb_clone(skb, GFP_ATOMIC);
kfree_skb(tmp_skb); kfree_skb(tmp_skb);
if (!skb) if (!skb) {
goto fail_rcu; ret = -ENOMEM;
goto free;
}
} }
hdr.frame_control = fc; hdr.frame_control = fc;
@ -2166,7 +2179,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
if (ieee80211_skb_resize(sdata, skb, head_need, true)) { if (ieee80211_skb_resize(sdata, skb, head_need, true)) {
ieee80211_free_txskb(&local->hw, skb); ieee80211_free_txskb(&local->hw, skb);
skb = NULL; skb = NULL;
goto fail_rcu; return ERR_PTR(-ENOMEM);
} }
} }
@ -2200,9 +2213,6 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
nh_pos += hdrlen; nh_pos += hdrlen;
h_pos += hdrlen; h_pos += hdrlen;
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
/* Update skb pointers to various headers since this modified frame /* Update skb pointers to various headers since this modified frame
* is going to go through Linux networking code that may potentially * is going to go through Linux networking code that may potentially
* need things like pointer to IP header. */ * need things like pointer to IP header. */
@ -2213,23 +2223,53 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
info = IEEE80211_SKB_CB(skb); info = IEEE80211_SKB_CB(skb);
memset(info, 0, sizeof(*info)); memset(info, 0, sizeof(*info));
dev->trans_start = jiffies;
info->flags = info_flags; info->flags = info_flags;
info->ack_frame_id = info_id; info->ack_frame_id = info_id;
info->band = band; info->band = band;
ieee80211_xmit(sdata, skb); return skb;
rcu_read_unlock(); free:
kfree_skb(skb);
return; return ERR_PTR(ret);
fail_rcu:
rcu_read_unlock();
fail:
dev_kfree_skb(skb);
} }
void __ieee80211_subif_start_xmit(struct sk_buff *skb,
struct net_device *dev,
u32 info_flags)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
if (unlikely(skb->len < ETH_HLEN)) {
kfree_skb(skb);
return;
}
rcu_read_lock();
/* Measure frame arrival for Tx latency statistics calculation */
ieee80211_tx_latency_start_msrmnt(local, skb);
skb = ieee80211_build_hdr(sdata, skb, info_flags);
if (IS_ERR(skb))
goto out;
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
dev->trans_start = jiffies;
ieee80211_xmit(sdata, skb);
out:
rcu_read_unlock();
}
/**
* ieee80211_subif_start_xmit - netif start_xmit function for 802.3 vifs
* @skb: packet to be sent
* @dev: incoming interface
*
* On failure skb will be freed.
*/
netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
struct net_device *dev) struct net_device *dev)
{ {