X-Git-Url: https://git.tcpdump.org/tcpdump/blobdiff_plain/6e8dc38172920c7b52ec7b553e01e102ecdc3689..6c8ef0eb86a39c277d1a43802dd8ea01b51cfb2a:/print-802_11.c diff --git a/print-802_11.c b/print-802_11.c index 87d60ca8..50a3e9f5 100644 --- a/print-802_11.c +++ b/print-802_11.c @@ -20,6 +20,8 @@ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ +/* \summary: IEEE 802.11 printer */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -1037,10 +1039,6 @@ parse_elements(netdissect_options *ndo, if (ssid.length != 0) { if (ssid.length > sizeof(ssid.ssid) - 1) return 0; - if (!ND_TTEST2(*(p + offset), ssid.length)) - return 0; - if (length < ssid.length) - return 0; memcpy(&ssid.ssid, p + offset, ssid.length); offset += ssid.length; length -= ssid.length; @@ -1066,10 +1064,6 @@ parse_elements(netdissect_options *ndo, if (challenge.length > sizeof(challenge.text) - 1) return 0; - if (!ND_TTEST2(*(p + offset), challenge.length)) - return 0; - if (length < challenge.length) - return 0; memcpy(&challenge.text, p + offset, challenge.length); offset += challenge.length; @@ -1095,10 +1089,6 @@ parse_elements(netdissect_options *ndo, if (rates.length != 0) { if (rates.length > sizeof rates.rate) return 0; - if (!ND_TTEST2(*(p + offset), rates.length)) - return 0; - if (length < rates.length) - return 0; memcpy(&rates.rate, p + offset, rates.length); offset += rates.length; length -= rates.length; @@ -1187,8 +1177,7 @@ parse_elements(netdissect_options *ndo, offset += 3; length -= 3; - memcpy(tim.bitmap, p + (tim.length - 3), - (tim.length - 3)); + memcpy(tim.bitmap, p + offset, tim.length - 3); offset += tim.length - 3; length -= tim.length - 3; /* @@ -2037,7 +2026,7 @@ ieee802_11_print(netdissect_options *ndo, { uint16_t fc; u_int caplen, hdrlen, meshdrlen; - const uint8_t *src, *dst; + struct lladdr_info src, dst; int llc_hdrlen; caplen = orig_caplen; @@ -2069,6 +2058,10 @@ ieee802_11_print(netdissect_options *ndo, hdrlen = roundup2(hdrlen, 4); if (ndo->ndo_Hflag && FC_TYPE(fc) == T_DATA && DATA_FRAME_IS_QOS(FC_SUBTYPE(fc))) { + if (caplen < hdrlen + 1) { + ND_PRINT((ndo, "%s", tstr)); + return hdrlen; + } meshdrlen = extract_mesh_header_length(p+hdrlen); hdrlen += meshdrlen; } else @@ -2089,10 +2082,12 @@ ieee802_11_print(netdissect_options *ndo, caplen -= hdrlen; p += hdrlen; + src.addr_string = etheraddr_string; + dst.addr_string = etheraddr_string; switch (FC_TYPE(fc)) { case T_MGMT: - get_mgmt_src_dst_mac(p - hdrlen, &src, &dst); - if (!mgmt_body_print(ndo, fc, src, p, length)) { + get_mgmt_src_dst_mac(p - hdrlen, &src.addr, &dst.addr); + if (!mgmt_body_print(ndo, fc, src.addr, p, length)) { ND_PRINT((ndo, "%s", tstr)); return hdrlen; } @@ -2114,8 +2109,8 @@ ieee802_11_print(netdissect_options *ndo, return hdrlen; } } else { - get_data_src_dst_mac(fc, p - hdrlen, &src, &dst); - llc_hdrlen = llc_print(ndo, p, length, caplen, src, dst); + get_data_src_dst_mac(fc, p - hdrlen, &src.addr, &dst.addr); + llc_hdrlen = llc_print(ndo, p, length, caplen, &src, &dst); if (llc_hdrlen < 0) { /* * Some kinds of LLC packet we cannot @@ -2492,16 +2487,6 @@ enum ieee80211_radiotap_type { #define IEEE80211_RADIOTAP_CODING_LDPC_USERn 0x01 - -/* Radiotap state */ -/* This is used to save state when parsing/processing parameters */ -struct radiotap_state -{ - uint32_t present; - - uint8_t rate; -}; - #define IEEE80211_CHAN_FHSS \ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK) #define IEEE80211_CHAN_A \ @@ -2528,7 +2513,7 @@ struct radiotap_state static void print_chaninfo(netdissect_options *ndo, - int freq, int flags, int presentflags) + uint16_t freq, int flags, int presentflags) { ND_PRINT((ndo, "%u MHz", freq)); if (presentflags & (1 << IEEE80211_RADIOTAP_MCS)) { @@ -2574,184 +2559,50 @@ print_chaninfo(netdissect_options *ndo, static int print_radiotap_field(netdissect_options *ndo, - struct cpack_state *s, uint32_t bit, uint8_t *flags, - struct radiotap_state *state, uint32_t presentflags) + struct cpack_state *s, uint32_t bit, uint8_t *flagsp, + uint32_t presentflags) { - union { - int8_t i8; - uint8_t u8; - int16_t i16; - uint16_t u16; - uint32_t u32; - uint64_t u64; - } u, u2, u3, u4, u5, u6; - uint8_t mcs_nss[4]; u_int i; int rc; switch (bit) { - case IEEE80211_RADIOTAP_FLAGS: - rc = cpack_uint8(s, &u.u8); - if (rc != 0) - break; - *flags = u.u8; - break; - case IEEE80211_RADIOTAP_RATE: - rc = cpack_uint8(s, &u.u8); - if (rc != 0) - break; - /* Save state rate */ - state->rate = u.u8; - break; - case IEEE80211_RADIOTAP_DB_ANTSIGNAL: - case IEEE80211_RADIOTAP_DB_ANTNOISE: - case IEEE80211_RADIOTAP_ANTENNA: - rc = cpack_uint8(s, &u.u8); - break; - case IEEE80211_RADIOTAP_DBM_ANTSIGNAL: - case IEEE80211_RADIOTAP_DBM_ANTNOISE: - rc = cpack_int8(s, &u.i8); - break; - case IEEE80211_RADIOTAP_CHANNEL: - rc = cpack_uint16(s, &u.u16); - if (rc != 0) - break; - rc = cpack_uint16(s, &u2.u16); - break; - case IEEE80211_RADIOTAP_FHSS: - case IEEE80211_RADIOTAP_LOCK_QUALITY: - case IEEE80211_RADIOTAP_TX_ATTENUATION: - case IEEE80211_RADIOTAP_RX_FLAGS: - rc = cpack_uint16(s, &u.u16); - break; - case IEEE80211_RADIOTAP_DB_TX_ATTENUATION: - rc = cpack_uint8(s, &u.u8); - break; - case IEEE80211_RADIOTAP_DBM_TX_POWER: - rc = cpack_int8(s, &u.i8); - break; - case IEEE80211_RADIOTAP_TSFT: - rc = cpack_uint64(s, &u.u64); - break; - case IEEE80211_RADIOTAP_XCHANNEL: - rc = cpack_uint32(s, &u.u32); - if (rc != 0) - break; - rc = cpack_uint16(s, &u2.u16); - if (rc != 0) - break; - rc = cpack_uint8(s, &u3.u8); - if (rc != 0) - break; - rc = cpack_uint8(s, &u4.u8); - break; - case IEEE80211_RADIOTAP_MCS: - rc = cpack_uint8(s, &u.u8); - if (rc != 0) - break; - rc = cpack_uint8(s, &u2.u8); - if (rc != 0) - break; - rc = cpack_uint8(s, &u3.u8); - break; - case IEEE80211_RADIOTAP_AMPDU_STATUS: - rc = cpack_uint32(s, &u.u32); - if (rc != 0) - break; - rc = cpack_uint16(s, &u2.u16); - if (rc != 0) - break; - rc = cpack_uint8(s, &u3.u8); + case IEEE80211_RADIOTAP_TSFT: { + uint64_t tsft; + + rc = cpack_uint64(s, &tsft); if (rc != 0) - break; - rc = cpack_uint8(s, &u4.u8); + goto trunc; + ND_PRINT((ndo, "%" PRIu64 "us tsft ", tsft)); break; - case IEEE80211_RADIOTAP_VHT: - rc = cpack_uint16(s, &u.u16); - if (rc != 0) - break; - rc = cpack_uint8(s, &u2.u8); - if (rc != 0) - break; - rc = cpack_uint8(s, &u3.u8); - if (rc != 0) - goto fail; - for (i = 0; i < 4; i++) { - rc = cpack_uint8(s, &mcs_nss[i]); - if (rc != 0) - goto fail; } - rc = cpack_uint8(s, &u4.u8); - if (rc != 0) - break; - rc = cpack_uint8(s, &u5.u8); - if (rc != 0) - goto fail; - rc = cpack_uint16(s, &u6.u16); - fail: - break; - case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: { - uint8_t vns[3]; - uint16_t length; - uint8_t subspace; - if ((cpack_align_and_reserve(s, 2)) == NULL) { - rc = -1; - break; - } + case IEEE80211_RADIOTAP_FLAGS: { + uint8_t flagsval; - rc = cpack_uint8(s, &vns[0]); - if (rc != 0) - break; - rc = cpack_uint8(s, &vns[1]); - if (rc != 0) - break; - rc = cpack_uint8(s, &vns[2]); + rc = cpack_uint8(s, &flagsval); if (rc != 0) - break; - rc = cpack_uint8(s, &subspace); - if (rc != 0) - break; - rc = cpack_uint16(s, &length); - if (rc != 0) - break; - - /* Skip up to length */ - s->c_next += length; + goto trunc; + *flagsp = flagsval; + if (flagsval & IEEE80211_RADIOTAP_F_CFP) + ND_PRINT((ndo, "cfp ")); + if (flagsval & IEEE80211_RADIOTAP_F_SHORTPRE) + ND_PRINT((ndo, "short preamble ")); + if (flagsval & IEEE80211_RADIOTAP_F_WEP) + ND_PRINT((ndo, "wep ")); + if (flagsval & IEEE80211_RADIOTAP_F_FRAG) + ND_PRINT((ndo, "fragmented ")); + if (flagsval & IEEE80211_RADIOTAP_F_BADFCS) + ND_PRINT((ndo, "bad-fcs ")); break; - } - default: - /* this bit indicates a field whose - * size we do not know, so we cannot - * proceed. Just print the bit number. - */ - ND_PRINT((ndo, "[bit %u] ", bit)); - return -1; - } - - if (rc != 0) { - ND_PRINT((ndo, "%s", tstr)); - return rc; - } + } - /* Preserve the state present flags */ - state->present = presentflags; + case IEEE80211_RADIOTAP_RATE: { + uint8_t rate; - switch (bit) { - case IEEE80211_RADIOTAP_CHANNEL: - /* - * If CHANNEL and XCHANNEL are both present, skip - * CHANNEL. - */ - if (presentflags & (1 << IEEE80211_RADIOTAP_XCHANNEL)) - break; - print_chaninfo(ndo, u.u16, u2.u16, presentflags); - break; - case IEEE80211_RADIOTAP_FHSS: - ND_PRINT((ndo, "fhset %d fhpat %d ", u.u16 & 0xff, (u.u16 >> 8) & 0xff)); - break; - case IEEE80211_RADIOTAP_RATE: + rc = cpack_uint8(s, &rate); + if (rc != 0) + goto trunc; /* * XXX On FreeBSD rate & 0x80 means we have an MCS. On * Linux and AirPcap it does not. (What about @@ -2773,7 +2624,7 @@ print_radiotap_field(netdissect_options *ndo, * setting. Such rates do exist, e.g. 11n * MCS 7 at 20 MHz with a long guard interval. */ - if (u.u8 >= 0x80 && u.u8 <= 0x8f) { + if (rate >= 0x80 && rate <= 0x8f) { /* * XXX - we don't know the channel width * or guard interval length, so we can't @@ -2790,59 +2641,172 @@ print_radiotap_field(netdissect_options *ndo, * information from Flags, at least on * FreeBSD? */ - ND_PRINT((ndo, "MCS %u ", u.u8 & 0x7f)); + ND_PRINT((ndo, "MCS %u ", rate & 0x7f)); } else - ND_PRINT((ndo, "%2.1f Mb/s ", .5 * u.u8)); + ND_PRINT((ndo, "%2.1f Mb/s ", .5 * rate)); break; - case IEEE80211_RADIOTAP_DBM_ANTSIGNAL: - ND_PRINT((ndo, "%ddBm signal ", u.i8)); + } + + case IEEE80211_RADIOTAP_CHANNEL: { + uint16_t frequency; + uint16_t flags; + + rc = cpack_uint16(s, &frequency); + if (rc != 0) + goto trunc; + rc = cpack_uint16(s, &flags); + if (rc != 0) + goto trunc; + /* + * If CHANNEL and XCHANNEL are both present, skip + * CHANNEL. + */ + if (presentflags & (1 << IEEE80211_RADIOTAP_XCHANNEL)) + break; + print_chaninfo(ndo, frequency, flags, presentflags); break; - case IEEE80211_RADIOTAP_DBM_ANTNOISE: - ND_PRINT((ndo, "%ddBm noise ", u.i8)); + } + + case IEEE80211_RADIOTAP_FHSS: { + uint8_t hopset; + uint8_t hoppat; + + rc = cpack_uint8(s, &hopset); + if (rc != 0) + goto trunc; + rc = cpack_uint8(s, &hoppat); + if (rc != 0) + goto trunc; + ND_PRINT((ndo, "fhset %d fhpat %d ", hopset, hoppat)); break; - case IEEE80211_RADIOTAP_DB_ANTSIGNAL: - ND_PRINT((ndo, "%ddB signal ", u.u8)); + } + + case IEEE80211_RADIOTAP_DBM_ANTSIGNAL: { + int8_t dbm_antsignal; + + rc = cpack_int8(s, &dbm_antsignal); + if (rc != 0) + goto trunc; + ND_PRINT((ndo, "%ddBm signal ", dbm_antsignal)); break; - case IEEE80211_RADIOTAP_DB_ANTNOISE: - ND_PRINT((ndo, "%ddB noise ", u.u8)); + } + + case IEEE80211_RADIOTAP_DBM_ANTNOISE: { + int8_t dbm_antnoise; + + rc = cpack_int8(s, &dbm_antnoise); + if (rc != 0) + goto trunc; + ND_PRINT((ndo, "%ddBm noise ", dbm_antnoise)); break; - case IEEE80211_RADIOTAP_LOCK_QUALITY: - ND_PRINT((ndo, "%u sq ", u.u16)); + } + + case IEEE80211_RADIOTAP_LOCK_QUALITY: { + uint16_t lock_quality; + + rc = cpack_uint16(s, &lock_quality); + if (rc != 0) + goto trunc; + ND_PRINT((ndo, "%u sq ", lock_quality)); break; - case IEEE80211_RADIOTAP_TX_ATTENUATION: - ND_PRINT((ndo, "%d tx power ", -(int)u.u16)); + } + + case IEEE80211_RADIOTAP_TX_ATTENUATION: { + uint16_t tx_attenuation; + + rc = cpack_uint16(s, &tx_attenuation); + if (rc != 0) + goto trunc; + ND_PRINT((ndo, "%d tx power ", -(int)tx_attenuation)); break; - case IEEE80211_RADIOTAP_DB_TX_ATTENUATION: - ND_PRINT((ndo, "%ddB tx power ", -(int)u.u8)); + } + + case IEEE80211_RADIOTAP_DB_TX_ATTENUATION: { + uint8_t db_tx_attenuation; + + rc = cpack_uint8(s, &db_tx_attenuation); + if (rc != 0) + goto trunc; + ND_PRINT((ndo, "%ddB tx attenuation ", -(int)db_tx_attenuation)); break; - case IEEE80211_RADIOTAP_DBM_TX_POWER: - ND_PRINT((ndo, "%ddBm tx power ", u.i8)); + } + + case IEEE80211_RADIOTAP_DBM_TX_POWER: { + int8_t dbm_tx_power; + + rc = cpack_int8(s, &dbm_tx_power); + if (rc != 0) + goto trunc; + ND_PRINT((ndo, "%ddBm tx power ", dbm_tx_power)); break; - case IEEE80211_RADIOTAP_FLAGS: - if (u.u8 & IEEE80211_RADIOTAP_F_CFP) - ND_PRINT((ndo, "cfp ")); - if (u.u8 & IEEE80211_RADIOTAP_F_SHORTPRE) - ND_PRINT((ndo, "short preamble ")); - if (u.u8 & IEEE80211_RADIOTAP_F_WEP) - ND_PRINT((ndo, "wep ")); - if (u.u8 & IEEE80211_RADIOTAP_F_FRAG) - ND_PRINT((ndo, "fragmented ")); - if (u.u8 & IEEE80211_RADIOTAP_F_BADFCS) - ND_PRINT((ndo, "bad-fcs ")); + } + + case IEEE80211_RADIOTAP_ANTENNA: { + uint8_t antenna; + + rc = cpack_uint8(s, &antenna); + if (rc != 0) + goto trunc; + ND_PRINT((ndo, "antenna %u ", antenna)); break; - case IEEE80211_RADIOTAP_ANTENNA: - ND_PRINT((ndo, "antenna %d ", u.u8)); + } + + case IEEE80211_RADIOTAP_DB_ANTSIGNAL: { + uint8_t db_antsignal; + + rc = cpack_uint8(s, &db_antsignal); + if (rc != 0) + goto trunc; + ND_PRINT((ndo, "%ddB signal ", db_antsignal)); break; - case IEEE80211_RADIOTAP_TSFT: - ND_PRINT((ndo, "%" PRIu64 "us tsft ", u.u64)); + } + + case IEEE80211_RADIOTAP_DB_ANTNOISE: { + uint8_t db_antnoise; + + rc = cpack_uint8(s, &db_antnoise); + if (rc != 0) + goto trunc; + ND_PRINT((ndo, "%ddB noise ", db_antnoise)); break; - case IEEE80211_RADIOTAP_RX_FLAGS: + } + + case IEEE80211_RADIOTAP_RX_FLAGS: { + uint16_t rx_flags; + + rc = cpack_uint16(s, &rx_flags); + if (rc != 0) + goto trunc; /* Do nothing for now */ break; - case IEEE80211_RADIOTAP_XCHANNEL: - print_chaninfo(ndo, u2.u16, u.u32, presentflags); + } + + case IEEE80211_RADIOTAP_XCHANNEL: { + uint32_t flags; + uint16_t frequency; + uint8_t channel; + uint8_t maxpower; + + rc = cpack_uint32(s, &flags); + if (rc != 0) + goto trunc; + rc = cpack_uint16(s, &frequency); + if (rc != 0) + goto trunc; + rc = cpack_uint8(s, &channel); + if (rc != 0) + goto trunc; + rc = cpack_uint8(s, &maxpower); + if (rc != 0) + goto trunc; + print_chaninfo(ndo, frequency, flags, presentflags); break; + } + case IEEE80211_RADIOTAP_MCS: { + uint8_t known; + uint8_t flags; + uint8_t mcs_index; static const char *ht_bandwidth[4] = { "20 MHz", "40 MHz", @@ -2851,15 +2815,24 @@ print_radiotap_field(netdissect_options *ndo, }; float htrate; - if (u.u8 & IEEE80211_RADIOTAP_MCS_MCS_INDEX_KNOWN) { + rc = cpack_uint8(s, &known); + if (rc != 0) + goto trunc; + rc = cpack_uint8(s, &flags); + if (rc != 0) + goto trunc; + rc = cpack_uint8(s, &mcs_index); + if (rc != 0) + goto trunc; + if (known & IEEE80211_RADIOTAP_MCS_MCS_INDEX_KNOWN) { /* * We know the MCS index. */ - if (u3.u8 <= MAX_MCS_INDEX) { + if (mcs_index <= MAX_MCS_INDEX) { /* * And it's in-range. */ - if (u.u8 & (IEEE80211_RADIOTAP_MCS_BANDWIDTH_KNOWN|IEEE80211_RADIOTAP_MCS_GUARD_INTERVAL_KNOWN)) { + if (known & (IEEE80211_RADIOTAP_MCS_BANDWIDTH_KNOWN|IEEE80211_RADIOTAP_MCS_GUARD_INTERVAL_KNOWN)) { /* * And we know both the bandwidth and * the guard interval, so we can look @@ -2867,9 +2840,9 @@ print_radiotap_field(netdissect_options *ndo, */ htrate = ieee80211_float_htrates \ - [u3.u8] \ - [((u2.u8 & IEEE80211_RADIOTAP_MCS_BANDWIDTH_MASK) == IEEE80211_RADIOTAP_MCS_BANDWIDTH_40 ? 1 : 0)] \ - [((u2.u8 & IEEE80211_RADIOTAP_MCS_SHORT_GI) ? 1 : 0)]; + [mcs_index] \ + [((flags & IEEE80211_RADIOTAP_MCS_BANDWIDTH_MASK) == IEEE80211_RADIOTAP_MCS_BANDWIDTH_40 ? 1 : 0)] \ + [((flags & IEEE80211_RADIOTAP_MCS_SHORT_GI) ? 1 : 0)]; } else { /* * We don't know both the bandwidth @@ -2889,44 +2862,71 @@ print_radiotap_field(netdissect_options *ndo, * We have the rate. * Print it. */ - ND_PRINT((ndo, "%.1f Mb/s MCS %u ", htrate, u3.u8)); + ND_PRINT((ndo, "%.1f Mb/s MCS %u ", htrate, mcs_index)); } else { /* * We at least have the MCS index. * Print it. */ - ND_PRINT((ndo, "MCS %u ", u3.u8)); + ND_PRINT((ndo, "MCS %u ", mcs_index)); } } - if (u.u8 & IEEE80211_RADIOTAP_MCS_BANDWIDTH_KNOWN) { + if (known & IEEE80211_RADIOTAP_MCS_BANDWIDTH_KNOWN) { ND_PRINT((ndo, "%s ", - ht_bandwidth[u2.u8 & IEEE80211_RADIOTAP_MCS_BANDWIDTH_MASK])); + ht_bandwidth[flags & IEEE80211_RADIOTAP_MCS_BANDWIDTH_MASK])); } - if (u.u8 & IEEE80211_RADIOTAP_MCS_GUARD_INTERVAL_KNOWN) { + if (known & IEEE80211_RADIOTAP_MCS_GUARD_INTERVAL_KNOWN) { ND_PRINT((ndo, "%s GI ", - (u2.u8 & IEEE80211_RADIOTAP_MCS_SHORT_GI) ? + (flags & IEEE80211_RADIOTAP_MCS_SHORT_GI) ? "short" : "long")); } - if (u.u8 & IEEE80211_RADIOTAP_MCS_HT_FORMAT_KNOWN) { + if (known & IEEE80211_RADIOTAP_MCS_HT_FORMAT_KNOWN) { ND_PRINT((ndo, "%s ", - (u2.u8 & IEEE80211_RADIOTAP_MCS_HT_GREENFIELD) ? + (flags & IEEE80211_RADIOTAP_MCS_HT_GREENFIELD) ? "greenfield" : "mixed")); } - if (u.u8 & IEEE80211_RADIOTAP_MCS_FEC_TYPE_KNOWN) { + if (known & IEEE80211_RADIOTAP_MCS_FEC_TYPE_KNOWN) { ND_PRINT((ndo, "%s FEC ", - (u2.u8 & IEEE80211_RADIOTAP_MCS_FEC_LDPC) ? + (flags & IEEE80211_RADIOTAP_MCS_FEC_LDPC) ? "LDPC" : "BCC")); } - if (u.u8 & IEEE80211_RADIOTAP_MCS_STBC_KNOWN) { + if (known & IEEE80211_RADIOTAP_MCS_STBC_KNOWN) { ND_PRINT((ndo, "RX-STBC%u ", - (u2.u8 & IEEE80211_RADIOTAP_MCS_STBC_MASK) >> IEEE80211_RADIOTAP_MCS_STBC_SHIFT)); + (flags & IEEE80211_RADIOTAP_MCS_STBC_MASK) >> IEEE80211_RADIOTAP_MCS_STBC_SHIFT)); } - break; } - case IEEE80211_RADIOTAP_AMPDU_STATUS: + + case IEEE80211_RADIOTAP_AMPDU_STATUS: { + uint32_t reference_num; + uint16_t flags; + uint8_t delim_crc; + uint8_t reserved; + + rc = cpack_uint32(s, &reference_num); + if (rc != 0) + goto trunc; + rc = cpack_uint16(s, &flags); + if (rc != 0) + goto trunc; + rc = cpack_uint8(s, &delim_crc); + if (rc != 0) + goto trunc; + rc = cpack_uint8(s, &reserved); + if (rc != 0) + goto trunc; + /* Do nothing for now */ break; + } + case IEEE80211_RADIOTAP_VHT: { + uint16_t known; + uint8_t flags; + uint8_t bandwidth; + uint8_t mcs_nss[4]; + uint8_t coding; + uint8_t group_id; + uint16_t partial_aid; static const char *vht_bandwidth[32] = { "20 MHz", "40 MHz", @@ -2953,7 +2953,7 @@ print_radiotap_field(netdissect_options *ndo, "160 MHz (ULL)", "160 MHz (ULU)", "160 MHz (UUL)", - "160 MHz (UUU)" + "160 MHz (UUU)", "unknown (26)", "unknown (27)", "unknown (28)", @@ -2962,6 +2962,29 @@ print_radiotap_field(netdissect_options *ndo, "unknown (31)" }; + rc = cpack_uint16(s, &known); + if (rc != 0) + goto trunc; + rc = cpack_uint8(s, &flags); + if (rc != 0) + goto trunc; + rc = cpack_uint8(s, &bandwidth); + if (rc != 0) + goto trunc; + for (i = 0; i < 4; i++) { + rc = cpack_uint8(s, &mcs_nss[i]); + if (rc != 0) + goto trunc; + } + rc = cpack_uint8(s, &coding); + if (rc != 0) + goto trunc; + rc = cpack_uint8(s, &group_id); + if (rc != 0) + goto trunc; + rc = cpack_uint16(s, &partial_aid); + if (rc != 0) + goto trunc; for (i = 0; i < 4; i++) { u_int nss, mcs; nss = mcs_nss[i] & IEEE80211_RADIOTAP_VHT_NSS_MASK; @@ -2972,49 +2995,107 @@ print_radiotap_field(netdissect_options *ndo, ND_PRINT((ndo, "User %u MCS %u ", i, mcs)); ND_PRINT((ndo, "%s FEC ", - (u4.u8 & (IEEE80211_RADIOTAP_CODING_LDPC_USERn << i)) ? + (coding & (IEEE80211_RADIOTAP_CODING_LDPC_USERn << i)) ? "LDPC" : "BCC")); } - if (u.u16 & IEEE80211_RADIOTAP_VHT_BANDWIDTH_KNOWN) { + if (known & IEEE80211_RADIOTAP_VHT_BANDWIDTH_KNOWN) { ND_PRINT((ndo, "%s ", - vht_bandwidth[u3.u8 & IEEE80211_RADIOTAP_VHT_BANDWIDTH_MASK])); + vht_bandwidth[bandwidth & IEEE80211_RADIOTAP_VHT_BANDWIDTH_MASK])); } - if (u.u16 & IEEE80211_RADIOTAP_VHT_GUARD_INTERVAL_KNOWN) { + if (known & IEEE80211_RADIOTAP_VHT_GUARD_INTERVAL_KNOWN) { ND_PRINT((ndo, "%s GI ", - (u2.u8 & IEEE80211_RADIOTAP_VHT_SHORT_GI) ? + (flags & IEEE80211_RADIOTAP_VHT_SHORT_GI) ? "short" : "long")); } break; } + + default: + /* this bit indicates a field whose + * size we do not know, so we cannot + * proceed. Just print the bit number. + */ + ND_PRINT((ndo, "[bit %u] ", bit)); + return -1; } + return 0; + +trunc: + ND_PRINT((ndo, "%s", tstr)); + return rc; } -static u_int -ieee802_11_radio_print(netdissect_options *ndo, - const u_char *p, u_int length, u_int caplen) + +static int +print_in_radiotap_namespace(netdissect_options *ndo, + struct cpack_state *s, uint8_t *flags, + uint32_t presentflags, int bit0) { #define BITNO_32(x) (((x) >> 16) ? 16 + BITNO_16((x) >> 16) : BITNO_16((x))) #define BITNO_16(x) (((x) >> 8) ? 8 + BITNO_8((x) >> 8) : BITNO_8((x))) #define BITNO_8(x) (((x) >> 4) ? 4 + BITNO_4((x) >> 4) : BITNO_4((x))) #define BITNO_4(x) (((x) >> 2) ? 2 + BITNO_2((x) >> 2) : BITNO_2((x))) #define BITNO_2(x) (((x) & 2) ? 1 : 0) + uint32_t present, next_present; + int bitno; + enum ieee80211_radiotap_type bit; + int rc; + + for (present = presentflags; present; present = next_present) { + /* + * Clear the least significant bit that is set. + */ + next_present = present & (present - 1); + + /* + * Get the bit number, within this presence word, + * of the remaining least significant bit that + * is set. + */ + bitno = BITNO_32(present ^ next_present); + + /* + * Stop if this is one of the "same meaning + * in all presence flags" bits. + */ + if (bitno >= IEEE80211_RADIOTAP_NAMESPACE) + break; + + /* + * Get the radiotap bit number of that bit. + */ + bit = (enum ieee80211_radiotap_type)(bit0 + bitno); + + rc = print_radiotap_field(ndo, s, bit, flags, presentflags); + if (rc != 0) + return rc; + } + + return 0; +} + +u_int +ieee802_11_radio_print(netdissect_options *ndo, + const u_char *p, u_int length, u_int caplen) +{ #define BIT(n) (1U << n) #define IS_EXTENDED(__p) \ (EXTRACT_LE_32BITS(__p) & BIT(IEEE80211_RADIOTAP_EXT)) != 0 struct cpack_state cpacker; const struct ieee80211_radiotap_header *hdr; - uint32_t present, next_present; - uint32_t presentflags = 0; + uint32_t presentflags; const uint32_t *presentp, *last_presentp; - enum ieee80211_radiotap_type bit; + int vendor_namespace; + uint8_t vendor_oui[3]; + uint8_t vendor_subnamespace; + uint16_t skip_length; int bit0; u_int len; uint8_t flags; int pad; u_int fcslen; - struct radiotap_state state; if (caplen < sizeof(*hdr)) { ND_PRINT((ndo, "%s", tstr)); @@ -3024,7 +3105,19 @@ ieee802_11_radio_print(netdissect_options *ndo, hdr = (const struct ieee80211_radiotap_header *)p; len = EXTRACT_LE_16BITS(&hdr->it_len); + if (len < sizeof(*hdr)) { + /* + * The length is the length of the entire header, so + * it must be as large as the fixed-length part of + * the header. + */ + ND_PRINT((ndo, "%s", tstr)); + return caplen; + } + /* + * If we don't have the entire radiotap header, just give up. + */ if (caplen < len) { ND_PRINT((ndo, "%s", tstr)); return caplen; @@ -3032,45 +3125,144 @@ ieee802_11_radio_print(netdissect_options *ndo, cpack_init(&cpacker, (const uint8_t *)hdr, len); /* align against header start */ cpack_advance(&cpacker, sizeof(*hdr)); /* includes the 1st bitmap */ for (last_presentp = &hdr->it_present; - IS_EXTENDED(last_presentp) && - (const u_char*)(last_presentp + 1) <= p + len; + (const u_char*)(last_presentp + 1) <= p + len && + IS_EXTENDED(last_presentp); last_presentp++) cpack_advance(&cpacker, sizeof(hdr->it_present)); /* more bitmaps */ /* are there more bitmap extensions than bytes in header? */ - if (IS_EXTENDED(last_presentp)) { + if ((const u_char*)(last_presentp + 1) > p + len) { ND_PRINT((ndo, "%s", tstr)); return caplen; } + /* + * Start out at the beginning of the default radiotap namespace. + */ + bit0 = 0; + vendor_namespace = 0; + memset(vendor_oui, 0, 3); + vendor_subnamespace = 0; + skip_length = 0; /* Assume no flags */ flags = 0; /* Assume no Atheros padding between 802.11 header and body */ pad = 0; /* Assume no FCS at end of frame */ fcslen = 0; - for (bit0 = 0, presentp = &hdr->it_present; presentp <= last_presentp; - presentp++, bit0 += 32) { + for (presentp = &hdr->it_present; presentp <= last_presentp; + presentp++) { presentflags = EXTRACT_LE_32BITS(presentp); - /* Clear state. */ - memset(&state, 0, sizeof(state)); + /* + * If this is a vendor namespace, we don't handle it. + */ + if (vendor_namespace) { + /* + * Skip past the stuff we don't understand. + * If we add support for any vendor namespaces, + * it'd be added here; use vendor_oui and + * vendor_subnamespace to interpret the fields. + */ + if (cpack_advance(&cpacker, skip_length) != 0) { + /* + * Ran out of space in the packet. + */ + break; + } + + /* + * We've skipped it all; nothing more to + * skip. + */ + skip_length = 0; + } else { + if (print_in_radiotap_namespace(ndo, &cpacker, + &flags, presentflags, bit0) != 0) { + /* + * Fatal error - can't process anything + * more in the radiotap header. + */ + break; + } + } + + /* + * Handle the namespace switch bits; we've already handled + * the extension bit in all but the last word above. + */ + switch (presentflags & + (BIT(IEEE80211_RADIOTAP_NAMESPACE)|BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE))) { - for (present = EXTRACT_LE_32BITS(presentp); present; - present = next_present) { - /* clear the least significant bit that is set */ - next_present = present & (present - 1); + case 0: + /* + * We're not changing namespaces. + * advance to the next 32 bits in the current + * namespace. + */ + bit0 += 32; + break; - /* extract the least significant bit that is set */ - bit = (enum ieee80211_radiotap_type) - (bit0 + BITNO_32(present ^ next_present)); + case BIT(IEEE80211_RADIOTAP_NAMESPACE): + /* + * We're switching to the radiotap namespace. + * Reset the presence-bitmap index to 0, and + * reset the namespace to the default radiotap + * namespace. + */ + bit0 = 0; + vendor_namespace = 0; + memset(vendor_oui, 0, 3); + vendor_subnamespace = 0; + skip_length = 0; + break; - if (print_radiotap_field(ndo, &cpacker, bit, &flags, &state, presentflags) != 0) - goto out; + case BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE): + /* + * We're switching to a vendor namespace. + * Reset the presence-bitmap index to 0, + * note that we're in a vendor namespace, + * and fetch the fields of the Vendor Namespace + * item. + */ + bit0 = 0; + vendor_namespace = 1; + if ((cpack_align_and_reserve(&cpacker, 2)) == NULL) { + ND_PRINT((ndo, "%s", tstr)); + break; + } + if (cpack_uint8(&cpacker, &vendor_oui[0]) != 0) { + ND_PRINT((ndo, "%s", tstr)); + break; + } + if (cpack_uint8(&cpacker, &vendor_oui[1]) != 0) { + ND_PRINT((ndo, "%s", tstr)); + break; + } + if (cpack_uint8(&cpacker, &vendor_oui[2]) != 0) { + ND_PRINT((ndo, "%s", tstr)); + break; + } + if (cpack_uint8(&cpacker, &vendor_subnamespace) != 0) { + ND_PRINT((ndo, "%s", tstr)); + break; + } + if (cpack_uint16(&cpacker, &skip_length) != 0) { + ND_PRINT((ndo, "%s", tstr)); + break; + } + break; + + default: + /* + * Illegal combination. The behavior in this + * case is undefined by the radiotap spec; we + * just ignore both bits. + */ + break; } } -out: if (flags & IEEE80211_RADIOTAP_F_DATAPAD) pad = 1; /* Atheros padding */ if (flags & IEEE80211_RADIOTAP_F_FCS)