diff options
Diffstat (limited to 'net/mac80211/tx.c')
-rw-r--r-- | net/mac80211/tx.c | 64 |
1 files changed, 60 insertions, 4 deletions
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 698c8233e6b3..c12f361d7185 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -624,7 +624,14 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) u8 *qc; int tid; - /* only for injected frames */ + /* + * Packet injection may want to control the sequence + * number, if we have no matching interface then we + * neither assign one ourselves nor ask the driver to. + */ + if (unlikely(!info->control.vif)) + return TX_CONTINUE; + if (unlikely(ieee80211_is_ctl(hdr->frame_control))) return TX_CONTINUE; @@ -849,7 +856,6 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx, sband = tx->local->hw.wiphy->bands[tx->channel->band]; skb->do_not_encrypt = 1; - info->flags |= IEEE80211_TX_CTL_INJECTED; tx->flags &= ~IEEE80211_TX_FRAGMENTED; /* @@ -981,7 +987,7 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx, /* process and remove the injection radiotap header */ sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (unlikely(sdata->vif.type == NL80211_IFTYPE_MONITOR)) { + if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED)) { if (__ieee80211_parse_tx_radiotap(tx, skb) == TX_DROP) return TX_DROP; @@ -1300,6 +1306,11 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct ieee80211_sub_if_data *osdata; int headroom; bool may_encrypt; + enum { + NOT_MONITOR, + FOUND_SDATA, + UNKNOWN_ADDRESS, + } monitor_iface = NOT_MONITOR; int ret; if (skb->iif) @@ -1335,6 +1346,50 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, IEEE80211_IFSTA_MESH_CTR_INC(&osdata->u.mesh, fwded_frames); } + } else if (unlikely(osdata->vif.type == NL80211_IFTYPE_MONITOR)) { + struct ieee80211_sub_if_data *sdata; + struct ieee80211_local *local = osdata->local; + struct ieee80211_hdr *hdr; + int hdrlen; + u16 len_rthdr; + + info->flags |= IEEE80211_TX_CTL_INJECTED; + monitor_iface = UNKNOWN_ADDRESS; + + len_rthdr = ieee80211_get_radiotap_len(skb->data); + hdr = (struct ieee80211_hdr *)skb->data + len_rthdr; + hdrlen = ieee80211_hdrlen(hdr->frame_control); + + /* check the header is complete in the frame */ + if (likely(skb->len >= len_rthdr + hdrlen)) { + /* + * We process outgoing injected frames that have a + * local address we handle as though they are our + * own frames. + * This code here isn't entirely correct, the local + * MAC address is not necessarily enough to find + * the interface to use; for that proper VLAN/WDS + * support we will need a different mechanism. + */ + + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, + list) { + if (!netif_running(sdata->dev)) + continue; + if (compare_ether_addr(sdata->dev->dev_addr, + hdr->addr2)) { + dev_hold(sdata->dev); + dev_put(odev); + osdata = sdata; + odev = osdata->dev; + skb->iif = sdata->dev->ifindex; + monitor_iface = FOUND_SDATA; + break; + } + } + rcu_read_unlock(); + } } may_encrypt = !skb->do_not_encrypt; @@ -1355,7 +1410,8 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, osdata = container_of(osdata->bss, struct ieee80211_sub_if_data, u.ap); - info->control.vif = &osdata->vif; + if (likely(monitor_iface != UNKNOWN_ADDRESS)) + info->control.vif = &osdata->vif; ret = ieee80211_tx(odev, skb); dev_put(odev); |