*/
return TX_DROP;
+ if (tx->sdata->vif.type == NL80211_IFTYPE_OCB)
+ return TX_CONTINUE;
+
if (tx->sdata->vif.type == NL80211_IFTYPE_WDS)
return TX_CONTINUE;
* Returns false if the frame couldn't be transmitted but was queued instead.
*/
static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, bool txpending,
- enum ieee80211_band band)
+ struct sk_buff *skb, bool txpending)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_data tx;
return true;
}
- info->band = band;
-
/* set up hw_queue value early */
if (!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) ||
!(local->hw.flags & IEEE80211_HW_QUEUE_CONTROL))
return 0;
}
-void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
- enum ieee80211_band band)
+void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
}
ieee80211_set_qos_hdr(sdata, skb);
- ieee80211_tx(sdata, skb, false, band);
+ ieee80211_tx(sdata, skb, false);
}
static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
sdata->vif.type))
goto fail_rcu;
- ieee80211_xmit(sdata, skb, chandef->chan->band);
+ info->band = chandef->chan->band;
+ ieee80211_xmit(sdata, skb);
rcu_read_unlock();
return NETDEV_TX_OK;
}
/**
- * ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type
- * subinterfaces (wlan#, WDS, and VLAN interfaces)
- * @skb: packet to be sent
- * @dev: incoming interface
+ * ieee80211_build_hdr - build 802.11 header in the given frame
+ * @sdata: virtual interface to build the header for
+ * @skb: the skb to build the header in
+ * @info_flags: skb flags to set
*
- * Returns: NETDEV_TX_OK both on success and on failure. 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
- * IEEE 802.11 header based on which interface the packet is coming in. The
- * encapsulated packet will then be passed to master interface, wlan#.11, for
- * transmission (through low-level driver).
+ * Note that this function also takes care of the TX status request and
+ * potential unsharing of the SKB - this needs to be interleaved with the
+ * header building.
+ *
+ * The function requires the read-side RCU lock held
+ *
+ * Returns: the (possibly reallocated) skb or an ERR_PTR() code
*/
-netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
+static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, u32 info_flags)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info;
int head_need;
bool wme_sta = false, authorized = false, tdls_auth = false;
bool tdls_peer = false, tdls_setup_frame = false;
bool multicast;
- u32 info_flags = 0;
u16 info_id = 0;
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_sub_if_data *ap_sdata;
enum ieee80211_band band;
-
- if (unlikely(skb->len < ETH_HLEN))
- goto fail;
+ int ret;
/* convert Ethernet header to proper 802.11 header (based on
* operation mode) */
ethertype = (skb->data[12] << 8) | skb->data[13];
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) {
case NL80211_IFTYPE_AP_VLAN:
sta = rcu_dereference(sdata->u.vlan.sta);
ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
u.ap);
chanctx_conf = rcu_dereference(ap_sdata->vif.chanctx_conf);
- if (!chanctx_conf)
- goto fail_rcu;
+ if (!chanctx_conf) {
+ ret = -ENOTCONN;
+ goto free;
+ }
band = chanctx_conf->def.chan->band;
if (sta)
break;
case NL80211_IFTYPE_AP:
if (sdata->vif.type == NL80211_IFTYPE_AP)
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!chanctx_conf)
- goto fail_rcu;
+ if (!chanctx_conf) {
+ ret = -ENOTCONN;
+ goto free;
+ }
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
/* DA BSSID SA */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
}
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!chanctx_conf)
- goto fail_rcu;
+ if (!chanctx_conf) {
+ ret = -ENOTCONN;
+ goto free;
+ }
band = chanctx_conf->def.chan->band;
break;
#endif
* of a link teardown after a TDLS sta is removed due to being
* unreachable.
*/
- if (tdls_peer && !tdls_auth && !tdls_setup_frame)
- goto fail_rcu;
+ if (tdls_peer && !tdls_auth && !tdls_setup_frame) {
+ ret = -EINVAL;
+ goto free;
+ }
/* send direct packets to authorized TDLS peers */
if (tdls_peer && tdls_auth) {
hdrlen = 24;
}
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!chanctx_conf)
- goto fail_rcu;
+ if (!chanctx_conf) {
+ ret = -ENOTCONN;
+ goto free;
+ }
+ band = chanctx_conf->def.chan->band;
+ break;
+ case NL80211_IFTYPE_OCB:
+ /* DA SA BSSID */
+ memcpy(hdr.addr1, skb->data, ETH_ALEN);
+ memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+ eth_broadcast_addr(hdr.addr3);
+ hdrlen = 24;
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf) {
+ ret = -ENOTCONN;
+ goto free;
+ }
band = chanctx_conf->def.chan->band;
break;
case NL80211_IFTYPE_ADHOC:
memcpy(hdr.addr3, sdata->u.ibss.bssid, ETH_ALEN);
hdrlen = 24;
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!chanctx_conf)
- goto fail_rcu;
+ if (!chanctx_conf) {
+ ret = -ENOTCONN;
+ goto free;
+ }
band = chanctx_conf->def.chan->band;
break;
default:
- goto fail_rcu;
+ ret = -EINVAL;
+ goto free;
}
/*
* EAPOL frames from the local station.
*/
if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) &&
+ (sdata->vif.type != NL80211_IFTYPE_OCB) &&
!multicast && !authorized &&
(cpu_to_be16(ethertype) != sdata->control_port_protocol ||
!ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
net_info_ratelimited("%s: dropped frame to %pM (unauthorized port)\n",
- dev->name, hdr.addr1);
+ sdata->name, hdr.addr1);
#endif
I802_DEBUG_INC(local->tx_handlers_drop_unauth_port);
- goto fail_rcu;
+ ret = -EPERM;
+ goto free;
}
if (unlikely(!multicast && skb->sk &&
skb = skb_clone(skb, GFP_ATOMIC);
kfree_skb(tmp_skb);
- if (!skb)
- goto fail_rcu;
+ if (!skb) {
+ ret = -ENOMEM;
+ goto free;
+ }
}
hdr.frame_control = fc;
if (ieee80211_skb_resize(sdata, skb, head_need, true)) {
ieee80211_free_txskb(&local->hw, skb);
skb = NULL;
- goto fail_rcu;
+ return ERR_PTR(-ENOMEM);
}
}
nh_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
* is going to go through Linux networking code that may potentially
* need things like pointer to IP header. */
info = IEEE80211_SKB_CB(skb);
memset(info, 0, sizeof(*info));
- dev->trans_start = jiffies;
-
info->flags = info_flags;
info->ack_frame_id = info_id;
+ info->band = band;
- ieee80211_xmit(sdata, skb, band);
- rcu_read_unlock();
+ return skb;
+ free:
+ kfree_skb(skb);
+ return ERR_PTR(ret);
+}
- return NETDEV_TX_OK;
+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;
- fail_rcu:
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+ dev->trans_start = jiffies;
+
+ ieee80211_xmit(sdata, skb);
+ out:
rcu_read_unlock();
- fail:
- dev_kfree_skb(skb);
+}
+
+/**
+ * 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,
+ struct net_device *dev)
+{
+ __ieee80211_subif_start_xmit(skb, dev, 0);
return NETDEV_TX_OK;
}
+struct sk_buff *
+ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, u32 info_flags)
+{
+ struct ieee80211_hdr *hdr;
+ struct ieee80211_tx_data tx = {
+ .local = sdata->local,
+ .sdata = sdata,
+ };
+
+ rcu_read_lock();
+
+ skb = ieee80211_build_hdr(sdata, skb, info_flags);
+ if (IS_ERR(skb))
+ goto out;
+
+ hdr = (void *)skb->data;
+ tx.sta = sta_info_get(sdata, hdr->addr1);
+ tx.skb = skb;
+
+ if (ieee80211_tx_h_select_key(&tx) != TX_CONTINUE) {
+ rcu_read_unlock();
+ kfree_skb(skb);
+ return ERR_PTR(-EINVAL);
+ }
+
+out:
+ rcu_read_unlock();
+ return skb;
+}
/*
* ieee80211_clear_tx_pending may not be called in a context where
dev_kfree_skb(skb);
return true;
}
- result = ieee80211_tx(sdata, skb, true,
- chanctx_conf->def.chan->band);
+ info->band = chanctx_conf->def.chan->band;
+ result = ieee80211_tx(sdata, skb, true);
} else {
struct sk_buff_head skbs;
* requirements are that we do not come into tx with bhs on.
*/
local_bh_disable();
- ieee80211_xmit(sdata, skb, band);
+ IEEE80211_SKB_CB(skb)->band = band;
+ ieee80211_xmit(sdata, skb);
local_bh_enable();
}