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