Index | Thread | Search

From:
Todd Carson <toc@daybefore.net>
Subject:
Re: pcap: add missing 802.11 subtype keywords
To:
Theo de Raadt <deraadt@openbsd.org>
Cc:
tech@openbsd.org
Date:
Sun, 25 Feb 2024 15:34:13 -1000

Download raw body.

Thread
"Theo de Raadt" <deraadt@openbsd.org> writes:

> This diff is killing me.
>
> Can someone put these into a sorted table and use bsearch?
>

This replaces all of the long if-else blocks (802.11 keywords and pf
actions) with table lookups, following the ipsecctl grammar and upstream
libpcap grammar. Also adds the missing 802.11 keywords.

Lightly tested with wifi traffic and a pflog file.

diff /usr/src
commit - 6c24eb55e021991196003dc7f0a643e806b14295
path + /usr/src
blob - 0c7db1641efc8c73baae467b25ca33c0b1bccbed
file + lib/libpcap/grammar.y
--- lib/libpcap/grammar.y
+++ lib/libpcap/grammar.y
@@ -37,6 +37,7 @@
 #include <net80211/ieee80211.h>
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 
 #include "pcap-int.h"
@@ -54,6 +55,99 @@
 
 int n_errors = 0;
 
+struct keywords {
+	const char *k_name;
+	int         k_val;
+};
+
+/* these must be kept sorted, as they are searched with bsearch() */
+const struct keywords pf_actions[] = {
+	{ "accept", PF_PASS },
+	{ "binat",  PF_BINAT },
+	{ "block",  PF_DROP },
+	{ "drop",   PF_DROP },
+	{ "match",  PF_MATCH },
+	{ "nat",    PF_NAT },
+	{ "pass",   PF_PASS },
+	{ "rdr",    PF_RDR },
+	{ "scrub",  PF_SCRUB }
+};
+const struct keywords ieee80211_dirs[] = {
+	{ "dstods", IEEE80211_FC1_DIR_DSTODS },
+	{ "fromds", IEEE80211_FC1_DIR_FROMDS },
+	{ "nods",   IEEE80211_FC1_DIR_TODS },
+	{ "tods",   IEEE80211_FC1_DIR_TODS }
+};
+const struct keywords ieee80211_types[] = {
+	{ "control",    IEEE80211_FC0_TYPE_CTL },
+	{ "ctl",        IEEE80211_FC0_TYPE_CTL },
+	{ "data",       IEEE80211_FC0_TYPE_DATA },
+	{ "management", IEEE80211_FC0_TYPE_MGT },
+	{ "mgt",        IEEE80211_FC0_TYPE_MGT }
+};
+const struct keywords ieee80211_subtypes[] = {
+	{ "ack",                  IEEE80211_FC0_SUBTYPE_ACK },
+	{ "assoc-req",            IEEE80211_FC0_SUBTYPE_ASSOC_REQ },
+	{ "assoc-resp",           IEEE80211_FC0_SUBTYPE_ASSOC_RESP },
+	{ "assocreq",             IEEE80211_FC0_SUBTYPE_ASSOC_REQ },
+	{ "assocresp",            IEEE80211_FC0_SUBTYPE_ASSOC_RESP },
+	{ "atim",                 IEEE80211_FC0_SUBTYPE_ATIM },
+	{ "auth",                 IEEE80211_FC0_SUBTYPE_AUTH },
+	{ "authentication",       IEEE80211_FC0_SUBTYPE_AUTH },
+	{ "beacon",               IEEE80211_FC0_SUBTYPE_BEACON },
+	{ "cf-ack",               IEEE80211_FC0_SUBTYPE_NODATA_CF_ACK },
+	{ "cf-ack-poll",          IEEE80211_FC0_SUBTYPE_NODATA_CF_ACKPOLL },
+	{ "cf-end",               IEEE80211_FC0_SUBTYPE_CF_END },
+	{ "cf-end-ack",           IEEE80211_FC0_SUBTYPE_CF_END_ACK },
+	{ "cf-poll",              IEEE80211_FC0_SUBTYPE_NODATA_CF_POLL },
+	{ "cts",                  IEEE80211_FC0_SUBTYPE_CTS },
+	{ "data",                 IEEE80211_FC0_SUBTYPE_DATA },
+	{ "data-cf-ack",          IEEE80211_FC0_SUBTYPE_DATA_CF_ACK },
+	{ "data-cf-ack-poll",     IEEE80211_FC0_SUBTYPE_DATA_CF_ACKPOLL },
+	{ "data-cf-poll",         IEEE80211_FC0_SUBTYPE_DATA_CF_POLL },
+	{ "deauth",               IEEE80211_FC0_SUBTYPE_DEAUTH },
+	{ "deauthentication",     IEEE80211_FC0_SUBTYPE_DEAUTH },
+	{ "disassoc",             IEEE80211_FC0_SUBTYPE_DISASSOC },
+	{ "disassociation",       IEEE80211_FC0_SUBTYPE_DISASSOC },
+	{ "null",                 IEEE80211_FC0_SUBTYPE_NODATA },
+	{ "probe-req",            IEEE80211_FC0_SUBTYPE_PROBE_REQ },
+	{ "probe-resp",           IEEE80211_FC0_SUBTYPE_PROBE_RESP },
+	{ "probereq",             IEEE80211_FC0_SUBTYPE_PROBE_REQ },
+	{ "proberesp",            IEEE80211_FC0_SUBTYPE_PROBE_RESP },
+	{ "ps-poll",              IEEE80211_FC0_SUBTYPE_PS_POLL },
+	{ "qos",                  IEEE80211_FC0_SUBTYPE_QOS |
+	                          IEEE80211_FC0_SUBTYPE_NODATA },
+	{ "qos-cf-ack-poll",      IEEE80211_FC0_SUBTYPE_QOS |
+	                          IEEE80211_FC0_SUBTYPE_NODATA_CF_ACKPOLL },
+	{ "qos-cf-poll",          IEEE80211_FC0_SUBTYPE_QOS |
+	                          IEEE80211_FC0_SUBTYPE_NODATA_CF_POLL },
+	{ "qos-data",             IEEE80211_FC0_SUBTYPE_QOS |
+	                          IEEE80211_FC0_SUBTYPE_DATA },
+	{ "qos-data-cf-ack",      IEEE80211_FC0_SUBTYPE_QOS |
+	                          IEEE80211_FC0_SUBTYPE_DATA_CF_ACK },
+	{ "qos-data-cf-ack-poll", IEEE80211_FC0_SUBTYPE_QOS |
+	                          IEEE80211_FC0_SUBTYPE_DATA_CF_ACKPOLL },
+	{ "qos-data-cf-poll",     IEEE80211_FC0_SUBTYPE_QOS |
+	                          IEEE80211_FC0_SUBTYPE_DATA_CF_POLL },
+	{ "reassoc-req",          IEEE80211_FC0_SUBTYPE_REASSOC_REQ },
+	{ "reassoc-resp",         IEEE80211_FC0_SUBTYPE_REASSOC_RESP },
+	{ "reassocreq",           IEEE80211_FC0_SUBTYPE_REASSOC_REQ },
+	{ "reassocresp",          IEEE80211_FC0_SUBTYPE_REASSOC_RESP },
+	{ "rts",                  IEEE80211_FC0_SUBTYPE_RTS }
+};
+
+static int
+kw_cmp(const void *str, const void *ent)
+{
+	return strcasecmp(str, ((const struct keywords *)ent)->k_name);
+}
+
+static const struct keywords *
+kw_match(const char *str, const struct keywords *tbl, size_t len)
+{
+	return bsearch(str, tbl, len / sizeof(*tbl), sizeof(*tbl), kw_cmp);
+}
+
 static struct qual qerr = { Q_UNDEF, Q_UNDEF, Q_UNDEF, Q_UNDEF };
 
 static void
