flowctl.c revision 144017
1/*-
2 * Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org>
3 * Copyright (c) 2001-2003 Roman V. Palagin <romanp@unshadow.net>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $SourceForge: flowctl.c,v 1.15 2004/08/31 20:24:58 glebius Exp $
28 */
29
30#ifndef lint
31static const char rcs_id[] =
32    "@(#) $FreeBSD: head/usr.sbin/flowctl/flowctl.c 144017 2005-03-23 09:40:18Z glebius $";
33#endif
34
35#include <sys/types.h>
36#include <sys/time.h>
37#include <sys/socket.h>
38#include <sys/queue.h>
39
40#include <net/if.h>
41#include <netinet/in.h>
42
43#include <arpa/inet.h>
44
45#include <err.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <unistd.h>
50
51#include <netgraph.h>
52#include <netgraph/netflow/ng_netflow.h>
53
54#define	CISCO_SH_FLOW_HEADER	"SrcIf         SrcIPaddress    DstIf         DstIPaddress    Pr SrcP DstP  Pkts\n"
55#define	CISCO_SH_FLOW	"%-13s %-15s %-13s %-15s %2u %4.4x %4.4x %6lu\n"
56
57#define	CISCO_SH_VERB_FLOW_HEADER "SrcIf          SrcIPaddress    DstIf          DstIPaddress    Pr TOS Flgs  Pkts\n" \
58"Port Msk AS                    Port Msk AS    NextHop              B/Pk  Active\n"
59
60#define	CISCO_SH_VERB_FLOW "%-14s %-15s %-14s %-15s %2u %3x %4x %6lu\n" \
61	"%4.4x /%-2u %-5u                 %4.4x /%-2u %-5u %-15s %9u %8u\n\n"
62
63static int flow_cache_print(struct ngnf_flows *recs);
64static int flow_cache_print_verbose(struct ngnf_flows *recs);
65static int ctl_show(int, char **);
66static void help(void);
67static void execute_command(int, char **);
68
69struct ip_ctl_cmd {
70	char	*cmd_name;
71	int	(*cmd_func)(int argc, char **argv);
72};
73
74struct ip_ctl_cmd cmds[] = {
75    {"show",	ctl_show},
76    {NULL,	NULL},
77};
78
79int	cs;
80char	ng_nodename[NG_PATHLEN + 1];
81
82int
83main(int argc, char **argv)
84{
85	int c;
86	char sname[NG_NODESIZ];
87	int rcvbuf = SORCVBUF_SIZE;
88	char	*ng_name;
89
90	/* parse options */
91	while ((c = getopt(argc, argv, "d:")) != -1) {
92		switch (c) {
93		case 'd':	/* set libnetgraph debug level. */
94			NgSetDebug(atoi(optarg));
95			break;
96		}
97	}
98
99	argc -= optind;
100	argv += optind;
101	ng_name = argv[0];
102	if (ng_name == NULL)
103		help();
104	argc--;
105	argv++;
106
107	snprintf(ng_nodename, sizeof(ng_nodename), "%s:", ng_name);
108
109	/* create control socket. */
110	snprintf(sname, sizeof(sname), "flowctl%i", getpid());
111
112	if (NgMkSockNode(sname, &cs, NULL) == -1)
113		err(1, "NgMkSockNode");
114
115	/* set receive buffer size */
116	if (setsockopt(cs, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) == -1)
117		err(1, "setsockopt(SOL_SOCKET, SO_RCVBUF)");
118
119	/* parse and execute command */
120	execute_command(argc, argv);
121
122	close(cs);
123
124	exit(0);
125}
126
127static void
128execute_command(int argc, char **argv)
129{
130	int cindex = -1;
131	int i;
132
133	if (!argc)
134		help();
135	for (i = 0; cmds[i].cmd_name != NULL; i++)
136		if (!strncmp(argv[0], cmds[i].cmd_name, strlen(argv[0]))) {
137			if (cindex != -1)
138				errx(1, "ambiguous command: %s", argv[0]);
139			cindex = i;
140		}
141	if (cindex == -1)
142		errx(1, "bad command: %s", argv[0]);
143	argc--;
144	argv++;
145	(*cmds[cindex].cmd_func)(argc, argv);
146}
147
148static int
149ctl_show(int argc, char **argv)
150{
151	struct ng_mesg *ng_mesg;
152	struct ngnf_flows *data;
153	char path[NG_PATHLEN + 1];
154	int token, nread, last = 0;
155	int verbose = 0;
156
157	if (argc > 0 && !strncmp(argv[0], "verbose", strlen(argv[0])))
158		verbose = 1;
159
160	ng_mesg = alloca(SORCVBUF_SIZE);
161
162	if (verbose)
163		printf(CISCO_SH_VERB_FLOW_HEADER);
164	else
165		printf(CISCO_SH_FLOW_HEADER);
166
167	for (;;) {
168		/* request set of accounting records */
169		token = NgSendMsg(cs, ng_nodename, NGM_NETFLOW_COOKIE,
170		    NGM_NETFLOW_SHOW, (void *)&last, sizeof(last));
171		if (token == -1)
172			err(1, "NgSendMsg(NGM_NETFLOW_SHOW)");
173
174		/* read reply */
175		nread = NgRecvMsg(cs, ng_mesg, SORCVBUF_SIZE, path);
176		if (nread == -1)
177			err(1, "NgRecvMsg() failed");
178
179		if (ng_mesg->header.token != token)
180			err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): token mismatch");
181
182		data = (struct ngnf_flows*)ng_mesg->data;
183		if ((ng_mesg->header.arglen < (sizeof(*data))) ||
184		    (ng_mesg->header.arglen < (sizeof(*data) +
185		    (data->nentries * sizeof(struct flow_entry_data)))))
186			err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): arglen too small");
187
188		if (verbose)
189			(void )flow_cache_print_verbose(data);
190		else
191			(void )flow_cache_print(data);
192
193		if (data->last != 0)
194			last = data->last;
195		else
196			break;
197	}
198
199	return (0);
200}
201
202static int
203flow_cache_print(struct ngnf_flows *recs)
204{
205	struct flow_entry_data *fle;
206	char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN];
207	char src_if[IFNAMSIZ], dst_if[IFNAMSIZ];
208	int i;
209
210	/* quick check */
211	if (recs->nentries == 0)
212		return (0);
213
214	fle = recs->entries;
215	for (i = 0; i < recs->nentries; i++, fle++) {
216		inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src));
217		inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst));
218		printf(CISCO_SH_FLOW,
219			if_indextoname(fle->fle_i_ifx, src_if),
220			src,
221			if_indextoname(fle->fle_o_ifx, dst_if),
222			dst,
223			fle->r.r_ip_p,
224			ntohs(fle->r.r_sport),
225			ntohs(fle->r.r_dport),
226			fle->packets);
227
228	}
229
230	return (i);
231}
232
233static int
234flow_cache_print_verbose(struct ngnf_flows *recs)
235{
236	struct flow_entry_data *fle;
237	char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN], next[INET_ADDRSTRLEN];
238	char src_if[IFNAMSIZ], dst_if[IFNAMSIZ];
239	int i;
240
241	/* quick check */
242	if (recs->nentries == 0)
243		return (0);
244
245	fle = recs->entries;
246	for (i = 0; i < recs->nentries; i++, fle++) {
247		inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src));
248		inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst));
249		inet_ntop(AF_INET, &fle->next_hop, next, sizeof(next));
250		printf(CISCO_SH_VERB_FLOW,
251			if_indextoname(fle->fle_i_ifx, src_if),
252			src,
253			if_indextoname(fle->fle_o_ifx, dst_if),
254			dst,
255			fle->r.r_ip_p,
256			fle->r.r_tos,
257			fle->tcp_flags,
258			fle->packets,
259			ntohs(fle->r.r_sport),
260			fle->src_mask,
261			0,
262			ntohs(fle->r.r_dport),
263			fle->dst_mask,
264			0,
265			next,
266			(u_int)(fle->bytes / fle->packets),
267			0);
268
269	}
270
271	return (i);
272}
273
274static void
275help(void)
276{
277	extern char *__progname;
278
279	fprintf(stderr, "usage: %s [-d level] nodename command\n", __progname);
280	exit (0);
281}
282