Thread (14 messages) 14 messages, 2 authors, 2006-07-28
STALE7276d
Revisions (7)
  1. v1 current
  2. v1 [diff vs current]
  3. v1 [diff vs current]
  4. v1 [diff vs current]
  5. v1 [diff vs current]
  6. v1 [diff vs current]
  7. v1 [diff vs current]

[PATCH 4/9] d80211: fix receiving through virtual interfaces

From: Jiri Benc <hidden>
Date: 2006-07-18 15:43:37
Subsystem: networking [general], the rest · Maintainers: "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds

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 <redacted>

---

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

53a84c48f3862bebeccf6b97f750be1e42f2bf14
diff --git a/net/d80211/ieee80211.c b/net/d80211/ieee80211.c
index 3d7091a..0b6d8a5 100644
--- a/net/d80211/ieee80211.c
+++ b/net/d80211/ieee80211.c
@@ -2461,27 +2461,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 */
@@ -2758,13 +2746,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);
@@ -3040,8 +3029,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;
@@ -3055,7 +3046,9 @@ ieee80211_rx_h_check(struct ieee80211_tx
 		return TXRX_DROP;
 	}
 
-	if (memcmp(rx->dev->dev_addr, hdr->addr1, ETH_ALEN) == 0)
+	if (!rx->u.rx.ra_match)
+		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))
@@ -3078,8 +3071,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;
 		}
 
@@ -3111,6 +3106,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",
@@ -3126,7 +3123,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 >
@@ -3166,6 +3163,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;
@@ -3201,7 +3202,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 */
@@ -3258,7 +3259,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,
@@ -3311,6 +3312,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) &&
@@ -3349,7 +3354,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. */
@@ -3500,14 +3506,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) {
@@ -3521,8 +3529,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);
 }
 
@@ -3566,37 +3585,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);
@@ -3608,10 +3647,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
@@ -3641,7 +3682,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;
 		}
@@ -3649,7 +3692,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);
 	}
@@ -3763,11 +3807,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)
 {
@@ -4111,10 +4161,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,
@@ -4124,7 +4182,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
@@ -4282,6 +4340,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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help