static void
dissect_hello_restart_clv(tvbuff_t *tvb,
                          proto_tree *tree, int offset, int id_length, int length)
{
    int restart_options=0;
    proto_tree *flags_tree;
    proto_item *restart_flags_item;
    proto_item *hold_time_item;
    const char *sep;
    const guint8 *neighbor_id;

    if (length >= 1) {
        restart_options = tvb_get_guint8(tvb, offset);
        restart_flags_item = proto_tree_add_uint ( tree, hf_isis_hello_clv_restart_flags,
                             tvb, offset, 1, restart_options);
        flags_tree = proto_item_add_subtree(restart_flags_item, ett_isis_hello_clv_restart_flags);
        proto_tree_add_boolean (flags_tree, hf_isis_hello_clv_restart_flags_sa,
                                tvb, offset, 1, restart_options );
        proto_tree_add_boolean (flags_tree, hf_isis_hello_clv_restart_flags_ra,
                                tvb, offset, 1, restart_options );
        proto_tree_add_boolean (flags_tree, hf_isis_hello_clv_restart_flags_rr,
                                tvb, offset, 1, restart_options );

        /* Append an indication of which flags are set in the restart
         * options
         */
        sep = initial_sep;
        APPEND_BOOLEAN_FLAG(ISIS_MASK_RESTART_SA(restart_options), restart_flags_item, "%sSA");
        APPEND_BOOLEAN_FLAG(ISIS_MASK_RESTART_RA(restart_options), restart_flags_item, "%sRA");
        APPEND_BOOLEAN_FLAG(ISIS_MASK_RESTART_RR(restart_options), restart_flags_item, "%sRR");
        if (sep != initial_sep)
        {
            proto_item_append_text (restart_flags_item, ")");
        }

    }

    /* The Remaining Time field should only be present if the RA flag is
     * set
     */
    if (length >= 3 && ISIS_MASK_RESTART_RA(restart_options)) {
        hold_time_item = proto_tree_add_uint ( tree, hf_isis_hello_clv_restart_remain_time,
                                               tvb, offset+1, 2, tvb_get_ntohs(tvb, offset+1) );
        proto_item_append_text( hold_time_item, "s" );
    }

    /* The Restarting Neighbor ID should only be present if the RA flag is
     * set.
     */
    if (length >= 3 + id_length && ISIS_MASK_RESTART_RA(restart_options)) {
        neighbor_id = tvb_get_ptr(tvb, offset+3, id_length);
        proto_tree_add_bytes_format( tree,
                                     hf_isis_hello_clv_restart_neighbor, tvb, offset+3,
                                     id_length, neighbor_id, "Restarting Neighbor ID: %s",
                                     print_system_id( neighbor_id, id_length ) );
    }
}
Example #2
0
static void
dissect_dec_bpdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
      guint8  protocol_version;
      guint8  bpdu_type;
      guint8  flags;
      proto_tree *bpdu_tree;
      proto_tree *flags_tree;
      proto_item *ti;
      const char *sep;

      col_set_str(pinfo->cinfo, COL_PROTOCOL, "DEC_STP");
      col_clear(pinfo->cinfo, COL_INFO);

      bpdu_type = tvb_get_guint8(tvb, BPDU_TYPE);

      if (check_col(pinfo->cinfo, COL_INFO)) {
	    col_add_str(pinfo->cinfo, COL_INFO,
			val_to_str(bpdu_type, bpdu_type_vals,
				   "Unknown BPDU type (%u)"));
      }

      set_actual_length(tvb, DEC_BPDU_SIZE);

      if (tree) {
	    ti = proto_tree_add_item(tree, proto_dec_bpdu, tvb, 0, DEC_BPDU_SIZE,
			    	FALSE);
	    bpdu_tree = proto_item_add_subtree(ti, ett_dec_bpdu);

	    protocol_version = tvb_get_guint8(tvb, BPDU_VERSION);

	    proto_tree_add_item(bpdu_tree, hf_dec_bpdu_proto_id, tvb,
				BPDU_DEC_CODE, 1, FALSE);

	    proto_tree_add_uint(bpdu_tree, hf_dec_bpdu_type, tvb,
				BPDU_TYPE, 1, bpdu_type);

	    proto_tree_add_item(bpdu_tree, hf_dec_bpdu_version_id, tvb,
				BPDU_VERSION, 1, FALSE);

	    flags = tvb_get_guint8(tvb, BPDU_FLAGS);
	    ti = proto_tree_add_uint(bpdu_tree, hf_dec_bpdu_flags, tvb,
				     BPDU_FLAGS, 1, flags);
	    flags_tree = proto_item_add_subtree(ti, ett_dec_bpdu_flags);
	    sep = initial_sep;
	    APPEND_BOOLEAN_FLAG(flags & BPDU_FLAGS_SHORT_TIMERS, ti,
				"%sUse short timers");
	    proto_tree_add_boolean(flags_tree, hf_dec_bpdu_flags_short_timers, tvb,
				   BPDU_FLAGS, 1, flags);
	    APPEND_BOOLEAN_FLAG(flags & BPDU_FLAGS_TCACK, ti,
				"%sTopology Change Acknowledgment");
	    proto_tree_add_boolean(flags_tree, hf_dec_bpdu_flags_tcack, tvb,
				   BPDU_FLAGS, 1, flags);
	    APPEND_BOOLEAN_FLAG(flags & BPDU_FLAGS_TC, ti,
				"%sTopology Change");
	    proto_tree_add_boolean(flags_tree, hf_dec_bpdu_flags_tc, tvb,
				   BPDU_FLAGS, 1, flags);
	    if (sep != initial_sep) {
	      /* We put something in; put in the terminating ")" */
	      proto_item_append_text(ti, ")");
	    }

	    proto_tree_add_item(bpdu_tree, hf_dec_bpdu_root_pri, tvb,
				BPDU_ROOT_PRI, 2, FALSE);
	    proto_tree_add_item(bpdu_tree, hf_dec_bpdu_root_mac, tvb,
				BPDU_ROOT_MAC, 6, FALSE);
	    proto_tree_add_item(bpdu_tree, hf_dec_bpdu_root_cost, tvb,
				BPDU_ROOT_PATH_COST, 2, FALSE);
	    proto_tree_add_item(bpdu_tree, hf_dec_bpdu_bridge_pri, tvb,
				BPDU_BRIDGE_PRI, 2, FALSE);
	    proto_tree_add_item(bpdu_tree, hf_dec_bpdu_bridge_mac, tvb,
				BPDU_BRIDGE_MAC, 6, FALSE);
	    proto_tree_add_item(bpdu_tree, hf_dec_bpdu_port_id, tvb,
				BPDU_PORT_IDENTIFIER, 1, FALSE);
	    proto_tree_add_item(bpdu_tree, hf_dec_bpdu_msg_age, tvb,
				BPDU_MESSAGE_AGE, 1, FALSE);
	    proto_tree_add_item(bpdu_tree, hf_dec_bpdu_hello_time, tvb,
				BPDU_HELLO_TIME, 1, FALSE);
	    proto_tree_add_item(bpdu_tree, hf_dec_bpdu_max_age, tvb,
				BPDU_MAX_AGE, 1, FALSE);
	    proto_tree_add_item(bpdu_tree, hf_dec_bpdu_forward_delay, tvb,
				BPDU_FORWARD_DELAY, 1, FALSE);

      }
}
static void
dissect_bfd_control(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    guint flags;
    guint bfd_version;
    guint bfd_diag;
    guint bfd_sta        = 0;
    guint bfd_flags;
    guint bfd_flags_h    = 0;
    guint bfd_flags_p    = 0;
    guint bfd_flags_f    = 0;
    guint bfd_flags_c    = 0;
    guint bfd_flags_a    = 0;
    guint bfd_flags_d    = 0;
    guint bfd_flags_m    = 0;
    guint bfd_flags_d_v0 = 0;
    guint bfd_flags_p_v0 = 0;
    guint bfd_flags_f_v0 = 0;
    guint bfd_detect_time_multiplier;
    guint bfd_length;
    guint bfd_my_discriminator;
    guint bfd_your_discriminator;
    guint bfd_desired_min_tx_interval;
    guint bfd_required_min_rx_interval;
    guint bfd_required_min_echo_interval;
    proto_tree *bfd_tree = NULL;

    col_set_str(pinfo->cinfo, COL_PROTOCOL, "BFD Control");
    col_clear(pinfo->cinfo, COL_INFO);

    bfd_version = (tvb_get_guint8(tvb, 0) & 0xe0) >> 5;
    bfd_diag    = (tvb_get_guint8(tvb, 0) & 0x1f);
    flags       = tvb_get_guint8(tvb, 1);
    switch (bfd_version) {
        case 0:
            bfd_flags      = flags;
            bfd_flags_h    = flags & 0x80;
            bfd_flags_d_v0 = flags & 0x40;
            bfd_flags_p_v0 = flags & 0x20;
            bfd_flags_f_v0 = flags & 0x10;
            break;
        case 1:
        default:
            bfd_sta        = flags & 0xc0;
            bfd_flags      = flags & 0x3e;
            bfd_flags_p    = flags & 0x20;
            bfd_flags_f    = flags & 0x10;
            bfd_flags_c    = flags & 0x08;
            bfd_flags_a    = flags & 0x04;
            bfd_flags_d    = flags & 0x02;
            bfd_flags_m    = flags & 0x01;
            break;
    }

    bfd_detect_time_multiplier     = tvb_get_guint8(tvb, 2);
    bfd_length                     = tvb_get_guint8(tvb, 3);
    bfd_my_discriminator           = tvb_get_ntohl(tvb, 4);
    bfd_your_discriminator         = tvb_get_ntohl(tvb, 8);
    bfd_desired_min_tx_interval    = tvb_get_ntohl(tvb, 12);
    bfd_required_min_rx_interval   = tvb_get_ntohl(tvb, 16);
    bfd_required_min_echo_interval = tvb_get_ntohl(tvb, 20);

    switch (bfd_version) {
        case 0:
            col_add_fstr(pinfo->cinfo, COL_INFO, "Diag: %s, Flags: 0x%02x",
                         val_to_str_const(bfd_diag, bfd_control_v0_diag_values, "Unknown"),
                         bfd_flags);
            break;
        case 1:
        default:
            col_add_fstr(pinfo->cinfo, COL_INFO, "Diag: %s, State: %s, Flags: 0x%02x",
                         val_to_str_const(bfd_diag, bfd_control_v1_diag_values, "Unknown"),
                         val_to_str_const(bfd_sta >> 6 , bfd_control_sta_values, "Unknown"),
                         bfd_flags);
            break;
    }

    if (tree) {
        proto_item *ti;
        proto_tree *bfd_flags_tree;
        const char *sep;

        ti = proto_tree_add_protocol_format(tree, proto_bfd, tvb, 0, bfd_length,
                                            "BFD Control message");

        bfd_tree = proto_item_add_subtree(ti, ett_bfd);

        proto_tree_add_uint(bfd_tree, hf_bfd_version, tvb, 0,
                                 1, bfd_version << 5);

        proto_tree_add_uint(bfd_tree, hf_bfd_diag, tvb, 0,
                                 1, bfd_diag);

        switch (bfd_version) {
            case 0:
                break;
            case 1:
            default:
                proto_tree_add_uint(bfd_tree, hf_bfd_sta, tvb, 1,
                                    1, bfd_sta);

                break;
        }
        switch (bfd_version) {
            case 0:
                ti = proto_tree_add_text ( bfd_tree, tvb, 1, 1, "Message Flags: 0x%02x",
                                           bfd_flags);
                bfd_flags_tree = proto_item_add_subtree(ti, ett_bfd_flags);
                proto_tree_add_boolean(bfd_flags_tree, hf_bfd_flags_h,    tvb, 1, 1, bfd_flags_h);
                proto_tree_add_boolean(bfd_flags_tree, hf_bfd_flags_d_v0, tvb, 1, 1, bfd_flags_d_v0);
                proto_tree_add_boolean(bfd_flags_tree, hf_bfd_flags_p_v0, tvb, 1, 1, bfd_flags_p_v0);
                proto_tree_add_boolean(bfd_flags_tree, hf_bfd_flags_f_v0, tvb, 1, 1, bfd_flags_f_v0);

                sep = initial_sep;
                APPEND_BOOLEAN_FLAG(bfd_flags_h,    ti, "%sH");
                APPEND_BOOLEAN_FLAG(bfd_flags_d_v0, ti, "%sD");
                APPEND_BOOLEAN_FLAG(bfd_flags_p_v0, ti, "%sP");
                APPEND_BOOLEAN_FLAG(bfd_flags_f_v0, ti, "%sF");
                if (sep != initial_sep) {
                    proto_item_append_text (ti, ")");
                }
                break;
            case 1:
            default:
                ti = proto_tree_add_text ( bfd_tree, tvb, 1, 1, "Message Flags: 0x%02x",
                                           bfd_flags);
                bfd_flags_tree = proto_item_add_subtree(ti, ett_bfd_flags);
                proto_tree_add_boolean(bfd_flags_tree, hf_bfd_flags_p, tvb, 1, 1, bfd_flags_p);
                proto_tree_add_boolean(bfd_flags_tree, hf_bfd_flags_f, tvb, 1, 1, bfd_flags_f);
                proto_tree_add_boolean(bfd_flags_tree, hf_bfd_flags_c, tvb, 1, 1, bfd_flags_c);
                proto_tree_add_boolean(bfd_flags_tree, hf_bfd_flags_a, tvb, 1, 1, bfd_flags_a);
                proto_tree_add_boolean(bfd_flags_tree, hf_bfd_flags_d, tvb, 1, 1, bfd_flags_d);
                proto_tree_add_boolean(bfd_flags_tree, hf_bfd_flags_m, tvb, 1, 1, bfd_flags_m);

                sep = initial_sep;
                APPEND_BOOLEAN_FLAG(bfd_flags_p, ti, "%sP");
                APPEND_BOOLEAN_FLAG(bfd_flags_f, ti, "%sF");
                APPEND_BOOLEAN_FLAG(bfd_flags_c, ti, "%sC");
                APPEND_BOOLEAN_FLAG(bfd_flags_a, ti, "%sA");
                APPEND_BOOLEAN_FLAG(bfd_flags_d, ti, "%sD");
                APPEND_BOOLEAN_FLAG(bfd_flags_m, ti, "%sM");
                if (sep != initial_sep) {
                    proto_item_append_text (ti, ")");
                }
                break;
        }

        proto_tree_add_uint_format_value(bfd_tree, hf_bfd_detect_time_multiplier, tvb, 2,
                                         1, bfd_detect_time_multiplier,
                                         "%u (= %u ms Detection time)",
                                         bfd_detect_time_multiplier,
                                         bfd_detect_time_multiplier * (bfd_desired_min_tx_interval/1000));

        proto_tree_add_uint_format_value(bfd_tree, hf_bfd_message_length, tvb, 3, 1, bfd_length,
                "%u bytes", bfd_length);

        proto_tree_add_uint(bfd_tree, hf_bfd_my_discriminator, tvb, 4,
                                 4, bfd_my_discriminator);

        proto_tree_add_uint(bfd_tree, hf_bfd_your_discriminator, tvb, 8,
                                 4, bfd_your_discriminator);

        proto_tree_add_uint_format_value(bfd_tree, hf_bfd_desired_min_tx_interval, tvb, 12,
                                              4, bfd_desired_min_tx_interval,
                                              "%4u ms (%u us)",
                                              bfd_desired_min_tx_interval/1000,
                                              bfd_desired_min_tx_interval);

        proto_tree_add_uint_format_value(bfd_tree, hf_bfd_required_min_rx_interval, tvb, 16,
                                              4, bfd_required_min_rx_interval,
                                              "%4u ms (%u us)",
                                              bfd_required_min_rx_interval/1000,
                                              bfd_required_min_rx_interval);

        proto_tree_add_uint_format_value(bfd_tree, hf_bfd_required_min_echo_interval, tvb, 20,
                                              4, bfd_required_min_echo_interval,
                                              "%4u ms (%u us)",
                                              bfd_required_min_echo_interval/1000,
                                              bfd_required_min_echo_interval);
    } /* if (tree) */

    /* Dissect the authentication fields if the Authentication flag has
     * been set
     */
    if (bfd_version && bfd_flags_a) {
        if (bfd_length >= 28) {
            dissect_bfd_authentication(tvb, pinfo, bfd_tree);
        } else {
            proto_item *ti = proto_tree_add_text(bfd_tree, tvb, 24, bfd_length-24,
                                         "Authentication: Length of the BFD frame is invalid (%d)", bfd_length);
            expert_add_info(pinfo, ti, &ei_bfd_auth_no_data);
        }
    }

    return;
}
Example #4
0
/* Code to actually dissect the PAGP packets */
static void
dissect_pagp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
      guint32 raw_word;
      guint16 raw_half_word;
      guint16 num_tlvs;
      guint16 tlv;
      guint16 len;
      guint16 ii;
      guint16 offset = PAGP_FIRST_TLV;
      guint8  raw_octet;

      guint8  flags;

      guchar *ch;

      proto_tree *pagp_tree = NULL;
      proto_item *pagp_item;
      proto_tree *flags_tree;
      proto_item *flags_item;
      proto_tree *tlv_tree;
      proto_item *tlv_item;


      const char *sep;


      col_set_str(pinfo->cinfo, COL_PROTOCOL, "PAGP"); /* PAGP Protocol */

      col_clear(pinfo->cinfo, COL_INFO);

      pinfo->current_proto = "PAGP";

      raw_octet = tvb_get_guint8(tvb, PAGP_VERSION_NUMBER);
      if (tree) {
	    pagp_item = proto_tree_add_protocol_format(tree, proto_pagp, tvb,
				0, -1, "Port Aggregation Protocol");
	    pagp_tree = proto_item_add_subtree(pagp_item, ett_pagp);
	    proto_tree_add_uint(pagp_tree, hf_pagp_version_number, tvb,
				PAGP_VERSION_NUMBER, 1, raw_octet);
      }
      col_append_str(pinfo->cinfo, COL_INFO,
           val_to_str_const(raw_octet, pdu_vers, "Unknown PDU version"));

      if (raw_octet == PAGP_FLUSH_PDU) {

         col_append_fstr(pinfo->cinfo, COL_INFO, "; Local DevID: %s",
               tvb_ether_to_str(tvb, PAGP_FLUSH_LOCAL_DEVICE_ID));

         proto_tree_add_item(pagp_tree, hf_pagp_flush_local_device_id, tvb,
            PAGP_FLUSH_LOCAL_DEVICE_ID, 6, ENC_NA);

         col_append_fstr(pinfo->cinfo, COL_INFO, ", Partner DevID: %s",
               tvb_ether_to_str(tvb, PAGP_FLUSH_PARTNER_DEVICE_ID));

         proto_tree_add_item(pagp_tree, hf_pagp_flush_partner_device_id, tvb,
            PAGP_FLUSH_PARTNER_DEVICE_ID, 6, ENC_NA);

         raw_word = tvb_get_ntohl(tvb, PAGP_FLUSH_TRANSACTION_ID);
         col_append_fstr(pinfo->cinfo, COL_INFO, "; Transaction ID: 0x%x ", raw_word);

         proto_tree_add_uint(pagp_tree, hf_pagp_flush_transaction_id, tvb,
            PAGP_FLUSH_TRANSACTION_ID, 4, raw_word);
         return;
      }

      /* Info PDU */

      flags = tvb_get_guint8(tvb, PAGP_FLAGS);
      col_append_fstr(pinfo->cinfo, COL_INFO, "; Flags 0x%x", flags);

      if (tree) {
         flags_item = proto_tree_add_uint(pagp_tree, hf_pagp_flags, tvb,
			PAGP_FLAGS, 1, flags);
         flags_tree = proto_item_add_subtree(flags_item, ett_pagp_flags);

	 sep = initial_sep;

	 APPEND_BOOLEAN_FLAG(flags & PAGP_FLAGS_SLOW_HELLO, flags_item, "%sSlow Hello");
	 proto_tree_add_boolean(flags_tree, hf_pagp_flags_slow_hello, tvb,
			PAGP_FLAGS, 1, flags);

	 APPEND_BOOLEAN_FLAG(flags & PAGP_FLAGS_AUTO_MODE, flags_item, "%sAuto Mode");
	 proto_tree_add_boolean(flags_tree, hf_pagp_flags_auto_mode, tvb,
			PAGP_FLAGS, 1, flags);

	 APPEND_BOOLEAN_FLAG(flags & PAGP_FLAGS_CONSISTENT_STATE, flags_item,
			"%sConsistent State");
	 proto_tree_add_boolean(flags_tree, hf_pagp_flags_consistent_state, tvb,
				PAGP_FLAGS, 1, flags);

	 sep = cont_sep;
	 if (sep != initial_sep) {
            /* We put something in; put in the terminating ")" */
            proto_item_append_text(flags_item, ")");
	 }
      }

      col_append_fstr(pinfo->cinfo, COL_INFO, "; Local DevID: %s",
            tvb_ether_to_str(tvb, PAGP_LOCAL_DEVICE_ID));

      proto_tree_add_item(pagp_tree, hf_pagp_local_device_id, tvb,
				PAGP_LOCAL_DEVICE_ID, 6, ENC_NA);

      if (tree) {
	    raw_octet = tvb_get_guint8(tvb, PAGP_LOCAL_LEARN_CAP);
	    proto_tree_add_uint(pagp_tree, hf_pagp_local_learn_cap, tvb,
				PAGP_LOCAL_LEARN_CAP, 1, raw_octet);

	    raw_octet = tvb_get_guint8(tvb, PAGP_LOCAL_PORT_PRIORITY);
	    proto_tree_add_uint(pagp_tree, hf_pagp_local_port_priority, tvb,
				PAGP_LOCAL_PORT_PRIORITY, 1, raw_octet);

	    raw_word = tvb_get_ntohl(tvb, PAGP_LOCAL_SENT_PORT_IFINDEX);
	    proto_tree_add_uint(pagp_tree, hf_pagp_local_sent_port_ifindex, tvb,
				PAGP_LOCAL_SENT_PORT_IFINDEX, 4, raw_word);

	    raw_word = tvb_get_ntohl(tvb, PAGP_LOCAL_GROUP_CAPABILITY);
	    proto_tree_add_uint(pagp_tree, hf_pagp_local_group_capability, tvb,
				PAGP_LOCAL_GROUP_CAPABILITY, 4, raw_word);

	    raw_word = tvb_get_ntohl(tvb, PAGP_LOCAL_GROUP_IFINDEX);
	    proto_tree_add_uint(pagp_tree, hf_pagp_local_group_ifindex, tvb,
				PAGP_LOCAL_GROUP_IFINDEX, 4, raw_word);
      }

      col_append_fstr(pinfo->cinfo, COL_INFO, ", Partner DevID: %s",
  	       tvb_ether_to_str(tvb, PAGP_PARTNER_DEVICE_ID));

      proto_tree_add_item(pagp_tree, hf_pagp_partner_device_id, tvb,
				PAGP_PARTNER_DEVICE_ID, 6, ENC_NA);

      if (tree) {
	    raw_octet = tvb_get_guint8(tvb, PAGP_PARTNER_LEARN_CAP);
	    proto_tree_add_uint(pagp_tree, hf_pagp_partner_learn_cap, tvb,
				PAGP_PARTNER_LEARN_CAP, 1, raw_octet);

	    raw_octet = tvb_get_guint8(tvb, PAGP_PARTNER_PORT_PRIORITY);
	    proto_tree_add_uint(pagp_tree, hf_pagp_partner_port_priority, tvb,
				PAGP_PARTNER_PORT_PRIORITY, 1, raw_octet);

	    raw_word = tvb_get_ntohl(tvb, PAGP_PARTNER_SENT_PORT_IFINDEX);
	    proto_tree_add_uint(pagp_tree, hf_pagp_partner_sent_port_ifindex, tvb,
				PAGP_PARTNER_SENT_PORT_IFINDEX, 4, raw_word);

	    raw_word = tvb_get_ntohl(tvb, PAGP_PARTNER_GROUP_CAPABILITY);
	    proto_tree_add_uint(pagp_tree, hf_pagp_partner_group_capability, tvb,
				PAGP_PARTNER_GROUP_CAPABILITY, 4, raw_word);

	    raw_word = tvb_get_ntohl(tvb, PAGP_PARTNER_GROUP_IFINDEX);
	    proto_tree_add_uint(pagp_tree, hf_pagp_partner_group_ifindex, tvb,
				PAGP_PARTNER_GROUP_IFINDEX, 4, raw_word);

	    raw_half_word = tvb_get_ntohs(tvb, PAGP_PARTNER_COUNT);
	    proto_tree_add_uint(pagp_tree, hf_pagp_partner_count, tvb,
				PAGP_PARTNER_COUNT, 2, raw_half_word);

	    num_tlvs = tvb_get_ntohs(tvb, PAGP_NUM_TLVS);
	    proto_tree_add_uint(pagp_tree, hf_pagp_num_tlvs, tvb,
				PAGP_NUM_TLVS, 2, num_tlvs);

	    /* dump TLV entries */

	    for ( ii = 0; ii < num_tlvs; ii++ ) {

		tlv = tvb_get_ntohs(tvb, offset);
		len = tvb_get_ntohs(tvb, offset + 2);
		if ( len == 0 ) {
		   proto_tree_add_text(pagp_tree, tvb, offset, -1,
			               "Unknown data - TLV len=0");
		   return;
		}

		tlv_item = proto_tree_add_text (pagp_tree, tvb, offset, len,
			   "TLV Entry #%d", ii+1);

		tlv_tree = proto_item_add_subtree (tlv_item, ett_pagp_tlvs);
		proto_tree_add_uint_format (tlv_tree, hf_pagp_tlv, tvb,
			offset,2,tlv,"Type = %d (%s)", tlv,
			val_to_str_const(tlv,tlv_types, "Unknown")) ;
		proto_tree_add_text (tlv_tree, tvb, offset+2, 2,
			"Length = %u bytes (includes Type and Length)", len) ;
		if ( tvb_reported_length_remaining(tvb, offset) < len ) {
		   proto_tree_add_text(tlv_tree, tvb, offset, -1,
			               "TLV length too large");
		   return;
		}

		switch (tlv) {
		   case PAGP_TLV_DEVICE_NAME:
			ch = tvb_get_string(wmem_packet_scope(), tvb, offset+4, len-4);
			proto_tree_add_string(tlv_tree, hf_pagp_tlv_device_name,
			   tvb, offset+4, len-4, ch);
			break;
		   case PAGP_TLV_PORT_NAME:
			ch = tvb_get_string(wmem_packet_scope(), tvb, offset+4, len-4);
			proto_tree_add_string(tlv_tree, hf_pagp_tlv_port_name,
			   tvb, offset+4, len-4, ch);
			break;
		   case PAGP_TLV_AGPORT_MAC:
			proto_tree_add_item(tlv_tree, hf_pagp_tlv_agport_mac,
			   tvb, offset+4, 6, ENC_NA);
			break;
		   case PAGP_TLV_RESERVED:
			break;
		}

		offset += len;

	    }
      }
}