Exemplo n.º 1
0
/*
 * Add an Ethernet trailer - which, for some captures, might be the FCS
 * rather than a pad-to-60-bytes trailer.
 *
 * If fcs_len is 0, we assume the frame has no FCS; if it's 4, we assume
 * it has an FCS; if it's anything else (such as -1, which means "maybe
 * it does, maybe it doesn't"), we try to infer whether it has an FCS.
 */
void
add_ethernet_trailer(packet_info *pinfo, proto_tree *tree, proto_tree *fh_tree,
    int trailer_id, tvbuff_t *tvb, tvbuff_t *trailer_tvb, int fcs_len)
{
  /* If there're some bytes left over, it could be a combination of:
     - padding to meet the minimum 64 byte frame length
     - an FCS, if present (if fcs_len is 0, we know it's not present;
       if fcs_len is 4, we know it's present; if fcs_len is -1, we
       need some heuristics to determine whether it's present)
     - information inserted by TAPs or other network monitoring equipment.
    
     If we don't know whether the FCS is present, then, if we don't have a
     network monitoring trailer, and if the Ethernet frame was claimed to
     have had 64 or more bytes - i.e., it was at least an FCS worth of data
     longer than the minimum payload size - we could assume the last 4 bytes
     of the trailer are an FCS. */
  proto_item *item;
  proto_tree *checksum_tree;

  if (trailer_tvb) {
    guint trailer_length, trailer_reported_length;
    guint padding_length = 0;
    gboolean has_fcs = FALSE;
    tvbuff_t *real_trailer_tvb;

    trailer_length = tvb_length(trailer_tvb);
    trailer_reported_length = tvb_reported_length(trailer_tvb);

    /* There can not have been padding when the length of the frame (including the 
       trailer) is less than 60 bytes. */
    if (eth_assume_padding && pinfo->fd->pkt_len>=60) {
        /* Calculate the amount of padding needed for a minimum sized frame */
        if ( (pinfo->fd->pkt_len - trailer_reported_length) < 60 )
            padding_length = 60 - (pinfo->fd->pkt_len - trailer_reported_length);

        /* Add the padding to the tree, unless it should be treated as 
           part of the trailer and therefor be handed over to (one of) 
           the ethernet-trailer dissectors */
        if (padding_length > 0) {
            tvb_ensure_bytes_exist(tvb, 0, padding_length);
            proto_tree_add_item(fh_tree, hf_eth_padding, trailer_tvb, 0,
                padding_length, ENC_NA);
            trailer_length -= padding_length;
            trailer_reported_length -= padding_length;
        }
    }

    if (fcs_len != 0) {
      /* If fcs_len is 4, we assume we definitely have an FCS.
         Otherwise, then, if the frame is big enough that, if we
         have a trailer, it probably inclues an FCS, and we have
         enough space in the trailer for the FCS, we assume we
         have an FCS.

         "Big enough" means 64 bytes or more; any frame that big
         needs no trailer, as there's no need to pad an Ethernet
         packet past 60 bytes.

         The trailer must be at least 4 bytes long to have enough
         space for an FCS. */

      if (fcs_len == 4 || (tvb_reported_length(tvb) >= 64 &&
        trailer_reported_length >= 4)) {
        /* Either we know we have an FCS, or we believe we have an FCS. */
        if (trailer_length < trailer_reported_length) {
          /* The packet is claimed to have enough data for a 4-byte FCS,
             but we didn't capture all of the packet.
             Slice off the 4-byte FCS from the reported length, and
             trim the captured length so it's no more than the reported
             length; that will slice off what of the FCS, if any, is
             in the captured packet. */
          trailer_reported_length -= 4;
          if (trailer_length > trailer_reported_length)
            trailer_length = trailer_reported_length;
          has_fcs = TRUE;
        } else {
          /* We captured all of the packet, including what appears to
             be a 4-byte FCS.  Slice it off. */
          trailer_length -= 4;
          trailer_reported_length -= 4;
          has_fcs = TRUE;
        }
      }
    }

    /* Create a new tvb without the padding and/or the (assumed) fcs */
    if (fcs_len==4) 
      real_trailer_tvb = tvb_new_subset(trailer_tvb, padding_length,
                                trailer_length, trailer_reported_length);
    else
      real_trailer_tvb = tvb_new_subset_remaining(trailer_tvb, padding_length);

    /* Call all ethernet trailer dissectors to dissect the trailer if
       we actually have a trailer.  */
    if (tvb_reported_length(real_trailer_tvb) != 0) {
      if (dissector_try_heuristic(eth_trailer_subdissector_list, 
                                   real_trailer_tvb, pinfo, tree, NULL) ) {
        /* If we're not sure that there is a FCS, all trailer data
           has been given to the ethernet-trailer dissector, so 
           stop dissecting here */
        if (fcs_len!=4) 
            return;
      } else {
        /* No luck with the trailer dissectors, so just display the
           extra bytes as general trailer */
        if (trailer_length != 0) {
          tvb_ensure_bytes_exist(tvb, 0, trailer_length);
          proto_tree_add_item(fh_tree, trailer_id, real_trailer_tvb, 0,
            trailer_length, ENC_NA);
        }
      }
    }

    if (has_fcs) {
      guint32 sent_fcs = tvb_get_ntohl(trailer_tvb, padding_length+trailer_length);
      if(eth_check_fcs){
        guint32 fcs = crc32_802_tvb(tvb, tvb_length(tvb) - 4);
        if (fcs == sent_fcs) {
          item = proto_tree_add_uint_format(fh_tree, hf_eth_fcs, trailer_tvb,
                                            padding_length+trailer_length, 4, sent_fcs,
                                            "Frame check sequence: 0x%08x [correct]", sent_fcs);
          checksum_tree = proto_item_add_subtree(item, ett_eth_fcs);
          item = proto_tree_add_boolean(checksum_tree, hf_eth_fcs_good, trailer_tvb,
                                        padding_length+trailer_length, 4, TRUE);
          PROTO_ITEM_SET_GENERATED(item);
          item = proto_tree_add_boolean(checksum_tree, hf_eth_fcs_bad, trailer_tvb,
                                        padding_length+trailer_length, 4, FALSE);
          PROTO_ITEM_SET_GENERATED(item);
        } else {
          item = proto_tree_add_uint_format(fh_tree, hf_eth_fcs, trailer_tvb,
                                            padding_length+trailer_length, 4, sent_fcs,
                                            "Frame check sequence: 0x%08x [incorrect, should be 0x%08x]",
                                            sent_fcs, fcs);
          checksum_tree = proto_item_add_subtree(item, ett_eth_fcs);
          item = proto_tree_add_boolean(checksum_tree, hf_eth_fcs_good, trailer_tvb,
                                        padding_length+trailer_length, 4, FALSE);
          PROTO_ITEM_SET_GENERATED(item);
          item = proto_tree_add_boolean(checksum_tree, hf_eth_fcs_bad, trailer_tvb,
                                        padding_length+trailer_length, 4, TRUE);
          PROTO_ITEM_SET_GENERATED(item);
          expert_add_info_format(pinfo, item, PI_CHECKSUM, PI_ERROR, "Bad checksum");
          col_append_str(pinfo->cinfo, COL_INFO, " [ETHERNET FRAME CHECK SEQUENCE INCORRECT]");
        }
      }else{
        item = proto_tree_add_uint_format(fh_tree, hf_eth_fcs, trailer_tvb,
                                          padding_length+trailer_length, 4, sent_fcs,
                                          "Frame check sequence: 0x%08x [validiation disabled]", sent_fcs);
        checksum_tree = proto_item_add_subtree(item, ett_eth_fcs);
        item = proto_tree_add_boolean(checksum_tree, hf_eth_fcs_good, trailer_tvb,
                                      padding_length+trailer_length, 4, FALSE);
        PROTO_ITEM_SET_GENERATED(item);
        item = proto_tree_add_boolean(checksum_tree, hf_eth_fcs_bad, trailer_tvb,
                                      padding_length+trailer_length, 4, FALSE);
        PROTO_ITEM_SET_GENERATED(item);
      }
      trailer_length += 4;
    }
    proto_tree_set_appendix(fh_tree, tvb, tvb_length(tvb) - padding_length - trailer_length, padding_length + trailer_length);
  }
}
static void
dissect_radiotap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    proto_tree *radiotap_tree = NULL;
    proto_tree *pt, *present_tree = NULL;
    proto_tree *ft;
    proto_item *ti = NULL, *hidden_item;
    int align_offset, offset;
    tvbuff_t *next_tvb;
    guint32 version;
    guint length, length_remaining;
    guint32 rate, freq, flags;
    gint8 dbm;
    guint8 db, rflags;
    guint32 present, next_present;
    int bit;
    /* backward compat with bit 14 == fcs in header */
    proto_item *hdr_fcs_ti = NULL;
    int hdr_fcs_offset = 0;
    guint32 sent_fcs = 0;
    guint32 calc_fcs;

    struct _radiotap_info *radiotap_info;
    static struct _radiotap_info rtp_info_arr[1];
    
    radiotap_info = &rtp_info_arr[0];

    col_set_str(pinfo->cinfo, COL_PROTOCOL, "WLAN");
    col_clear(pinfo->cinfo, COL_INFO);
    offset = 0;

    version = tvb_get_guint8(tvb, offset);
    length = tvb_get_letohs(tvb, offset+2);
    present = tvb_get_letohl(tvb, offset+4);
    
    radiotap_info->radiotap_length = length;

    col_add_fstr(pinfo->cinfo, COL_INFO, "Radiotap Capture v%u, Length %u",
		version, length);

    /* Dissect the packet */
    if (tree) {
	ti = proto_tree_add_protocol_format(tree, proto_radiotap,
		tvb, 0, length, "Radiotap Header v%u, Length %u", version, length);
	radiotap_tree = proto_item_add_subtree(ti, ett_radiotap);
	proto_tree_add_uint(radiotap_tree, hf_radiotap_version,
		tvb, offset, 1, version);
	proto_tree_add_item(radiotap_tree, hf_radiotap_pad,
		tvb, offset + 1, 1, FALSE);
	ti = proto_tree_add_uint(radiotap_tree, hf_radiotap_length,
		tvb, offset + 2, 2, length);
    }
    length_remaining = length;

    /*
     * FIXME: This only works if there is exactly 1 it_present
     *        field in the header
     */
    if (length_remaining < RADIOTAP_MIN_HEADER_LEN) {
	/*
	 * Radiotap header is shorter than the fixed-length portion
	 * plus one "present" bitset.
	 */
	if (tree)
	    proto_item_append_text(ti, " (bogus - minimum length is 8)");
	return;
    }
    /* Subtree for the "present flags" bitfield. */
    if (tree) {
	pt = proto_tree_add_uint(radiotap_tree, hf_radiotap_present,
	    tvb, offset + 4, 4, present);
	present_tree = proto_item_add_subtree(pt, ett_radiotap_present);

	proto_tree_add_item(present_tree, hf_radiotap_present_tsft,
	    tvb, offset + 4, 4, TRUE);
	proto_tree_add_item(present_tree, hf_radiotap_present_flags,
	    tvb, offset + 4, 4, TRUE);
	proto_tree_add_item(present_tree, hf_radiotap_present_rate,
	    tvb, offset + 4, 4, TRUE);
	proto_tree_add_item(present_tree, hf_radiotap_present_channel,
	    tvb, offset + 4, 4, TRUE);
	proto_tree_add_item(present_tree, hf_radiotap_present_fhss,
	    tvb, offset + 4, 4, TRUE);
	proto_tree_add_item(present_tree, hf_radiotap_present_dbm_antsignal,
	    tvb, offset + 4, 4, TRUE);
	proto_tree_add_item(present_tree, hf_radiotap_present_dbm_antnoise,
	    tvb, offset + 4, 4, TRUE);
	proto_tree_add_item(present_tree, hf_radiotap_present_lock_quality,
	    tvb, offset + 4, 4, TRUE);
	proto_tree_add_item(present_tree, hf_radiotap_present_tx_attenuation,
	    tvb, offset + 4, 4, TRUE);
	proto_tree_add_item(present_tree, hf_radiotap_present_db_tx_attenuation,
	    tvb, offset + 4, 4, TRUE);
	proto_tree_add_item(present_tree, hf_radiotap_present_dbm_tx_attenuation,
	    tvb, offset + 4, 4, TRUE);
	proto_tree_add_item(present_tree, hf_radiotap_present_antenna,
	    tvb, offset + 4, 4, TRUE);
	proto_tree_add_item(present_tree, hf_radiotap_present_db_antsignal,
	    tvb, offset + 4, 4, TRUE);
	proto_tree_add_item(present_tree, hf_radiotap_present_db_antnoise,
	    tvb, offset + 4, 4, TRUE);
	if (radiotap_bit14_fcs) {
		proto_tree_add_item(present_tree, hf_radiotap_present_hdrfcs,
			tvb, offset + 4, 4, TRUE);
	} else {
		proto_tree_add_item(present_tree, hf_radiotap_present_rxflags,
			tvb, offset + 4, 4, TRUE);
	}
	proto_tree_add_item(present_tree, hf_radiotap_present_xchannel,
	    tvb, offset + 4, 4, TRUE);
	proto_tree_add_item(present_tree, hf_radiotap_present_ext,
	    tvb, offset + 4, 4, TRUE);
    }
    offset += RADIOTAP_MIN_HEADER_LEN;
    length_remaining -= RADIOTAP_MIN_HEADER_LEN;

    rflags = 0;
    for (; present; present = next_present) {
	/* clear the least significant bit that is set */
	next_present = present & (present - 1);

	/* extract the least significant bit that is set */
	bit = BITNO_32(present ^ next_present);

	switch (bit) {

	case IEEE80211_RADIOTAP_TSFT:
	    align_offset = ALIGN_OFFSET(offset, 8);
	    offset += align_offset;
	    length_remaining -= align_offset;
	    if (length_remaining < 8)
		break;
            radiotap_info->tsft=tvb_get_letoh64(tvb, offset);
	    if (tree) {
		proto_tree_add_uint64(radiotap_tree, hf_radiotap_mactime,
				tvb, offset, 8,radiotap_info->tsft );
	    }
	    offset+=8;
	    length_remaining-=8;
	    break;

	case IEEE80211_RADIOTAP_FLAGS:
	{
	    proto_tree *flags_tree;
	    if (length_remaining < 1)
		break;
	    rflags = tvb_get_guint8(tvb, offset);
	    if (tree) {
		ft = proto_tree_add_item(radiotap_tree, hf_radiotap_flags,
					 tvb, offset, 1, FALSE);
		flags_tree = proto_item_add_subtree(ft, ett_radiotap_flags);

		proto_tree_add_item(flags_tree, hf_radiotap_flags_cfp,
			tvb, offset, 1, FALSE);
		proto_tree_add_item(flags_tree, hf_radiotap_flags_preamble,
			tvb, offset, 1, FALSE);
		proto_tree_add_item(flags_tree, hf_radiotap_flags_wep,
			tvb, offset, 1, FALSE);
		proto_tree_add_item(flags_tree, hf_radiotap_flags_frag,
			tvb, offset, 1, FALSE);
		proto_tree_add_item(flags_tree, hf_radiotap_flags_fcs,
			tvb, offset, 1, FALSE);
		proto_tree_add_item(flags_tree, hf_radiotap_flags_datapad,
			tvb, offset, 1, FALSE);
		proto_tree_add_item(flags_tree, hf_radiotap_flags_badfcs,
			tvb, offset, 1, FALSE);
		proto_tree_add_item(flags_tree, hf_radiotap_flags_shortgi,
			tvb, offset, 1, FALSE);
	    }
	    offset++;
	    length_remaining--;
	    break;
	}

	case IEEE80211_RADIOTAP_RATE:
	    if (length_remaining < 1)
		break;
	    rate = tvb_get_guint8(tvb, offset);
	    if (rate & 0x80) {
		/* XXX adjust by CW and short GI like other sniffers? */
		rate = ieee80211_htrates[rate & 0xf];
	    }
	    col_add_fstr(pinfo->cinfo, COL_TX_RATE, "%d.%d",
		rate / 2, rate & 1 ? 5 : 0);
	    if (tree) {
		proto_tree_add_float_format(radiotap_tree, hf_radiotap_datarate,
			tvb, offset, 1, (float)rate / 2,
			"Data Rate: %.1f Mb/s", (float)rate / 2);
	    }
	    offset++;
	    length_remaining--;
            radiotap_info->rate = rate;
	    break;

	case IEEE80211_RADIOTAP_CHANNEL:
	{
	    proto_item *it;
	    proto_tree *flags_tree;
	    gchar *chan_str;

	    align_offset = ALIGN_OFFSET(offset, 2);
	    offset += align_offset;
	    length_remaining -= align_offset;
	    if (length_remaining < 2)
		break;
	    if (tree) {
		freq = tvb_get_letohs(tvb, offset);
		flags = tvb_get_letohs(tvb, offset+2);
		chan_str = ieee80211_mhz_to_str(freq);
		col_add_fstr(pinfo->cinfo, COL_FREQ_CHAN, "%s", chan_str);
		proto_tree_add_uint_format(radiotap_tree, hf_radiotap_channel_frequency,
				tvb, offset, 2, freq,
				"Channel frequency: %s", chan_str);
		g_free(chan_str);
		/* We're already 2-byte aligned. */
		it = proto_tree_add_uint(radiotap_tree, hf_radiotap_channel_flags,
			tvb, offset+2, 2, flags);
		flags_tree = proto_item_add_subtree(it, ett_radiotap_channel_flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_channel_flags_turbo,
			tvb, offset+2, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_channel_flags_cck,
			tvb, offset+2, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_channel_flags_ofdm,
			tvb, offset+2, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_channel_flags_2ghz,
			tvb, offset+2, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_channel_flags_5ghz,
			tvb, offset+3, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_channel_flags_passive,
			tvb, offset+3, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_channel_flags_dynamic,
			tvb, offset+3, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_channel_flags_gfsk,
			tvb, offset+3, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_channel_flags_gsm,
			tvb, offset+3, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_channel_flags_sturbo,
			tvb, offset+3, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_channel_flags_half,
			tvb, offset+3, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_channel_flags_quarter,
			tvb, offset+3, 1, flags);
		radiotap_info->freq=freq;
		radiotap_info->flags=flags;
	    }
	    offset+=4 /* Channel + flags */;
	    length_remaining-=4;
	    break;
	}

	case IEEE80211_RADIOTAP_FHSS:
	    align_offset = ALIGN_OFFSET(offset, 2);
	    offset += align_offset;
	    length_remaining -= align_offset;
	    if (length_remaining < 2)
		break;
	    proto_tree_add_item(radiotap_tree, hf_radiotap_fhss_hopset,
		tvb, offset, 1, FALSE);
	    proto_tree_add_item(radiotap_tree, hf_radiotap_fhss_pattern,
		tvb, offset, 1, FALSE);
	    offset+=2;
	    length_remaining-=2;
	    break;

	case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:
	    if (length_remaining < 1)
		break;
	    dbm = (gint8) tvb_get_guint8(tvb, offset);
	    col_add_fstr(pinfo->cinfo, COL_RSSI, "%d dBm", dbm);
	    if (tree) {
		proto_tree_add_int_format(radiotap_tree,
					  hf_radiotap_dbm_antsignal,
					  tvb, offset, 1, dbm,
					  "SSI Signal: %d dBm", dbm);
	    }
	    offset++;
	    length_remaining--;
            radiotap_info->dbm_antsignal=dbm;
	    break;

	case IEEE80211_RADIOTAP_DBM_ANTNOISE:
	    if (length_remaining < 1)
		break;
	    dbm = (gint8) tvb_get_guint8(tvb, offset);
	    if (tree) {
		proto_tree_add_int_format(radiotap_tree,
					  hf_radiotap_dbm_antnoise,
					  tvb, offset, 1, dbm,
					  "SSI Noise: %d dBm", dbm);
	    }
	    offset++;
	    length_remaining--;
            radiotap_info->dbm_antnoise=dbm;
	    break;

	case IEEE80211_RADIOTAP_LOCK_QUALITY:
	    align_offset = ALIGN_OFFSET(offset, 2);
	    offset += align_offset;
	    length_remaining -= align_offset;
	    if (length_remaining < 2)
		break;
	    if (tree) {
		proto_tree_add_uint(radiotap_tree, hf_radiotap_quality,
				tvb, offset, 2, tvb_get_letohs(tvb, offset));
	    }
	    offset+=2;
	    length_remaining-=2;
	    break;

	case IEEE80211_RADIOTAP_TX_ATTENUATION:
	    align_offset = ALIGN_OFFSET(offset, 2);
	    offset += align_offset;
	    length_remaining -= align_offset;
	    if (length_remaining < 2)
		break;
	    proto_tree_add_item(radiotap_tree, hf_radiotap_tx_attenuation,
		tvb, offset, 2, FALSE);
	    offset+=2;
	    length_remaining-=2;
	    break;

	case IEEE80211_RADIOTAP_DB_TX_ATTENUATION:
	    align_offset = ALIGN_OFFSET(offset, 2);
	    offset += align_offset;
	    length_remaining -= align_offset;
	    if (length_remaining < 2)
		break;
	    proto_tree_add_item(radiotap_tree, hf_radiotap_db_tx_attenuation,
		tvb, offset, 2, FALSE);
	    offset+=2;
	    length_remaining-=2;
	    break;

	case IEEE80211_RADIOTAP_DBM_TX_POWER:
	    if (length_remaining < 1)
		break;
	    if (tree) {
		proto_tree_add_int(radiotap_tree, hf_radiotap_txpower,
				   tvb, offset, 1, tvb_get_guint8(tvb, offset));
	    }
	    offset++;
	    length_remaining--;
	    break;

	case IEEE80211_RADIOTAP_ANTENNA:
	    if (length_remaining < 1)
		break;
	    if (tree) {
		proto_tree_add_uint(radiotap_tree, hf_radiotap_antenna,
				   tvb, offset, 1, tvb_get_guint8(tvb, offset));
	    }
	    offset++;
	    length_remaining--;
	    break;

	case IEEE80211_RADIOTAP_DB_ANTSIGNAL:
	    if (length_remaining < 1)
		break;
	    db = tvb_get_guint8(tvb, offset);
	    col_add_fstr(pinfo->cinfo, COL_RSSI, "%u dB", db);
	    if (tree) {
		proto_tree_add_uint_format(radiotap_tree,
					   hf_radiotap_db_antsignal,
					   tvb, offset, 1, db,
					   "SSI Signal: %u dB", db);
	    }
	    offset++;
	    length_remaining--;
	    break;

	case IEEE80211_RADIOTAP_DB_ANTNOISE:
	    if (length_remaining < 1)
		break;
	    db = tvb_get_guint8(tvb, offset);
	    if (tree) {
		proto_tree_add_uint_format(radiotap_tree,
					   hf_radiotap_db_antnoise,
					   tvb, offset, 1, db,
					   "SSI Noise: %u dB", db);
	    }
	    offset++;
	    length_remaining--;
	    break;

	case IEEE80211_RADIOTAP_RX_FLAGS:
	{
	    proto_tree *flags_tree;
	    if (radiotap_bit14_fcs) {
	        align_offset = ALIGN_OFFSET(offset, 4);
	        offset += align_offset;
	        length_remaining -= align_offset;
	        if (length_remaining < 4)
	            break;
                if (tree) {
                    sent_fcs = tvb_get_ntohl(tvb, offset);
                    hdr_fcs_ti = proto_tree_add_uint(radiotap_tree, hf_radiotap_fcs,
                                                     tvb, offset, 4, sent_fcs);
                    hdr_fcs_offset = offset;
                }
                offset+=4;
                length_remaining-=4;
	    } else {
	        proto_item *it;

                align_offset = ALIGN_OFFSET(offset, 2);
                offset += align_offset;
                length_remaining -= align_offset;
                if (length_remaining < 2)
                    break;
                if (tree) {
                    flags = tvb_get_letohs(tvb, offset);
                    it = proto_tree_add_uint(radiotap_tree, hf_radiotap_rxflags,
                            tvb, offset, 2, flags);
                    flags_tree = proto_item_add_subtree(it, ett_radiotap_rxflags);
                    proto_tree_add_boolean(flags_tree, hf_radiotap_rxflags_badplcp,
                            tvb, offset, 1, flags);
                }
                offset+=2;
                length_remaining-=2;
            }
	    break;
	}

	case IEEE80211_RADIOTAP_XCHANNEL:
	{
	    proto_item *it;
	    proto_tree *flags_tree;

	    align_offset = ALIGN_OFFSET(offset, 4);
	    offset += align_offset;
	    length_remaining -= align_offset;
	    if (length_remaining < 8)
		break;
	    if (tree) {
	        int channel;
	        guint8 maxpower;

		flags = tvb_get_letohl(tvb, offset);
		freq = tvb_get_letohs(tvb, offset+4);
		channel = tvb_get_guint8(tvb, offset+6);
		maxpower = tvb_get_guint8(tvb, offset+7);
		proto_tree_add_uint(radiotap_tree, hf_radiotap_xchannel,
			tvb, offset+6, 1, (guint32) channel);
		proto_tree_add_uint(radiotap_tree, hf_radiotap_xchannel_frequency,
			tvb, offset+4, 2, freq);
		it = proto_tree_add_uint(radiotap_tree, hf_radiotap_xchannel_flags,
			tvb, offset+0, 4, flags);
		flags_tree = proto_item_add_subtree(it, ett_radiotap_xchannel_flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_xchannel_flags_turbo,
			tvb, offset+0, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_xchannel_flags_cck,
			tvb, offset+0, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_xchannel_flags_ofdm,
			tvb, offset+0, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_xchannel_flags_2ghz,
			tvb, offset+0, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_xchannel_flags_5ghz,
			tvb, offset+1, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_xchannel_flags_passive,
			tvb, offset+1, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_xchannel_flags_dynamic,
			tvb, offset+1, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_xchannel_flags_gfsk,
			tvb, offset+1, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_xchannel_flags_gsm,
			tvb, offset+1, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_xchannel_flags_sturbo,
			tvb, offset+1, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_xchannel_flags_half,
			tvb, offset+1, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_xchannel_flags_quarter,
			tvb, offset+1, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_xchannel_flags_ht20,
			tvb, offset+2, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_xchannel_flags_ht40u,
			tvb, offset+2, 1, flags);
		proto_tree_add_boolean(flags_tree, hf_radiotap_xchannel_flags_ht40d,
			tvb, offset+2, 1, flags);
#if 0
		proto_tree_add_uint(radiotap_tree, hf_radiotap_xchannel_maxpower,
			tvb, offset+7, 1, maxpower);
#endif
	    }
	    offset+=8 /* flags + freq + ieee + maxregpower */;
	    length_remaining-=8;
	    break;
	}

	default:
	    /*
	     * This indicates a field whose size we do not
	     * know, so we cannot proceed.
	     */
	    next_present = 0;
	    continue;
	}
    }

    /* This handles the case of an FCS exiting at the end of the frame. */
    if (rflags & IEEE80211_RADIOTAP_F_FCS)
	pinfo->pseudo_header->ieee_802_11.fcs_len = 4;
    else
	pinfo->pseudo_header->ieee_802_11.fcs_len = 0;

    /* Grab the rest of the frame. */
    next_tvb = tvb_new_subset_remaining(tvb, length);

    /* If we had an in-header FCS, check it.
     * This can only happen if the backward-compat configuration option
     * is chosen by the user. */
    if (hdr_fcs_ti) {
        /* It would be very strange for the header to have an FCS for the
         * frame *and* the frame to have the FCS at the end, but it's possible, so
         * take that into account by using the FCS length recorded in pinfo. */

        /* Watch out for [erroneously] short frames */
        if (tvb_length(next_tvb) > (unsigned int) pinfo->pseudo_header->ieee_802_11.fcs_len) {
            calc_fcs = crc32_802_tvb(next_tvb,
                    tvb_length(next_tvb) - pinfo->pseudo_header->ieee_802_11.fcs_len);

            /* By virtue of hdr_fcs_ti being set, we know that 'tree' is set,
             * so there's no need to check it here. */
            if (calc_fcs == sent_fcs) {
                proto_item_append_text(hdr_fcs_ti, " [correct]");
            }
            else {
                proto_item_append_text(hdr_fcs_ti,
                        " [incorrect, should be 0x%08x]", calc_fcs);
                hidden_item = proto_tree_add_boolean(radiotap_tree, hf_radiotap_fcs_bad,
                        tvb, hdr_fcs_offset, 4, TRUE);
                PROTO_ITEM_SET_HIDDEN(hidden_item);
            }
        }
        else {
                proto_item_append_text(hdr_fcs_ti,
                        " [cannot verify - not enough data]");
        }
    }

    /* dissect the 802.11 header next */
    call_dissector((rflags & IEEE80211_RADIOTAP_F_DATAPAD) ?
        ieee80211_datapad_handle : ieee80211_handle,
        next_tvb, pinfo, tree);
    
    tap_queue_packet(radiotap_tap, pinfo, radiotap_info);
}