xref: /freebsd/contrib/tcpdump/print-cnfp.c (revision c5f3a7f62217f20f0c7b2c4fc3fb2646336b0802)
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
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
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
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
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