Search Linux Wireless

[PATCH Try#14 4/4] mac80211: Monitor mode radiotap-based packet injection

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Try #14
 - Changed to Jiri Benc's injection encapsulation method

Try #Lucky13
 - Integrated a patch from Michael Wu with  (Thanks again Michael)
    - whitespace errors
    - an endian error
    - a strict endian typing problem
 - Johannes Berg's suggested use of get_unaligned() to protect unaligned
   defreferences of all multibyte radiotap stuff

Try #12 Comments from Michael Wu
 - Revert enum retcode stuff
 - Handle flags, checking if FCS marked as present and snipping it
 - kill a blank line

Try #11
 - Change parser loop code around to deal with enum retcodes

Try #10  Comments from Michael Wu
 - __ieee80211_convert_radiotap_to_control_and_remove ->
   __ieee80211_parse_tx_radiotap
 - death to vertical justification
 - -ve to negative
 - struct ieee80211_hw_mode should be called "mode" by convention

Try #9
 - Normalize multiline comment style

Try #6
 - Accounted for various comments from Johannes Berg
 - Radiotap parsing is moved to cfg80211 as requested in a separate patch

Try #5
 - De-indent last few indented comments

Try #4
 - All from Michael Wu's feedback: further style heresies removed
 - took account of radiotap arg alignment requirement.  n-byte arg must be
   placed on n-byte boundary using padding where necessary

Try #3
 - moved to Michael Wu's method of tracking if we came in on a
   monitor interface by using ifindex
 - removed older proposed monitor interface tracking method and flags
 - style fixes
 - removed duped #include that is present in Michael Wu's patch already

Try #2
 - took Michael Wu's advice about better tools and basing on wireless-dev
 - took Luis Rodriguez's advice about coding style makeover
 - took Pavel Roskin's advice about little-endian radiotap

CC: John Linville <linville@xxxxxxxxxxxxx>
CC: Johannes Berg <johannes@xxxxxxxxxxxxxxxx>
CC: Jiri Benc <jbenc@xxxxxxx>
Signed-off-by: Andy Green <andy@xxxxxxxxxxx>


---
 net/mac80211/ieee80211.c |  283 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 273 insertions(+), 10 deletions(-)

Index: wireless-dev/net/mac80211/ieee80211.c
===================================================================
--- wireless-dev.orig/net/mac80211/ieee80211.c
+++ wireless-dev/net/mac80211/ieee80211.c
@@ -24,6 +24,7 @@
 #include <linux/compiler.h>
 #include <linux/bitmap.h>
 #include <net/cfg80211.h>
+#include <asm/unaligned.h>
 
 #include "ieee80211_common.h"
 #include "ieee80211_i.h"
@@ -1118,7 +1119,135 @@ ieee80211_tx_h_ps_buf(struct ieee80211_t
 }
 
 
-static void inline
+/*
+ * deal with packet injection down monitor interface
+ * with Radiotap Header -- only called for monitor mode interface
+ */
+
+static ieee80211_txrx_result
+__ieee80211_parse_tx_radiotap(
+	struct ieee80211_txrx_data *tx,
+	struct sk_buff *skb, struct ieee80211_tx_control *control)
+{
+	/*
+	 * this is the moment to interpret and discard the radiotap header that
+	 * must be at the start of the packet injected in Monitor mode
+	 *
+	 * Need to take some care with endian-ness, since radiotap
+	 * args are always little-endian
+	 */
+	struct ieee80211_radiotap_iterator iterator;
+	struct ieee80211_radiotap_header *rthdr =
+		(struct ieee80211_radiotap_header *) skb->data;
+	struct ieee80211_hw_mode *mode = tx->local->hw.conf.mode;
+	int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
+
+	/*
+	 * default control situation for all injected packets
+	 * FIXME: this does not suit all usage cases, expand to allow control
+	 */
+	control->retry_limit = 1; /* no retry */
+	control->key_idx = -1; /* no encryption key */
+	control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS |
+			    IEEE80211_TXCTL_USE_CTS_PROTECT);
+	control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT |
+			  IEEE80211_TXCTL_NO_ACK;
+	control->antenna_sel_tx = 0; /* default to default antenna */
+
+	/*
+	 * for every radiotap entry that is present
+	 * (ieee80211_radiotap_iterator_next returns -ENOENT when no more
+	 * entries present, or -EINVAL on error)
+	 */
+	while (!ret) {
+		int i, target_rate;
+
+		ret = ieee80211_radiotap_iterator_next(&iterator);
+
+		if (ret)
+			continue;
+
+		/* see if this argument is something we can use */
+		switch (iterator.this_arg_index) {
+		/*
+ 		 * You must take care when dereferencing iterator.this_arg
+ 		 * for multibyte types... the pointer is not aligned.  Use
+ 		 * get_unaligned((type *)iterator.this_arg) to dereference
+ 		 * iterator.this_arg for type "type" safely on all arches.
+		*/
+		case IEEE80211_RADIOTAP_RATE:
+			/*
+			 * radiotap rate u8 is in 500kbps units eg, 0x02=1Mbps
+			 * ieee80211 rate int is in 100kbps units eg, 0x0a=1Mbps
+			 */
+			target_rate = (*iterator.this_arg) * 5;
+			for (i = 0; i < mode->num_rates; i++) {
+				struct ieee80211_rate *r = &mode->rates[i];
+
+				if (r->rate > target_rate)
+					continue;
+
+				control->rate = r;
+
+				if (r->flags & IEEE80211_RATE_PREAMBLE2)
+					control->tx_rate = r->val2;
+				else
+					control->tx_rate = r->val;
+
+				/* end on exact match */
+				if (r->rate == target_rate)
+					i = mode->num_rates;
+			}
+			break;
+
+		case IEEE80211_RADIOTAP_ANTENNA:
+			/*
+			 * radiotap uses 0 for 1st ant, mac80211 is 1 for
+			 * 1st ant
+			 */
+			control->antenna_sel_tx = (*iterator.this_arg) + 1;
+			break;
+
+		case IEEE80211_RADIOTAP_DBM_TX_POWER:
+			control->power_level = *iterator.this_arg;
+			break;
+
+		case IEEE80211_RADIOTAP_FLAGS:
+			if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FCS) {
+				/*
+				 * this indicates that the skb we have been
+				 * handed has the 32-bit FCS CRC at the end...
+				 * we should react to that by snipping it off
+				 * because it will be recomputed and added
+				 * on transmission
+				 */
+				if (skb->len < (iterator.max_length + FCS_LEN))
+					return TXRX_DROP;
+
+				skb_trim(skb, skb->len - FCS_LEN);
+			}
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */
+		return TXRX_DROP;
+
+	/*
+	 * remove the radiotap header
+	 * iterator->max_length was sanity-checked against
+	 * skb->len by iterator init
+	 */
+	skb_pull(skb, iterator.max_length);
+
+	return TXRX_CONTINUE;
+}
+
+
+static ieee80211_txrx_result inline
 __ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
 		       struct sk_buff *skb,
 		       struct net_device *dev,
@@ -1126,6 +1255,8 @@ __ieee80211_tx_prepare(struct ieee80211_
 {
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+	ieee80211_txrx_result res = TXRX_CONTINUE;
+
 	int hdrlen;
 
 	memset(tx, 0, sizeof(*tx));
@@ -1135,7 +1266,38 @@ __ieee80211_tx_prepare(struct ieee80211_
 	tx->sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	tx->sta = sta_info_get(local, hdr->addr1);
 	tx->fc = le16_to_cpu(hdr->frame_control);
+
+	/*
+	 * set defaults for things that can be set by
+	 * injected radiotap headers
+	 */
 	control->power_level = local->hw.conf.power_level;
+	control->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
+	if (local->sta_antenna_sel != STA_ANTENNA_SEL_AUTO && tx->sta)
+		control->antenna_sel_tx = tx->sta->antenna_sel_tx;
+
+	/*
+	 * if the skb came in as in encapsulated injection packet, process
+	 * and then remove the radiotap header
+	 */
+	if (skb->protocol == ETH_P_80211_RT) {
+
+		printk("__ieee80211_tx_prepare sees ETH_P_80211_RT\n");
+
+		if (__ieee80211_parse_tx_radiotap(tx, skb, control) ==
+								TXRX_DROP) {
+			printk("  drop\n");
+			return TXRX_DROP;
+		}
+		/*
+		 * we removed the radiotap header after this point,
+		 * we filled control with what we could use
+		 * set to the actual ieee header now
+		 */
+		hdr = (struct ieee80211_hdr *) skb->data;
+		res = TXRX_QUEUED; /* indication it was injected packet */
+	}
+
 	tx->u.tx.control = control;
 	tx->u.tx.unicast = !is_multicast_ether_addr(hdr->addr1);
 	if (is_multicast_ether_addr(hdr->addr1))
@@ -1152,9 +1314,6 @@ __ieee80211_tx_prepare(struct ieee80211_
 		control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK;
 		tx->sta->clear_dst_mask = 0;
 	}
-	control->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
-	if (local->sta_antenna_sel != STA_ANTENNA_SEL_AUTO && tx->sta)
-		control->antenna_sel_tx = tx->sta->antenna_sel_tx;
 	hdrlen = ieee80211_get_hdrlen(tx->fc);
 	if (skb->len > hdrlen + sizeof(rfc1042_header) + 2) {
 		u8 *pos = &skb->data[hdrlen + sizeof(rfc1042_header)];
@@ -1162,6 +1321,7 @@ __ieee80211_tx_prepare(struct ieee80211_
 	}
 	control->flags |= IEEE80211_TXCTL_FIRST_FRAGMENT;
 
+	return res;
 }
 
 static int inline is_ieee80211_device(struct net_device *dev,
@@ -1274,7 +1434,7 @@ static int ieee80211_tx(struct net_devic
 	struct sta_info *sta;
 	ieee80211_tx_handler *handler;
 	struct ieee80211_txrx_data tx;
-	ieee80211_txrx_result res = TXRX_DROP;
+	ieee80211_txrx_result res = TXRX_DROP, res_prepare;
 	int ret, i;
 
 	WARN_ON(__ieee80211_queue_pending(local, control->queue));
@@ -1284,15 +1444,26 @@ static int ieee80211_tx(struct net_devic
 		return 0;
 	}
 
-	__ieee80211_tx_prepare(&tx, skb, dev, control);
+	res_prepare = __ieee80211_tx_prepare(&tx, skb, dev, control);
+
+	if (res_prepare == TXRX_DROP) {
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
 	sta = tx.sta;
 	tx.u.tx.mgmt_interface = mgmt;
 	tx.u.tx.mode = local->hw.conf.mode;
 
-	for (handler = local->tx_handlers; *handler != NULL; handler++) {
-		res = (*handler)(&tx);
-		if (res != TXRX_CONTINUE)
-			break;
+	if (res_prepare == TXRX_QUEUED) { /* if it was an injected packet */
+		res = TXRX_CONTINUE;
+	} else {
+		for (handler = local->tx_handlers; *handler != NULL;
+		     handler++) {
+			res = (*handler)(&tx);
+			if (res != TXRX_CONTINUE)
+				break;
+		}
 	}
 
 	skb = tx.skb; /* handlers are allowed to change skb */
@@ -1493,6 +1664,76 @@ static int ieee80211_master_start_xmit(s
 	return ret;
 }
 
+/*
+ * snips off the injection encapsulation ethernet header when the packet
+ * has been identified as coming into ieee80211_subif_start_xmit
+ * having the ETH_P_80211_RT type
+ */
+static int
+ieee80211_subif_tx_rt_encap_remove(struct sk_buff *skb, struct net_device *dev,
+    struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local)
+{
+	struct ieee80211_tx_packet_data *pkt_data;
+	struct ieee80211_radiotap_header *prthdr;
+	u16 len;
+
+	/*
+	 * snip off the encapsulating ethernet type header
+	 * caller ieee80211_subif_start_xmit confirmed we have at least
+	 * ETH_HLEN in the skb
+	 */
+	skb_pull(skb, ETH_HLEN);
+	prthdr = (struct ieee80211_radiotap_header *)skb->data;
+	/*
+	 * there must be a radiotap header at the
+	 * start in this case
+	 */
+	if (unlikely(skb->len < sizeof(*prthdr))) {
+		printk(KERN_DEBUG "%s: short skb (len=%d)\n",
+			dev->name, skb->len);
+		return -EINVAL;
+	}
+	/*
+	 * only version 0 is supported... must check here because
+	 * we are interested in the length inside in a moment
+	 */
+	if (unlikely(prthdr->it_version))
+		return -EINVAL;
+
+	skb->dev = local->mdev;
+	pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
+	memset(pkt_data, 0, sizeof(*pkt_data));
+	/* needed because we set skb device to master */
+	pkt_data->ifindex = sdata->dev->ifindex;
+	pkt_data->mgmt_iface = 0;
+	pkt_data->do_not_encrypt = 1;
+	/*
+	 * fix up the skb pointers accounting for the radiotap
+	 * header still being in there.  We are being given
+	 * a precooked IEEE80211 header so no need for
+	 * normal processing
+	 */
+	len = le16_to_cpu(get_unaligned(&prthdr->it_len));
+	/*
+	 * detect possible insantity with the reported rt length
+	 * vs the actual skb length
+	 */
+	if (unlikely(skb->len < (sizeof(*prthdr) + len))) {
+		printk(KERN_DEBUG "%s: short skb (len=%d)\n",
+			dev->name, skb->len);
+		return -EINVAL;
+	}
+	skb_set_mac_header(skb, len);
+	skb_set_network_header(skb, len + sizeof(struct ieee80211_hdr));
+	skb_set_transport_header(skb, len + sizeof(struct ieee80211_hdr));
+	/*
+	 * mark the SKB as having had the encapsulation so we can recognize
+	 * that this is an injected packet with radiotap subsequently
+	 */
+	skb->protocol = ETH_P_80211_RT;
+
+	return 0;
+}
 
 /**
  * ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type
@@ -1537,6 +1778,28 @@ static int ieee80211_subif_start_xmit(st
 	/* convert Ethernet header to proper 802.11 header (based on
 	 * operation mode) */
 	ethertype = (skb->data[12] << 8) | skb->data[13];
+
+	/*
+	 * Case where an injected radiotap + 80211 packet is encapsulated
+	 * in a ETH_P_80211_RT frame
+	 */
+	if (ethertype == ETH_P_80211_RT) {
+		ret = 0; /* we should free the skb on fail */
+		/*
+		 * strip the encapsulating ethernet header and mark skb's
+		 * protocol as being ETH_P_80211_RT
+		 */
+		if (ieee80211_subif_tx_rt_encap_remove(skb, dev, sdata, local))
+			goto fail;
+		/*
+		 * pass the remaining radiotap header + 80211 header + payload
+		 * up to the next stage
+		 */
+		dev_queue_xmit(skb);
+
+		return 0;
+	}
+
 	/* TODO: handling for 802.1x authorized/unauthorized port */
 	fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA;
 

-- 
-
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux