+
+ if (len < sizeof(struct sflow_vlan_counter_t))
+ return 1;
+
+ sflow_vlan_counter = (const struct sflow_vlan_counter_t *)pointer;
+ ND_PRINT("\n\t vlan_id %u, octets %" PRIu64
+ ", unicast_pkt %u, multicast_pkt %u, broadcast_pkt %u, discards %u",
+ GET_BE_U_4(sflow_vlan_counter->vlan_id),
+ GET_BE_U_8(sflow_vlan_counter->octets),
+ GET_BE_U_4(sflow_vlan_counter->unicast_pkt),
+ GET_BE_U_4(sflow_vlan_counter->multicast_pkt),
+ GET_BE_U_4(sflow_vlan_counter->broadcast_pkt),
+ GET_BE_U_4(sflow_vlan_counter->discards));
+
+ return 0;
+}
+
+struct sflow_processor_counter_t {
+ nd_uint32_t five_sec_util;
+ nd_uint32_t one_min_util;
+ nd_uint32_t five_min_util;
+ nd_uint64_t total_memory;
+ nd_uint64_t free_memory;
+};
+
+static int
+print_sflow_counter_processor(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_processor_counter_t *sflow_processor_counter;
+
+ if (len < sizeof(struct sflow_processor_counter_t))
+ return 1;
+
+ sflow_processor_counter = (const struct sflow_processor_counter_t *)pointer;
+ ND_PRINT("\n\t 5sec %u, 1min %u, 5min %u, total_mem %" PRIu64
+ ", total_mem %" PRIu64,
+ GET_BE_U_4(sflow_processor_counter->five_sec_util),
+ GET_BE_U_4(sflow_processor_counter->one_min_util),
+ GET_BE_U_4(sflow_processor_counter->five_min_util),
+ GET_BE_U_8(sflow_processor_counter->total_memory),
+ GET_BE_U_8(sflow_processor_counter->free_memory));
+
+ return 0;
+}
+
+static int
+sflow_print_counter_records(netdissect_options *ndo,
+ const u_char *pointer, u_int len, u_int records)
+{
+ u_int nrecords;
+ const u_char *tptr;
+ u_int tlen;
+ u_int counter_type;
+ u_int counter_len;
+ u_int enterprise;
+ const struct sflow_counter_record_t *sflow_counter_record;
+
+ nrecords = records;
+ tptr = pointer;
+ tlen = len;
+
+ while (nrecords > 0) {
+ /* do we have the "header?" */
+ if (tlen < sizeof(struct sflow_counter_record_t))
+ return 1;
+ sflow_counter_record = (const struct sflow_counter_record_t *)tptr;
+
+ enterprise = GET_BE_U_4(sflow_counter_record->format);
+ counter_type = enterprise & 0x0FFF;
+ enterprise = enterprise >> 20;
+ counter_len = GET_BE_U_4(sflow_counter_record->length);
+ ND_PRINT("\n\t enterprise %u, %s (%u) length %u",
+ enterprise,
+ (enterprise == 0) ? tok2str(sflow_counter_type_values,"Unknown",counter_type) : "Unknown",
+ counter_type,
+ counter_len);
+
+ tptr += sizeof(struct sflow_counter_record_t);
+ tlen -= sizeof(struct sflow_counter_record_t);
+
+ if (tlen < counter_len)
+ return 1;
+ if (enterprise == 0) {
+ switch (counter_type) {
+ case SFLOW_COUNTER_GENERIC:
+ if (print_sflow_counter_generic(ndo, tptr, tlen))
+ return 1;
+ break;
+ case SFLOW_COUNTER_ETHERNET:
+ if (print_sflow_counter_ethernet(ndo, tptr, tlen))
+ return 1;
+ break;
+ case SFLOW_COUNTER_TOKEN_RING:
+ if (print_sflow_counter_token_ring(ndo, tptr,tlen))
+ return 1;
+ break;
+ case SFLOW_COUNTER_BASEVG:
+ if (print_sflow_counter_basevg(ndo, tptr, tlen))
+ return 1;
+ break;
+ case SFLOW_COUNTER_VLAN:
+ if (print_sflow_counter_vlan(ndo, tptr, tlen))
+ return 1;
+ break;
+ case SFLOW_COUNTER_PROCESSOR:
+ if (print_sflow_counter_processor(ndo, tptr, tlen))
+ return 1;
+ break;
+ default:
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo, tptr, "\n\t\t", counter_len);
+ break;
+ }
+ }
+ tptr += counter_len;
+ tlen -= counter_len;
+ nrecords--;
+
+ }
+
+ return 0;
+}
+
+static int
+sflow_print_counter_sample(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_counter_sample_t *sflow_counter_sample;
+ u_int nrecords;
+
+ if (len < sizeof(struct sflow_counter_sample_t))
+ return 1;
+
+ sflow_counter_sample = (const struct sflow_counter_sample_t *)pointer;
+
+ nrecords = GET_BE_U_4(sflow_counter_sample->records);
+
+ ND_PRINT(" seqnum %u, type %u, idx %u, records %u",
+ GET_BE_U_4(sflow_counter_sample->seqnum),
+ GET_U_1(sflow_counter_sample->type),
+ GET_BE_U_3(sflow_counter_sample->index),
+ nrecords);
+
+ return sflow_print_counter_records(ndo, pointer + sizeof(struct sflow_counter_sample_t),
+ len - sizeof(struct sflow_counter_sample_t),
+ nrecords);
+}
+
+static int
+sflow_print_expanded_counter_sample(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_expanded_counter_sample_t *sflow_expanded_counter_sample;
+ u_int nrecords;
+
+
+ if (len < sizeof(struct sflow_expanded_counter_sample_t))
+ return 1;
+
+ sflow_expanded_counter_sample = (const struct sflow_expanded_counter_sample_t *)pointer;
+
+ nrecords = GET_BE_U_4(sflow_expanded_counter_sample->records);
+
+ ND_PRINT(" seqnum %u, type %u, idx %u, records %u",
+ GET_BE_U_4(sflow_expanded_counter_sample->seqnum),
+ GET_BE_U_4(sflow_expanded_counter_sample->type),
+ GET_BE_U_4(sflow_expanded_counter_sample->index),
+ nrecords);
+
+ return sflow_print_counter_records(ndo, pointer + sizeof(struct sflow_expanded_counter_sample_t),
+ len - sizeof(struct sflow_expanded_counter_sample_t),
+ nrecords);
+}
+
+static int
+print_sflow_raw_packet(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_expanded_flow_raw_t *sflow_flow_raw;
+
+ if (len < sizeof(struct sflow_expanded_flow_raw_t))
+ return 1;
+
+ sflow_flow_raw = (const struct sflow_expanded_flow_raw_t *)pointer;
+ ND_PRINT("\n\t protocol %s (%u), length %u, stripped bytes %u, header_size %u",
+ tok2str(sflow_flow_raw_protocol_values,"Unknown",GET_BE_U_4(sflow_flow_raw->protocol)),
+ GET_BE_U_4(sflow_flow_raw->protocol),
+ GET_BE_U_4(sflow_flow_raw->length),
+ GET_BE_U_4(sflow_flow_raw->stripped_bytes),
+ GET_BE_U_4(sflow_flow_raw->header_size));
+
+ /* QUESTION - should we attempt to print the raw header itself?
+ assuming of course there is enough data present to do so... */
+
+ return 0;
+}
+
+static int
+print_sflow_ethernet_frame(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_ethernet_frame_t *sflow_ethernet_frame;
+
+ if (len < sizeof(struct sflow_ethernet_frame_t))
+ return 1;
+
+ sflow_ethernet_frame = (const struct sflow_ethernet_frame_t *)pointer;
+
+ ND_PRINT("\n\t frame len %u, type %u",
+ GET_BE_U_4(sflow_ethernet_frame->length),
+ GET_BE_U_4(sflow_ethernet_frame->type));
+
+ return 0;
+}
+
+static int
+print_sflow_extended_switch_data(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_extended_switch_data_t *sflow_extended_sw_data;
+
+ if (len < sizeof(struct sflow_extended_switch_data_t))
+ return 1;
+
+ sflow_extended_sw_data = (const struct sflow_extended_switch_data_t *)pointer;
+ ND_PRINT("\n\t src vlan %u, src pri %u, dst vlan %u, dst pri %u",
+ GET_BE_U_4(sflow_extended_sw_data->src_vlan),
+ GET_BE_U_4(sflow_extended_sw_data->src_pri),
+ GET_BE_U_4(sflow_extended_sw_data->dst_vlan),
+ GET_BE_U_4(sflow_extended_sw_data->dst_pri));
+
+ return 0;
+}
+
+static int
+sflow_print_flow_records(netdissect_options *ndo,
+ const u_char *pointer, u_int len, u_int records)
+{
+ u_int nrecords;
+ const u_char *tptr;
+ u_int tlen;
+ u_int flow_type;
+ u_int enterprise;
+ u_int flow_len;
+ const struct sflow_flow_record_t *sflow_flow_record;
+
+ nrecords = records;
+ tptr = pointer;
+ tlen = len;
+
+ while (nrecords > 0) {
+ /* do we have the "header?" */
+ if (tlen < sizeof(struct sflow_flow_record_t))
+ return 1;
+
+ sflow_flow_record = (const struct sflow_flow_record_t *)tptr;
+
+ /* so, the funky encoding means we cannot blythly mask-off
+ bits, we must also check the enterprise. */
+
+ enterprise = GET_BE_U_4(sflow_flow_record->format);
+ flow_type = enterprise & 0x0FFF;
+ enterprise = enterprise >> 12;
+ flow_len = GET_BE_U_4(sflow_flow_record->length);
+ ND_PRINT("\n\t enterprise %u %s (%u) length %u",
+ enterprise,
+ (enterprise == 0) ? tok2str(sflow_flow_type_values,"Unknown",flow_type) : "Unknown",
+ flow_type,
+ flow_len);
+
+ tptr += sizeof(struct sflow_flow_record_t);
+ tlen -= sizeof(struct sflow_flow_record_t);
+
+ if (tlen < flow_len)
+ return 1;
+
+ if (enterprise == 0) {
+ switch (flow_type) {
+ case SFLOW_FLOW_RAW_PACKET:
+ if (print_sflow_raw_packet(ndo, tptr, tlen))
+ return 1;
+ break;
+ case SFLOW_FLOW_EXTENDED_SWITCH_DATA:
+ if (print_sflow_extended_switch_data(ndo, tptr, tlen))
+ return 1;
+ break;
+ case SFLOW_FLOW_ETHERNET_FRAME:
+ if (print_sflow_ethernet_frame(ndo, tptr, tlen))
+ return 1;
+ break;
+ /* FIXME these need a decoder */
+ case SFLOW_FLOW_IPV4_DATA:
+ case SFLOW_FLOW_IPV6_DATA:
+ case SFLOW_FLOW_EXTENDED_ROUTER_DATA:
+ case SFLOW_FLOW_EXTENDED_GATEWAY_DATA:
+ case SFLOW_FLOW_EXTENDED_USER_DATA:
+ case SFLOW_FLOW_EXTENDED_URL_DATA:
+ case SFLOW_FLOW_EXTENDED_MPLS_DATA:
+ case SFLOW_FLOW_EXTENDED_NAT_DATA:
+ case SFLOW_FLOW_EXTENDED_MPLS_TUNNEL:
+ case SFLOW_FLOW_EXTENDED_MPLS_VC:
+ case SFLOW_FLOW_EXTENDED_MPLS_FEC:
+ case SFLOW_FLOW_EXTENDED_MPLS_LVP_FEC:
+ case SFLOW_FLOW_EXTENDED_VLAN_TUNNEL:
+ break;
+ default:
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo, tptr, "\n\t\t", flow_len);
+ break;
+ }
+ }
+ tptr += flow_len;
+ tlen -= flow_len;
+ nrecords--;
+
+ }
+
+ return 0;
+}
+
+static int
+sflow_print_flow_sample(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_flow_sample_t *sflow_flow_sample;
+ u_int nrecords;
+
+ if (len < sizeof(struct sflow_flow_sample_t))
+ return 1;
+
+ sflow_flow_sample = (const struct sflow_flow_sample_t *)pointer;
+
+ nrecords = GET_BE_U_4(sflow_flow_sample->records);
+
+ ND_PRINT(" seqnum %u, type %u, idx %u, rate %u, pool %u, drops %u, input %u output %u records %u",
+ GET_BE_U_4(sflow_flow_sample->seqnum),
+ GET_U_1(sflow_flow_sample->type),
+ GET_BE_U_3(sflow_flow_sample->index),
+ GET_BE_U_4(sflow_flow_sample->rate),
+ GET_BE_U_4(sflow_flow_sample->pool),
+ GET_BE_U_4(sflow_flow_sample->drops),
+ GET_BE_U_4(sflow_flow_sample->in_interface),
+ GET_BE_U_4(sflow_flow_sample->out_interface),
+ nrecords);
+
+ return sflow_print_flow_records(ndo, pointer + sizeof(struct sflow_flow_sample_t),
+ len - sizeof(struct sflow_flow_sample_t),
+ nrecords);
+}
+
+static int
+sflow_print_expanded_flow_sample(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_expanded_flow_sample_t *sflow_expanded_flow_sample;
+ u_int nrecords;
+
+ if (len < sizeof(struct sflow_expanded_flow_sample_t))
+ return 1;
+
+ sflow_expanded_flow_sample = (const struct sflow_expanded_flow_sample_t *)pointer;
+
+ nrecords = GET_BE_U_4(sflow_expanded_flow_sample->records);
+
+ ND_PRINT(" seqnum %u, type %u, idx %u, rate %u, pool %u, drops %u, records %u",
+ GET_BE_U_4(sflow_expanded_flow_sample->seqnum),
+ GET_BE_U_4(sflow_expanded_flow_sample->type),
+ GET_BE_U_4(sflow_expanded_flow_sample->index),
+ GET_BE_U_4(sflow_expanded_flow_sample->rate),
+ GET_BE_U_4(sflow_expanded_flow_sample->pool),
+ GET_BE_U_4(sflow_expanded_flow_sample->drops),
+ nrecords);
+
+ return sflow_print_flow_records(ndo, pointer + sizeof(struct sflow_expanded_flow_sample_t),
+ len - sizeof(struct sflow_expanded_flow_sample_t),
+ nrecords);
+}
+
+void
+sflow_print(netdissect_options *ndo,
+ const u_char *pptr, u_int len)
+{
+ const struct sflow_datagram_t *sflow_datagram;
+ const struct sflow_v6_datagram_t *sflow_v6_datagram;
+ const struct sflow_sample_header *sflow_sample;
+