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 #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 
58 struct 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 
66 struct 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 
86 struct 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 
98 struct 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 
122 struct 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 
132 struct 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 
156 static void
cnfp_v1_print(netdissect_options * ndo,const u_char * cp)157 cnfp_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 
247 trunc:
248 	nd_print_trunc(ndo);
249 }
250 
251 static void
cnfp_v5_print(netdissect_options * ndo,const u_char * cp)252 cnfp_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 
349 trunc:
350 	nd_print_trunc(ndo);
351 }
352 
353 static void
cnfp_v6_print(netdissect_options * ndo,const u_char * cp)354 cnfp_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 
454 trunc:
455 	nd_print_trunc(ndo);
456 }
457 
458 void
cnfp_print(netdissect_options * ndo,const u_char * cp)459 cnfp_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