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 #include <config.h> 44 45 #include "netdissect-stdinc.h" 46 47 #include <stdio.h> 48 49 #include "netdissect.h" 50 #include "addrtoname.h" 51 #include "extract.h" 52 53 #include "tcp.h" 54 #include "ipproto.h" 55 56 struct nfhdr_v1 { 57 nd_uint16_t version; /* version number */ 58 nd_uint16_t count; /* # of records */ 59 nd_uint32_t msys_uptime; 60 nd_uint32_t utc_sec; 61 nd_uint32_t utc_nsec; 62 }; 63 64 struct nfrec_v1 { 65 nd_ipv4 src_ina; 66 nd_ipv4 dst_ina; 67 nd_ipv4 nhop_ina; 68 nd_uint16_t input; /* SNMP index of input interface */ 69 nd_uint16_t output; /* SNMP index of output interface */ 70 nd_uint32_t packets; /* packets in the flow */ 71 nd_uint32_t octets; /* layer 3 octets in the packets of the flow */ 72 nd_uint32_t start_time; /* sys_uptime value at start of flow */ 73 nd_uint32_t last_time; /* sys_uptime value when last packet of flow was received */ 74 nd_uint16_t srcport; /* TCP/UDP source port or equivalent */ 75 nd_uint16_t dstport; /* TCP/UDP source port or equivalent */ 76 nd_byte pad1[2]; /* pad */ 77 nd_uint8_t proto; /* IP protocol type */ 78 nd_uint8_t tos; /* IP type of service */ 79 nd_uint8_t tcp_flags; /* cumulative OR of TCP flags */ 80 nd_byte pad[3]; /* padding */ 81 nd_uint32_t reserved; /* unused */ 82 }; 83 84 struct nfhdr_v5 { 85 nd_uint16_t version; /* version number */ 86 nd_uint16_t count; /* # of records */ 87 nd_uint32_t msys_uptime; 88 nd_uint32_t utc_sec; 89 nd_uint32_t utc_nsec; 90 nd_uint32_t sequence; /* flow sequence number */ 91 nd_uint8_t engine_type; /* type of flow-switching engine */ 92 nd_uint8_t engine_id; /* slot number of the flow-switching engine */ 93 nd_uint16_t sampling_interval; /* sampling mode and interval */ 94 }; 95 96 struct nfrec_v5 { 97 nd_ipv4 src_ina; 98 nd_ipv4 dst_ina; 99 nd_ipv4 nhop_ina; 100 nd_uint16_t input; /* SNMP index of input interface */ 101 nd_uint16_t output; /* SNMP index of output interface */ 102 nd_uint32_t packets; /* packets in the flow */ 103 nd_uint32_t octets; /* layer 3 octets in the packets of the flow */ 104 nd_uint32_t start_time; /* sys_uptime value at start of flow */ 105 nd_uint32_t last_time; /* sys_uptime value when last packet of flow was received */ 106 nd_uint16_t srcport; /* TCP/UDP source port or equivalent */ 107 nd_uint16_t dstport; /* TCP/UDP source port or equivalent */ 108 nd_byte pad1; /* pad */ 109 nd_uint8_t tcp_flags; /* cumulative OR of TCP flags */ 110 nd_uint8_t proto; /* IP protocol type */ 111 nd_uint8_t tos; /* IP type of service */ 112 nd_uint16_t src_as; /* AS number of the source */ 113 nd_uint16_t dst_as; /* AS number of the destination */ 114 nd_uint8_t src_mask; /* source address mask bits */ 115 nd_uint8_t dst_mask; /* destination address prefix mask bits */ 116 nd_byte pad2[2]; 117 nd_ipv4 peer_nexthop; /* v6: IP address of the nexthop within the peer (FIB)*/ 118 }; 119 120 struct nfhdr_v6 { 121 nd_uint16_t version; /* version number */ 122 nd_uint16_t count; /* # of records */ 123 nd_uint32_t msys_uptime; 124 nd_uint32_t utc_sec; 125 nd_uint32_t utc_nsec; 126 nd_uint32_t sequence; /* v5 flow sequence number */ 127 nd_uint32_t reserved; /* v5 only */ 128 }; 129 130 struct nfrec_v6 { 131 nd_ipv4 src_ina; 132 nd_ipv4 dst_ina; 133 nd_ipv4 nhop_ina; 134 nd_uint16_t input; /* SNMP index of input interface */ 135 nd_uint16_t output; /* SNMP index of output interface */ 136 nd_uint32_t packets; /* packets in the flow */ 137 nd_uint32_t octets; /* layer 3 octets in the packets of the flow */ 138 nd_uint32_t start_time; /* sys_uptime value at start of flow */ 139 nd_uint32_t last_time; /* sys_uptime value when last packet of flow was received */ 140 nd_uint16_t srcport; /* TCP/UDP source port or equivalent */ 141 nd_uint16_t dstport; /* TCP/UDP source port or equivalent */ 142 nd_byte pad1; /* pad */ 143 nd_uint8_t tcp_flags; /* cumulative OR of TCP flags */ 144 nd_uint8_t proto; /* IP protocol type */ 145 nd_uint8_t tos; /* IP type of service */ 146 nd_uint16_t src_as; /* AS number of the source */ 147 nd_uint16_t dst_as; /* AS number of the destination */ 148 nd_uint8_t src_mask; /* source address mask bits */ 149 nd_uint8_t dst_mask; /* destination address prefix mask bits */ 150 nd_uint16_t flags; 151 nd_ipv4 peer_nexthop; /* v6: IP address of the nexthop within the peer (FIB)*/ 152 }; 153 154 static void 155 cnfp_v1_print(netdissect_options *ndo, const u_char *cp) 156 { 157 const struct nfhdr_v1 *nh; 158 const struct nfrec_v1 *nr; 159 const char *p_name; 160 uint8_t proto; 161 u_int nrecs, ver; 162 #if 0 163 time_t t; 164 #endif 165 166 nh = (const struct nfhdr_v1 *)cp; 167 ND_TCHECK_SIZE(nh); 168 169 ver = GET_BE_U_2(nh->version); 170 nrecs = GET_BE_U_4(nh->count); 171 #if 0 172 /* 173 * This is seconds since the UN*X epoch, and is followed by 174 * nanoseconds. XXX - format it, rather than just dumping the 175 * raw seconds-since-the-Epoch. 176 */ 177 t = GET_BE_U_4(nh->utc_sec); 178 #endif 179 180 ND_PRINT("NetFlow v%x, %u.%03u uptime, %u.%09u, ", ver, 181 GET_BE_U_4(nh->msys_uptime)/1000, 182 GET_BE_U_4(nh->msys_uptime)%1000, 183 GET_BE_U_4(nh->utc_sec), GET_BE_U_4(nh->utc_nsec)); 184 185 nr = (const struct nfrec_v1 *)&nh[1]; 186 187 ND_PRINT("%2u recs", nrecs); 188 189 for (; nrecs != 0; nr++, nrecs--) { 190 char buf[20]; 191 char asbuf[20]; 192 193 /* 194 * Make sure we have the entire record. 195 */ 196 ND_TCHECK_SIZE(nr); 197 ND_PRINT("\n started %u.%03u, last %u.%03u", 198 GET_BE_U_4(nr->start_time)/1000, 199 GET_BE_U_4(nr->start_time)%1000, 200 GET_BE_U_4(nr->last_time)/1000, 201 GET_BE_U_4(nr->last_time)%1000); 202 203 asbuf[0] = buf[0] = '\0'; 204 ND_PRINT("\n %s%s%s:%u ", 205 intoa(GET_IPV4_TO_NETWORK_ORDER(nr->src_ina)), 206 buf, asbuf, 207 GET_BE_U_2(nr->srcport)); 208 209 ND_PRINT("> %s%s%s:%u ", 210 intoa(GET_IPV4_TO_NETWORK_ORDER(nr->dst_ina)), 211 buf, asbuf, 212 GET_BE_U_2(nr->dstport)); 213 214 ND_PRINT(">> %s\n ", 215 intoa(GET_IPV4_TO_NETWORK_ORDER(nr->nhop_ina))); 216 217 proto = GET_U_1(nr->proto); 218 if (!ndo->ndo_nflag && (p_name = netdb_protoname(proto)) != NULL) 219 ND_PRINT("%s ", p_name); 220 else 221 ND_PRINT("%u ", proto); 222 223 /* tcp flags for tcp only */ 224 if (proto == IPPROTO_TCP) { 225 u_int flags; 226 flags = GET_U_1(nr->tcp_flags); 227 ND_PRINT("%s%s%s%s%s%s%s", 228 flags & TH_FIN ? "F" : "", 229 flags & TH_SYN ? "S" : "", 230 flags & TH_RST ? "R" : "", 231 flags & TH_PUSH ? "P" : "", 232 flags & TH_ACK ? "A" : "", 233 flags & TH_URG ? "U" : "", 234 flags ? " " : ""); 235 } 236 237 buf[0]='\0'; 238 ND_PRINT("tos %u, %u (%u octets) %s", 239 GET_U_1(nr->tos), 240 GET_BE_U_4(nr->packets), 241 GET_BE_U_4(nr->octets), buf); 242 } 243 return; 244 245 trunc: 246 nd_print_trunc(ndo); 247 } 248 249 static void 250 cnfp_v5_print(netdissect_options *ndo, const u_char *cp) 251 { 252 const struct nfhdr_v5 *nh; 253 const struct nfrec_v5 *nr; 254 const char *p_name; 255 uint8_t proto; 256 u_int nrecs, ver; 257 #if 0 258 time_t t; 259 #endif 260 261 nh = (const struct nfhdr_v5 *)cp; 262 ND_TCHECK_SIZE(nh); 263 264 ver = GET_BE_U_2(nh->version); 265 nrecs = GET_BE_U_4(nh->count); 266 #if 0 267 /* 268 * This is seconds since the UN*X epoch, and is followed by 269 * nanoseconds. XXX - format it, rather than just dumping the 270 * raw seconds-since-the-Epoch. 271 */ 272 t = GET_BE_U_4(nh->utc_sec); 273 #endif 274 275 ND_PRINT("NetFlow v%x, %u.%03u uptime, %u.%09u, ", ver, 276 GET_BE_U_4(nh->msys_uptime)/1000, 277 GET_BE_U_4(nh->msys_uptime)%1000, 278 GET_BE_U_4(nh->utc_sec), GET_BE_U_4(nh->utc_nsec)); 279 280 ND_PRINT("#%u, ", GET_BE_U_4(nh->sequence)); 281 nr = (const struct nfrec_v5 *)&nh[1]; 282 283 ND_PRINT("%2u recs", nrecs); 284 285 for (; nrecs != 0; nr++, nrecs--) { 286 char buf[20]; 287 char asbuf[20]; 288 289 /* 290 * Make sure we have the entire record. 291 */ 292 ND_TCHECK_SIZE(nr); 293 ND_PRINT("\n started %u.%03u, last %u.%03u", 294 GET_BE_U_4(nr->start_time)/1000, 295 GET_BE_U_4(nr->start_time)%1000, 296 GET_BE_U_4(nr->last_time)/1000, 297 GET_BE_U_4(nr->last_time)%1000); 298 299 asbuf[0] = buf[0] = '\0'; 300 snprintf(buf, sizeof(buf), "/%u", GET_U_1(nr->src_mask)); 301 snprintf(asbuf, sizeof(asbuf), ":%u", 302 GET_BE_U_2(nr->src_as)); 303 ND_PRINT("\n %s%s%s:%u ", 304 intoa(GET_IPV4_TO_NETWORK_ORDER(nr->src_ina)), 305 buf, asbuf, 306 GET_BE_U_2(nr->srcport)); 307 308 snprintf(buf, sizeof(buf), "/%u", GET_U_1(nr->dst_mask)); 309 snprintf(asbuf, sizeof(asbuf), ":%u", 310 GET_BE_U_2(nr->dst_as)); 311 ND_PRINT("> %s%s%s:%u ", 312 intoa(GET_IPV4_TO_NETWORK_ORDER(nr->dst_ina)), 313 buf, asbuf, 314 GET_BE_U_2(nr->dstport)); 315 316 ND_PRINT(">> %s\n ", 317 intoa(GET_IPV4_TO_NETWORK_ORDER(nr->nhop_ina))); 318 319 proto = GET_U_1(nr->proto); 320 if (!ndo->ndo_nflag && (p_name = netdb_protoname(proto)) != NULL) 321 ND_PRINT("%s ", p_name); 322 else 323 ND_PRINT("%u ", proto); 324 325 /* tcp flags for tcp only */ 326 if (proto == IPPROTO_TCP) { 327 u_int flags; 328 flags = GET_U_1(nr->tcp_flags); 329 ND_PRINT("%s%s%s%s%s%s%s", 330 flags & TH_FIN ? "F" : "", 331 flags & TH_SYN ? "S" : "", 332 flags & TH_RST ? "R" : "", 333 flags & TH_PUSH ? "P" : "", 334 flags & TH_ACK ? "A" : "", 335 flags & TH_URG ? "U" : "", 336 flags ? " " : ""); 337 } 338 339 buf[0]='\0'; 340 ND_PRINT("tos %u, %u (%u octets) %s", 341 GET_U_1(nr->tos), 342 GET_BE_U_4(nr->packets), 343 GET_BE_U_4(nr->octets), buf); 344 } 345 return; 346 347 trunc: 348 nd_print_trunc(ndo); 349 } 350 351 static void 352 cnfp_v6_print(netdissect_options *ndo, const u_char *cp) 353 { 354 const struct nfhdr_v6 *nh; 355 const struct nfrec_v6 *nr; 356 const char *p_name; 357 uint8_t proto; 358 u_int nrecs, ver; 359 #if 0 360 time_t t; 361 #endif 362 363 nh = (const struct nfhdr_v6 *)cp; 364 ND_TCHECK_SIZE(nh); 365 366 ver = GET_BE_U_2(nh->version); 367 nrecs = GET_BE_U_4(nh->count); 368 #if 0 369 /* 370 * This is seconds since the UN*X epoch, and is followed by 371 * nanoseconds. XXX - format it, rather than just dumping the 372 * raw seconds-since-the-Epoch. 373 */ 374 t = GET_BE_U_4(nh->utc_sec); 375 #endif 376 377 ND_PRINT("NetFlow v%x, %u.%03u uptime, %u.%09u, ", ver, 378 GET_BE_U_4(nh->msys_uptime)/1000, 379 GET_BE_U_4(nh->msys_uptime)%1000, 380 GET_BE_U_4(nh->utc_sec), GET_BE_U_4(nh->utc_nsec)); 381 382 ND_PRINT("#%u, ", GET_BE_U_4(nh->sequence)); 383 nr = (const struct nfrec_v6 *)&nh[1]; 384 385 ND_PRINT("%2u recs", nrecs); 386 387 for (; nrecs != 0; nr++, nrecs--) { 388 char buf[20]; 389 char asbuf[20]; 390 391 /* 392 * Make sure we have the entire record. 393 */ 394 ND_TCHECK_SIZE(nr); 395 ND_PRINT("\n started %u.%03u, last %u.%03u", 396 GET_BE_U_4(nr->start_time)/1000, 397 GET_BE_U_4(nr->start_time)%1000, 398 GET_BE_U_4(nr->last_time)/1000, 399 GET_BE_U_4(nr->last_time)%1000); 400 401 asbuf[0] = buf[0] = '\0'; 402 snprintf(buf, sizeof(buf), "/%u", GET_U_1(nr->src_mask)); 403 snprintf(asbuf, sizeof(asbuf), ":%u", 404 GET_BE_U_2(nr->src_as)); 405 ND_PRINT("\n %s%s%s:%u ", 406 intoa(GET_IPV4_TO_NETWORK_ORDER(nr->src_ina)), 407 buf, asbuf, 408 GET_BE_U_2(nr->srcport)); 409 410 snprintf(buf, sizeof(buf), "/%u", GET_U_1(nr->dst_mask)); 411 snprintf(asbuf, sizeof(asbuf), ":%u", 412 GET_BE_U_2(nr->dst_as)); 413 ND_PRINT("> %s%s%s:%u ", 414 intoa(GET_IPV4_TO_NETWORK_ORDER(nr->dst_ina)), 415 buf, asbuf, 416 GET_BE_U_2(nr->dstport)); 417 418 ND_PRINT(">> %s\n ", 419 intoa(GET_IPV4_TO_NETWORK_ORDER(nr->nhop_ina))); 420 421 proto = GET_U_1(nr->proto); 422 if (!ndo->ndo_nflag && (p_name = netdb_protoname(proto)) != NULL) 423 ND_PRINT("%s ", p_name); 424 else 425 ND_PRINT("%u ", proto); 426 427 /* tcp flags for tcp only */ 428 if (proto == IPPROTO_TCP) { 429 u_int flags; 430 flags = GET_U_1(nr->tcp_flags); 431 ND_PRINT("%s%s%s%s%s%s%s", 432 flags & TH_FIN ? "F" : "", 433 flags & TH_SYN ? "S" : "", 434 flags & TH_RST ? "R" : "", 435 flags & TH_PUSH ? "P" : "", 436 flags & TH_ACK ? "A" : "", 437 flags & TH_URG ? "U" : "", 438 flags ? " " : ""); 439 } 440 441 buf[0]='\0'; 442 snprintf(buf, sizeof(buf), "(%u<>%u encaps)", 443 (GET_BE_U_2(nr->flags) >> 8) & 0xff, 444 (GET_BE_U_2(nr->flags)) & 0xff); 445 ND_PRINT("tos %u, %u (%u octets) %s", 446 GET_U_1(nr->tos), 447 GET_BE_U_4(nr->packets), 448 GET_BE_U_4(nr->octets), buf); 449 } 450 return; 451 452 trunc: 453 nd_print_trunc(ndo); 454 } 455 456 void 457 cnfp_print(netdissect_options *ndo, const u_char *cp) 458 { 459 int ver; 460 461 /* 462 * First 2 bytes are the version number. 463 */ 464 ndo->ndo_protocol = "cnfp"; 465 ver = GET_BE_U_2(cp); 466 switch (ver) { 467 468 case 1: 469 cnfp_v1_print(ndo, cp); 470 break; 471 472 case 5: 473 cnfp_v5_print(ndo, cp); 474 break; 475 476 case 6: 477 cnfp_v6_print(ndo, cp); 478 break; 479 480 default: 481 ND_PRINT("NetFlow v%x", ver); 482 break; 483 } 484 } 485