X-Git-Url: https://git.tcpdump.org/tcpdump/blobdiff_plain/1d595056836329648019d35713df16e58426671f..ebb51ef0e725737ca8c1d618da6527a58660d2bf:/print-802_11.c diff --git a/print-802_11.c b/print-802_11.c index 4014746a..1bbe47ac 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 @@ -2037,7 +2039,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; @@ -2089,10 +2091,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 +2118,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 @@ -2209,12 +2213,12 @@ struct ieee80211_radiotap_header { * new fields does not count. */ uint8_t it_pad; - uint16_t it_len; /* length of the whole + uint16_t it_len; /* length of the whole * header in bytes, including * it_version, it_pad, * it_len, and data fields. */ - uint32_t it_present; /* A bitmap telling which + uint32_t it_present; /* A bitmap telling which * fields are present. Set bit 31 * (0x80000000) to extend the * bitmap by another 32 bits. @@ -2330,6 +2334,15 @@ struct ieee80211_radiotap_header { * by bitset of flag values, followed by the MCS rate index as * in IEEE 802.11n. * + * + * IEEE80211_RADIOTAP_AMPDU_STATUS u32, u16, u8, u8 unitless + * + * Contains the AMPDU information for the subframe. + * + * IEEE80211_RADIOTAP_VHT u16, u8, u8, u8[4], u8, u8, u16 + * + * Contains VHT information about this frame. + * * IEEE80211_RADIOTAP_VENDOR_NAMESPACE * uint8_t OUI[3] * uint8_t subspace @@ -2360,6 +2373,8 @@ enum ieee80211_radiotap_type { /* NB: gap for netbsd definitions */ IEEE80211_RADIOTAP_XCHANNEL = 18, IEEE80211_RADIOTAP_MCS = 19, + IEEE80211_RADIOTAP_AMPDU_STATUS = 20, + IEEE80211_RADIOTAP_VHT = 21, IEEE80211_RADIOTAP_NAMESPACE = 29, IEEE80211_RADIOTAP_VENDOR_NAMESPACE = 30, IEEE80211_RADIOTAP_EXT = 31 @@ -2427,6 +2442,8 @@ enum ieee80211_radiotap_type { #define IEEE80211_RADIOTAP_MCS_HT_FORMAT_KNOWN 0x08 #define IEEE80211_RADIOTAP_MCS_FEC_TYPE_KNOWN 0x10 #define IEEE80211_RADIOTAP_MCS_STBC_KNOWN 0x20 +#define IEEE80211_RADIOTAP_MCS_NESS_KNOWN 0x40 +#define IEEE80211_RADIOTAP_MCS_NESS_BIT_1 0x80 /* For IEEE80211_RADIOTAP_MCS flags */ #define IEEE80211_RADIOTAP_MCS_BANDWIDTH_MASK 0x03 @@ -2442,15 +2459,42 @@ enum ieee80211_radiotap_type { #define IEEE80211_RADIOTAP_MCS_STBC_2 2 #define IEEE80211_RADIOTAP_MCS_STBC_3 3 #define IEEE80211_RADIOTAP_MCS_STBC_SHIFT 5 - -/* Radiotap state */ -/* This is used to save state when parsing/processing parameters */ -struct radiotap_state -{ - uint32_t present; - - uint8_t rate; -}; +#define IEEE80211_RADIOTAP_MCS_NESS_BIT_0 0x80 + +/* For IEEE80211_RADIOTAP_AMPDU_STATUS */ +#define IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN 0x0001 +#define IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN 0x0002 +#define IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN 0x0004 +#define IEEE80211_RADIOTAP_AMPDU_IS_LAST 0x0008 +#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR 0x0010 +#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN 0x0020 + +/* For IEEE80211_RADIOTAP_VHT known */ +#define IEEE80211_RADIOTAP_VHT_STBC_KNOWN 0x0001 +#define IEEE80211_RADIOTAP_VHT_TXOP_PS_NA_KNOWN 0x0002 +#define IEEE80211_RADIOTAP_VHT_GUARD_INTERVAL_KNOWN 0x0004 +#define IEEE80211_RADIOTAP_VHT_SGI_NSYM_DIS_KNOWN 0x0008 +#define IEEE80211_RADIOTAP_VHT_LDPC_EXTRA_OFDM_SYM_KNOWN 0x0010 +#define IEEE80211_RADIOTAP_VHT_BEAMFORMED_KNOWN 0x0020 +#define IEEE80211_RADIOTAP_VHT_BANDWIDTH_KNOWN 0x0040 +#define IEEE80211_RADIOTAP_VHT_GROUP_ID_KNOWN 0x0080 +#define IEEE80211_RADIOTAP_VHT_PARTIAL_AID_KNOWN 0x0100 + +/* For IEEE80211_RADIOTAP_VHT flags */ +#define IEEE80211_RADIOTAP_VHT_STBC 0x01 +#define IEEE80211_RADIOTAP_VHT_TXOP_PS_NA 0x02 +#define IEEE80211_RADIOTAP_VHT_SHORT_GI 0x04 +#define IEEE80211_RADIOTAP_VHT_SGI_NSYM_M10_9 0x08 +#define IEEE80211_RADIOTAP_VHT_LDPC_EXTRA_OFDM_SYM 0x10 +#define IEEE80211_RADIOTAP_VHT_BEAMFORMED 0x20 + +#define IEEE80211_RADIOTAP_VHT_BANDWIDTH_MASK 0x1f + +#define IEEE80211_RADIOTAP_VHT_NSS_MASK 0x0f +#define IEEE80211_RADIOTAP_VHT_MCS_MASK 0xf0 +#define IEEE80211_RADIOTAP_VHT_MCS_SHIFT 4 + +#define IEEE80211_RADIOTAP_CODING_LDPC_USERn 0x01 #define IEEE80211_CHAN_FHSS \ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK) @@ -2478,30 +2522,41 @@ struct radiotap_state static void print_chaninfo(netdissect_options *ndo, - int freq, int flags) + uint16_t freq, int flags, int presentflags) { ND_PRINT((ndo, "%u MHz", freq)); - if (IS_CHAN_FHSS(flags)) - ND_PRINT((ndo, " FHSS")); - if (IS_CHAN_A(flags)) { - if (flags & IEEE80211_CHAN_HALF) - ND_PRINT((ndo, " 11a/10Mhz")); - else if (flags & IEEE80211_CHAN_QUARTER) - ND_PRINT((ndo, " 11a/5Mhz")); - else - ND_PRINT((ndo, " 11a")); + if (presentflags & (1 << IEEE80211_RADIOTAP_MCS)) { + /* + * We have the MCS field, so this is 11n, regardless + * of what the channel flags say. + */ + ND_PRINT((ndo, " 11n")); + } else { + if (IS_CHAN_FHSS(flags)) + ND_PRINT((ndo, " FHSS")); + if (IS_CHAN_A(flags)) { + if (flags & IEEE80211_CHAN_HALF) + ND_PRINT((ndo, " 11a/10Mhz")); + else if (flags & IEEE80211_CHAN_QUARTER) + ND_PRINT((ndo, " 11a/5Mhz")); + else + ND_PRINT((ndo, " 11a")); + } + if (IS_CHAN_ANYG(flags)) { + if (flags & IEEE80211_CHAN_HALF) + ND_PRINT((ndo, " 11g/10Mhz")); + else if (flags & IEEE80211_CHAN_QUARTER) + ND_PRINT((ndo, " 11g/5Mhz")); + else + ND_PRINT((ndo, " 11g")); + } else if (IS_CHAN_B(flags)) + ND_PRINT((ndo, " 11b")); + if (flags & IEEE80211_CHAN_TURBO) + ND_PRINT((ndo, " Turbo")); } - if (IS_CHAN_ANYG(flags)) { - if (flags & IEEE80211_CHAN_HALF) - ND_PRINT((ndo, " 11g/10Mhz")); - else if (flags & IEEE80211_CHAN_QUARTER) - ND_PRINT((ndo, " 11g/5Mhz")); - else - ND_PRINT((ndo, " 11g")); - } else if (IS_CHAN_B(flags)) - ND_PRINT((ndo, " 11b")); - if (flags & IEEE80211_CHAN_TURBO) - ND_PRINT((ndo, " Turbo")); + /* + * These apply to 11n. + */ if (flags & IEEE80211_CHAN_HT20) ND_PRINT((ndo, " ht/20")); else if (flags & IEEE80211_CHAN_HT40D) @@ -2513,146 +2568,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; + 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); + case IEEE80211_RADIOTAP_TSFT: { + uint64_t tsft; + + rc = cpack_uint64(s, &tsft); if (rc != 0) - break; - rc = cpack_uint8(s, &u3.u8); + goto trunc; + ND_PRINT((ndo, "%" PRIu64 "us tsft ", tsft)); 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; } - 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]); - if (rc != 0) - break; - rc = cpack_uint8(s, &subspace); - if (rc != 0) - break; - rc = cpack_uint16(s, &length); - if (rc != 0) - break; + case IEEE80211_RADIOTAP_FLAGS: { + uint8_t flagsval; - /* Skip up to length */ - s->c_next += length; + rc = cpack_uint8(s, &flagsval); + if (rc != 0) + 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); - 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 @@ -2674,7 +2633,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 @@ -2691,60 +2650,173 @@ 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); + } + + 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: { - static const char *bandwidth[4] = { + uint8_t known; + uint8_t flags; + uint8_t mcs_index; + static const char *ht_bandwidth[4] = { "20 MHz", "40 MHz", "20 MHz (L)", @@ -2752,15 +2824,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 @@ -2768,9 +2849,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 @@ -2790,70 +2871,240 @@ 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 ", - 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) ? - "short" : "lon")); + (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: { + 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", + "20 MHz (L)", + "20 MHz (U)", + "80 MHz", + "80 MHz (L)", + "80 MHz (U)", + "80 MHz (LL)", + "80 MHz (LU)", + "80 MHz (UL)", + "80 MHz (UU)", + "160 MHz", + "160 MHz (L)", + "160 MHz (U)", + "160 MHz (LL)", + "160 MHz (LU)", + "160 MHz (UL)", + "160 MHz (UU)", + "160 MHz (LLL)", + "160 MHz (LLU)", + "160 MHz (LUL)", + "160 MHz (UUU)", + "160 MHz (ULL)", + "160 MHz (ULU)", + "160 MHz (UUL)", + "160 MHz (UUU)", + "unknown (26)", + "unknown (27)", + "unknown (28)", + "unknown (29)", + "unknown (30)", + "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; + mcs = (mcs_nss[i] & IEEE80211_RADIOTAP_VHT_MCS_MASK) >> IEEE80211_RADIOTAP_VHT_MCS_SHIFT; + + if (nss == 0) + continue; + + ND_PRINT((ndo, "User %u MCS %u ", i, mcs)); + ND_PRINT((ndo, "%s FEC ", + (coding & (IEEE80211_RADIOTAP_CODING_LDPC_USERn << i)) ? + "LDPC" : "BCC")); + } + if (known & IEEE80211_RADIOTAP_VHT_BANDWIDTH_KNOWN) { + ND_PRINT((ndo, "%s ", + vht_bandwidth[bandwidth & IEEE80211_RADIOTAP_VHT_BANDWIDTH_MASK])); + } + if (known & IEEE80211_RADIOTAP_VHT_GUARD_INTERVAL_KNOWN) { + ND_PRINT((ndo, "%s 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; +} + +static 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)); @@ -2864,6 +3115,9 @@ ieee802_11_radio_print(netdissect_options *ndo, len = EXTRACT_LE_16BITS(&hdr->it_len); + /* + * If we don't have the entire radiotap header, just give up. + */ if (caplen < len) { ND_PRINT((ndo, "%s", tstr)); return caplen; @@ -2871,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; + + 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; - /* extract the least significant bit that is set */ - bit = (enum ieee80211_radiotap_type) - (bit0 + BITNO_32(present ^ next_present)); + 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; - if (print_radiotap_field(ndo, &cpacker, bit, &flags, &state, presentflags) != 0) - goto out; + 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)