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
print_sflow_counter_generic(netdissect_options * ndo,const u_char * pointer,u_int len)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
print_sflow_counter_ethernet(netdissect_options * ndo,const u_char * pointer,u_int len)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
print_sflow_counter_token_ring(netdissect_options * ndo _U_,const u_char * pointer _U_,u_int len _U_)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
print_sflow_counter_basevg(netdissect_options * ndo,const u_char * pointer,u_int len)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
print_sflow_counter_vlan(netdissect_options * ndo,const u_char * pointer,u_int len)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
print_sflow_counter_processor(netdissect_options * ndo,const u_char * pointer,u_int len)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
sflow_print_counter_records(netdissect_options * ndo,const u_char * pointer,u_int len,u_int records)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
sflow_print_counter_sample(netdissect_options * ndo,const u_char * pointer,u_int len)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
sflow_print_expanded_counter_sample(netdissect_options * ndo,const u_char * pointer,u_int len)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
print_sflow_raw_packet(netdissect_options * ndo,const u_char * pointer,u_int len)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
print_sflow_ethernet_frame(netdissect_options * ndo,const u_char * pointer,u_int len)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
print_sflow_extended_switch_data(netdissect_options * ndo,const u_char * pointer,u_int len)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
sflow_print_flow_records(netdissect_options * ndo,const u_char * pointer,u_int len,u_int records)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
sflow_print_flow_sample(netdissect_options * ndo,const u_char * pointer,u_int len)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
sflow_print_expanded_flow_sample(netdissect_options * ndo,const u_char * pointer,u_int len)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
sflow_print(netdissect_options * ndo,const u_char * pptr,u_int len)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