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