batctl: add raw wifi packet decapsulation support

Message ID 1296052208-6041-1-git-send-email-lindner_marek@yahoo.de (mailing list archive)
State Superseded, archived
Headers

Commit Message

Marek Lindner Jan. 26, 2011, 2:30 p.m. UTC
  Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
---
 batctl/tcpdump.c |  104 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 batctl/tcpdump.h |   49 +++++++++++++++++++++++++
 2 files changed, 152 insertions(+), 1 deletions(-)
  

Comments

Sven Eckelmann Jan. 26, 2011, 3:02 p.m. UTC | #1
On Wednesday 26 January 2011 15:30:08 Marek Lindner wrote:
> Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
> ---
> +	buff_len -= PRISM_HEADER_LEN;
> +	packet_buff += PRISM_HEADER_LEN;
> +
> +	/* we assume a minimum size of 38 bytes
> +	 * (802.11 data frame + LLC)
> +	 * before we calculate the real size */
> +	if (buff_len <= 38)
> +		return;
> +
> +	wifi_hdr = (struct ieee80211_hdr *)packet_buff;
> +	fc = wifi_hdr->frame_control;
> +
> +	/* not carrying payload */
> +	if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)
> +		return;

Didn't we say yesterday that we must either use ntohs or only read the first 
byte of the framecontrol? The last option which comes to my mind is to define 
IEEE80211_FCTL_FTYPE and IEEE80211_FTYPE_DATA for big and little endian 
independently.

The same for
IEEE80211_FCTL_TODS 0x0100
IEEE80211_FCTL_FROMDS 0x0200
IEEE80211_FCTL_PROTECTED 0x4000
IEEE80211_STYPE_QOS_DATA 0x0080

Your current version should only work on little endian systems. I would 
suggest following version:

#define IEEE80211_FCTL_FTYPE 0x0c00
#define IEEE80211_FTYPE_DATA 0x0800
#define IEEE80211_FCTL_TODS 0x0001
#define IEEE80211_FCTL_FROMDS 0x0002
#define IEEE80211_FCTL_PROTECTED 0x0040
#define IEEE80211_STYPE_QOS_DATA 0x8000

fc = ntohs(wifi_hdr->frame_control);


> +       if (fc & IEEE80211_STYPE_QOS_DATA)
> +               hdr_len += 2;

Here are you testing only on bit. Are you sure that the other 3 bits aren't 
interesting? At least one combination isn't well defined by the 802.11 
standard from 2007 and some others are null data types.

Best regards,
	Sven
  
Andrew Lunn Jan. 26, 2011, 3:41 p.m. UTC | #2
Hi Marek

> +	shost = wifi_hdr->addr2;
> +	if (fc & IEEE80211_FCTL_FROMDS)
> +		shost = wifi_hdr->addr3;
> +	else if (fc & IEEE80211_FCTL_TODS)
> +		shost = wifi_hdr->addr4;
> +
> +	dhost = wifi_hdr->addr1;
> +	if (fc & IEEE80211_FCTL_TODS)
> +		dhost = wifi_hdr->addr3;

What would happen if you happened to pick up a WDS packet? Four
addresses in the packet. Do we see sensible addresses?

> +
> +	hdr_len = 24;
> +	if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS))
> +		hdr_len = 30;

Again, WDS? We are now a few bytes out when decoding the rest of the
packet.

       Andrew
  
Marek Lindner Jan. 27, 2011, 12:27 p.m. UTC | #3
Hi,

> > +	shost = wifi_hdr->addr2;
> > +	if (fc & IEEE80211_FCTL_FROMDS)
> > +		shost = wifi_hdr->addr3;
> > +	else if (fc & IEEE80211_FCTL_TODS)
> > +		shost = wifi_hdr->addr4;
> > +
> > +	dhost = wifi_hdr->addr1;
> > +	if (fc & IEEE80211_FCTL_TODS)
> > +		dhost = wifi_hdr->addr3;
> 
> What would happen if you happened to pick up a WDS packet? Four
> addresses in the packet. Do we see sensible addresses?

