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