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