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