1135380Sglebius/*-
2144017Sglebius * Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org>
3135380Sglebius * Copyright (c) 2001-2003 Roman V. Palagin <romanp@unshadow.net>
4135380Sglebius * All rights reserved.
5135380Sglebius *
6135380Sglebius * Redistribution and use in source and binary forms, with or without
7135380Sglebius * modification, are permitted provided that the following conditions
8135380Sglebius * are met:
9135380Sglebius * 1. Redistributions of source code must retain the above copyright
10135380Sglebius *    notice, this list of conditions and the following disclaimer.
11135380Sglebius * 2. Redistributions in binary form must reproduce the above copyright
12135380Sglebius *    notice, this list of conditions and the following disclaimer in the
13135380Sglebius *    documentation and/or other materials provided with the distribution.
14135380Sglebius *
15135380Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16135380Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17135380Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18135380Sglebius * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19135380Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20135380Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21135380Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22135380Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23135380Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24135380Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25135380Sglebius * SUCH DAMAGE.
26135380Sglebius *
27135380Sglebius * $SourceForge: flowctl.c,v 1.15 2004/08/31 20:24:58 glebius Exp $
28135380Sglebius */
29135380Sglebius
30135380Sglebius#ifndef lint
31135380Sglebiusstatic const char rcs_id[] =
32135380Sglebius    "@(#) $FreeBSD$";
33135380Sglebius#endif
34135380Sglebius
35135380Sglebius#include <sys/types.h>
36135380Sglebius#include <sys/time.h>
37135380Sglebius#include <sys/socket.h>
38135380Sglebius#include <sys/queue.h>
39135380Sglebius
40135380Sglebius#include <net/if.h>
41135380Sglebius#include <netinet/in.h>
42135380Sglebius
43135380Sglebius#include <arpa/inet.h>
44135380Sglebius
45135380Sglebius#include <err.h>
46135380Sglebius#include <stdio.h>
47135380Sglebius#include <stdlib.h>
48135380Sglebius#include <string.h>
49223788Sglebius#include <sysexits.h>
50135380Sglebius#include <unistd.h>
51135380Sglebius
52135380Sglebius#include <netgraph.h>
53135380Sglebius#include <netgraph/netflow/ng_netflow.h>
54135380Sglebius
55236808Smelifaro#define	CISCO_SH_FLOW_HEADER	"SrcIf         SrcIPaddress    " \
56236808Smelifaro"DstIf         DstIPaddress    Pr SrcP DstP  Pkts\n"
57135380Sglebius#define	CISCO_SH_FLOW	"%-13s %-15s %-13s %-15s %2u %4.4x %4.4x %6lu\n"
58135380Sglebius
59236808Smelifaro/* human-readable IPv4 header */
60236808Smelifaro#define	CISCO_SH_FLOW_HHEADER	"SrcIf         SrcIPaddress    " \
61236808Smelifaro"DstIf         DstIPaddress    Proto  SrcPort  DstPort     Pkts\n"
62236808Smelifaro#define	CISCO_SH_FLOW_H	"%-13s %-15s %-13s %-15s %5u %8d %8d %8lu\n"
63223788Sglebius
64236808Smelifaro#define	CISCO_SH_FLOW6_HEADER	"SrcIf         SrcIPaddress                   " \
65236808Smelifaro"DstIf         DstIPaddress                   Pr SrcP DstP  Pkts\n"
66236808Smelifaro#define	CISCO_SH_FLOW6		"%-13s %-30s %-13s %-30s %2u %4.4x %4.4x %6lu\n"
67236808Smelifaro
68236808Smelifaro/* Human-readable IPv6 headers */
69236808Smelifaro#define	CISCO_SH_FLOW6_HHEADER	"SrcIf         SrcIPaddress                         " \
70236808Smelifaro"DstIf         DstIPaddress                         Proto  SrcPort  DstPort     Pkts\n"
71236808Smelifaro#define	CISCO_SH_FLOW6_H	"%-13s %-36s %-13s %-36s %5u %8d %8d %8lu\n"
72236808Smelifaro
73236808Smelifaro#define	CISCO_SH_VERB_FLOW_HEADER "SrcIf          SrcIPaddress    " \
74236808Smelifaro"DstIf          DstIPaddress    Pr TOS Flgs  Pkts\n" \
75144017Sglebius"Port Msk AS                    Port Msk AS    NextHop              B/Pk  Active\n"
76135380Sglebius
77144017Sglebius#define	CISCO_SH_VERB_FLOW "%-14s %-15s %-14s %-15s %2u %3x %4x %6lu\n" \
78144017Sglebius	"%4.4x /%-2u %-5u                 %4.4x /%-2u %-5u %-15s %9u %8u\n\n"
79144017Sglebius
80236808Smelifaro#define	CISCO_SH_VERB_FLOW6_HEADER "SrcIf          SrcIPaddress                   " \
81236808Smelifaro"DstIf          DstIPaddress                   Pr TOS Flgs  Pkts\n" \
82223788Sglebius"Port Msk AS                    Port Msk AS    NextHop                             B/Pk  Active\n"
83223788Sglebius
84223788Sglebius#define	CISCO_SH_VERB_FLOW6 "%-14s %-30s %-14s %-30s %2u %3x %4x %6lu\n" \
85223788Sglebius	"%4.4x /%-2u %-5u                 %4.4x /%-2u %-5u %-30s %9u %8u\n\n"
86223824Spluknet#ifdef INET
87223788Sglebiusstatic void flow_cache_print(struct ngnf_show_header *resp);
88223824Spluknetstatic void flow_cache_print_verbose(struct ngnf_show_header *resp);
89223824Spluknet#endif
90223824Spluknet#ifdef INET6
91223788Sglebiusstatic void flow_cache_print6(struct ngnf_show_header *resp);
92223788Sglebiusstatic void flow_cache_print6_verbose(struct ngnf_show_header *resp);
93223824Spluknet#endif
94223788Sglebiusstatic void ctl_show(int, char **);
95223824Spluknet#if defined(INET) || defined(INET6)
96223788Sglebiusstatic void do_show(int, void (*func)(struct ngnf_show_header *));
97223824Spluknet#endif
98135380Sglebiusstatic void help(void);
99135380Sglebiusstatic void execute_command(int, char **);
100135380Sglebius
101135380Sglebiusstruct ip_ctl_cmd {
102135380Sglebius	char	*cmd_name;
103223788Sglebius	void	(*cmd_func)(int argc, char **argv);
104135380Sglebius};
105135380Sglebius
106135380Sglebiusstruct ip_ctl_cmd cmds[] = {
107140413Sglebius    {"show",	ctl_show},
108140413Sglebius    {NULL,	NULL},
109135380Sglebius};
110135380Sglebius
111236808Smelifaroint	cs, human = 0;
112223788Sglebiuschar	*ng_path;
113135380Sglebius
114135380Sglebiusint
115135380Sglebiusmain(int argc, char **argv)
116135380Sglebius{
117141393Sglebius	int c;
118135380Sglebius	char sname[NG_NODESIZ];
119135380Sglebius	int rcvbuf = SORCVBUF_SIZE;
120135380Sglebius
121135380Sglebius	/* parse options */
122135380Sglebius	while ((c = getopt(argc, argv, "d:")) != -1) {
123135380Sglebius		switch (c) {
124135380Sglebius		case 'd':	/* set libnetgraph debug level. */
125135380Sglebius			NgSetDebug(atoi(optarg));
126135380Sglebius			break;
127135380Sglebius		}
128135380Sglebius	}
129135380Sglebius
130135380Sglebius	argc -= optind;
131135380Sglebius	argv += optind;
132223788Sglebius	ng_path = argv[0];
133223788Sglebius	if (ng_path == NULL || (strlen(ng_path) > NG_PATHSIZ))
134135380Sglebius		help();
135135380Sglebius	argc--;
136135380Sglebius	argv++;
137135380Sglebius
138135380Sglebius	/* create control socket. */
139135380Sglebius	snprintf(sname, sizeof(sname), "flowctl%i", getpid());
140135380Sglebius
141135380Sglebius	if (NgMkSockNode(sname, &cs, NULL) == -1)
142135380Sglebius		err(1, "NgMkSockNode");
143135380Sglebius
144135380Sglebius	/* set receive buffer size */
145135380Sglebius	if (setsockopt(cs, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) == -1)
146135380Sglebius		err(1, "setsockopt(SOL_SOCKET, SO_RCVBUF)");
147135380Sglebius
148135380Sglebius	/* parse and execute command */
149135380Sglebius	execute_command(argc, argv);
150135380Sglebius
151135380Sglebius	close(cs);
152135380Sglebius
153135380Sglebius	exit(0);
154135380Sglebius}
155135380Sglebius
156135380Sglebiusstatic void
157135380Sglebiusexecute_command(int argc, char **argv)
158135380Sglebius{
159135380Sglebius	int cindex = -1;
160135380Sglebius	int i;
161135380Sglebius
162135380Sglebius	if (!argc)
163135380Sglebius		help();
164135380Sglebius	for (i = 0; cmds[i].cmd_name != NULL; i++)
165135380Sglebius		if (!strncmp(argv[0], cmds[i].cmd_name, strlen(argv[0]))) {
166135380Sglebius			if (cindex != -1)
167135380Sglebius				errx(1, "ambiguous command: %s", argv[0]);
168135380Sglebius			cindex = i;
169135380Sglebius		}
170135380Sglebius	if (cindex == -1)
171135380Sglebius		errx(1, "bad command: %s", argv[0]);
172135380Sglebius	argc--;
173135380Sglebius	argv++;
174140413Sglebius	(*cmds[cindex].cmd_func)(argc, argv);
175135380Sglebius}
176135380Sglebius
177223788Sglebiusstatic void
178140413Sglebiusctl_show(int argc, char **argv)
179135380Sglebius{
180223824Spluknet	int ipv4, ipv6, verbose = 0;
181135380Sglebius
182223824Spluknet	ipv4 = feature_present("inet");
183223824Spluknet	ipv6 = feature_present("inet6");
184223824Spluknet
185223788Sglebius	if (argc > 0 && !strncmp(argv[0], "ipv4", 4)) {
186223788Sglebius		ipv6 = 0;
187223788Sglebius		argc--;
188223788Sglebius		argv++;
189223788Sglebius	}
190223788Sglebius	if (argc > 0 && !strncmp(argv[0], "ipv6", 4)) {
191223788Sglebius		ipv4 = 0;
192223788Sglebius		argc--;
193223788Sglebius		argv++;
194223788Sglebius	}
195223788Sglebius
196144017Sglebius	if (argc > 0 && !strncmp(argv[0], "verbose", strlen(argv[0])))
197144017Sglebius		verbose = 1;
198144017Sglebius
199236808Smelifaro	if (argc > 0 && !strncmp(argv[0], "human", strlen(argv[0])))
200236808Smelifaro		human = 1;
201236808Smelifaro
202223824Spluknet#ifdef INET
203223788Sglebius	if (ipv4) {
204223788Sglebius		if (verbose)
205223788Sglebius			do_show(4, &flow_cache_print_verbose);
206223788Sglebius		else
207223788Sglebius			do_show(4, &flow_cache_print);
208223788Sglebius	}
209223824Spluknet#endif
210223788Sglebius
211223824Spluknet#ifdef INET6
212223788Sglebius	if (ipv6) {
213223788Sglebius		if (verbose)
214223788Sglebius			do_show(6, &flow_cache_print6_verbose);
215223788Sglebius		else
216223788Sglebius			do_show(6, &flow_cache_print6);
217223788Sglebius	}
218223824Spluknet#endif
219223788Sglebius}
220223788Sglebius
221223824Spluknet#if defined(INET) || defined(INET6)
222223788Sglebiusstatic void
223223788Sglebiusdo_show(int version, void (*func)(struct ngnf_show_header *))
224223788Sglebius{
225223788Sglebius	struct ng_mesg *ng_mesg;
226223788Sglebius	struct ngnf_show_header req, *resp;
227223788Sglebius	int token, nread;
228223788Sglebius
229135380Sglebius	ng_mesg = alloca(SORCVBUF_SIZE);
230135380Sglebius
231223788Sglebius	req.version = version;
232223788Sglebius	req.hash_id = req.list_id = 0;
233135380Sglebius
234135380Sglebius	for (;;) {
235135380Sglebius		/* request set of accounting records */
236223788Sglebius		token = NgSendMsg(cs, ng_path, NGM_NETFLOW_COOKIE,
237223788Sglebius		    NGM_NETFLOW_SHOW, (void *)&req, sizeof(req));
238135380Sglebius		if (token == -1)
239135380Sglebius			err(1, "NgSendMsg(NGM_NETFLOW_SHOW)");
240135380Sglebius
241135380Sglebius		/* read reply */
242223788Sglebius		nread = NgRecvMsg(cs, ng_mesg, SORCVBUF_SIZE, NULL);
243135380Sglebius		if (nread == -1)
244135380Sglebius			err(1, "NgRecvMsg() failed");
245135380Sglebius
246135380Sglebius		if (ng_mesg->header.token != token)
247135380Sglebius			err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): token mismatch");
248135380Sglebius
249223788Sglebius		resp = (struct ngnf_show_header *)ng_mesg->data;
250223788Sglebius		if ((ng_mesg->header.arglen < (sizeof(*resp))) ||
251223788Sglebius		    (ng_mesg->header.arglen < (sizeof(*resp) +
252223788Sglebius		    (resp->nentries * sizeof(struct flow_entry_data)))))
253135380Sglebius			err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): arglen too small");
254135380Sglebius
255223788Sglebius		(*func)(resp);
256135380Sglebius
257223788Sglebius		if (resp->hash_id != 0)
258223788Sglebius			req.hash_id = resp->hash_id;
259135380Sglebius		else
260135380Sglebius			break;
261223788Sglebius		req.list_id = resp->list_id;
262135380Sglebius	}
263135380Sglebius}
264223824Spluknet#endif
265135380Sglebius
266223824Spluknet#ifdef INET
267223788Sglebiusstatic void
268223788Sglebiusflow_cache_print(struct ngnf_show_header *resp)
269135380Sglebius{
270135380Sglebius	struct flow_entry_data *fle;
271135380Sglebius	char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN];
272135380Sglebius	char src_if[IFNAMSIZ], dst_if[IFNAMSIZ];
273135380Sglebius	int i;
274135380Sglebius
275223788Sglebius	if (resp->version != 4)
276223788Sglebius		errx(EX_SOFTWARE, "%s: version mismatch: %u",
277223788Sglebius		    __func__, resp->version);
278135380Sglebius
279236808Smelifaro	if (resp->nentries > 0)
280236808Smelifaro		printf(human ? CISCO_SH_FLOW_HHEADER : CISCO_SH_FLOW_HEADER);
281223788Sglebius
282223788Sglebius	fle = (struct flow_entry_data *)(resp + 1);
283223788Sglebius	for (i = 0; i < resp->nentries; i++, fle++) {
284135380Sglebius		inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src));
285135380Sglebius		inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst));
286236808Smelifaro		printf(human ? CISCO_SH_FLOW_H : CISCO_SH_FLOW,
287135380Sglebius			if_indextoname(fle->fle_i_ifx, src_if),
288135380Sglebius			src,
289135380Sglebius			if_indextoname(fle->fle_o_ifx, dst_if),
290135380Sglebius			dst,
291135380Sglebius			fle->r.r_ip_p,
292135380Sglebius			ntohs(fle->r.r_sport),
293135380Sglebius			ntohs(fle->r.r_dport),
294135380Sglebius			fle->packets);
295135380Sglebius
296135380Sglebius	}
297135380Sglebius}
298223824Spluknet#endif
299135380Sglebius
300223788Sglebius#ifdef INET6
301223788Sglebiusstatic void
302223788Sglebiusflow_cache_print6(struct ngnf_show_header *resp)
303144017Sglebius{
304223788Sglebius	struct flow6_entry_data *fle6;
305223788Sglebius	char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN];
306223788Sglebius	char src_if[IFNAMSIZ], dst_if[IFNAMSIZ];
307223788Sglebius	int i;
308223788Sglebius
309223788Sglebius	if (resp->version != 6)
310223788Sglebius		errx(EX_SOFTWARE, "%s: version mismatch: %u",
311223788Sglebius		    __func__, resp->version);
312223788Sglebius
313236808Smelifaro	if (resp->nentries > 0)
314236808Smelifaro		printf(human ? CISCO_SH_FLOW6_HHEADER : CISCO_SH_FLOW6_HEADER);
315223788Sglebius
316223788Sglebius	fle6 = (struct flow6_entry_data *)(resp + 1);
317223788Sglebius	for (i = 0; i < resp->nentries; i++, fle6++) {
318223788Sglebius		inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6));
319223788Sglebius		inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6));
320236808Smelifaro		printf(human ? CISCO_SH_FLOW6_H : CISCO_SH_FLOW6,
321223788Sglebius			if_indextoname(fle6->fle_i_ifx, src_if),
322223788Sglebius			src6,
323223788Sglebius			if_indextoname(fle6->fle_o_ifx, dst_if),
324223788Sglebius			dst6,
325223788Sglebius			fle6->r.r_ip_p,
326223788Sglebius			ntohs(fle6->r.r_sport),
327223788Sglebius			ntohs(fle6->r.r_dport),
328223788Sglebius			fle6->packets);
329223788Sglebius
330223788Sglebius	}
331223788Sglebius}
332223788Sglebius#endif
333223788Sglebius
334223824Spluknet#ifdef INET
335223788Sglebiusstatic void
336223788Sglebiusflow_cache_print_verbose(struct ngnf_show_header *resp)
337223788Sglebius{
338144017Sglebius	struct flow_entry_data *fle;
339144017Sglebius	char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN], next[INET_ADDRSTRLEN];
340144017Sglebius	char src_if[IFNAMSIZ], dst_if[IFNAMSIZ];
341144017Sglebius	int i;
342144017Sglebius
343223788Sglebius	if (resp->version != 4)
344223788Sglebius		errx(EX_SOFTWARE, "%s: version mismatch: %u",
345223788Sglebius		    __func__, resp->version);
346144017Sglebius
347223788Sglebius	printf(CISCO_SH_VERB_FLOW_HEADER);
348223788Sglebius
349223788Sglebius	fle = (struct flow_entry_data *)(resp + 1);
350223788Sglebius	for (i = 0; i < resp->nentries; i++, fle++) {
351144017Sglebius		inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src));
352144017Sglebius		inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst));
353144017Sglebius		inet_ntop(AF_INET, &fle->next_hop, next, sizeof(next));
354144017Sglebius		printf(CISCO_SH_VERB_FLOW,
355144017Sglebius			if_indextoname(fle->fle_i_ifx, src_if),
356144017Sglebius			src,
357144017Sglebius			if_indextoname(fle->fle_o_ifx, dst_if),
358144017Sglebius			dst,
359144017Sglebius			fle->r.r_ip_p,
360144017Sglebius			fle->r.r_tos,
361144017Sglebius			fle->tcp_flags,
362144017Sglebius			fle->packets,
363144017Sglebius			ntohs(fle->r.r_sport),
364144017Sglebius			fle->src_mask,
365144017Sglebius			0,
366144017Sglebius			ntohs(fle->r.r_dport),
367144017Sglebius			fle->dst_mask,
368144017Sglebius			0,
369144017Sglebius			next,
370144017Sglebius			(u_int)(fle->bytes / fle->packets),
371144017Sglebius			0);
372144017Sglebius
373144017Sglebius	}
374144017Sglebius}
375223824Spluknet#endif
376144017Sglebius
377223788Sglebius#ifdef INET6
378135380Sglebiusstatic void
379223788Sglebiusflow_cache_print6_verbose(struct ngnf_show_header *resp)
380223788Sglebius{
381223788Sglebius	struct flow6_entry_data *fle6;
382223788Sglebius	char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN], next6[INET6_ADDRSTRLEN];
383223788Sglebius	char src_if[IFNAMSIZ], dst_if[IFNAMSIZ];
384223788Sglebius	int i;
385223788Sglebius
386223788Sglebius	if (resp->version != 6)
387223788Sglebius		errx(EX_SOFTWARE, "%s: version mismatch: %u",
388223788Sglebius		    __func__, resp->version);
389223788Sglebius
390223788Sglebius	printf(CISCO_SH_VERB_FLOW6_HEADER);
391223788Sglebius
392223788Sglebius	fle6 = (struct flow6_entry_data *)(resp + 1);
393223788Sglebius	for (i = 0; i < resp->nentries; i++, fle6++) {
394223788Sglebius		inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6));
395223788Sglebius		inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6));
396223788Sglebius		inet_ntop(AF_INET6, &fle6->n.next_hop6, next6, sizeof(next6));
397223788Sglebius		printf(CISCO_SH_VERB_FLOW6,
398223788Sglebius			if_indextoname(fle6->fle_i_ifx, src_if),
399223788Sglebius			src6,
400223788Sglebius			if_indextoname(fle6->fle_o_ifx, dst_if),
401223788Sglebius			dst6,
402223788Sglebius			fle6->r.r_ip_p,
403223788Sglebius			fle6->r.r_tos,
404223788Sglebius			fle6->tcp_flags,
405223788Sglebius			fle6->packets,
406223788Sglebius			ntohs(fle6->r.r_sport),
407223788Sglebius			fle6->src_mask,
408223788Sglebius			0,
409223788Sglebius			ntohs(fle6->r.r_dport),
410223788Sglebius			fle6->dst_mask,
411223788Sglebius			0,
412223788Sglebius			next6,
413223788Sglebius			(u_int)(fle6->bytes / fle6->packets),
414223788Sglebius			0);
415223788Sglebius	}
416223788Sglebius}
417223788Sglebius#endif
418223788Sglebius
419223788Sglebiusstatic void
420135380Sglebiushelp(void)
421135380Sglebius{
422135380Sglebius	extern char *__progname;
423135380Sglebius
424135380Sglebius	fprintf(stderr, "usage: %s [-d level] nodename command\n", __progname);
425135380Sglebius	exit (0);
426135380Sglebius}
427