From: Todd Carson Subject: Re: pcap: add missing 802.11 subtype keywords To: Theo de Raadt Cc: tech@openbsd.org Date: Sun, 25 Feb 2024 15:34:13 -1000 "Theo de Raadt" 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 #include +#include #include #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 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"); >> } >>