we should - that is what addr4 is used for but I did not test it. Let me know 
if it does not work for you.


> > +
> > +	hdr_len = 24;
> > +	if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS))
> > +		hdr_len = 30;
> 
> Again, WDS? We are now a few bytes out when decoding the rest of the
> packet.

I don't understand what you are trying to say here. Are you sure it is not 
working or are you worried that WDS might not work ? If you have doubts please 
explain them in more detail.

Thanks,
Marek
  
Marek Lindner Jan. 27, 2011, 12:37 p.m. UTC | #4
On Wednesday 26 January 2011 16:02:32 Sven Eckelmann wrote:
> Didn't we say yesterday that we must either use ntohs or only read the
> first byte of the framecontrol? The last option which comes to my mind is
> to define IEEE80211_FCTL_FTYPE and IEEE80211_FTYPE_DATA for big and little
> endian independently.

I must have misunderstood something but you are right - I'm going to change it 
and publish a second patch.


> > +       if (fc & IEEE80211_STYPE_QOS_DATA)
> > +               hdr_len += 2;
> 
> Here are you testing only on bit. Are you sure that the other 3 bits aren't
> interesting? At least one combination isn't well defined by the 802.11
> standard from 2007 and some others are null data types.

I'd claim we only care if it is QoS or not because all QoS packets have a 
longer header (QoS control field). 
The spec says: "bit 7 is set to 1 in the QoS data subtypes, which have QoS 
Control fields in their MAC headers"

Regards,
Marek
  
Andrew Lunn Jan. 28, 2011, 5:24 p.m. UTC | #5
On Thu, Jan 27, 2011 at 01:27:57PM +0100, Marek Lindner wrote:
> 
> Hi,
> 
> > > +	shost = wifi_hdr->addr2;
> > > +	if (fc & IEEE80211_FCTL_FROMDS)
> > > +		shost = wifi_hdr->addr3;
> > > +	else if (fc & IEEE80211_FCTL_TODS)
> > > +		shost = wifi_hdr->addr4;
> > > +
> > > +	dhost = wifi_hdr->addr1;
> > > +	if (fc & IEEE80211_FCTL_TODS)
> > > +		dhost = wifi_hdr->addr3;
> > 
> > What would happen if you happened to pick up a WDS packet? Four
> > addresses in the packet. Do we see sensible addresses?
> 
> we should - that is what addr4 is used for but I did not test it. Let me know 
> if it does not work for you.
> 
> 
> > > +
> > > +	hdr_len = 24;
> > > +	if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS))
> > > +		hdr_len = 30;
> > 
> > Again, WDS? We are now a few bytes out when decoding the rest of the
> > packet.

OK. Sorry. I'm wrong.

What confused me is time spent debugging WDS in madwifi. It has macros
similar to what you have:

#define IEEE80211_FC1_DIR_NODS                  0x00    /* STA->STA */
#define IEEE80211_FC1_DIR_TODS                  0x01    /* STA->AP  */
#define IEEE80211_FC1_DIR_FROMDS                0x02    /* AP ->STA */
#define IEEE80211_FC1_DIR_DSTODS                0x03    /* AP ->AP  */

So i was expecting to see something like DSTODS. However the madwifi
macros are for the complete DIR field, where as you are looking at
individual bits. So 

(fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS) 

is equivalent to the madwifi IEEE80211_FC1_DIR_DSTODS.

So you header length calculation looks O.K.

   Andrew
  
Marek Lindner Jan. 28, 2011, 8:14 p.m. UTC | #6
On Friday 28 January 2011 18:24:16 Andrew Lunn wrote:
> So i was expecting to see something like DSTODS. However the madwifi
> macros are for the complete DIR field, where as you are looking at
> individual bits. So 
> 
> (fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS) 
> 
> is equivalent to the madwifi IEEE80211_FC1_DIR_DSTODS.
> 
> So you header length calculation looks O.K.

