1 /* $OpenBSD: print-cnfp.c,v 1.2 1998/06/25 20:26:59 mickey Exp $ */ 2 3 /* 4 * Copyright (c) 1998 Michael Shalayeff 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Michael Shalayeff. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* \summary: Cisco NetFlow protocol printer */ 34 35 /* 36 * Cisco NetFlow protocol 37 * 38 * See 39 * 40 * https://www.cisco.com/c/en/us/td/docs/net_mgmt/netflow_collection_engine/3-6/user/guide/format.html#wp1005892 41 */ 42 43 #ifdef HAVE_CONFIG_H 44 #include <config.h> 45 #endif 46 47 #include "netdissect-stdinc.h" 48 49 #include <stdio.h> 50 51 #include "netdissect.h" 52 #include "addrtoname.h" 53 #include "extract.h" 54 55 #include "tcp.h" 56 #include "ipproto.h" 57 58 struct nfhdr_v1 { 59 nd_uint16_t version; /* version number */ 60 nd_uint16_t count; /* # of records */ 61 nd_uint32_t msys_uptime; 62 nd_uint32_t utc_sec; 63 nd_uint32_t utc_nsec; 64 }; 65 66 struct nfrec_v1 { 67 nd_ipv4 src_ina; 68 nd_ipv4 dst_ina; 69 nd_ipv4 nhop_ina; 70 nd_uint16_t input; /* SNMP index of input interface */ 71 nd_uint16_t output; /* SNMP index of output interface */ 72 nd_uint32_t packets; /* packets in the flow */ 73 nd_uint32_t octets; /* layer 3 octets in the packets of the flow */ 74 nd_uint32_t start_time; /* sys_uptime value at start of flow */ 75 nd_uint32_t last_time; /* sys_uptime value when last packet of flow was received */ 76 nd_uint16_t srcport; /* TCP/UDP source port or equivalent */ 77 nd_uint16_t dstport; /* TCP/UDP source port or equivalent */ 78 nd_byte pad1[2]; /* pad */ 79 nd_uint8_t proto; /* IP protocol type */ 80 nd_uint8_t tos; /* IP type of service */ 81 nd_uint8_t tcp_flags; /* cumulative OR of TCP flags */ 82 nd_byte pad[3]; /* padding */ 83 nd_uint32_t reserved; /* unused */ 84 }; 85 86 struct nfhdr_v5 { 87 nd_uint16_t version; /* version number */ 88 nd_uint16_t count; /* # of records */ 89 nd_uint32_t msys_uptime; 90 nd_uint32_t utc_sec; 91 nd_uint32_t utc_nsec; 92 nd_uint32_t sequence; /* flow sequence number */ 93 nd_uint8_t engine_type; /* type of flow-switching engine */ 94 nd_uint8_t engine_id; /* slot number of the flow-switching engine */ 95 nd_uint16_t sampling_interval; /* sampling mode and interval */ 96 }; 97 98 struct nfrec_v5 { 99 nd_ipv4 src_ina; 100 nd_ipv4 dst_ina; 101 nd_ipv4 nhop_ina; 102 nd_uint16_t input; /* SNMP index of input interface */ 103 nd_uint16_t output; /* SNMP index of output interface */ 104 nd_uint32_t packets; /* packets in the flow */ 105 nd_uint32_t octets; /* layer 3 octets in the packets of the flow */ 106 nd_uint32_t start_time; /* sys_uptime value at start of flow */ 107 nd_uint32_t last_time; /* sys_uptime value when last packet of flow was received */ 108 nd_uint16_t srcport; /* TCP/UDP source port or equivalent */ 109 nd_uint16_t dstport; /* TCP/UDP source port or equivalent */ 110 nd_byte pad1; /* pad */ 111 nd_uint8_t tcp_flags; /* cumulative OR of TCP flags */ 112 nd_uint8_t proto; /* IP protocol type */ 113 nd_uint8_t tos; /* IP type of service */ 114 nd_uint16_t src_as; /* AS number of the source */ 115 nd_uint16_t dst_as; /* AS number of the destination */ 116 nd_uint8_t src_mask; /* source address mask bits */ 117 nd_uint8_t dst_mask; /* destination address prefix mask bits */ 118 nd_byte pad2[2]; 119 nd_ipv4 peer_nexthop; /* v6: IP address of the nexthop within the peer (FIB)*/ 120 }; 121 122 struct nfhdr_v6 { 123 nd_uint16_t version; /* version number */ 124 nd_uint16_t count; /* # of records */ 125 nd_uint32_t msys_uptime; 126 nd_uint32_t utc_sec; 127 nd_uint32_t utc_nsec; 128 nd_uint32_t sequence; /* v5 flow sequence number */ 129 nd_uint32_t reserved; /* v5 only */ 130 }; 131 132 struct nfrec_v6 { 133 nd_ipv4 src_ina; 134 nd_ipv4 dst_ina; 135 nd_ipv4 nhop_ina; 136 nd_uint16_t input; /* SNMP index of input interface */ 137 nd_uint16_t output; /* SNMP index of output interface */ 138 nd_uint32_t packets; /* packets in the flow */ 139 nd_uint32_t octets; /* layer 3 octets in the packets of the flow */ 140 nd_uint32_t start_time; /* sys_uptime value at start of flow */ 141 nd_uint32_t last_time; /* sys_uptime value when last packet of flow was received */ 142 nd_uint16_t srcport; /* TCP/UDP source port or equivalent */ 143 nd_uint16_t dstport; /* TCP/UDP source port or equivalent */ 144 nd_byte pad1; /* pad */ 145 nd_uint8_t tcp_flags; /* cumulative OR of TCP flags */ 146 nd_uint8_t proto; /* IP protocol type */ 147 nd_uint8_t tos; /* IP type of service */ 148 nd_uint16_t src_as; /* AS number of the source */ 149 nd_uint16_t dst_as; /* AS number of the destination */ 150 nd_uint8_t src_mask; /* source address mask bits */ 151 nd_uint8_t dst_mask; /* destination address prefix mask bits */ 152 nd_uint16_t flags; 153 nd_ipv4 peer_nexthop; /* v6: IP address of the nexthop within the peer (FIB)*/ 154 }; 155 156 static void 157 cnfp_v1_print(netdissect_options *ndo, const u_char *cp) 158 { 159 const struct nfhdr_v1 *nh; 160 const struct nfrec_v1 *nr; 161 const char *p_name; 162 uint8_t proto; 163 u_int nrecs, ver; 164 #if 0 165 time_t t; 166 #endif 167 168 nh = (const struct nfhdr_v1 *)cp; 169 ND_TCHECK_SIZE(nh); 170 171 ver = GET_BE_U_2(nh->version); 172 nrecs = GET_BE_U_4(nh->count); 173 #if 0 174 /* 175 * This is seconds since the UN*X epoch, and is followed by 176 * nanoseconds. XXX - format it, rather than just dumping the 177 * raw seconds-since-the-Epoch. 178 */ 179 t = GET_BE_U_4(nh->utc_sec); 180 #endif 181 182 ND_PRINT("NetFlow v%x, %u.%03u uptime, %u.%09u, ", ver, 183 GET_BE_U_4(nh->msys_uptime)/1000, 184 GET_BE_U_4(nh->msys_uptime)%1000, 185 GET_BE_U_4(nh->utc_sec), GET_BE_U_4(nh->utc_nsec)); 186 187 nr = (const struct nfrec_v1 *)&nh[1]; 188 189 ND_PRINT("%2u recs", nrecs); 190 191 for (; nrecs != 0; nr++, nrecs--) { 192 char buf[20]; 193 char asbuf[20]; 194 195 /* 196 * Make sure we have the entire record. 197 */ 198 ND_TCHECK_SIZE(nr); 199 ND_PRINT("\n started %u.%03u, last %u.%03u", 200 GET_BE_U_4(nr->start_time)/1000, 201 GET_BE_U_4(nr->start_time)%1000, 202 GET_BE_U_4(nr->last_time)/1000, 203 GET_BE_U_4(nr->last_time)%1000); 204 205 asbuf[0] = buf[0] = '\0'; 206 ND_PRINT("\n %s%s%s:%u ", 207 intoa(GET_IPV4_TO_NETWORK_ORDER(nr->src_ina)), 208 buf, asbuf, 209 GET_BE_U_2(nr->srcport)); 210 211 ND_PRINT("> %s%s%s:%u ", 212 intoa(GET_IPV4_TO_NETWORK_ORDER(nr->dst_ina)), 213 buf, asbuf, 214 GET_BE_U_2(nr->dstport)); 215 216 ND_PRINT(">> %s\n ", 217 intoa(GET_IPV4_TO_NETWORK_ORDER(nr->nhop_ina))); 218 219 proto = GET_U_1(nr->proto); 220 if (!ndo->ndo_nflag && (p_name = netdb_protoname(proto)) != NULL) 221 ND_PRINT("%s ", p_name); 222 else 223 ND_PRINT("%u ", proto); 224 225 /* tcp flags for tcp only */ 226 if (proto == IPPROTO_TCP) { 227 u_int flags; 228 flags = GET_U_1(nr->tcp_flags); 229 ND_PRINT("%s%s%s%s%s%s%s", 230 flags & TH_FIN ? "F" : "", 231 flags & TH_SYN ? "S" : "", 232 flags & TH_RST ? "R" : "", 233 flags & TH_PUSH ? "P" : "", 234 flags & TH_ACK ? "A" : "", 235 flags & TH_URG ? "U" : "", 236 flags ? " " : ""); 237 } 238 239 buf[0]='\0'; 240 ND_PRINT("tos %u, %u (%u octets) %s", 241 GET_U_1(nr->tos), 242 GET_BE_U_4(nr->packets), 243 GET_BE_U_4(nr->octets), buf); 244 } 245 return; 246 247 trunc: 248 nd_print_trunc(ndo); 249 } 250 251 static void 252 cnfp_v5_print(netdissect_options *ndo, const u_char *cp) 253 { 254 const struct nfhdr_v5 *nh; 255 const struct nfrec_v5 *nr; 256 const char *p_name; 257 uint8_t proto; 258 u_int nrecs, ver; 259 #if 0 260 time_t t; 261 #endif 262 263 nh = (const struct nfhdr_v5 *)cp; 264 ND_TCHECK_SIZE(nh); 265 266 ver = GET_BE_U_2(nh->version); 267 nrecs = GET_BE_U_4(nh->count); 268 #if 0 269 /* 270 * This is seconds since the UN*X epoch, and is followed by 271 * nanoseconds. XXX - format it, rather than just dumping the 272 * raw seconds-since-the-Epoch. 273 */ 274 t = GET_BE_U_4(nh->utc_sec); 275 #endif 276 277 ND_PRINT("NetFlow v%x, %u.%03u uptime, %u.%09u, ", ver, 278 GET_BE_U_4(nh->msys_uptime)/1000, 279 GET_BE_U_4(nh->msys_uptime)%1000, 280 GET_BE_U_4(nh->utc_sec), GET_BE_U_4(nh->utc_nsec)); 281 282 ND_PRINT("#%u, ", GET_BE_U_4(nh->sequence)); 283 nr = (const struct nfrec_v5 *)&nh[1]; 284 285 ND_PRINT("%2u recs", nrecs); 286 287 for (; nrecs != 0; nr++, nrecs--) { 288 char buf[20]; 289 char asbuf[20]; 290 291 /* 292 * Make sure we have the entire record. 293 */ 294 ND_TCHECK_SIZE(nr); 295 ND_PRINT("\n started %u.%03u, last %u.%03u", 296 GET_BE_U_4(nr->start_time)/1000, 297 GET_BE_U_4(nr->start_time)%1000, 298 GET_BE_U_4(nr->last_time)/1000, 299 GET_BE_U_4(nr->last_time)%1000); 300 301 asbuf[0] = buf[0] = '\0'; 302 snprintf(buf, sizeof(buf), "/%u", GET_U_1(nr->src_mask)); 303 snprintf(asbuf, sizeof(asbuf), ":%u", 304 GET_BE_U_2(nr->src_as)); 305 ND_PRINT("\n %s%s%s:%u ", 306 intoa(GET_IPV4_TO_NETWORK_ORDER(nr->src_ina)), 307 buf, asbuf, 308 GET_BE_U_2(nr->srcport)); 309 310 snprintf(buf, sizeof(buf), "/%u", GET_U_1(nr->dst_mask)); 311 snprintf(asbuf, sizeof(asbuf), ":%u", 312 GET_BE_U_2(nr->dst_as)); 313 ND_PRINT("> %s%s%s:%u ", 314 intoa(GET_IPV4_TO_NETWORK_ORDER(nr->dst_ina)), 315 buf, asbuf, 316 GET_BE_U_2(nr->dstport)); 317 318 ND_PRINT(">> %s\n ", 319 intoa(GET_IPV4_TO_NETWORK_ORDER(nr->nhop_ina))); 320 321 proto = GET_U_1(nr->proto); 322 if (!ndo->ndo_nflag && (p_name = netdb_protoname(proto)) != NULL) 323 ND_PRINT("%s ", p_name); 324 else 325 ND_PRINT("%u ", proto); 326 327 /* tcp flags for tcp only */ 328 if (proto == IPPROTO_TCP) { 329 u_int flags; 330 flags = GET_U_1(nr->tcp_flags); 331 ND_PRINT("%s%s%s%s%s%s%s", 332 flags & TH_FIN ? "F" : "", 333 flags & TH_SYN ? "S" : "", 334 flags & TH_RST ? "R" : "", 335 flags & TH_PUSH ? "P" : "", 336 flags & TH_ACK ? "A" : "", 337 flags & TH_URG ? "U" : "", 338 flags ? " " : ""); 339 } 340 341 buf[0]='\0'; 342 ND_PRINT("tos %u, %u (%u octets) %s", 343 GET_U_1(nr->tos), 344 GET_BE_U_4(nr->packets), 345 GET_BE_U_4(nr->octets), buf); 346 } 347 return; 348 349 trunc: 350 nd_print_trunc(ndo); 351 } 352 353 static void 354 cnfp_v6_print(netdissect_options *ndo, const u_char *cp) 355 { 356 const struct nfhdr_v6 *nh; 357 const struct nfrec_v6 *nr; 358 const char *p_name; 359 uint8_t proto; 360 u_int nrecs, ver; 361 #if 0 362 time_t t; 363 #endif 364 365 nh = (const struct nfhdr_v6 *)cp; 366 ND_TCHECK_SIZE(nh); 367 368 ver = GET_BE_U_2(nh->version); 369 nrecs = GET_BE_U_4(nh->count); 370 #if 0 371 /* 372 * This is seconds since the UN*X epoch, and is followed by 373 * nanoseconds. XXX - format it, rather than just dumping the 374 * raw seconds-since-the-Epoch. 375 */ 376 t = GET_BE_U_4(nh->utc_sec); 377 #endif 378 379 ND_PRINT("NetFlow v%x, %u.%03u uptime, %u.%09u, ", ver, 380 GET_BE_U_4(nh->msys_uptime)/1000, 381 GET_BE_U_4(nh->msys_uptime)%1000, 382 GET_BE_U_4(nh->utc_sec), GET_BE_U_4(nh->utc_nsec)); 383 384 ND_PRINT("#%u, ", GET_BE_U_4(nh->sequence)); 385 nr = (const struct nfrec_v6 *)&nh[1]; 386 387 ND_PRINT("%2u recs", nrecs); 388 389 for (; nrecs != 0; nr++, nrecs--) { 390 char buf[20]; 391 char asbuf[20]; 392 393 /* 394 * Make sure we have the entire record. 395 */ 396 ND_TCHECK_SIZE(nr); 397 ND_PRINT("\n started %u.%03u, last %u.%03u", 398 GET_BE_U_4(nr->start_time)/1000, 399 GET_BE_U_4(nr->start_time)%1000, 400 GET_BE_U_4(nr->last_time)/1000, 401 GET_BE_U_4(nr->last_time)%1000); 402 403 asbuf[0] = buf[0] = '\0'; 404 snprintf(buf, sizeof(buf), "/%u", GET_U_1(nr->src_mask)); 405 snprintf(asbuf, sizeof(asbuf), ":%u", 406 GET_BE_U_2(nr->src_as)); 407 ND_PRINT("\n %s%s%s:%u ", 408 intoa(GET_IPV4_TO_NETWORK_ORDER(nr->src_ina)), 409 buf, asbuf, 410 GET_BE_U_2(nr->srcport)); 411 412 snprintf(buf, sizeof(buf), "/%u", GET_U_1(nr->dst_mask)); 413 snprintf(asbuf, sizeof(asbuf), ":%u", 414 GET_BE_U_2(nr->dst_as)); 415 ND_PRINT("> %s%s%s:%u ", 416 intoa(GET_IPV4_TO_NETWORK_ORDER(nr->dst_ina)), 417 buf, asbuf, 418 GET_BE_U_2(nr->dstport)); 419 420 ND_PRINT(">> %s\n ", 421 intoa(GET_IPV4_TO_NETWORK_ORDER(nr->nhop_ina))); 422 423 proto = GET_U_1(nr->proto); 424 if (!ndo->ndo_nflag && (p_name = netdb_protoname(proto)) != NULL) 425 ND_PRINT("%s ", p_name); 426 else 427 ND_PRINT("%u ", proto); 428 429 /* tcp flags for tcp only */ 430 if (proto == IPPROTO_TCP) { 431 u_int flags; 432 flags = GET_U_1(nr->tcp_flags); 433 ND_PRINT("%s%s%s%s%s%s%s", 434 flags & TH_FIN ? "F" : "", 435 flags & TH_SYN ? "S" : "", 436 flags & TH_RST ? "R" : "", 437 flags & TH_PUSH ? "P" : "", 438 flags & TH_ACK ? "A" : "", 439 flags & TH_URG ? "U" : "", 440 flags ? " " : ""); 441 } 442 443 buf[0]='\0'; 444 snprintf(buf, sizeof(buf), "(%u<>%u encaps)", 445 (GET_BE_U_2(nr->flags) >> 8) & 0xff, 446 (GET_BE_U_2(nr->flags)) & 0xff); 447 ND_PRINT("tos %u, %u (%u octets) %s", 448 GET_U_1(nr->tos), 449 GET_BE_U_4(nr->packets), 450 GET_BE_U_4(nr->octets), buf); 451 } 452 return; 453 454 trunc: 455 nd_print_trunc(ndo); 456 } 457 458 void 459 cnfp_print(netdissect_options *ndo, const u_char *cp) 460 { 461 int ver; 462 463 /* 464 * First 2 bytes are the version number. 465 */ 466 ndo->ndo_protocol = "cnfp"; 467 ver = GET_BE_U_2(cp); 468 switch (ver) { 469 470 case 1: 471 cnfp_v1_print(ndo, cp); 472 break; 473 474 case 5: 475 cnfp_v5_print(ndo, cp); 476 break; 477 478 case 6: 479 cnfp_v6_print(ndo, cp); 480 break; 481 482 default: 483 ND_PRINT("NetFlow v%x", ver); 484 break; 485 } 486 } 487