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  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 /* \summary: Cisco NetFlow protocol printer */
29 
30 /*
31  * Cisco NetFlow protocol
32  *
33  * See
34  *
35  *    https://www.cisco.com/c/en/us/td/docs/net_mgmt/netflow_collection_engine/3-6/user/guide/format.html#wp1005892
36  */
37 
38 #include <sys/cdefs.h>
39 #ifndef lint
40 __RCSID("$NetBSD: print-cnfp.c,v 1.10 2024/09/02 16:15:30 christos Exp $");
41 #endif
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