This fixes several problems with receiving when multiple interfaces are
present or when some interface is in promiscious mode:

- Packet type (PACKET_HOST and PACKET_OTHER_HOST) is set correctly now.
- Failed decryption of a frame is reported only once for each frame.
- Failed decryption of a frame not destined to the interface (e.g. when the
  interface is in promisc mode) is not reported.
- Channel utilization is counted correctly (i.e. once for each frame only,
  independently on number of active virtual interfaces). To achieve this,
  ieee80211_rx_handlers needed to be separated into new
  ieee80211_rx_handlers and ieee80211_rx_pre_handlers structures.

Defragmentation still doesn't work correctly in promisc mode. This is fixed
by subsequent patches.

Signed-off-by: Jiri Benc <[EMAIL PROTECTED]>

---

 net/d80211/ieee80211.c   |  171 +++++++++++++++++++++++++++++++---------------
 net/d80211/ieee80211_i.h |    5 +
 net/d80211/wpa.c         |    4 +
 3 files changed, 124 insertions(+), 56 deletions(-)

6cc76c3c2c5de4e1ad40a8b82201eff5eab81939
diff --git a/net/d80211/ieee80211.c b/net/d80211/ieee80211.c
index fe1003b..52316f9 100644
--- a/net/d80211/ieee80211.c
+++ b/net/d80211/ieee80211.c
@@ -2463,27 +2463,15 @@ ieee80211_rx_h_data(struct ieee80211_txr
                memcpy(ehdr->h_source, src, ETH_ALEN);
                 ehdr->h_proto = len;
        }
-
-        if (rx->sta && !rx->sta->assoc_ap &&
-           !(rx->sta && (rx->sta->flags & WLAN_STA_WDS)))
-                skb->dev = rx->sta->dev;
-        else
-                skb->dev = dev;
+       skb->dev = dev;
 
         skb2 = NULL;
-        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-        /*
-         * don't count the master since the low level code
-         * counts it already for us.
-         */
-        if (skb->dev != sdata->master) {
-               sdata->stats.rx_packets++;
-               sdata->stats.rx_bytes += skb->len;
-        }
+       sdata->stats.rx_packets++;
+       sdata->stats.rx_bytes += skb->len;
 
        if (local->bridge_packets && (sdata->type == IEEE80211_IF_TYPE_AP
-           || sdata->type == IEEE80211_IF_TYPE_VLAN)) {
+           || sdata->type == IEEE80211_IF_TYPE_VLAN) && rx->u.rx.ra_match) {
                if (is_multicast_ether_addr(skb->data)) {
                        /* send multicast frames both to higher layers in
                         * local net stack and back to the wireless media */
@@ -2760,13 +2748,14 @@ #endif /* IEEE80211_VERBOSE_DEBUG_PS */
 
 
 static ieee80211_txrx_result
-ieee80211_rx_h_ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx)
 {
        struct sk_buff *skb;
        int no_pending_pkts;
 
        if (likely(!rx->sta || WLAN_FC_GET_TYPE(rx->fc) != WLAN_FC_TYPE_CTRL ||
-                  WLAN_FC_GET_STYPE(rx->fc) != WLAN_FC_STYPE_PSPOLL))
+                  WLAN_FC_GET_STYPE(rx->fc) != WLAN_FC_STYPE_PSPOLL ||
+                  !rx->u.rx.ra_match))
                return TXRX_CONTINUE;
 
        skb = skb_dequeue(&rx->sta->tx_filtered);
