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 struct protoent *pent; 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 pent = getprotobynumber(nr->proto); 215 if (!pent || ndo->ndo_nflag) 216 ND_PRINT((ndo, "%u ", nr->proto)); 217 else 218 ND_PRINT((ndo, "%s ", pent->p_name)); 219 220 /* tcp flags for tcp only */ 221 if (pent && pent->p_proto == IPPROTO_TCP) { 222 int flags; 223 flags = nr->tcp_flags; 224 ND_PRINT((ndo, "%s%s%s%s%s%s%s", 225 flags & TH_FIN ? "F" : "", 226 flags & TH_SYN ? "S" : "", 227 flags & TH_RST ? "R" : "", 228 flags & TH_PUSH ? "P" : "", 229 flags & TH_ACK ? "A" : "", 230 flags & TH_URG ? "U" : "", 231 flags ? " " : "")); 232 } 233 234 buf[0]='\0'; 235 ND_PRINT((ndo, "tos %u, %u (%u octets) %s", 236 nr->tos, 237 EXTRACT_32BITS(&nr->packets), 238 EXTRACT_32BITS(&nr->octets), buf)); 239 } 240 return; 241 242 trunc: 243 ND_PRINT((ndo, "[|cnfp]")); 244 return; 245 } 246 247 static void 248 cnfp_v5_print(netdissect_options *ndo, const u_char *cp) 249 { 250 register const struct nfhdr_v5 *nh; 251 register const struct nfrec_v5 *nr; 252 struct protoent *pent; 253 int nrecs, ver; 254 #if 0 255 time_t t; 256 #endif 257 258 nh = (const struct nfhdr_v5 *)cp; 259 ND_TCHECK(*nh); 260 261 ver = EXTRACT_16BITS(&nh->version); 262 nrecs = EXTRACT_32BITS(&nh->count); 263 #if 0 264 /* 265 * This is seconds since the UN*X epoch, and is followed by 266 * nanoseconds. XXX - format it, rather than just dumping the 267 * raw seconds-since-the-Epoch. 268 */ 269 t = EXTRACT_32BITS(&nh->utc_sec); 270 #endif 271 272 ND_PRINT((ndo, "NetFlow v%x, %u.%03u uptime, %u.%09u, ", ver, 273 EXTRACT_32BITS(&nh->msys_uptime)/1000, 274 EXTRACT_32BITS(&nh->msys_uptime)%1000, 275 EXTRACT_32BITS(&nh->utc_sec), EXTRACT_32BITS(&nh->utc_nsec))); 276 277 ND_PRINT((ndo, "#%u, ", EXTRACT_32BITS(&nh->sequence))); 278 nr = (const struct nfrec_v5 *)&nh[1]; 279 280 ND_PRINT((ndo, "%2u recs", nrecs)); 281 282 for (; nrecs != 0; nr++, nrecs--) { 283 char buf[20]; 284 char asbuf[20]; 285 286 /* 287 * Make sure we have the entire record. 288 */ 289 ND_TCHECK(*nr); 290 ND_PRINT((ndo, "\n started %u.%03u, last %u.%03u", 291 EXTRACT_32BITS(&nr->start_time)/1000, 292 EXTRACT_32BITS(&nr->start_time)%1000, 293 EXTRACT_32BITS(&nr->last_time)/1000, 294 EXTRACT_32BITS(&nr->last_time)%1000)); 295 296 asbuf[0] = buf[0] = '\0'; 297 snprintf(buf, sizeof(buf), "/%u", nr->src_mask); 298 snprintf(asbuf, sizeof(asbuf), ":%u", 299 EXTRACT_16BITS(&nr->src_as)); 300 ND_PRINT((ndo, "\n %s%s%s:%u ", intoa(nr->src_ina.s_addr), buf, asbuf, 301 EXTRACT_16BITS(&nr->srcport))); 302 303 snprintf(buf, sizeof(buf), "/%d", nr->dst_mask); 304 snprintf(asbuf, sizeof(asbuf), ":%u", 305 EXTRACT_16BITS(&nr->dst_as)); 306 ND_PRINT((ndo, "> %s%s%s:%u ", intoa(nr->dst_ina.s_addr), buf, asbuf, 307 EXTRACT_16BITS(&nr->dstport))); 308 309 ND_PRINT((ndo, ">> %s\n ", intoa(nr->nhop_ina.s_addr))); 310 311 pent = getprotobynumber(nr->proto); 312 if (!pent || ndo->ndo_nflag) 313 ND_PRINT((ndo, "%u ", nr->proto)); 314 else 315 ND_PRINT((ndo, "%s ", pent->p_name)); 316 317 /* tcp flags for tcp only */ 318 if (pent && pent->p_proto == IPPROTO_TCP) { 319 int flags; 320 flags = nr->tcp_flags; 321 ND_PRINT((ndo, "%s%s%s%s%s%s%s", 322 flags & TH_FIN ? "F" : "", 323 flags & TH_SYN ? "S" : "", 324 flags & TH_RST ? "R" : "", 325 flags & TH_PUSH ? "P" : "", 326 flags & TH_ACK ? "A" : "", 327 flags & TH_URG ? "U" : "", 328 flags ? " " : "")); 329 } 330 331 buf[0]='\0'; 332 ND_PRINT((ndo, "tos %u, %u (%u octets) %s", 333 nr->tos, 334 EXTRACT_32BITS(&nr->packets), 335 EXTRACT_32BITS(&nr->octets), buf)); 336 } 337 return; 338 339 trunc: 340 ND_PRINT((ndo, "[|cnfp]")); 341 return; 342 } 343 344 static void 345 cnfp_v6_print(netdissect_options *ndo, const u_char *cp) 346 { 347 register const struct nfhdr_v6 *nh; 348 register const struct nfrec_v6 *nr; 349 struct protoent *pent; 350 int nrecs, ver; 351 #if 0 352 time_t t; 353 #endif 354 355 nh = (const struct nfhdr_v6 *)cp; 356 ND_TCHECK(*nh); 357 358 ver = EXTRACT_16BITS(&nh->version); 359 nrecs = EXTRACT_32BITS(&nh->count); 360 #if 0 361 /* 362 * This is seconds since the UN*X epoch, and is followed by 363 * nanoseconds. XXX - format it, rather than just dumping the 364 * raw seconds-since-the-Epoch. 365 */ 366 t = EXTRACT_32BITS(&nh->utc_sec); 367 #endif 368 369 ND_PRINT((ndo, "NetFlow v%x, %u.%03u uptime, %u.%09u, ", ver, 370 EXTRACT_32BITS(&nh->msys_uptime)/1000, 371 EXTRACT_32BITS(&nh->msys_uptime)%1000, 372 EXTRACT_32BITS(&nh->utc_sec), EXTRACT_32BITS(&nh->utc_nsec))); 373 374 ND_PRINT((ndo, "#%u, ", EXTRACT_32BITS(&nh->sequence))); 375 nr = (const struct nfrec_v6 *)&nh[1]; 376 377 ND_PRINT((ndo, "%2u recs", nrecs)); 378 379 for (; nrecs != 0; nr++, nrecs--) { 380 char buf[20]; 381 char asbuf[20]; 382 383 /* 384 * Make sure we have the entire record. 385 */ 386 ND_TCHECK(*nr); 387 ND_PRINT((ndo, "\n started %u.%03u, last %u.%03u", 388 EXTRACT_32BITS(&nr->start_time)/1000, 389 EXTRACT_32BITS(&nr->start_time)%1000, 390 EXTRACT_32BITS(&nr->last_time)/1000, 391 EXTRACT_32BITS(&nr->last_time)%1000)); 392 393 asbuf[0] = buf[0] = '\0'; 394 snprintf(buf, sizeof(buf), "/%u", nr->src_mask); 395 snprintf(asbuf, sizeof(asbuf), ":%u", 396 EXTRACT_16BITS(&nr->src_as)); 397 ND_PRINT((ndo, "\n %s%s%s:%u ", intoa(nr->src_ina.s_addr), buf, asbuf, 398 EXTRACT_16BITS(&nr->srcport))); 399 400 snprintf(buf, sizeof(buf), "/%d", nr->dst_mask); 401 snprintf(asbuf, sizeof(asbuf), ":%u", 402 EXTRACT_16BITS(&nr->dst_as)); 403 ND_PRINT((ndo, "> %s%s%s:%u ", intoa(nr->dst_ina.s_addr), buf, asbuf, 404 EXTRACT_16BITS(&nr->dstport))); 405 406 ND_PRINT((ndo, ">> %s\n ", intoa(nr->nhop_ina.s_addr))); 407 408 pent = getprotobynumber(nr->proto); 409 if (!pent || ndo->ndo_nflag) 410 ND_PRINT((ndo, "%u ", nr->proto)); 411 else 412 ND_PRINT((ndo, "%s ", pent->p_name)); 413 414 /* tcp flags for tcp only */ 415 if (pent && pent->p_proto == IPPROTO_TCP) { 416 int flags; 417 flags = nr->tcp_flags; 418 ND_PRINT((ndo, "%s%s%s%s%s%s%s", 419 flags & TH_FIN ? "F" : "", 420 flags & TH_SYN ? "S" : "", 421 flags & TH_RST ? "R" : "", 422 flags & TH_PUSH ? "P" : "", 423 flags & TH_ACK ? "A" : "", 424 flags & TH_URG ? "U" : "", 425 flags ? " " : "")); 426 } 427 428 buf[0]='\0'; 429 snprintf(buf, sizeof(buf), "(%u<>%u encaps)", 430 (EXTRACT_16BITS(&nr->flags) >> 8) & 0xff, 431 (EXTRACT_16BITS(&nr->flags)) & 0xff); 432 ND_PRINT((ndo, "tos %u, %u (%u octets) %s", 433 nr->tos, 434 EXTRACT_32BITS(&nr->packets), 435 EXTRACT_32BITS(&nr->octets), buf)); 436 } 437 return; 438 439 trunc: 440 ND_PRINT((ndo, "[|cnfp]")); 441 return; 442 } 443 444 void 445 cnfp_print(netdissect_options *ndo, const u_char *cp) 446 { 447 int ver; 448 449 /* 450 * First 2 bytes are the version number. 451 */ 452 ND_TCHECK2(*cp, 2); 453 ver = EXTRACT_16BITS(cp); 454 switch (ver) { 455 456 case 1: 457 cnfp_v1_print(ndo, cp); 458 break; 459 460 case 5: 461 cnfp_v5_print(ndo, cp); 462 break; 463 464 case 6: 465 cnfp_v6_print(ndo, cp); 466 break; 467 468 default: 469 ND_PRINT((ndo, "NetFlow v%x", ver)); 470 break; 471 } 472 return; 473 474 trunc: 475 ND_PRINT((ndo, "[|cnfp]")); 476 return; 477 } 478