I rather discuss potential bugs even if they turn out not to be bugs instead 
of letting them through.  :)

Thanks for your review,
Marek
  

Patch

diff --git a/batctl/tcpdump.c b/batctl/tcpdump.c
index 4bff970..bdf81bc 100644
--- a/batctl/tcpdump.c
+++ b/batctl/tcpdump.c
@@ -29,6 +29,7 @@ 
 #include <sys/time.h>
 #include <arpa/inet.h>
 #include <net/if.h>
+#include <net/if_arp.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
 #include <netinet/tcp.h>
@@ -458,6 +459,75 @@  static void parse_eth_hdr(unsigned char *packet_buff, ssize_t buff_len, int read
 	}
 }
 
+static void parse_wifi_hdr(unsigned char *packet_buff, ssize_t buff_len, int read_opt, int time_printed)
+{
+	struct ether_header *eth_hdr;
+	struct ieee80211_hdr *wifi_hdr;
+	unsigned char *shost, *dhost;
+	uint16_t fc;
+	int hdr_len;
+
+	if (buff_len <= (ssize_t)PRISM_HEADER_LEN)
+		return;
+
+	buff_len -= PRISM_HEADER_LEN;
+	packet_buff += PRISM_HEADER_LEN;
+
+	/* we assume a minimum size of 38 bytes
+	 * (802.11 data frame + LLC)
+	 * before we calculate the real size */
+	if (buff_len <= 38)
+		return;
+
+	wifi_hdr = (struct ieee80211_hdr *)packet_buff;
+	fc = wifi_hdr->frame_control;
+
+	/* not carrying payload */
+	if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)
+		return;
+
+	/* encrypted packet */
+	if (fc & IEEE80211_FCTL_PROTECTED)
+		return;
+
+	shost = wifi_hdr->addr2;
+	if (fc & IEEE80211_FCTL_FROMDS)
+		shost = wifi_hdr->addr3;
+	else if (fc & IEEE80211_FCTL_TODS)
+		shost = wifi_hdr->addr4;
+
+	dhost = wifi_hdr->addr1;
+	if (fc & IEEE80211_FCTL_TODS)
+		dhost = wifi_hdr->addr3;
+
+	hdr_len = 24;
+	if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS))
+		hdr_len = 30;
+
+	if (fc & IEEE80211_STYPE_QOS_DATA)
+		hdr_len += 2;
+
+	/* LLC */
+	hdr_len += 8;
+	hdr_len -= sizeof(struct ether_header);
+
+	if (buff_len <= hdr_len)
+		return;
+
+	buff_len -= hdr_len;
+	packet_buff += hdr_len;
+
+	eth_hdr = (struct ether_header *)packet_buff;
+	memmove(eth_hdr->ether_shost, shost, ETH_ALEN);
+	memmove(eth_hdr->ether_dhost, dhost, ETH_ALEN);
+
+	 /* printf("parse_wifi_hdr(): ether_type: 0x%04x\n", ntohs(eth_hdr->ether_type));
+	printf("parse_wifi_hdr(): shost: %s\n", ether_ntoa_long((struct ether_addr *)eth_hdr->ether_shost));
+	printf("parse_wifi_hdr(): dhost: %s\n", ether_ntoa_long((struct ether_addr *)eth_hdr->ether_dhost)); */
+
+	parse_eth_hdr(packet_buff, buff_len, read_opt, time_printed);
+}
+
 int tcpdump(int argc, char **argv)
 {
 	struct ifreq req;
@@ -526,6 +596,27 @@  int tcpdump(int argc, char **argv)
 		memset(&req, 0, sizeof (struct ifreq));
 		strncpy(req.ifr_name, dump_if->dev, IFNAMSIZ);
 
+		res = ioctl(dump_if->raw_sock, SIOCGIFHWADDR, &req);
+		if (res < 0) {
+			printf("Error - can't create raw socket (SIOCGIFHWADDR): %s\n", strerror(errno));
+			close(dump_if->raw_sock);
+			goto out;
+		}
+
+		dump_if->hw_type = req.ifr_hwaddr.sa_family;
+
+		switch (dump_if->hw_type) {
+		case ARPHRD_ETHER:
+		case ARPHRD_IEEE80211_PRISM:
+			break;
+		default:
+			printf("Error - interface '%s' is of unknown type: %i\n", dump_if->dev, dump_if->hw_type);
+			goto out;
+		}
+
+		memset(&req, 0, sizeof (struct ifreq));
+		strncpy(req.ifr_name, dump_if->dev, IFNAMSIZ);
+
 		res = ioctl(dump_if->raw_sock, SIOCGIFINDEX, &req);
 
 		if (res < 0) {
@@ -588,7 +679,18 @@  int tcpdump(int argc, char **argv)
 				continue;
 			}
 
-			parse_eth_hdr(packet_buff, read_len, read_opt, 0);
+			switch (dump_if->hw_type) {
+			case ARPHRD_ETHER:
+				parse_eth_hdr(packet_buff, read_len, read_opt, 0);
+				break;
+			case ARPHRD_IEEE80211_PRISM:
+				parse_wifi_hdr(packet_buff, read_len, read_opt, 0);
+				break;
+			default:
+				/* should not happen */
+				break;
+			}
+
 			fflush(stdout);
 		}
 
