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