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