xref: /freebsd/contrib/tcpdump/print-cnfp.c (revision 39ee7a7a6bdd1557b1c3532abf60d139798ac88b)
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 /*
34  * Cisco NetFlow protocol
35  *
36  * See
37  *
38  *    http://www.cisco.com/c/en/us/td/docs/net_mgmt/netflow_collection_engine/3-6/user/guide/format.html#wp1005892
39  */
40 
41 #define NETDISSECT_REWORKED
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45 
46 #include <tcpdump-stdinc.h>
47 
48 #include <stdio.h>
49 #include <string.h>
50 
51 #include "interface.h"
52 #include "addrtoname.h"
53 #include "extract.h"
54 
55 #include "tcp.h"
56 #include "ipproto.h"
57 
58 struct nfhdr_v1 {
59 	uint16_t	version;	/* version number */
60 	uint16_t	count;		/* # of records */
61 	uint32_t	msys_uptime;
62 	uint32_t	utc_sec;
63 	uint32_t	utc_nsec;
64 };
65 
66 struct nfrec_v1 {
67 	struct in_addr	src_ina;
68 	struct in_addr	dst_ina;
69 	struct in_addr	nhop_ina;
70 	uint16_t	input;		/* SNMP index of input interface */
71 	uint16_t	output;		/* SNMP index of output interface */
72 	uint32_t	packets;	/* packets in the flow */
73 	uint32_t	octets;		/* layer 3 octets in the packets of the flow */
74 	uint32_t	start_time;	/* sys_uptime value at start of flow */
75 	uint32_t	last_time;	/* sys_uptime value when last packet of flow was received */
76 	uint16_t	srcport;	/* TCP/UDP source port or equivalent */
77 	uint16_t	dstport;	/* TCP/UDP source port or equivalent */
78 	uint16_t	pad1;		/* pad */
79 	uint8_t		proto;		/* IP protocol type */
80 	uint8_t		tos;		/* IP type of service */
81 	uint8_t		tcp_flags;	/* cumulative OR of TCP flags */
82 	uint8_t		pad[3];		/* padding */
83 	uint32_t	reserved;	/* unused */
84 };
85 
86 struct nfhdr_v5 {
87 	uint16_t	version;	/* version number */
88 	uint16_t	count;		/* # of records */
89 	uint32_t	msys_uptime;
90 	uint32_t	utc_sec;
91 	uint32_t	utc_nsec;
92 	uint32_t	sequence;	/* flow sequence number */
93 	uint8_t		engine_type;	/* type of flow-switching engine */
94 	uint8_t		engine_id;	/* slot number of the flow-switching engine */
95 	uint16_t	sampling_interval; /* sampling mode and interval */
96 };
97 
98 struct nfrec_v5 {
99 	struct in_addr	src_ina;
100 	struct in_addr	dst_ina;
101 	struct in_addr	nhop_ina;
102 	uint16_t	input;		/* SNMP index of input interface */
103 	uint16_t	output;		/* SNMP index of output interface */
104 	uint32_t	packets;	/* packets in the flow */
105 	uint32_t	octets;		/* layer 3 octets in the packets of the flow */
106 	uint32_t	start_time;	/* sys_uptime value at start of flow */
107 	uint32_t	last_time;	/* sys_uptime value when last packet of flow was received */
108 	uint16_t	srcport;	/* TCP/UDP source port or equivalent */
109 	uint16_t	dstport;	/* TCP/UDP source port or equivalent */
110 	uint8_t		pad1;		/* pad */
111 	uint8_t		tcp_flags;	/* cumulative OR of TCP flags */
112 	uint8_t		proto;		/* IP protocol type */
113 	uint8_t		tos;		/* IP type of service */
114 	uint16_t	src_as;		/* AS number of the source */
115 	uint16_t	dst_as;		/* AS number of the destination */
116 	uint8_t		src_mask;	/* source address mask bits */
117 	uint8_t		dst_mask;	/* destination address prefix mask bits */
118 	uint16_t	pad2;
119 	struct in_addr	peer_nexthop;	/* v6: IP address of the nexthop within the peer (FIB)*/
120 };
121 
122 struct nfhdr_v6 {
123 	uint16_t	version;	/* version number */
124 	uint16_t	count;		/* # of records */
125 	uint32_t	msys_uptime;
126 	uint32_t	utc_sec;
127 	uint32_t	utc_nsec;
128 	uint32_t	sequence;	/* v5 flow sequence number */
129 	uint32_t	reserved;	/* v5 only */
130 };
131 
132 struct nfrec_v6 {
133 	struct in_addr	src_ina;
134 	struct in_addr	dst_ina;
135 	struct in_addr	nhop_ina;
136 	uint16_t	input;		/* SNMP index of input interface */
137 	uint16_t	output;		/* SNMP index of output interface */
138 	uint32_t	packets;	/* packets in the flow */
139 	uint32_t	octets;		/* layer 3 octets in the packets of the flow */
140 	uint32_t	start_time;	/* sys_uptime value at start of flow */
141 	uint32_t	last_time;	/* sys_uptime value when last packet of flow was received */
142 	uint16_t	srcport;	/* TCP/UDP source port or equivalent */
143 	uint16_t	dstport;	/* TCP/UDP source port or equivalent */
144 	uint8_t		pad1;		/* pad */
145 	uint8_t		tcp_flags;	/* cumulative OR of TCP flags */
146 	uint8_t		proto;		/* IP protocol type */
147 	uint8_t		tos;		/* IP type of service */
148 	uint16_t	src_as;		/* AS number of the source */
149 	uint16_t	dst_as;		/* AS number of the destination */
150 	uint8_t		src_mask;	/* source address mask bits */
151 	uint8_t		dst_mask;	/* destination address prefix mask bits */
152 	uint16_t	flags;
153 	struct in_addr	peer_nexthop;	/* v6: IP address of the nexthop within the peer (FIB)*/
154 };
155 
156 static void
157 cnfp_v1_print(netdissect_options *ndo, const u_char *cp)
158 {
159 	register const struct nfhdr_v1 *nh;
160 	register const struct nfrec_v1 *nr;
161 	struct protoent *pent;
162 	int nrecs, ver;
163 #if 0
164 	time_t t;
165 #endif
166 
167 	nh = (const struct nfhdr_v1 *)cp;
168 	ND_TCHECK(*nh);
169 
170 	ver = EXTRACT_16BITS(&nh->version);
171 	nrecs = EXTRACT_32BITS(&nh->count);
172 #if 0
173 	/*
174 	 * This is seconds since the UN*X epoch, and is followed by
175 	 * nanoseconds.  XXX - format it, rather than just dumping the
176 	 * raw seconds-since-the-Epoch.
177 	 */
178 	t = EXTRACT_32BITS(&nh->utc_sec);
179 #endif
180 
181 	ND_PRINT((ndo, "NetFlow v%x, %u.%03u uptime, %u.%09u, ", ver,
182 	       EXTRACT_32BITS(&nh->msys_uptime)/1000,
183 	       EXTRACT_32BITS(&nh->msys_uptime)%1000,
184 	       EXTRACT_32BITS(&nh->utc_sec), EXTRACT_32BITS(&nh->utc_nsec)));
185 
186 	nr = (const struct nfrec_v1 *)&nh[1];
187 
188 	ND_PRINT((ndo, "%2u recs", nrecs));
189 
190 	for (; nrecs != 0; nr++, nrecs--) {
191 		char buf[20];
192 		char asbuf[20];
193 
194 		/*
195 		 * Make sure we have the entire record.
196 		 */
197 		ND_TCHECK(*nr);
198 		ND_PRINT((ndo, "\n  started %u.%03u, last %u.%03u",
199 		       EXTRACT_32BITS(&nr->start_time)/1000,
200 		       EXTRACT_32BITS(&nr->start_time)%1000,
201 		       EXTRACT_32BITS(&nr->last_time)/1000,
202 		       EXTRACT_32BITS(&nr->last_time)%1000));
203 
204 		asbuf[0] = buf[0] = '\0';
205 		ND_PRINT((ndo, "\n    %s%s%s:%u ", intoa(nr->src_ina.s_addr), buf, asbuf,
206 			EXTRACT_16BITS(&nr->srcport)));
207 
208 		ND_PRINT((ndo, "> %s%s%s:%u ", intoa(nr->dst_ina.s_addr), buf, asbuf,
209 			EXTRACT_16BITS(&nr->dstport)));
210 
211 		ND_PRINT((ndo, ">> %s\n    ", intoa(nr->nhop_ina.s_addr)));
212 
213 		pent = getprotobynumber(nr->proto);
214 		if (!pent || ndo->ndo_nflag)
215 			ND_PRINT((ndo, "%u ", nr->proto));
216 		else
217 			ND_PRINT((ndo, "%s ", pent->p_name));
218 
219 		/* tcp flags for tcp only */
220 		if (pent && pent->p_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 
241 trunc:
242 	ND_PRINT((ndo, "[|cnfp]"));
243 	return;
244 }
245 
246 static void
247 cnfp_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 	struct protoent *pent;
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 		pent = getprotobynumber(nr->proto);
311 		if (!pent || ndo->ndo_nflag)
312 			ND_PRINT((ndo, "%u ", nr->proto));
313 		else
314 			ND_PRINT((ndo, "%s ", pent->p_name));
315 
316 		/* tcp flags for tcp only */
317 		if (pent && pent->p_proto == IPPROTO_TCP) {
318 			int flags;
319 			flags = nr->tcp_flags;
320 			ND_PRINT((ndo, "%s%s%s%s%s%s%s",
321 				flags & TH_FIN  ? "F" : "",
322 				flags & TH_SYN  ? "S" : "",
323 				flags & TH_RST  ? "R" : "",
324 				flags & TH_PUSH ? "P" : "",
325 				flags & TH_ACK  ? "A" : "",
326 				flags & TH_URG  ? "U" : "",
327 				flags           ? " " : ""));
328 		}
329 
330 		buf[0]='\0';
331 		ND_PRINT((ndo, "tos %u, %u (%u octets) %s",
332 		       nr->tos,
333 		       EXTRACT_32BITS(&nr->packets),
334 		       EXTRACT_32BITS(&nr->octets), buf));
335 	}
336 	return;
337 
338 trunc:
339 	ND_PRINT((ndo, "[|cnfp]"));
340 	return;
341 }
342 
343 static void
344 cnfp_v6_print(netdissect_options *ndo, const u_char *cp)
345 {
346 	register const struct nfhdr_v6 *nh;
347 	register const struct nfrec_v6 *nr;
348 	struct protoent *pent;
349 	int nrecs, ver;
350 #if 0
351 	time_t t;
352 #endif
353 
354 	nh = (const struct nfhdr_v6 *)cp;
355 	ND_TCHECK(*nh);
356 
357 	ver = EXTRACT_16BITS(&nh->version);
358 	nrecs = EXTRACT_32BITS(&nh->count);
359 #if 0
360 	/*
361 	 * This is seconds since the UN*X epoch, and is followed by
362 	 * nanoseconds.  XXX - format it, rather than just dumping the
363 	 * raw seconds-since-the-Epoch.
364 	 */
365 	t = EXTRACT_32BITS(&nh->utc_sec);
366 #endif
367 
368 	ND_PRINT((ndo, "NetFlow v%x, %u.%03u uptime, %u.%09u, ", ver,
369 	       EXTRACT_32BITS(&nh->msys_uptime)/1000,
370 	       EXTRACT_32BITS(&nh->msys_uptime)%1000,
371 	       EXTRACT_32BITS(&nh->utc_sec), EXTRACT_32BITS(&nh->utc_nsec)));
372 
373 	ND_PRINT((ndo, "#%u, ", EXTRACT_32BITS(&nh->sequence)));
374 	nr = (const struct nfrec_v6 *)&nh[1];
375 
376 	ND_PRINT((ndo, "%2u recs", nrecs));
377 
378 	for (; nrecs != 0; nr++, nrecs--) {
379 		char buf[20];
380 		char asbuf[20];
381 
382 		/*
383 		 * Make sure we have the entire record.
384 		 */
385 		ND_TCHECK(*nr);
386 		ND_PRINT((ndo, "\n  started %u.%03u, last %u.%03u",
387 		       EXTRACT_32BITS(&nr->start_time)/1000,
388 		       EXTRACT_32BITS(&nr->start_time)%1000,
389 		       EXTRACT_32BITS(&nr->last_time)/1000,
390 		       EXTRACT_32BITS(&nr->last_time)%1000));
391 
392 		asbuf[0] = buf[0] = '\0';
393 		snprintf(buf, sizeof(buf), "/%u", nr->src_mask);
394 		snprintf(asbuf, sizeof(asbuf), ":%u",
395 			EXTRACT_16BITS(&nr->src_as));
396 		ND_PRINT((ndo, "\n    %s%s%s:%u ", intoa(nr->src_ina.s_addr), buf, asbuf,
397 			EXTRACT_16BITS(&nr->srcport)));
398 
399 		snprintf(buf, sizeof(buf), "/%d", nr->dst_mask);
400 		snprintf(asbuf, sizeof(asbuf), ":%u",
401 			 EXTRACT_16BITS(&nr->dst_as));
402 		ND_PRINT((ndo, "> %s%s%s:%u ", intoa(nr->dst_ina.s_addr), buf, asbuf,
403 			EXTRACT_16BITS(&nr->dstport)));
404 
405 		ND_PRINT((ndo, ">> %s\n    ", intoa(nr->nhop_ina.s_addr)));
406 
407 		pent = getprotobynumber(nr->proto);
408 		if (!pent || ndo->ndo_nflag)
409 			ND_PRINT((ndo, "%u ", nr->proto));
410 		else
411 			ND_PRINT((ndo, "%s ", pent->p_name));
412 
413 		/* tcp flags for tcp only */
414 		if (pent && pent->p_proto == IPPROTO_TCP) {
415 			int flags;
416 			flags = nr->tcp_flags;
417 			ND_PRINT((ndo, "%s%s%s%s%s%s%s",
418 				flags & TH_FIN  ? "F" : "",
419 				flags & TH_SYN  ? "S" : "",
420 				flags & TH_RST  ? "R" : "",
421 				flags & TH_PUSH ? "P" : "",
422 				flags & TH_ACK  ? "A" : "",
423 				flags & TH_URG  ? "U" : "",
424 				flags           ? " " : ""));
425 		}
426 
427 		buf[0]='\0';
428 		snprintf(buf, sizeof(buf), "(%u<>%u encaps)",
429 			 (EXTRACT_16BITS(&nr->flags) >> 8) & 0xff,
430 			 (EXTRACT_16BITS(&nr->flags)) & 0xff);
431 		ND_PRINT((ndo, "tos %u, %u (%u octets) %s",
432 		       nr->tos,
433 		       EXTRACT_32BITS(&nr->packets),
434 		       EXTRACT_32BITS(&nr->octets), buf));
435 	}
436 	return;
437 
438 trunc:
439 	ND_PRINT((ndo, "[|cnfp]"));
440 	return;
441 }
442 
443 void
444 cnfp_print(netdissect_options *ndo, const u_char *cp)
445 {
446 	int ver;
447 
448 	/*
449 	 * First 2 bytes are the version number.
450 	 */
451 	ND_TCHECK2(*cp, 2);
452 	ver = EXTRACT_16BITS(cp);
453 	switch (ver) {
454 
455 	case 1:
456 		cnfp_v1_print(ndo, cp);
457 		break;
458 
459 	case 5:
460 		cnfp_v5_print(ndo, cp);
461 		break;
462 
463 	case 6:
464 		cnfp_v6_print(ndo, cp);
465 		break;
466 
467 	default:
468 		ND_PRINT((ndo, "NetFlow v%x", ver));
469 		break;
470 	}
471 	return;
472 
473 trunc:
474 	ND_PRINT((ndo, "[|cnfp]"));
475 	return;
476 }
477