@@ -3042,8 +3031,10 @@ ieee80211_rx_h_check(struct ieee80211_tx
                if (unlikely(rx->fc & WLAN_FC_RETRY &&
                             rx->sta->last_seq_ctrl[rx->u.rx.queue] ==
                             hdr->seq_ctrl)) {
-                       rx->local->dot11FrameDuplicateCount++;
-                       rx->sta->num_duplicates++;
+                       if (rx->u.rx.ra_match) {
+                               rx->local->dot11FrameDuplicateCount++;
+                               rx->sta->num_duplicates++;
+                       }
                        return TXRX_DROP;
                } else
                        rx->sta->last_seq_ctrl[rx->u.rx.queue] = hdr->seq_ctrl;
@@ -3057,7 +3048,9 @@ ieee80211_rx_h_check(struct ieee80211_tx
                return TXRX_DROP;
        }
 
-       if (memcmp(rx->dev->dev_addr, hdr->addr1, ETH_ALEN) == 0)
+       if (rx->fc & WLAN_FC_TODS)
+               rx->skb->pkt_type = PACKET_OTHERHOST;
+       else if (memcmp(rx->dev->dev_addr, hdr->addr1, ETH_ALEN) == 0)
                rx->skb->pkt_type = PACKET_HOST;
        else if (is_multicast_ether_addr(hdr->addr1)) {
                if (is_broadcast_ether_addr(hdr->addr1))
@@ -3080,8 +3073,10 @@ ieee80211_rx_h_check(struct ieee80211_tx
                       WLAN_FC_GET_STYPE(rx->fc) == WLAN_FC_STYPE_PSPOLL)) &&
                     rx->sdata->type != IEEE80211_IF_TYPE_IBSS &&
                     (!rx->sta || !(rx->sta->flags & WLAN_STA_ASSOC)))) {
-               if (!(rx->fc & WLAN_FC_FROMDS) && !(rx->fc & WLAN_FC_TODS)) {
-                       /* Drop IBSS frames silently. */
+               if ((!(rx->fc & WLAN_FC_FROMDS) && !(rx->fc & WLAN_FC_TODS)) ||
+                   !rx->u.rx.ra_match) {
+                       /* Drop IBSS frames and frames for other hosts
+                        * silently. */
                        return TXRX_DROP;
                }
 
@@ -3113,6 +3108,8 @@ ieee80211_rx_h_check(struct ieee80211_tx
                                rx->key = rx->sdata->keys[keyidx];
                        }
                        if (!rx->key) {
+                               if (!rx->u.rx.ra_match)
+                                       return TXRX_DROP;
                                printk(KERN_DEBUG "%s: RX WEP frame with "
                                       "unknown keyidx %d (A1=" MACSTR " A2="
                                       MACSTR " A3=" MACSTR ")\n",
@@ -3128,7 +3125,7 @@ ieee80211_rx_h_check(struct ieee80211_tx
                }
         }
 
-       if (rx->fc & WLAN_FC_ISWEP && rx->key) {
+       if (rx->fc & WLAN_FC_ISWEP && rx->key && rx->u.rx.ra_match) {
                rx->key->tx_rx_count++;
                if (unlikely(rx->local->key_tx_rx_threshold &&
                             rx->key->tx_rx_count >
@@ -3168,6 +3165,10 @@ ieee80211_rx_h_sta_process(struct ieee80
                 */
                sta->last_rx = jiffies;
        }
+
+       if (!rx->u.rx.ra_match)
+               return TXRX_CONTINUE;
+
        sta->rx_fragments++;
        sta->rx_bytes += rx->skb->len;
        sta->last_rssi = rx->u.rx.status->ssi;
@@ -3203,7 +3204,7 @@ ieee80211_rx_h_wep_weak_iv_detection(str
 {
        if (!rx->sta || !(rx->fc & WLAN_FC_ISWEP) ||
            WLAN_FC_GET_TYPE(rx->fc) != WLAN_FC_TYPE_DATA || !rx->key ||
-           rx->key->alg != ALG_WEP)
+           rx->key->alg != ALG_WEP || !rx->u.rx.ra_match)
                return TXRX_CONTINUE;
 
        /* Check for weak IVs, if hwaccel did not remove IV from the frame */
@@ -3260,7 +3261,7 @@ static ieee80211_txrx_result
 ieee80211_rx_h_802_1x_pae(struct ieee80211_txrx_data *rx)
 {
        if (rx->sdata->eapol && ieee80211_is_eapol(rx->skb) &&
-           rx->sdata->type != IEEE80211_IF_TYPE_STA) {
+           rx->sdata->type != IEEE80211_IF_TYPE_STA && rx->u.rx.ra_match) {
                /* Pass both encrypted and unencrypted EAPOL frames to user
                 * space for processing. */
                ieee80211_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status,
@@ -3313,6 +3314,10 @@ static ieee80211_txrx_result
 ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx)
 {
         struct ieee80211_sub_if_data *sdata;
+
+       if (!rx->u.rx.ra_match)
+               return TXRX_DROP;
+
        sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
        if ((sdata->type == IEEE80211_IF_TYPE_STA ||
             sdata->type == IEEE80211_IF_TYPE_IBSS) &&
@@ -3351,7 +3356,8 @@ ieee80211_rx_h_passive_scan(struct ieee8
                 fc = le16_to_cpu(hdr->frame_control);
 
                 if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
-                   WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) {
+                   WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON &&
+                   rx->dev == local->mdev) {
                        local->scan.rx_beacon++;
                        /* Need to trim FCS here because it is normally
                         * removed only after this passive scan handler. */
@@ -3502,14 +3508,16 @@ static void ieee80211_rx_michael_mic_rep
        rx->skb = NULL;
 }
 
-static inline void ieee80211_invoke_rx_handlers(struct ieee80211_local *local,
-                                               struct ieee80211_txrx_data *rx,
-                                               struct sta_info *sta)
+static inline ieee80211_txrx_result __ieee80211_invoke_rx_handlers(
+                               struct ieee80211_local *local,
+                               ieee80211_rx_handler *handlers,
+                               struct ieee80211_txrx_data *rx,
+                               struct sta_info *sta)
 {
        ieee80211_rx_handler *handler;
         ieee80211_txrx_result res = TXRX_DROP;
 
-       for (handler = local->rx_handlers; *handler != NULL; handler++) {
+       for (handler = handlers; *handler != NULL; handler++) {
                res = (*handler)(rx);
                if (res != TXRX_CONTINUE) {
                        if (res == TXRX_DROP) {
@@ -3523,8 +3531,19 @@ static inline void ieee80211_invoke_rx_h
                }
        }
 
+       if (res == TXRX_DROP) {
+               dev_kfree_skb(rx->skb);
+       }
+       return res;
+}
 
-       if (res == TXRX_DROP || *handler == NULL)
+static inline void ieee80211_invoke_rx_handlers(struct ieee80211_local *local,
+                                               ieee80211_rx_handler *handlers,
+                                               struct ieee80211_txrx_data *rx,
+                                               struct sta_info *sta)
+{
+       if (__ieee80211_invoke_rx_handlers(local, handlers, rx, sta) ==
+           TXRX_CONTINUE)
                dev_kfree_skb(rx->skb);
 }
 
@@ -3568,37 +3587,57 @@ void __ieee80211_rx(struct net_device *d
        if (unlikely(local->sta_scanning || local->scan.in_scan))
                rx.u.rx.in_scan = 1;
 
+       if (__ieee80211_invoke_rx_handlers(local, local->rx_pre_handlers, &rx,
+                                          sta) != TXRX_CONTINUE)
+               goto end;
+       skb = rx.skb;
+
        if (sta && !sta->assoc_ap && !(sta->flags & WLAN_STA_WDS) &&
            !local->iff_promiscs && !multicast) {
                rx.dev = sta->dev;
                rx.sdata = IEEE80211_DEV_TO_SUB_IF(rx.dev);
-               ieee80211_invoke_rx_handlers(local, &rx, sta);
+               rx.u.rx.ra_match = 1;
+               ieee80211_invoke_rx_handlers(local, local->rx_handlers, &rx,
+                                            sta);
        } else {
                struct ieee80211_sub_if_data *prev = NULL;
                struct sk_buff *skb_new;
                u8 *bssid = ieee80211_get_bssid(hdr, skb->len);
 
                list_for_each_entry(sdata, &local->sub_if_list, list) {
+                       rx.u.rx.ra_match = 1;
                        switch (sdata->type) {
                        case IEEE80211_IF_TYPE_STA:
-                               if (!bssid || memcmp(sdata->u.sta.bssid,
-                                                    bssid, ETH_ALEN) != 0)
-                                       continue;
-                               if (!multicast && !sdata->promisc &&
-                                   memcmp(sdata->dev->dev_addr, hdr->addr1,
-                                          ETH_ALEN) != 0)
+                               if (!bssid)
                                        continue;
+                               if (!ieee80211_bssid_match(bssid,
+                                                       sdata->u.sta.bssid)) {
+                                       if (!rx.u.rx.in_scan)
+                                               continue;
+                                       rx.u.rx.ra_match = 0;
+                               } else if (!multicast &&
+                                          memcmp(sdata->dev->dev_addr,
+                                                 hdr->addr1, ETH_ALEN) != 0) {
+                                       if (!sdata->promisc)
+                                               continue;
+                                       rx.u.rx.ra_match = 0;
+                               }
                                break;
                        case IEEE80211_IF_TYPE_IBSS:
-                               if (!bssid ||
-                                   !ieee80211_bssid_match(bssid,
-                                                          sdata->u.sta.bssid))
-                                       continue;
-                               if (!multicast && !sdata->promisc &&
-                                   memcmp(sdata->dev->dev_addr, hdr->addr1,
-                                          ETH_ALEN) != 0)
+                               if (!bssid)
                                        continue;
-                               if (sta == NULL) {
+                               if (!ieee80211_bssid_match(bssid,
+                                                       sdata->u.sta.bssid)) {
+                                       if (!rx.u.rx.in_scan)
+                                               continue;
+                                       rx.u.rx.ra_match = 0;
+                               } else if (!multicast &&
+                                          memcmp(sdata->dev->dev_addr,
+                                                 hdr->addr1, ETH_ALEN) != 0) {
+                                       if (!sdata->promisc)
+                                               continue;
+                                       rx.u.rx.ra_match = 0;
+                               } else if (sta == NULL) {
                                        sta = rx.sta =
                                                ieee80211_ibss_add_sta(dev, 
skb, bssid,
                                                                       
hdr->addr2);
@@ -3610,10 +3649,12 @@ void __ieee80211_rx(struct net_device *d
                                        if (memcmp(sdata->dev->dev_addr,
                                                   hdr->addr1, ETH_ALEN) != 0)
                                                continue;
-                               } else if (!rx.u.rx.in_scan &&
-                                          !ieee80211_bssid_match(bssid,
-                                                       sdata->dev->dev_addr))
-                                       continue;
+                               } else if (!ieee80211_bssid_match(bssid,
+                                                       sdata->dev->dev_addr)) {
+                                       if (!rx.u.rx.in_scan)
+                                               continue;
+                                       rx.u.rx.ra_match = 0;
+                               }
                                if (sdata->dev == sdata->master &&
                                    !rx.u.rx.in_scan)
                                        /* do not receive anything via
@@ -3643,7 +3684,9 @@ void __ieee80211_rx(struct net_device *d
                                rx.skb = skb_new;
                                rx.dev = prev->dev;
                                rx.sdata = prev;
-                               ieee80211_invoke_rx_handlers(local, &rx, sta);
+                               ieee80211_invoke_rx_handlers(local,
+                                                            local->rx_handlers,
+                                                            &rx, sta);
                        }
                        prev = sdata;
                }
@@ -3651,7 +3694,8 @@ void __ieee80211_rx(struct net_device *d
                        rx.skb = skb;
                        rx.dev = prev->dev;
                        rx.sdata = prev;
-                       ieee80211_invoke_rx_handlers(local, &rx, sta);
+                       ieee80211_invoke_rx_handlers(local, local->rx_handlers,
+                                                    &rx, sta);
                } else
                        dev_kfree_skb(skb);
        }
@@ -3765,11 +3809,17 @@ ieee80211_rx_h_load_stats(struct ieee802
         local->channel_use_raw += load;
        if (rx->sta)
                rx->sta->channel_use_raw += load;
-        rx->sdata->channel_use_raw += load;
+       rx->u.rx.load = load;
 
        return TXRX_CONTINUE;
 }
 
+static ieee80211_txrx_result
+ieee80211_rx_h_if_stats(struct ieee80211_txrx_data *rx)
+{
+       rx->sdata->channel_use_raw += rx->u.rx.load;
+       return TXRX_CONTINUE;
+}
 
 static void ieee80211_stat_refresh(unsigned long data)
 {
@@ -4113,10 +4163,18 @@ #endif /* IEEE80211_LEDS */
 /* TODO: implement register/unregister functions for adding TX/RX handlers
  * into ordered list */
 
-static ieee80211_rx_handler ieee80211_rx_handlers[] =
+/* rx_pre handlers don't have dev and sdata fields available in
+ * ieee80211_txrx_data */
+static ieee80211_rx_handler ieee80211_rx_pre_handlers[] =
 {
        ieee80211_rx_h_parse_qos,
        ieee80211_rx_h_load_stats,
+       NULL
+};
+
+static ieee80211_rx_handler ieee80211_rx_handlers[] =
+{
+       ieee80211_rx_h_if_stats,
        ieee80211_rx_h_monitor,
        ieee80211_rx_h_passive_scan,
        ieee80211_rx_h_check,
@@ -4126,7 +4184,7 @@ static ieee80211_rx_handler ieee80211_rx
        ieee80211_rx_h_wep_weak_iv_detection,
        ieee80211_rx_h_wep_decrypt,
        ieee80211_rx_h_defragment,
-       ieee80211_rx_h_ieee80211_rx_h_ps_poll,
+       ieee80211_rx_h_ps_poll,
        ieee80211_rx_h_michael_mic_verify,
        /* this must be after decryption - so header is counted in MPDU mic
         * must be before pae and data, so QOS_DATA format frames
@@ -4284,6 +4342,7 @@ struct net_device *ieee80211_alloc_hw(si
                           NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST);
 
        local->mdev = mdev;
+       local->rx_pre_handlers = ieee80211_rx_pre_handlers;
         local->rx_handlers = ieee80211_rx_handlers;
         local->tx_handlers = ieee80211_tx_handlers;
 
diff --git a/net/d80211/ieee80211_i.h b/net/d80211/ieee80211_i.h
index 7ffeae2..3f4d00e 100644
--- a/net/d80211/ieee80211_i.h
+++ b/net/d80211/ieee80211_i.h
@@ -137,7 +137,11 @@ struct ieee80211_txrx_data {
                        struct ieee80211_rx_status *status;
                        int sent_ps_buffered;
                        int queue;
+                       int load;
                        int in_scan:1;
+                       int ra_match:1; /* frame is destined to interface
+                                        * currently processed (including
+                                        * multicast frames) */
                } rx;
        } u;
 #ifdef CONFIG_HOSTAPD_WPA_TESTING
@@ -413,6 +417,7 @@ #define IEEE80211_IRQSAFE_QUEUE_LIMIT 12
        struct ieee80211_passive_scan scan;
 
 
+       ieee80211_rx_handler *rx_pre_handlers;
        ieee80211_rx_handler *rx_handlers;
         ieee80211_tx_handler *tx_handlers;
 
diff --git a/net/d80211/wpa.c b/net/d80211/wpa.c
index 04856a9..37f5af4 100644
--- a/net/d80211/wpa.c
+++ b/net/d80211/wpa.c
@@ -235,6 +235,10 @@ #endif /* CONFIG_HOSTAPD_WPA_TESTING */
 #ifdef CONFIG_HOSTAPD_WPA_TESTING
                int i;
 #endif /* CONFIG_HOSTAPD_WPA_TESTING */
+
+               if (!rx->u.rx.ra_match)
+                       return TXRX_DROP;
+
                printk(KERN_DEBUG "%s: invalid Michael MIC in data frame from "
                       MACSTR "\n", rx->dev->name, MAC2STR(sa));
 #ifdef CONFIG_HOSTAPD_WPA_TESTING
-- 
1.3.0

-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to