@@ -305,22 +399,11 @@ reason:	  NUM			{ $$ = $1; }
 				}
 	;
 
-action:	  ID			{ if (strcasecmp($1, "pass") == 0 ||
-				      strcasecmp($1, "accept") == 0)
-					$$ = PF_PASS;
-				  else if (strcasecmp($1, "drop") == 0 ||
-				      strcasecmp($1, "block") == 0)
-					$$ = PF_DROP;
-				  else if (strcasecmp($1, "match") == 0)
-					$$ = PF_MATCH;
-				  else if (strcasecmp($1, "rdr") == 0)
-				  	$$ = PF_RDR;
-				  else if (strcasecmp($1, "nat") == 0)
-				  	$$ = PF_NAT;
-				  else if (strcasecmp($1, "binat") == 0)
-				  	$$ = PF_BINAT;
-				  else if (strcasecmp($1, "scrub") == 0)
-				  	$$ = PF_SCRUB;
+action:	  ID			{ const struct keywords *p;
+	                          p = kw_match($1, pf_actions,
+				      sizeof(pf_actions));
+				  if (p != NULL)
+					$$ = p->k_val;
 				  else
 					  bpf_error("unknown PF action");
 				}
@@ -339,61 +422,33 @@ p80211:   TYPE type SUBTYPE subtype
 	;
 
 type:	  NUM
-	| ID			{ if (strcasecmp($1, "data") == 0)
-					$$ = IEEE80211_FC0_TYPE_DATA;
-				  else if (strcasecmp($1, "mgt") == 0 ||
-					strcasecmp($1, "management") == 0)
-					$$ = IEEE80211_FC0_TYPE_MGT;
-				  else if (strcasecmp($1, "ctl") == 0 ||
-					strcasecmp($1, "control") == 0)
-					$$ = IEEE80211_FC0_TYPE_CTL;
-				  else
-					  bpf_error("unknown 802.11 type");
+        | ID			{ const struct keywords *p;
+	                          p = kw_match($1, ieee80211_types,
+	                              sizeof(ieee80211_types));
+	                          if (p != NULL)
+		                        $$ = p->k_val;
+	                          else
+		                        bpf_error("unknown 802.11 type");
 				}
 	;
 
 subtype:  NUM
-	| ID			{ if (strcasecmp($1, "assocreq") == 0)
-					$$ = IEEE80211_FC0_SUBTYPE_ASSOC_REQ;
-				  else if (strcasecmp($1, "assocresp") == 0)
-					$$ = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
-				  else if (strcasecmp($1, "reassocreq") == 0)
-					$$ = IEEE80211_FC0_SUBTYPE_REASSOC_REQ;
-				  else if (strcasecmp($1, "reassocresp") == 0)
-					$$ = IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
-				  else if (strcasecmp($1, "probereq") == 0)
-					$$ = IEEE80211_FC0_SUBTYPE_PROBE_REQ;
-				  else if (strcasecmp($1, "proberesp") == 0)
-					$$ = IEEE80211_FC0_SUBTYPE_PROBE_RESP;
-				  else if (strcasecmp($1, "beacon") == 0)
-					$$ = IEEE80211_FC0_SUBTYPE_BEACON;
-				  else if (strcasecmp($1, "atim") == 0)
-					$$ = IEEE80211_FC0_SUBTYPE_ATIM;
-				  else if (strcasecmp($1, "disassoc") == 0 ||
-				      strcasecmp($1, "disassociation") == 0)
-					$$ = IEEE80211_FC0_SUBTYPE_DISASSOC;
-				  else if (strcasecmp($1, "auth") == 0 ||
-				      strcasecmp($1, "authentication") == 0)
-					$$ = IEEE80211_FC0_SUBTYPE_AUTH;
-				  else if (strcasecmp($1, "deauth") == 0 ||
-				      strcasecmp($1, "deauthentication") == 0)
-					$$ = IEEE80211_FC0_SUBTYPE_DEAUTH;
-				  else if (strcasecmp($1, "data") == 0)
-					$$ = IEEE80211_FC0_SUBTYPE_DATA;
+        | ID			{ const struct keywords *p;
+	                          p = kw_match($1, ieee80211_subtypes,
+				      sizeof(ieee80211_subtypes));
+                                  if (p != NULL)
+				        $$ = p->k_val;
 				  else
-					  bpf_error("unknown 802.11 subtype");
-				}
+					bpf_error("unknown 802.11 subtype");
+	                        }
 	;
 
 dir:	  NUM
-	| ID			{ if (strcasecmp($1, "nods") == 0)
-					$$ = IEEE80211_FC1_DIR_NODS;
-				  else if (strcasecmp($1, "tods") == 0)
-					$$ = IEEE80211_FC1_DIR_TODS;
-				  else if (strcasecmp($1, "fromds") == 0)
-					$$ = IEEE80211_FC1_DIR_FROMDS;
-				  else if (strcasecmp($1, "dstods") == 0)
-					$$ = IEEE80211_FC1_DIR_DSTODS;
+        | ID			{ const struct keywords *p;
+	                          p = kw_match($1, ieee80211_dirs,
+	                          sizeof(ieee80211_dirs));
+				  if (p != NULL)
+			                $$ = p->k_val;
 				  else
 					bpf_error("unknown 802.11 direction");
 				}


> Todd Carson <toc@daybefore.net> wrote:
>
>> 
>> 
>> Many of the subtype keywords for matching various 802.11 frame types
>> which are documented in the tcpdump(8) and pcap-filter(5) manpages
>> aren't implemented in the grammar and result in an
>> "unknown 802.11 subtype" when used.
>> 
>> They are found in the grammar in upstream libpcap from tcpdump.org.
>> 
>> The diff below adds the missing subtype keywords mentioned in the
>> manpages.
>> Tested with real traffic on a few subtypes; the rest by sanity-checking
>> compiled BPF printed with 'tcpdump -d'.
>> 
>> I don't know if this requires bumping the libpcap minor version so I
>> left that alone.
>> 
>> diff /usr/src
>> commit - 6c24eb55e021991196003dc7f0a643e806b14295
>> path + /usr/src
>> blob - 0c7db1641efc8c73baae467b25ca33c0b1bccbed
>> file + lib/libpcap/grammar.y
>> --- lib/libpcap/grammar.y
>> +++ lib/libpcap/grammar.y
>> @@ -353,17 +353,23 @@ type:	  NUM
>>  	;
>>  
>>  subtype:  NUM
>> -	| ID			{ if (strcasecmp($1, "assocreq") == 0)
>> +	| ID			{ if (strcasecmp($1, "assocreq") == 0 ||
>> +		 		      strcasecmp($1, "assoc-req") == 0)
>>  					$$ = IEEE80211_FC0_SUBTYPE_ASSOC_REQ;
>> -				  else if (strcasecmp($1, "assocresp") == 0)
>> +				  else if (strcasecmp($1, "assocresp") == 0 ||
>> +				      strcasecmp($1, "assoc-resp") == 0)
>>  					$$ = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
>> -				  else if (strcasecmp($1, "reassocreq") == 0)
>> +				  else if (strcasecmp($1, "reassocreq") == 0 ||
>> +				      strcasecmp($1, "reassoc-req") == 0)
>>  					$$ = IEEE80211_FC0_SUBTYPE_REASSOC_REQ;
>> -				  else if (strcasecmp($1, "reassocresp") == 0)
>> +				  else if (strcasecmp($1, "reassocresp") == 0 ||
>> +				      strcasecmp($1, "reassoc-resp") == 0)
>>  					$$ = IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
>> -				  else if (strcasecmp($1, "probereq") == 0)
>> +				  else if (strcasecmp($1, "probereq") == 0 ||
>> +				      strcasecmp($1, "probe-req") == 0)
>>  					$$ = IEEE80211_FC0_SUBTYPE_PROBE_REQ;
>> -				  else if (strcasecmp($1, "proberesp") == 0)
>> +				  else if (strcasecmp($1, "proberesp") == 0 ||
>> +				      strcasecmp($1, "probe-resp") == 0)
>>  					$$ = IEEE80211_FC0_SUBTYPE_PROBE_RESP;
>>  				  else if (strcasecmp($1, "beacon") == 0)
>>  					$$ = IEEE80211_FC0_SUBTYPE_BEACON;
>> @@ -378,8 +384,55 @@ subtype:  NUM
>>  				  else if (strcasecmp($1, "deauth") == 0 ||
>>  				      strcasecmp($1, "deauthentication") == 0)
>>  					$$ = IEEE80211_FC0_SUBTYPE_DEAUTH;
>> +				  else if (strcasecmp($1, "ps-poll") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_PS_POLL;
>> +				  else if (strcasecmp($1, "rts") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_RTS;
>> +				  else if (strcasecmp($1, "cts") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_CTS;
>> +				  else if (strcasecmp($1, "ack") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_ACK;
>> +				  else if (strcasecmp($1, "cf-end") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_CF_END;
>> +				  else if (strcasecmp($1, "cf-end-ack") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_CF_END_ACK;
>>  				  else if (strcasecmp($1, "data") == 0)
>>  					$$ = IEEE80211_FC0_SUBTYPE_DATA;
>> +				  else if (strcasecmp($1, "data-cf-ack") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_DATA_CF_ACK;
>> +				  else if (strcasecmp($1, "data-cf-poll") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_DATA_CF_POLL;
>> +				  else if (strcasecmp($1, "data-cf-ack-poll") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_DATA_CF_ACKPOLL;
>> +				  else if (strcasecmp($1, "null") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_NODATA;
>> +				  else if (strcasecmp($1, "cf-ack") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_NODATA_CF_ACK;
>> +				  else if (strcasecmp($1, "cf-poll") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_NODATA_CF_POLL;
>> +				  else if (strcasecmp($1, "cf-ack-poll") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_NODATA_CF_ACKPOLL;
>> +				  else if (strcasecmp($1, "qos-data") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_QOS|
>> +					    IEEE80211_FC0_SUBTYPE_DATA;
>> +				  else if (strcasecmp($1, "qos-data-cf-ack") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_QOS|
>> +					    IEEE80211_FC0_SUBTYPE_DATA_CF_ACK;
>> +				  else if (strcasecmp($1, "qos-data-cf-poll") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_QOS|
>> +					    IEEE80211_FC0_SUBTYPE_DATA_CF_POLL;
>> +				  else if (strcasecmp($1, "qos-data-cf-ack-poll") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_QOS|
>> +					    IEEE80211_FC0_SUBTYPE_DATA_CF_ACKPOLL;
>> +				  else if (strcasecmp($1, "qos") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_QOS|
>> +					    IEEE80211_FC0_SUBTYPE_NODATA;
>> +				  else if (strcasecmp($1, "qos-cf-poll") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_QOS|
>> +					    IEEE80211_FC0_SUBTYPE_NODATA_CF_POLL;
>> +				  else if (strcasecmp($1, "qos-cf-ack-poll") == 0)
>> +					$$ = IEEE80211_FC0_SUBTYPE_QOS|
>> +					    IEEE80211_FC0_SUBTYPE_NODATA_CF_ACKPOLL;
>>  				  else
>>  					  bpf_error("unknown 802.11 subtype");
>>  				}
>>