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