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