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
cnfp_v1_print(netdissect_options * ndo,const u_char * cp)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
cnfp_v5_print(netdissect_options * ndo,const u_char * cp)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
cnfp_v6_print(netdissect_options * ndo,const u_char * cp)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
cnfp_print(netdissect_options * ndo,const u_char * cp)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