diff --git a/batctl/tcpdump.h b/batctl/tcpdump.h
index f98d63f..f4276dc 100644
--- a/batctl/tcpdump.h
+++ b/batctl/tcpdump.h
@@ -23,6 +23,10 @@ 
 #include <netpacket/packet.h>
 #include "list-batman.h"
 
+#ifndef ARPHRD_IEEE80211_PRISM
+#define ARPHRD_IEEE80211_PRISM 802
+#endif
+
 #define DUMP_TYPE_BATOGM 1
 #define DUMP_TYPE_BATICMP 2
 #define DUMP_TYPE_BATUCAST 4
@@ -31,11 +35,21 @@ 
 #define DUMP_TYPE_BATFRAG 32
 #define DUMP_TYPE_NONBAT 64
 
+#define IEEE80211_FCTL_FTYPE 0x000c
+#define IEEE80211_FCTL_TODS 0x0100
+#define IEEE80211_FCTL_FROMDS 0x0200
+#define IEEE80211_FCTL_PROTECTED 0x4000
+
+#define IEEE80211_FTYPE_DATA 0x0008
+
+#define IEEE80211_STYPE_QOS_DATA 0x0080
+
 struct dump_if {
 	struct list_head list;
 	char *dev;
 	int32_t raw_sock;
 	struct sockaddr_ll addr;
+	int32_t hw_type;
 };
 
 struct vlanhdr {
@@ -43,4 +57,39 @@  struct vlanhdr {
 	u_int16_t ether_type;
 } __attribute__ ((packed));
 
+struct ieee80211_hdr {
+	u_int16_t frame_control;
+	u_int16_t duration_id;
+	u_int8_t addr1[6];
+	u_int8_t addr2[6];
+	u_int8_t addr3[6];
+	u_int16_t seq_ctrl;
+	u_int8_t addr4[6];
+} __attribute__ ((packed));
+
+struct prism_item {
+	u_int32_t did;
+	u_int16_t status;
+	u_int16_t len;
+	u_int32_t data;
+};
+
+struct prism_header {
+	u_int32_t msgcode;
+	u_int32_t msglen;
+	u_int8_t devname[16];
+	struct prism_item hosttime;
+	struct prism_item mactime;
+	struct prism_item channel;
+	struct prism_item rssi;
+	struct prism_item sq;
+	struct prism_item signal;
+	struct prism_item noise;
+	struct prism_item rate;
+	struct prism_item istx;
+	struct prism_item frmlen;
+};
+
+#define PRISM_HEADER_LEN sizeof(struct prism_header)
+
 int tcpdump(int argc, char **argv);