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