mac80211: add parsing of TDLS specific IEs
[cascardo/linux.git] / net / mac80211 / tx.c
index 900632a..2dd8967 100644 (file)
@@ -296,6 +296,9 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
                 */
                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;
 
@@ -1423,8 +1426,7 @@ EXPORT_SYMBOL(ieee80211_tx_prepare_skb);
  * 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;
@@ -1449,8 +1451,6 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
                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))
@@ -1498,8 +1498,7 @@ static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata,
        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);
@@ -1534,7 +1533,7 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *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)
@@ -1754,7 +1753,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(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;
@@ -1784,23 +1784,26 @@ static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local,
 }
 
 /**
- * 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;
@@ -1816,25 +1819,17 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
        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);
@@ -1852,8 +1847,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                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;
@@ -1861,8 +1858,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
        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);
@@ -1949,8 +1948,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
 
                }
                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
@@ -1980,8 +1981,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                 * 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) {
@@ -2009,8 +2012,23 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                        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:
@@ -2020,12 +2038,15 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                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;
        }
 
        /*
@@ -2057,17 +2078,19 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
         * 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 &&
@@ -2104,8 +2127,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                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;
@@ -2154,7 +2179,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                if (ieee80211_skb_resize(sdata, skb, head_need, true)) {
                        ieee80211_free_txskb(&local->hw, skb);
                        skb = NULL;
-                       goto fail_rcu;
+                       return ERR_PTR(-ENOMEM);
                }
        }
 
@@ -2188,9 +2213,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
        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. */
@@ -2201,23 +2223,90 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
        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
@@ -2257,8 +2346,8 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
                        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;
 
@@ -3039,6 +3128,7 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
         * 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();
 }