print-sflow.c revision 285275
1/*
2 * Copyright (c) 1998-2007 The TCPDUMP project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that: (1) source code
6 * distributions retain the above copyright notice and this paragraph
7 * in its entirety, and (2) distributions including binary code include
8 * the above copyright notice and this paragraph in its entirety in
9 * the documentation or other materials provided with the distribution.
10 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
11 * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
12 * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
13 * FOR A PARTICULAR PURPOSE.
14 *
15 * The SFLOW protocol as per http://www.sflow.org/developers/specifications.php
16 *
17 * Original code by Carles Kishimoto <carles.kishimoto@gmail.com>
18 *
19 * Expansion and refactoring by Rick Jones <rick.jones2@hp.com>
20 */
21
22#define NETDISSECT_REWORKED
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include <tcpdump-stdinc.h>
28
29#include "interface.h"
30#include "extract.h"
31#include "addrtoname.h"
32
33/*
34 * sFlow datagram
35 *
36 * 0                   1                   2                   3
37 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
38 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
39 * |                     Sflow version (2,4,5)                     |
40 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41 * |               IP version (1 for IPv4 | 2 for IPv6)            |
42 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43 * |                     IP Address AGENT (4 or 16 bytes)          |
44 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45 * |                          Sub agent ID                         |
46 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47 * |                      Datagram sequence number                 |
48 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
49 * |                      Switch uptime in ms                      |
50 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
51 * |                    num samples in datagram                    |
52 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
53 *
54 */
55
56struct sflow_datagram_t {
57    uint8_t 	version[4];
58    uint8_t 	ip_version[4];
59    uint8_t 	agent[4];
60    uint8_t 	agent_id[4];
61    uint8_t 	seqnum[4];
62    uint8_t 	uptime[4];
63    uint8_t 	samples[4];
64};
65
66struct sflow_sample_header {
67    uint8_t	format[4];
68    uint8_t	len[4];
69};
70
71#define		SFLOW_FLOW_SAMPLE		1
72#define		SFLOW_COUNTER_SAMPLE		2
73#define		SFLOW_EXPANDED_FLOW_SAMPLE	3
74#define		SFLOW_EXPANDED_COUNTER_SAMPLE	4
75
76static const struct tok sflow_format_values[] = {
77    { SFLOW_FLOW_SAMPLE, "flow sample" },
78    { SFLOW_COUNTER_SAMPLE, "counter sample" },
79    { SFLOW_EXPANDED_FLOW_SAMPLE, "expanded flow sample" },
80    { SFLOW_EXPANDED_COUNTER_SAMPLE, "expanded counter sample" },
81    { 0, NULL}
82};
83
84struct sflow_flow_sample_t {
85    uint8_t    seqnum[4];
86    uint8_t    typesource[4];
87    uint8_t    rate[4];
88    uint8_t    pool[4];
89    uint8_t    drops[4];
90    uint8_t    in_interface[4];
91    uint8_t    out_interface[4];
92    uint8_t    records[4];
93
94};
95
96struct sflow_expanded_flow_sample_t {
97    uint8_t    seqnum[4];
98    uint8_t    type[4];
99    uint8_t    index[4];
100    uint8_t    rate[4];
101    uint8_t    pool[4];
102    uint8_t    drops[4];
103    uint8_t    in_interface_format[4];
104    uint8_t    in_interface_value[4];
105    uint8_t    out_interface_format[4];
106    uint8_t    out_interface_value[4];
107    uint8_t    records[4];
108};
109
110#define 	SFLOW_FLOW_RAW_PACKET			1
111#define 	SFLOW_FLOW_ETHERNET_FRAME		2
112#define 	SFLOW_FLOW_IPV4_DATA			3
113#define 	SFLOW_FLOW_IPV6_DATA			4
114#define 	SFLOW_FLOW_EXTENDED_SWITCH_DATA		1001
115#define 	SFLOW_FLOW_EXTENDED_ROUTER_DATA		1002
116#define 	SFLOW_FLOW_EXTENDED_GATEWAY_DATA 	1003
117#define 	SFLOW_FLOW_EXTENDED_USER_DATA		1004
118#define 	SFLOW_FLOW_EXTENDED_URL_DATA		1005
119#define 	SFLOW_FLOW_EXTENDED_MPLS_DATA		1006
120#define 	SFLOW_FLOW_EXTENDED_NAT_DATA		1007
121#define 	SFLOW_FLOW_EXTENDED_MPLS_TUNNEL		1008
122#define 	SFLOW_FLOW_EXTENDED_MPLS_VC		1009
123#define 	SFLOW_FLOW_EXTENDED_MPLS_FEC		1010
124#define 	SFLOW_FLOW_EXTENDED_MPLS_LVP_FEC	1011
125#define 	SFLOW_FLOW_EXTENDED_VLAN_TUNNEL		1012
126
127static const struct tok sflow_flow_type_values[] = {
128    { SFLOW_FLOW_RAW_PACKET, "Raw packet"},
129    { SFLOW_FLOW_ETHERNET_FRAME, "Ethernet frame"},
130    { SFLOW_FLOW_IPV4_DATA, "IPv4 Data"},
131    { SFLOW_FLOW_IPV6_DATA, "IPv6 Data"},
132    { SFLOW_FLOW_EXTENDED_SWITCH_DATA, "Extended Switch data"},
133    { SFLOW_FLOW_EXTENDED_ROUTER_DATA, "Extended Router data"},
134    { SFLOW_FLOW_EXTENDED_GATEWAY_DATA, "Extended Gateway data"},
135    { SFLOW_FLOW_EXTENDED_USER_DATA, "Extended User data"},
136    { SFLOW_FLOW_EXTENDED_URL_DATA, "Extended URL data"},
137    { SFLOW_FLOW_EXTENDED_MPLS_DATA, "Extended MPLS data"},
138    { SFLOW_FLOW_EXTENDED_NAT_DATA, "Extended NAT data"},
139    { SFLOW_FLOW_EXTENDED_MPLS_TUNNEL, "Extended MPLS tunnel"},
140    { SFLOW_FLOW_EXTENDED_MPLS_VC, "Extended MPLS VC"},
141    { SFLOW_FLOW_EXTENDED_MPLS_FEC, "Extended MPLS FEC"},
142    { SFLOW_FLOW_EXTENDED_MPLS_LVP_FEC, "Extended MPLS LVP FEC"},
143    { SFLOW_FLOW_EXTENDED_VLAN_TUNNEL, "Extended VLAN Tunnel"},
144    { 0, NULL}
145};
146
147#define		SFLOW_HEADER_PROTOCOL_ETHERNET	1
148#define		SFLOW_HEADER_PROTOCOL_IPV4	11
149#define		SFLOW_HEADER_PROTOCOL_IPV6	12
150
151static const struct tok sflow_flow_raw_protocol_values[] = {
152    { SFLOW_HEADER_PROTOCOL_ETHERNET, "Ethernet"},
153    { SFLOW_HEADER_PROTOCOL_IPV4, "IPv4"},
154    { SFLOW_HEADER_PROTOCOL_IPV6, "IPv6"},
155    { 0, NULL}
156};
157
158struct sflow_expanded_flow_raw_t {
159    uint8_t    protocol[4];
160    uint8_t    length[4];
161    uint8_t    stripped_bytes[4];
162    uint8_t    header_size[4];
163};
164
165struct sflow_ethernet_frame_t {
166    uint8_t length[4];
167    uint8_t src_mac[8];
168    uint8_t dst_mac[8];
169    uint8_t type[4];
170};
171
172struct sflow_extended_switch_data_t {
173    uint8_t src_vlan[4];
174    uint8_t src_pri[4];
175    uint8_t dst_vlan[4];
176    uint8_t dst_pri[4];
177};
178
179struct sflow_counter_record_t {
180    uint8_t    format[4];
181    uint8_t    length[4];
182};
183
184struct sflow_flow_record_t {
185    uint8_t    format[4];
186    uint8_t    length[4];
187};
188
189struct sflow_counter_sample_t {
190    uint8_t    seqnum[4];
191    uint8_t    typesource[4];
192    uint8_t    records[4];
193};
194
195struct sflow_expanded_counter_sample_t {
196    uint8_t    seqnum[4];
197    uint8_t    type[4];
198    uint8_t    index[4];
199    uint8_t    records[4];
200};
201
202#define         SFLOW_COUNTER_GENERIC           1
203#define         SFLOW_COUNTER_ETHERNET          2
204#define         SFLOW_COUNTER_TOKEN_RING        3
205#define         SFLOW_COUNTER_BASEVG            4
206#define         SFLOW_COUNTER_VLAN              5
207#define         SFLOW_COUNTER_PROCESSOR         1001
208
209static const struct tok sflow_counter_type_values[] = {
210    { SFLOW_COUNTER_GENERIC, "Generic counter"},
211    { SFLOW_COUNTER_ETHERNET, "Ethernet counter"},
212    { SFLOW_COUNTER_TOKEN_RING, "Token ring counter"},
213    { SFLOW_COUNTER_BASEVG, "100 BaseVG counter"},
214    { SFLOW_COUNTER_VLAN, "Vlan counter"},
215    { SFLOW_COUNTER_PROCESSOR, "Processor counter"},
216    { 0, NULL}
217};
218
219#define		SFLOW_IFACE_DIRECTION_UNKNOWN		0
220#define		SFLOW_IFACE_DIRECTION_FULLDUPLEX	1
221#define		SFLOW_IFACE_DIRECTION_HALFDUPLEX	2
222#define		SFLOW_IFACE_DIRECTION_IN		3
223#define		SFLOW_IFACE_DIRECTION_OUT		4
224
225static const struct tok sflow_iface_direction_values[] = {
226    { SFLOW_IFACE_DIRECTION_UNKNOWN, "unknown"},
227    { SFLOW_IFACE_DIRECTION_FULLDUPLEX, "full-duplex"},
228    { SFLOW_IFACE_DIRECTION_HALFDUPLEX, "half-duplex"},
229    { SFLOW_IFACE_DIRECTION_IN, "in"},
230    { SFLOW_IFACE_DIRECTION_OUT, "out"},
231    { 0, NULL}
232};
233
234struct sflow_generic_counter_t {
235    uint8_t    ifindex[4];
236    uint8_t    iftype[4];
237    uint8_t    ifspeed[8];
238    uint8_t    ifdirection[4];
239    uint8_t    ifstatus[4];
240    uint8_t    ifinoctets[8];
241    uint8_t    ifinunicastpkts[4];
242    uint8_t    ifinmulticastpkts[4];
243    uint8_t    ifinbroadcastpkts[4];
244    uint8_t    ifindiscards[4];
245    uint8_t    ifinerrors[4];
246    uint8_t    ifinunkownprotos[4];
247    uint8_t    ifoutoctets[8];
248    uint8_t    ifoutunicastpkts[4];
249    uint8_t    ifoutmulticastpkts[4];
250    uint8_t    ifoutbroadcastpkts[4];
251    uint8_t    ifoutdiscards[4];
252    uint8_t    ifouterrors[4];
253    uint8_t    ifpromiscmode[4];
254};
255
256struct sflow_ethernet_counter_t {
257    uint8_t    alignerrors[4];
258    uint8_t    fcserrors[4];
259    uint8_t    single_collision_frames[4];
260    uint8_t    multiple_collision_frames[4];
261    uint8_t    test_errors[4];
262    uint8_t    deferred_transmissions[4];
263    uint8_t    late_collisions[4];
264    uint8_t    excessive_collisions[4];
265    uint8_t    mac_transmit_errors[4];
266    uint8_t    carrier_sense_errors[4];
267    uint8_t    frame_too_longs[4];
268    uint8_t    mac_receive_errors[4];
269    uint8_t    symbol_errors[4];
270};
271
272struct sflow_100basevg_counter_t {
273    uint8_t    in_highpriority_frames[4];
274    uint8_t    in_highpriority_octets[8];
275    uint8_t    in_normpriority_frames[4];
276    uint8_t    in_normpriority_octets[8];
277    uint8_t    in_ipmerrors[4];
278    uint8_t    in_oversized[4];
279    uint8_t    in_data_errors[4];
280    uint8_t    in_null_addressed_frames[4];
281    uint8_t    out_highpriority_frames[4];
282    uint8_t    out_highpriority_octets[8];
283    uint8_t    transitioninto_frames[4];
284    uint8_t    hc_in_highpriority_octets[8];
285    uint8_t    hc_in_normpriority_octets[8];
286    uint8_t    hc_out_highpriority_octets[8];
287};
288
289struct sflow_vlan_counter_t {
290    uint8_t    vlan_id[4];
291    uint8_t    octets[8];
292    uint8_t    unicast_pkt[4];
293    uint8_t    multicast_pkt[4];
294    uint8_t    broadcast_pkt[4];
295    uint8_t    discards[4];
296};
297
298static int
299print_sflow_counter_generic(netdissect_options *ndo,
300                            const u_char *pointer, u_int len)
301{
302    const struct sflow_generic_counter_t *sflow_gen_counter;
303
304    if (len < sizeof(struct sflow_generic_counter_t))
305	return 1;
306
307
308    sflow_gen_counter = (const struct sflow_generic_counter_t *)pointer;
309    ND_PRINT((ndo, "\n\t      ifindex %u, iftype %u, ifspeed %" PRIu64 ", ifdirection %u (%s)",
310	   EXTRACT_32BITS(sflow_gen_counter->ifindex),
311	   EXTRACT_32BITS(sflow_gen_counter->iftype),
312	   EXTRACT_64BITS(sflow_gen_counter->ifspeed),
313	   EXTRACT_32BITS(sflow_gen_counter->ifdirection),
314	   tok2str(sflow_iface_direction_values, "Unknown",
315	   EXTRACT_32BITS(sflow_gen_counter->ifdirection))));
316    ND_PRINT((ndo, "\n\t      ifstatus %u, adminstatus: %s, operstatus: %s",
317	   EXTRACT_32BITS(sflow_gen_counter->ifstatus),
318	   EXTRACT_32BITS(sflow_gen_counter->ifstatus)&1 ? "up" : "down",
319	   (EXTRACT_32BITS(sflow_gen_counter->ifstatus)>>1)&1 ? "up" : "down"));
320    ND_PRINT((ndo, "\n\t      In octets %" PRIu64
321	   ", unicast pkts %u, multicast pkts %u, broadcast pkts %u, discards %u",
322	   EXTRACT_64BITS(sflow_gen_counter->ifinoctets),
323	   EXTRACT_32BITS(sflow_gen_counter->ifinunicastpkts),
324	   EXTRACT_32BITS(sflow_gen_counter->ifinmulticastpkts),
325	   EXTRACT_32BITS(sflow_gen_counter->ifinbroadcastpkts),
326	   EXTRACT_32BITS(sflow_gen_counter->ifindiscards)));
327    ND_PRINT((ndo, "\n\t      In errors %u, unknown protos %u",
328	   EXTRACT_32BITS(sflow_gen_counter->ifinerrors),
329	   EXTRACT_32BITS(sflow_gen_counter->ifinunkownprotos)));
330    ND_PRINT((ndo, "\n\t      Out octets %" PRIu64
331	   ", unicast pkts %u, multicast pkts %u, broadcast pkts %u, discards %u",
332	   EXTRACT_64BITS(sflow_gen_counter->ifoutoctets),
333	   EXTRACT_32BITS(sflow_gen_counter->ifoutunicastpkts),
334	   EXTRACT_32BITS(sflow_gen_counter->ifoutmulticastpkts),
335	   EXTRACT_32BITS(sflow_gen_counter->ifoutbroadcastpkts),
336	   EXTRACT_32BITS(sflow_gen_counter->ifoutdiscards)));
337    ND_PRINT((ndo, "\n\t      Out errors %u, promisc mode %u",
338	   EXTRACT_32BITS(sflow_gen_counter->ifouterrors),
339	   EXTRACT_32BITS(sflow_gen_counter->ifpromiscmode)));
340
341    return 0;
342}
343
344static int
345print_sflow_counter_ethernet(netdissect_options *ndo,
346                             const u_char *pointer, u_int len)
347{
348    const struct sflow_ethernet_counter_t *sflow_eth_counter;
349
350    if (len < sizeof(struct sflow_ethernet_counter_t))
351	return 1;
352
353    sflow_eth_counter = (const struct sflow_ethernet_counter_t *)pointer;
354    ND_PRINT((ndo, "\n\t      align errors %u, fcs errors %u, single collision %u, multiple collision %u, test error %u",
355	   EXTRACT_32BITS(sflow_eth_counter->alignerrors),
356	   EXTRACT_32BITS(sflow_eth_counter->fcserrors),
357	   EXTRACT_32BITS(sflow_eth_counter->single_collision_frames),
358	   EXTRACT_32BITS(sflow_eth_counter->multiple_collision_frames),
359	   EXTRACT_32BITS(sflow_eth_counter->test_errors)));
360    ND_PRINT((ndo, "\n\t      deferred %u, late collision %u, excessive collision %u, mac trans error %u",
361	   EXTRACT_32BITS(sflow_eth_counter->deferred_transmissions),
362	   EXTRACT_32BITS(sflow_eth_counter->late_collisions),
363	   EXTRACT_32BITS(sflow_eth_counter->excessive_collisions),
364	   EXTRACT_32BITS(sflow_eth_counter->mac_transmit_errors)));
365    ND_PRINT((ndo, "\n\t      carrier error %u, frames too long %u, mac receive errors %u, symbol errors %u",
366	   EXTRACT_32BITS(sflow_eth_counter->carrier_sense_errors),
367	   EXTRACT_32BITS(sflow_eth_counter->frame_too_longs),
368	   EXTRACT_32BITS(sflow_eth_counter->mac_receive_errors),
369	   EXTRACT_32BITS(sflow_eth_counter->symbol_errors)));
370
371    return 0;
372}
373
374static int
375print_sflow_counter_token_ring(netdissect_options *ndo _U_,
376                               const u_char *pointer _U_, u_int len _U_)
377{
378    return 0;
379}
380
381static int
382print_sflow_counter_basevg(netdissect_options *ndo,
383                           const u_char *pointer, u_int len)
384{
385    const struct sflow_100basevg_counter_t *sflow_100basevg_counter;
386
387    if (len < sizeof(struct sflow_100basevg_counter_t))
388	return 1;
389
390    sflow_100basevg_counter = (const struct sflow_100basevg_counter_t *)pointer;
391    ND_PRINT((ndo, "\n\t      in high prio frames %u, in high prio octets %" PRIu64,
392	   EXTRACT_32BITS(sflow_100basevg_counter->in_highpriority_frames),
393	   EXTRACT_64BITS(sflow_100basevg_counter->in_highpriority_octets)));
394    ND_PRINT((ndo, "\n\t      in norm prio frames %u, in norm prio octets %" PRIu64,
395	   EXTRACT_32BITS(sflow_100basevg_counter->in_normpriority_frames),
396	   EXTRACT_64BITS(sflow_100basevg_counter->in_normpriority_octets)));
397    ND_PRINT((ndo, "\n\t      in ipm errors %u, oversized %u, in data errors %u, null addressed frames %u",
398	   EXTRACT_32BITS(sflow_100basevg_counter->in_ipmerrors),
399	   EXTRACT_32BITS(sflow_100basevg_counter->in_oversized),
400	   EXTRACT_32BITS(sflow_100basevg_counter->in_data_errors),
401	   EXTRACT_32BITS(sflow_100basevg_counter->in_null_addressed_frames)));
402    ND_PRINT((ndo, "\n\t      out high prio frames %u, out high prio octets %" PRIu64
403	   ", trans into frames %u",
404	   EXTRACT_32BITS(sflow_100basevg_counter->out_highpriority_frames),
405	   EXTRACT_64BITS(sflow_100basevg_counter->out_highpriority_octets),
406	   EXTRACT_32BITS(sflow_100basevg_counter->transitioninto_frames)));
407    ND_PRINT((ndo, "\n\t      in hc high prio octets %" PRIu64
408	   ", in hc norm prio octets %" PRIu64
409	   ", out hc high prio octets %" PRIu64,
410	   EXTRACT_64BITS(sflow_100basevg_counter->hc_in_highpriority_octets),
411	   EXTRACT_64BITS(sflow_100basevg_counter->hc_in_normpriority_octets),
412	   EXTRACT_64BITS(sflow_100basevg_counter->hc_out_highpriority_octets)));
413
414    return 0;
415}
416
417static int
418print_sflow_counter_vlan(netdissect_options *ndo,
419                         const u_char *pointer, u_int len)
420{
421    const struct sflow_vlan_counter_t *sflow_vlan_counter;
422
423    if (len < sizeof(struct sflow_vlan_counter_t))
424	return 1;
425
426    sflow_vlan_counter = (const struct sflow_vlan_counter_t *)pointer;
427    ND_PRINT((ndo, "\n\t      vlan_id %u, octets %" PRIu64
428	   ", unicast_pkt %u, multicast_pkt %u, broadcast_pkt %u, discards %u",
429	   EXTRACT_32BITS(sflow_vlan_counter->vlan_id),
430	   EXTRACT_64BITS(sflow_vlan_counter->octets),
431	   EXTRACT_32BITS(sflow_vlan_counter->unicast_pkt),
432	   EXTRACT_32BITS(sflow_vlan_counter->multicast_pkt),
433	   EXTRACT_32BITS(sflow_vlan_counter->broadcast_pkt),
434	   EXTRACT_32BITS(sflow_vlan_counter->discards)));
435
436    return 0;
437}
438
439struct sflow_processor_counter_t {
440    uint8_t five_sec_util[4];
441    uint8_t one_min_util[4];
442    uint8_t five_min_util[4];
443    uint8_t total_memory[8];
444    uint8_t free_memory[8];
445};
446
447static int
448print_sflow_counter_processor(netdissect_options *ndo,
449                              const u_char *pointer, u_int len)
450{
451    const struct sflow_processor_counter_t *sflow_processor_counter;
452
453    if (len < sizeof(struct sflow_processor_counter_t))
454	return 1;
455
456    sflow_processor_counter = (const struct sflow_processor_counter_t *)pointer;
457    ND_PRINT((ndo, "\n\t      5sec %u, 1min %u, 5min %u, total_mem %" PRIu64
458	   ", total_mem %" PRIu64,
459	   EXTRACT_32BITS(sflow_processor_counter->five_sec_util),
460	   EXTRACT_32BITS(sflow_processor_counter->one_min_util),
461	   EXTRACT_32BITS(sflow_processor_counter->five_min_util),
462	   EXTRACT_64BITS(sflow_processor_counter->total_memory),
463	   EXTRACT_64BITS(sflow_processor_counter->free_memory)));
464
465    return 0;
466}
467
468static int
469sflow_print_counter_records(netdissect_options *ndo,
470                            const u_char *pointer, u_int len, u_int records)
471{
472    u_int nrecords;
473    const u_char *tptr;
474    u_int tlen;
475    u_int counter_type;
476    u_int counter_len;
477    u_int enterprise;
478    const struct sflow_counter_record_t *sflow_counter_record;
479
480    nrecords = records;
481    tptr = pointer;
482    tlen = len;
483
484    while (nrecords > 0) {
485	/* do we have the "header?" */
486	if (tlen < sizeof(struct sflow_counter_record_t))
487	    return 1;
488	sflow_counter_record = (const struct sflow_counter_record_t *)tptr;
489
490	enterprise = EXTRACT_32BITS(sflow_counter_record->format);
491	counter_type = enterprise & 0x0FFF;
492	enterprise = enterprise >> 20;
493	counter_len  = EXTRACT_32BITS(sflow_counter_record->length);
494	ND_PRINT((ndo, "\n\t    enterprise %u, %s (%u) length %u",
495	       enterprise,
496	       (enterprise == 0) ? tok2str(sflow_counter_type_values,"Unknown",counter_type) : "Unknown",
497	       counter_type,
498	       counter_len));
499
500	tptr += sizeof(struct sflow_counter_record_t);
501	tlen -= sizeof(struct sflow_counter_record_t);
502
503	if (tlen < counter_len)
504	    return 1;
505	if (enterprise == 0) {
506	    switch (counter_type) {
507	    case SFLOW_COUNTER_GENERIC:
508		if (print_sflow_counter_generic(ndo, tptr, tlen))
509		    return 1;
510		break;
511	    case SFLOW_COUNTER_ETHERNET:
512		if (print_sflow_counter_ethernet(ndo, tptr, tlen))
513		    return 1;
514		break;
515	    case SFLOW_COUNTER_TOKEN_RING:
516		if (print_sflow_counter_token_ring(ndo, tptr,tlen))
517		    return 1;
518		break;
519	    case SFLOW_COUNTER_BASEVG:
520		if (print_sflow_counter_basevg(ndo, tptr, tlen))
521		    return 1;
522		break;
523	    case SFLOW_COUNTER_VLAN:
524		if (print_sflow_counter_vlan(ndo, tptr, tlen))
525		    return 1;
526		break;
527	    case SFLOW_COUNTER_PROCESSOR:
528		if (print_sflow_counter_processor(ndo, tptr, tlen))
529		    return 1;
530		break;
531	    default:
532		if (ndo->ndo_vflag <= 1)
533		    print_unknown_data(ndo, tptr, "\n\t\t", counter_len);
534		break;
535	    }
536	}
537	tptr += counter_len;
538	tlen -= counter_len;
539	nrecords--;
540
541    }
542
543    return 0;
544}
545
546static int
547sflow_print_counter_sample(netdissect_options *ndo,
548                           const u_char *pointer, u_int len)
549{
550    const struct sflow_counter_sample_t *sflow_counter_sample;
551    u_int           nrecords;
552    u_int           typesource;
553    u_int           type;
554    u_int           index;
555
556
557    if (len < sizeof(struct sflow_counter_sample_t))
558	return 1;
559
560    sflow_counter_sample = (const struct sflow_counter_sample_t *)pointer;
561
562    typesource = EXTRACT_32BITS(sflow_counter_sample->typesource);
563    nrecords   = EXTRACT_32BITS(sflow_counter_sample->records);
564    type = typesource >> 24;
565    index = typesource & 0x0FFF;
566
567    ND_PRINT((ndo, " seqnum %u, type %u, idx %u, records %u",
568	   EXTRACT_32BITS(sflow_counter_sample->seqnum),
569	   type,
570	   index,
571	   nrecords));
572
573    return sflow_print_counter_records(ndo, pointer + sizeof(struct sflow_counter_sample_t),
574				       len - sizeof(struct sflow_counter_sample_t),
575				       nrecords);
576
577}
578
579static int
580sflow_print_expanded_counter_sample(netdissect_options *ndo,
581                                    const u_char *pointer, u_int len)
582{
583    const struct sflow_expanded_counter_sample_t *sflow_expanded_counter_sample;
584    u_int           nrecords;
585
586
587    if (len < sizeof(struct sflow_expanded_counter_sample_t))
588	return 1;
589
590    sflow_expanded_counter_sample = (const struct sflow_expanded_counter_sample_t *)pointer;
591
592    nrecords = EXTRACT_32BITS(sflow_expanded_counter_sample->records);
593
594    ND_PRINT((ndo, " seqnum %u, type %u, idx %u, records %u",
595	   EXTRACT_32BITS(sflow_expanded_counter_sample->seqnum),
596	   EXTRACT_32BITS(sflow_expanded_counter_sample->type),
597	   EXTRACT_32BITS(sflow_expanded_counter_sample->index),
598	   nrecords));
599
600    return sflow_print_counter_records(ndo, pointer + sizeof(struct sflow_expanded_counter_sample_t),
601				       len - sizeof(struct sflow_expanded_counter_sample_t),
602				       nrecords);
603
604}
605
606static int
607print_sflow_raw_packet(netdissect_options *ndo,
608                       const u_char *pointer, u_int len)
609{
610    const struct sflow_expanded_flow_raw_t *sflow_flow_raw;
611
612    if (len < sizeof(struct sflow_expanded_flow_raw_t))
613	return 1;
614
615    sflow_flow_raw = (const struct sflow_expanded_flow_raw_t *)pointer;
616    ND_PRINT((ndo, "\n\t      protocol %s (%u), length %u, stripped bytes %u, header_size %u",
617	   tok2str(sflow_flow_raw_protocol_values,"Unknown",EXTRACT_32BITS(sflow_flow_raw->protocol)),
618	   EXTRACT_32BITS(sflow_flow_raw->protocol),
619	   EXTRACT_32BITS(sflow_flow_raw->length),
620	   EXTRACT_32BITS(sflow_flow_raw->stripped_bytes),
621	   EXTRACT_32BITS(sflow_flow_raw->header_size)));
622
623    /* QUESTION - should we attempt to print the raw header itself?
624       assuming of course there is wnough data present to do so... */
625
626    return 0;
627}
628
629static int
630print_sflow_ethernet_frame(netdissect_options *ndo,
631                           const u_char *pointer, u_int len)
632{
633    const struct sflow_ethernet_frame_t *sflow_ethernet_frame;
634
635    if (len < sizeof(struct sflow_ethernet_frame_t))
636	return 1;
637
638    sflow_ethernet_frame = (const struct sflow_ethernet_frame_t *)pointer;
639
640    ND_PRINT((ndo, "\n\t      frame len %u, type %u",
641	   EXTRACT_32BITS(sflow_ethernet_frame->length),
642	   EXTRACT_32BITS(sflow_ethernet_frame->type)));
643
644    return 0;
645}
646
647static int
648print_sflow_extended_switch_data(netdissect_options *ndo,
649                                 const u_char *pointer, u_int len)
650{
651    const struct sflow_extended_switch_data_t *sflow_extended_sw_data;
652
653    if (len < sizeof(struct sflow_extended_switch_data_t))
654	return 1;
655
656    sflow_extended_sw_data = (const struct sflow_extended_switch_data_t *)pointer;
657    ND_PRINT((ndo, "\n\t      src vlan %u, src pri %u, dst vlan %u, dst pri %u",
658	   EXTRACT_32BITS(sflow_extended_sw_data->src_vlan),
659	   EXTRACT_32BITS(sflow_extended_sw_data->src_pri),
660	   EXTRACT_32BITS(sflow_extended_sw_data->dst_vlan),
661	   EXTRACT_32BITS(sflow_extended_sw_data->dst_pri)));
662
663    return 0;
664}
665
666static int
667sflow_print_flow_records(netdissect_options *ndo,
668                         const u_char *pointer, u_int len, u_int records)
669{
670    u_int nrecords;
671    const u_char *tptr;
672    u_int tlen;
673    u_int flow_type;
674    u_int enterprise;
675    u_int flow_len;
676    const struct sflow_flow_record_t *sflow_flow_record;
677
678    nrecords = records;
679    tptr = pointer;
680    tlen = len;
681
682    while (nrecords > 0) {
683	/* do we have the "header?" */
684	if (tlen < sizeof(struct sflow_flow_record_t))
685	    return 1;
686
687	sflow_flow_record = (const struct sflow_flow_record_t *)tptr;
688
689	/* so, the funky encoding means we cannot blythly mask-off
690	   bits, we must also check the enterprise. */
691
692	enterprise = EXTRACT_32BITS(sflow_flow_record->format);
693	flow_type = enterprise & 0x0FFF;
694	enterprise = enterprise >> 12;
695	flow_len  = EXTRACT_32BITS(sflow_flow_record->length);
696	ND_PRINT((ndo, "\n\t    enterprise %u %s (%u) length %u",
697	       enterprise,
698	       (enterprise == 0) ? tok2str(sflow_flow_type_values,"Unknown",flow_type) : "Unknown",
699	       flow_type,
700	       flow_len));
701
702	tptr += sizeof(struct sflow_flow_record_t);
703	tlen -= sizeof(struct sflow_flow_record_t);
704
705	if (tlen < flow_len)
706	    return 1;
707
708	if (enterprise == 0) {
709	    switch (flow_type) {
710	    case SFLOW_FLOW_RAW_PACKET:
711		if (print_sflow_raw_packet(ndo, tptr, tlen))
712		    return 1;
713		break;
714	    case SFLOW_FLOW_EXTENDED_SWITCH_DATA:
715		if (print_sflow_extended_switch_data(ndo, tptr, tlen))
716		    return 1;
717		break;
718	    case SFLOW_FLOW_ETHERNET_FRAME:
719		if (print_sflow_ethernet_frame(ndo, tptr, tlen))
720		    return 1;
721		break;
722		/* FIXME these need a decoder */
723	    case SFLOW_FLOW_IPV4_DATA:
724	    case SFLOW_FLOW_IPV6_DATA:
725	    case SFLOW_FLOW_EXTENDED_ROUTER_DATA:
726	    case SFLOW_FLOW_EXTENDED_GATEWAY_DATA:
727	    case SFLOW_FLOW_EXTENDED_USER_DATA:
728	    case SFLOW_FLOW_EXTENDED_URL_DATA:
729	    case SFLOW_FLOW_EXTENDED_MPLS_DATA:
730	    case SFLOW_FLOW_EXTENDED_NAT_DATA:
731	    case SFLOW_FLOW_EXTENDED_MPLS_TUNNEL:
732	    case SFLOW_FLOW_EXTENDED_MPLS_VC:
733	    case SFLOW_FLOW_EXTENDED_MPLS_FEC:
734	    case SFLOW_FLOW_EXTENDED_MPLS_LVP_FEC:
735	    case SFLOW_FLOW_EXTENDED_VLAN_TUNNEL:
736		break;
737	    default:
738		if (ndo->ndo_vflag <= 1)
739		    print_unknown_data(ndo, tptr, "\n\t\t", flow_len);
740		break;
741	    }
742	}
743	tptr += flow_len;
744	tlen -= flow_len;
745	nrecords--;
746
747    }
748
749    return 0;
750}
751
752static int
753sflow_print_flow_sample(netdissect_options *ndo,
754                        const u_char *pointer, u_int len)
755{
756    const struct sflow_flow_sample_t *sflow_flow_sample;
757    u_int          nrecords;
758    u_int          typesource;
759    u_int          type;
760    u_int          index;
761
762    if (len < sizeof(struct sflow_flow_sample_t))
763	return 1;
764
765    sflow_flow_sample = (struct sflow_flow_sample_t *)pointer;
766
767    typesource = EXTRACT_32BITS(sflow_flow_sample->typesource);
768    nrecords = EXTRACT_32BITS(sflow_flow_sample->records);
769    type = typesource >> 24;
770    index = typesource & 0x0FFF;
771
772    ND_PRINT((ndo, " seqnum %u, type %u, idx %u, rate %u, pool %u, drops %u, input %u output %u records %u",
773	   EXTRACT_32BITS(sflow_flow_sample->seqnum),
774	   type,
775	   index,
776	   EXTRACT_32BITS(sflow_flow_sample->rate),
777	   EXTRACT_32BITS(sflow_flow_sample->pool),
778	   EXTRACT_32BITS(sflow_flow_sample->drops),
779	   EXTRACT_32BITS(sflow_flow_sample->in_interface),
780	   EXTRACT_32BITS(sflow_flow_sample->out_interface),
781	   nrecords));
782
783    return sflow_print_flow_records(ndo, pointer + sizeof(struct sflow_flow_sample_t),
784				    len - sizeof(struct sflow_flow_sample_t),
785				    nrecords);
786
787}
788
789static int
790sflow_print_expanded_flow_sample(netdissect_options *ndo,
791                                 const u_char *pointer, u_int len)
792{
793    const struct sflow_expanded_flow_sample_t *sflow_expanded_flow_sample;
794    u_int nrecords;
795
796    if (len < sizeof(struct sflow_expanded_flow_sample_t))
797	return 1;
798
799    sflow_expanded_flow_sample = (const struct sflow_expanded_flow_sample_t *)pointer;
800
801    nrecords = EXTRACT_32BITS(sflow_expanded_flow_sample->records);
802
803    ND_PRINT((ndo, " seqnum %u, type %u, idx %u, rate %u, pool %u, drops %u, records %u",
804	   EXTRACT_32BITS(sflow_expanded_flow_sample->seqnum),
805	   EXTRACT_32BITS(sflow_expanded_flow_sample->type),
806	   EXTRACT_32BITS(sflow_expanded_flow_sample->index),
807	   EXTRACT_32BITS(sflow_expanded_flow_sample->rate),
808	   EXTRACT_32BITS(sflow_expanded_flow_sample->pool),
809	   EXTRACT_32BITS(sflow_expanded_flow_sample->drops),
810	   EXTRACT_32BITS(sflow_expanded_flow_sample->records)));
811
812    return sflow_print_flow_records(ndo, pointer + sizeof(struct sflow_expanded_flow_sample_t),
813				    len - sizeof(struct sflow_expanded_flow_sample_t),
814				    nrecords);
815
816}
817
818void
819sflow_print(netdissect_options *ndo,
820            const u_char *pptr, u_int len)
821{
822    const struct sflow_datagram_t *sflow_datagram;
823    const struct sflow_sample_header *sflow_sample;
824
825    const u_char *tptr;
826    u_int tlen;
827    uint32_t sflow_sample_type, sflow_sample_len;
828    uint32_t nsamples;
829
830
831    tptr = pptr;
832    tlen = len;
833    sflow_datagram = (const struct sflow_datagram_t *)pptr;
834    ND_TCHECK(*sflow_datagram);
835
836    /*
837     * Sanity checking of the header.
838     */
839    if (EXTRACT_32BITS(sflow_datagram->version) != 5) {
840        ND_PRINT((ndo, "sFlow version %u packet not supported",
841               EXTRACT_32BITS(sflow_datagram->version)));
842        return;
843    }
844
845    if (ndo->ndo_vflag < 1) {
846        ND_PRINT((ndo, "sFlowv%u, %s agent %s, agent-id %u, length %u",
847               EXTRACT_32BITS(sflow_datagram->version),
848               EXTRACT_32BITS(sflow_datagram->ip_version) == 1 ? "IPv4" : "IPv6",
849               ipaddr_string(ndo, sflow_datagram->agent),
850               EXTRACT_32BITS(sflow_datagram->agent_id),
851               len));
852        return;
853    }
854
855    /* ok they seem to want to know everything - lets fully decode it */
856    nsamples=EXTRACT_32BITS(sflow_datagram->samples);
857    ND_PRINT((ndo, "sFlowv%u, %s agent %s, agent-id %u, seqnum %u, uptime %u, samples %u, length %u",
858           EXTRACT_32BITS(sflow_datagram->version),
859           EXTRACT_32BITS(sflow_datagram->ip_version) == 1 ? "IPv4" : "IPv6",
860           ipaddr_string(ndo, sflow_datagram->agent),
861           EXTRACT_32BITS(sflow_datagram->agent_id),
862           EXTRACT_32BITS(sflow_datagram->seqnum),
863           EXTRACT_32BITS(sflow_datagram->uptime),
864           nsamples,
865           len));
866
867    /* skip Common header */
868    tptr += sizeof(const struct sflow_datagram_t);
869    tlen -= sizeof(const struct sflow_datagram_t);
870
871    while (nsamples > 0 && tlen > 0) {
872        sflow_sample = (const struct sflow_sample_header *)tptr;
873        ND_TCHECK(*sflow_sample);
874
875        sflow_sample_type = (EXTRACT_32BITS(sflow_sample->format)&0x0FFF);
876        sflow_sample_len = EXTRACT_32BITS(sflow_sample->len);
877
878	if (tlen < sizeof(struct sflow_sample_header))
879	    goto trunc;
880
881        tptr += sizeof(struct sflow_sample_header);
882        tlen -= sizeof(struct sflow_sample_header);
883
884        ND_PRINT((ndo, "\n\t%s (%u), length %u,",
885               tok2str(sflow_format_values, "Unknown", sflow_sample_type),
886               sflow_sample_type,
887               sflow_sample_len));
888
889        /* basic sanity check */
890        if (sflow_sample_type == 0 || sflow_sample_len ==0) {
891            return;
892        }
893
894	if (tlen < sflow_sample_len)
895	    goto trunc;
896
897        /* did we capture enough for fully decoding the sample ? */
898        ND_TCHECK2(*tptr, sflow_sample_len);
899
900	switch(sflow_sample_type) {
901        case SFLOW_FLOW_SAMPLE:
902	    if (sflow_print_flow_sample(ndo, tptr, tlen))
903		goto trunc;
904            break;
905
906        case SFLOW_COUNTER_SAMPLE:
907	    if (sflow_print_counter_sample(ndo, tptr,tlen))
908		goto trunc;
909            break;
910
911        case SFLOW_EXPANDED_FLOW_SAMPLE:
912	    if (sflow_print_expanded_flow_sample(ndo, tptr, tlen))
913		goto trunc;
914	    break;
915
916        case SFLOW_EXPANDED_COUNTER_SAMPLE:
917	    if (sflow_print_expanded_counter_sample(ndo, tptr,tlen))
918		goto trunc;
919	    break;
920
921        default:
922            if (ndo->ndo_vflag <= 1)
923                print_unknown_data(ndo, tptr, "\n\t    ", sflow_sample_len);
924            break;
925        }
926        tptr += sflow_sample_len;
927        tlen -= sflow_sample_len;
928        nsamples--;
929    }
930    return;
931
932 trunc:
933    ND_PRINT((ndo, "[|SFLOW]"));
934}
935
936/*
937 * Local Variables:
938 * c-style: whitesmith
939 * c-basic-offset: 4
940 * End:
941 */
942