Discussion:
tcpdump: show 802.11 control frames
Stefan Sperling
2016-02-03 12:27:50 UTC
Permalink
Currently, tcpdump displays 802.11 control frames as "type#4" and
doesn't give more information than that.

This diff makes tcpdump show the subtype of such frames, and pretty-print
a few subtypes in verbose mode. There are more subtypes but these can
be pretty-printed later.

This can be tested with:

ifconfig iwn0 mediaopt monitor up
tcpdump -n -i iwn0 -y IEEE802_11_RADIO -v -s 1500 type ctl

Whether such frames can be seen depends on the driver's RX filter
settings in monitor mode. Another driver I've tested with is zyd(4).

ok?

Index: print-802_11.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/print-802_11.c,v
retrieving revision 1.29
diff -u -p -r1.29 print-802_11.c
--- print-802_11.c 1 Feb 2016 10:09:44 -0000 1.29
+++ print-802_11.c 3 Feb 2016 12:21:20 -0000
@@ -37,6 +37,25 @@
#include "addrtoname.h"
#include "interface.h"

+const char *ieee80211_ctl_subtype_name[] = {
+ "reserved#0",
+ "reserved#1",
+ "reserved#2",
+ "reserved#3",
+ "reserved#4",
+ "reserved#5",
+ "reserved#6",
+ "wrapper",
+ "block ack request",
+ "block ack",
+ "ps poll",
+ "rts",
+ "cts",
+ "ack",
+ "cf-end",
+ "cf-end-ack",
+};
+
const char *ieee80211_mgt_subtype_name[] = {
"association request",
"association response",
@@ -813,6 +832,69 @@ ieee80211_frame(struct ieee80211_frame *
break;
}
break;
+ case IEEE80211_FC0_TYPE_CTL: {
+ u_int8_t *t = (u_int8_t *) wh;
+
+ printf(": %s", ieee80211_ctl_subtype_name[
+ subtype >> IEEE80211_FC0_SUBTYPE_SHIFT]);
+ if (!vflag)
+ break;
+
+ /* See 802.11 2012 "8.3.1 Control frames". */
+ t += 2; /* skip Frame Control */
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_RTS:
+ case IEEE80211_FC0_SUBTYPE_BAR:
+ case IEEE80211_FC0_SUBTYPE_BA:
+ TCHECK2(*t, 2); /* Duration */
+ printf(", duration %dms", (t[0] || t[1] << 8));
+ t += 2;
+ TCHECK2(*t, 6); /* RA */
+ printf(", ra %s", etheraddr_string(t));
+ t += 6;
+ TCHECK2(*t, 6); /* TA */
+ printf(", ta %s", etheraddr_string(t));
+ if (subtype == IEEE80211_FC0_SUBTYPE_BAR ||
+ subtype == IEEE80211_FC0_SUBTYPE_BA) {
+ u_int16_t ctrl;
+
+ TCHECK2(*t, 2); /* BAR/BA control */
+ ctrl = t[0] | (t[1] << 8);
+ if (ctrl & IEEE80211_BA_ACK_POLICY)
+ printf(", no ack");
+ else
+ printf(", normal ack");
+ if ((ctrl & IEEE80211_BA_MULTI_TID) == 0 &&
+ (ctrl & IEEE80211_BA_COMPRESSED) == 0)
+ printf(", basic variant");
+ else if ((ctrl & IEEE80211_BA_MULTI_TID) &&
+ (ctrl & IEEE80211_BA_COMPRESSED))
+ printf(", multi-tid variant");
+ else if (ctrl & IEEE80211_BA_COMPRESSED)
+ printf(", compressed variant");
+ }
+ break;
+ case IEEE80211_FC0_SUBTYPE_CTS:
+ case IEEE80211_FC0_SUBTYPE_ACK:
+ TCHECK2(*t, 2); /* Duration */
+ printf(", duration %dms", (t[0] || t[1] << 8));
+ t += 2;
+ TCHECK2(*t, 6); /* RA */
+ printf(", ra %s", etheraddr_string(t));
+ break;
+ case IEEE80211_FC0_SUBTYPE_PS_POLL:
+ TCHECK2(*t, 2); /* AID */
+ printf(", aid 0x%x", (t[0] || t[1] << 8));
+ t += 2;
+ TCHECK2(*t, 6); /* BSSID(RA) */
+ printf(", RA %s", etheraddr_string(t));
+ t += 6;
+ TCHECK2(*t, 6); /* TA */
+ printf(", ta %s", etheraddr_string(t));
+ break;
+ }
+ break;
+ }
default:
printf(": type#%d", type);
break;
Mark Kettenis
2016-02-03 12:36:50 UTC
Permalink
Date: Wed, 3 Feb 2016 13:27:50 +0100
Currently, tcpdump displays 802.11 control frames as "type#4" and
doesn't give more information than that.
This diff makes tcpdump show the subtype of such frames, and pretty-print
a few subtypes in verbose mode. There are more subtypes but these can
be pretty-printed later.
ifconfig iwn0 mediaopt monitor up
tcpdump -n -i iwn0 -y IEEE802_11_RADIO -v -s 1500 type ctl
Whether such frames can be seen depends on the driver's RX filter
settings in monitor mode. Another driver I've tested with is zyd(4).
ok?
Index: print-802_11.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/print-802_11.c,v
retrieving revision 1.29
diff -u -p -r1.29 print-802_11.c
--- print-802_11.c 1 Feb 2016 10:09:44 -0000 1.29
+++ print-802_11.c 3 Feb 2016 12:21:20 -0000
@@ -37,6 +37,25 @@
#include "addrtoname.h"
#include "interface.h"
+const char *ieee80211_ctl_subtype_name[] = {
+ "reserved#0",
+ "reserved#1",
+ "reserved#2",
+ "reserved#3",
+ "reserved#4",
+ "reserved#5",
+ "reserved#6",
+ "wrapper",
+ "block ack request",
+ "block ack",
+ "ps poll",
+ "rts",
+ "cts",
+ "ack",
+ "cf-end",
+ "cf-end-ack",
+};
+
const char *ieee80211_mgt_subtype_name[] = {
"association request",
"association response",
@@ -813,6 +832,69 @@ ieee80211_frame(struct ieee80211_frame *
break;
}
break;
+ case IEEE80211_FC0_TYPE_CTL: {
+ u_int8_t *t = (u_int8_t *) wh;
+
+ printf(": %s", ieee80211_ctl_subtype_name[
+ subtype >> IEEE80211_FC0_SUBTYPE_SHIFT]);
+ if (!vflag)
+ break;
+
+ /* See 802.11 2012 "8.3.1 Control frames". */
+ t += 2; /* skip Frame Control */
+ switch (subtype) {
+ TCHECK2(*t, 2); /* Duration */
+ printf(", duration %dms", (t[0] || t[1] << 8));
+ t += 2;
+ TCHECK2(*t, 6); /* RA */
+ printf(", ra %s", etheraddr_string(t));
+ t += 6;
+ TCHECK2(*t, 6); /* TA */
+ printf(", ta %s", etheraddr_string(t));
+ if (subtype == IEEE80211_FC0_SUBTYPE_BAR ||
+ subtype == IEEE80211_FC0_SUBTYPE_BA) {
+ u_int16_t ctrl;
+
+ TCHECK2(*t, 2); /* BAR/BA control */
+ ctrl = t[0] | (t[1] << 8);
+ if (ctrl & IEEE80211_BA_ACK_POLICY)
+ printf(", no ack");
+ else
+ printf(", normal ack");
+ if ((ctrl & IEEE80211_BA_MULTI_TID) == 0 &&
+ (ctrl & IEEE80211_BA_COMPRESSED) == 0)
+ printf(", basic variant");
+ else if ((ctrl & IEEE80211_BA_MULTI_TID) &&
+ (ctrl & IEEE80211_BA_COMPRESSED))
+ printf(", multi-tid variant");
+ else if (ctrl & IEEE80211_BA_COMPRESSED)
+ printf(", compressed variant");
+ }
+ break;
+ TCHECK2(*t, 2); /* Duration */
+ printf(", duration %dms", (t[0] || t[1] << 8));
+ t += 2;
+ TCHECK2(*t, 6); /* RA */
+ printf(", ra %s", etheraddr_string(t));
+ break;
+ TCHECK2(*t, 2); /* AID */
+ printf(", aid 0x%x", (t[0] || t[1] << 8));
+ t += 2;
+ TCHECK2(*t, 6); /* BSSID(RA) */
+ printf(", RA %s", etheraddr_string(t));
+ t += 6;
+ TCHECK2(*t, 6); /* TA */
+ printf(", ta %s", etheraddr_string(t));
+ break;
+ }
+ break;
+ }
printf(": type#%d", type);
break;
Stefan Sperling
2016-02-03 13:05:29 UTC
Permalink
Date: Wed, 3 Feb 2016 13:27:50 +0100
Currently, tcpdump displays 802.11 control frames as "type#4" and
doesn't give more information than that.
This diff makes tcpdump show the subtype of such frames, and pretty-print
a few subtypes in verbose mode. There are more subtypes but these can
be pretty-printed later.
ifconfig iwn0 mediaopt monitor up
tcpdump -n -i iwn0 -y IEEE802_11_RADIO -v -s 1500 type ctl
Whether such frames can be seen depends on the driver's RX filter
settings in monitor mode. Another driver I've tested with is zyd(4).
ok?
I spotted a bug in my diff. A missing t += 6 inside this if-block:

+ if (subtype == IEEE80211_FC0_SUBTYPE_BAR ||
+ subtype == IEEE80211_FC0_SUBTYPE_BA) {


Also, s/BA/ba/ in one printf.

Fixed version below.

Index: print-802_11.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/print-802_11.c,v
retrieving revision 1.29
diff -u -p -r1.29 print-802_11.c
--- print-802_11.c 1 Feb 2016 10:09:44 -0000 1.29
+++ print-802_11.c 3 Feb 2016 12:40:59 -0000
@@ -37,6 +37,25 @@
#include "addrtoname.h"
#include "interface.h"

+const char *ieee80211_ctl_subtype_name[] = {
+ "reserved#0",
+ "reserved#1",
+ "reserved#2",
+ "reserved#3",
+ "reserved#4",
+ "reserved#5",
+ "reserved#6",
+ "wrapper",
+ "block ack request",
+ "block ack",
+ "ps poll",
+ "rts",
+ "cts",
+ "ack",
+ "cf-end",
+ "cf-end-ack",
+};
+
const char *ieee80211_mgt_subtype_name[] = {
"association request",
"association response",
@@ -813,6 +832,70 @@ ieee80211_frame(struct ieee80211_frame *
break;
}
break;
+ case IEEE80211_FC0_TYPE_CTL: {
+ u_int8_t *t = (u_int8_t *) wh;
+
+ printf(": %s", ieee80211_ctl_subtype_name[
+ subtype >> IEEE80211_FC0_SUBTYPE_SHIFT]);
+ if (!vflag)
+ break;
+
+ /* See 802.11 2012 "8.3.1 Control frames". */
+ t += 2; /* skip Frame Control */
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_RTS:
+ case IEEE80211_FC0_SUBTYPE_BAR:
+ case IEEE80211_FC0_SUBTYPE_BA:
+ TCHECK2(*t, 2); /* Duration */
+ printf(", duration %dms", (t[0] || t[1] << 8));
+ t += 2;
+ TCHECK2(*t, 6); /* RA */
+ printf(", ra %s", etheraddr_string(t));
+ t += 6;
+ TCHECK2(*t, 6); /* TA */
+ printf(", ta %s", etheraddr_string(t));
+ if (subtype == IEEE80211_FC0_SUBTYPE_BAR ||
+ subtype == IEEE80211_FC0_SUBTYPE_BA) {
+ u_int16_t ctrl;
+
+ t += 6;
+ TCHECK2(*t, 2); /* BAR/BA control */
+ ctrl = t[0] | (t[1] << 8);
+ if (ctrl & IEEE80211_BA_ACK_POLICY)
+ printf(", no ack");
+ else
+ printf(", normal ack");
+ if ((ctrl & IEEE80211_BA_MULTI_TID) == 0 &&
+ (ctrl & IEEE80211_BA_COMPRESSED) == 0)
+ printf(", basic variant");
+ else if ((ctrl & IEEE80211_BA_MULTI_TID) &&
+ (ctrl & IEEE80211_BA_COMPRESSED))
+ printf(", multi-tid variant");
+ else if (ctrl & IEEE80211_BA_COMPRESSED)
+ printf(", compressed variant");
+ }
+ break;
+ case IEEE80211_FC0_SUBTYPE_CTS:
+ case IEEE80211_FC0_SUBTYPE_ACK:
+ TCHECK2(*t, 2); /* Duration */
+ printf(", duration %dms", (t[0] || t[1] << 8));
+ t += 2;
+ TCHECK2(*t, 6); /* RA */
+ printf(", ra %s", etheraddr_string(t));
+ break;
+ case IEEE80211_FC0_SUBTYPE_PS_POLL:
+ TCHECK2(*t, 2); /* AID */
+ printf(", aid 0x%x", (t[0] || t[1] << 8));
+ t += 2;
+ TCHECK2(*t, 6); /* BSSID(RA) */
+ printf(", ra %s", etheraddr_string(t));
+ t += 6;
+ TCHECK2(*t, 6); /* TA */
+ printf(", ta %s", etheraddr_string(t));
+ break;
+ }
+ break;
+ }
default:
printf(": type#%d", type);
break;
Index: print-802_11.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/print-802_11.c,v
retrieving revision 1.29
diff -u -p -r1.29 print-802_11.c
--- print-802_11.c 1 Feb 2016 10:09:44 -0000 1.29
+++ print-802_11.c 3 Feb 2016 12:21:20 -0000
@@ -37,6 +37,25 @@
#include "addrtoname.h"
#include "interface.h"
+const char *ieee80211_ctl_subtype_name[] = {
+ "reserved#0",
+ "reserved#1",
+ "reserved#2",
+ "reserved#3",
+ "reserved#4",
+ "reserved#5",
+ "reserved#6",
+ "wrapper",
+ "block ack request",
+ "block ack",
+ "ps poll",
+ "rts",
+ "cts",
+ "ack",
+ "cf-end",
+ "cf-end-ack",
+};
+
const char *ieee80211_mgt_subtype_name[] = {
"association request",
"association response",
@@ -813,6 +832,69 @@ ieee80211_frame(struct ieee80211_frame *
break;
}
break;
+ case IEEE80211_FC0_TYPE_CTL: {
+ u_int8_t *t = (u_int8_t *) wh;
+
+ printf(": %s", ieee80211_ctl_subtype_name[
+ subtype >> IEEE80211_FC0_SUBTYPE_SHIFT]);
+ if (!vflag)
+ break;
+
+ /* See 802.11 2012 "8.3.1 Control frames". */
+ t += 2; /* skip Frame Control */
+ switch (subtype) {
+ TCHECK2(*t, 2); /* Duration */
+ printf(", duration %dms", (t[0] || t[1] << 8));
+ t += 2;
+ TCHECK2(*t, 6); /* RA */
+ printf(", ra %s", etheraddr_string(t));
+ t += 6;
+ TCHECK2(*t, 6); /* TA */
+ printf(", ta %s", etheraddr_string(t));
+ if (subtype == IEEE80211_FC0_SUBTYPE_BAR ||
+ subtype == IEEE80211_FC0_SUBTYPE_BA) {
+ u_int16_t ctrl;
+
+ TCHECK2(*t, 2); /* BAR/BA control */
+ ctrl = t[0] | (t[1] << 8);
+ if (ctrl & IEEE80211_BA_ACK_POLICY)
+ printf(", no ack");
+ else
+ printf(", normal ack");
+ if ((ctrl & IEEE80211_BA_MULTI_TID) == 0 &&
+ (ctrl & IEEE80211_BA_COMPRESSED) == 0)
+ printf(", basic variant");
+ else if ((ctrl & IEEE80211_BA_MULTI_TID) &&
+ (ctrl & IEEE80211_BA_COMPRESSED))
+ printf(", multi-tid variant");
+ else if (ctrl & IEEE80211_BA_COMPRESSED)
+ printf(", compressed variant");
+ }
+ break;
+ TCHECK2(*t, 2); /* Duration */
+ printf(", duration %dms", (t[0] || t[1] << 8));
+ t += 2;
+ TCHECK2(*t, 6); /* RA */
+ printf(", ra %s", etheraddr_string(t));
+ break;
+ TCHECK2(*t, 2); /* AID */
+ printf(", aid 0x%x", (t[0] || t[1] << 8));
+ t += 2;
+ TCHECK2(*t, 6); /* BSSID(RA) */
+ printf(", RA %s", etheraddr_string(t));
+ t += 6;
+ TCHECK2(*t, 6); /* TA */
+ printf(", ta %s", etheraddr_string(t));
+ break;
+ }
+ break;
+ }
printf(": type#%d", type);
break;
Stefan Sperling
2016-02-03 15:02:22 UTC
Permalink
Post by Stefan Sperling
Date: Wed, 3 Feb 2016 13:27:50 +0100
Currently, tcpdump displays 802.11 control frames as "type#4" and
doesn't give more information than that.
This diff makes tcpdump show the subtype of such frames, and pretty-print
a few subtypes in verbose mode. There are more subtypes but these can
be pretty-printed later.
ifconfig iwn0 mediaopt monitor up
tcpdump -n -i iwn0 -y IEEE802_11_RADIO -v -s 1500 type ctl
Whether such frames can be seen depends on the driver's RX filter
settings in monitor mode. Another driver I've tested with is zyd(4).
ok?
+ if (subtype == IEEE80211_FC0_SUBTYPE_BAR ||
+ subtype == IEEE80211_FC0_SUBTYPE_BA) {
Also, s/BA/ba/ in one printf.
Fixed version below.
David Vasek spotted another problem:
I was using boolean || where binary | was intended.

Fixed in this version.

Index: print-802_11.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/print-802_11.c,v
retrieving revision 1.29
diff -u -p -r1.29 print-802_11.c
--- print-802_11.c 1 Feb 2016 10:09:44 -0000 1.29
+++ print-802_11.c 3 Feb 2016 14:59:35 -0000
@@ -37,6 +37,25 @@
#include "addrtoname.h"
#include "interface.h"

+const char *ieee80211_ctl_subtype_name[] = {
+ "reserved#0",
+ "reserved#1",
+ "reserved#2",
+ "reserved#3",
+ "reserved#4",
+ "reserved#5",
+ "reserved#6",
+ "wrapper",
+ "block ack request",
+ "block ack",
+ "ps poll",
+ "rts",
+ "cts",
+ "ack",
+ "cf-end",
+ "cf-end-ack",
+};
+
const char *ieee80211_mgt_subtype_name[] = {
"association request",
"association response",
@@ -813,6 +832,70 @@ ieee80211_frame(struct ieee80211_frame *
break;
}
break;
+ case IEEE80211_FC0_TYPE_CTL: {
+ u_int8_t *t = (u_int8_t *) wh;
+
+ printf(": %s", ieee80211_ctl_subtype_name[
+ subtype >> IEEE80211_FC0_SUBTYPE_SHIFT]);
+ if (!vflag)
+ break;
+
+ /* See 802.11 2012 "8.3.1 Control frames". */
+ t += 2; /* skip Frame Control */
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_RTS:
+ case IEEE80211_FC0_SUBTYPE_BAR:
+ case IEEE80211_FC0_SUBTYPE_BA:
+ TCHECK2(*t, 2); /* Duration */
+ printf(", duration %dms", (t[0] | t[1] << 8));
+ t += 2;
+ TCHECK2(*t, 6); /* RA */
+ printf(", ra %s", etheraddr_string(t));
+ t += 6;
+ TCHECK2(*t, 6); /* TA */
+ printf(", ta %s", etheraddr_string(t));
+ if (subtype == IEEE80211_FC0_SUBTYPE_BAR ||
+ subtype == IEEE80211_FC0_SUBTYPE_BA) {
+ u_int16_t ctrl;
+
+ t += 6;
+ TCHECK2(*t, 2); /* BAR/BA control */
+ ctrl = t[0] | (t[1] << 8);
+ if (ctrl & IEEE80211_BA_ACK_POLICY)
+ printf(", no ack");
+ else
+ printf(", normal ack");
+ if ((ctrl & IEEE80211_BA_MULTI_TID) == 0 &&
+ (ctrl & IEEE80211_BA_COMPRESSED) == 0)
+ printf(", basic variant");
+ else if ((ctrl & IEEE80211_BA_MULTI_TID) &&
+ (ctrl & IEEE80211_BA_COMPRESSED))
+ printf(", multi-tid variant");
+ else if (ctrl & IEEE80211_BA_COMPRESSED)
+ printf(", compressed variant");
+ }
+ break;
+ case IEEE80211_FC0_SUBTYPE_CTS:
+ case IEEE80211_FC0_SUBTYPE_ACK:
+ TCHECK2(*t, 2); /* Duration */
+ printf(", duration %dms", (t[0] | t[1] << 8));
+ t += 2;
+ TCHECK2(*t, 6); /* RA */
+ printf(", ra %s", etheraddr_string(t));
+ break;
+ case IEEE80211_FC0_SUBTYPE_PS_POLL:
+ TCHECK2(*t, 2); /* AID */
+ printf(", aid 0x%x", (t[0] | t[1] << 8));
+ t += 2;
+ TCHECK2(*t, 6); /* BSSID(RA) */
+ printf(", ra %s", etheraddr_string(t));
+ t += 6;
+ TCHECK2(*t, 6); /* TA */
+ printf(", ta %s", etheraddr_string(t));
+ break;
+ }
+ break;
+ }
default:
printf(": type#%d", type);
break;

Loading...