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
59struct 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
67struct 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
87struct 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
99struct 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
123struct 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
133struct 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
157static void
158cnfp_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
241trunc:
242	ND_PRINT((ndo, "[|cnfp]"));
243	return;
244}
245
246static void
247cnfp_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
337trunc:
338	ND_PRINT((ndo, "[|cnfp]"));
339	return;
340}
341
342static void
343cnfp_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
436trunc:
437	ND_PRINT((ndo, "[|cnfp]"));
438	return;
439}
440
441void
442cnfp_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
471trunc:
472	ND_PRINT((ndo, "[|cnfp]"));
473	return;
474}
475