]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.sbin/flowctl/flowctl.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / usr.sbin / flowctl / flowctl.c
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
31 static const char rcs_id[] =
32     "@(#) $FreeBSD$";
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 <sysexits.h>
50 #include <unistd.h>
51
52 #include <netgraph.h>
53 #include <netgraph/netflow/ng_netflow.h>
54
55 #define CISCO_SH_FLOW_HEADER    "SrcIf         SrcIPaddress    DstIf         DstIPaddress    Pr SrcP DstP  Pkts\n"
56 #define CISCO_SH_FLOW   "%-13s %-15s %-13s %-15s %2u %4.4x %4.4x %6lu\n"
57
58 #define CISCO_SH_FLOW6_HEADER   "SrcIf         SrcIPaddress                   DstIf         DstIPaddress                   Pr SrcP DstP  Pkts\n"
59 #define CISCO_SH_FLOW6  "%-13s %-30s %-13s %-30s %2u %4.4x %4.4x %6lu\n"
60
61 #define CISCO_SH_VERB_FLOW_HEADER "SrcIf          SrcIPaddress    DstIf          DstIPaddress    Pr TOS Flgs  Pkts\n" \
62 "Port Msk AS                    Port Msk AS    NextHop              B/Pk  Active\n"
63
64 #define CISCO_SH_VERB_FLOW "%-14s %-15s %-14s %-15s %2u %3x %4x %6lu\n" \
65         "%4.4x /%-2u %-5u                 %4.4x /%-2u %-5u %-15s %9u %8u\n\n"
66
67 #define CISCO_SH_VERB_FLOW6_HEADER "SrcIf          SrcIPaddress                   DstIf          DstIPaddress                   Pr TOS Flgs  Pkts\n" \
68 "Port Msk AS                    Port Msk AS    NextHop                             B/Pk  Active\n"
69
70 #define CISCO_SH_VERB_FLOW6 "%-14s %-30s %-14s %-30s %2u %3x %4x %6lu\n" \
71         "%4.4x /%-2u %-5u                 %4.4x /%-2u %-5u %-30s %9u %8u\n\n"
72 #ifdef INET
73 static void flow_cache_print(struct ngnf_show_header *resp);
74 static void flow_cache_print_verbose(struct ngnf_show_header *resp);
75 #endif
76 #ifdef INET6 
77 static void flow_cache_print6(struct ngnf_show_header *resp);
78 static void flow_cache_print6_verbose(struct ngnf_show_header *resp);
79 #endif
80 static void ctl_show(int, char **);
81 #if defined(INET) || defined(INET6)
82 static void do_show(int, void (*func)(struct ngnf_show_header *));
83 #endif
84 static void help(void);
85 static void execute_command(int, char **);
86
87 struct ip_ctl_cmd {
88         char    *cmd_name;
89         void    (*cmd_func)(int argc, char **argv);
90 };
91
92 struct ip_ctl_cmd cmds[] = {
93     {"show",    ctl_show},
94     {NULL,      NULL},
95 };
96
97 int     cs;
98 char    *ng_path;
99
100 int
101 main(int argc, char **argv)
102 {
103         int c;
104         char sname[NG_NODESIZ];
105         int rcvbuf = SORCVBUF_SIZE;
106
107         /* parse options */
108         while ((c = getopt(argc, argv, "d:")) != -1) {
109                 switch (c) {
110                 case 'd':       /* set libnetgraph debug level. */
111                         NgSetDebug(atoi(optarg));
112                         break;
113                 }
114         }
115
116         argc -= optind;
117         argv += optind;
118         ng_path = argv[0];
119         if (ng_path == NULL || (strlen(ng_path) > NG_PATHSIZ))
120                 help();
121         argc--;
122         argv++;
123
124         /* create control socket. */
125         snprintf(sname, sizeof(sname), "flowctl%i", getpid());
126
127         if (NgMkSockNode(sname, &cs, NULL) == -1)
128                 err(1, "NgMkSockNode");
129
130         /* set receive buffer size */
131         if (setsockopt(cs, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) == -1)
132                 err(1, "setsockopt(SOL_SOCKET, SO_RCVBUF)");
133
134         /* parse and execute command */
135         execute_command(argc, argv);
136
137         close(cs);
138         
139         exit(0);
140 }
141
142 static void
143 execute_command(int argc, char **argv)
144 {
145         int cindex = -1;
146         int i;
147
148         if (!argc)
149                 help();
150         for (i = 0; cmds[i].cmd_name != NULL; i++)
151                 if (!strncmp(argv[0], cmds[i].cmd_name, strlen(argv[0]))) {
152                         if (cindex != -1)
153                                 errx(1, "ambiguous command: %s", argv[0]);
154                         cindex = i;
155                 }
156         if (cindex == -1)
157                 errx(1, "bad command: %s", argv[0]);
158         argc--;
159         argv++;
160         (*cmds[cindex].cmd_func)(argc, argv);
161 }
162
163 static void
164 ctl_show(int argc, char **argv)
165 {
166         int ipv4, ipv6, verbose = 0;
167
168         ipv4 = feature_present("inet");
169         ipv6 = feature_present("inet6");
170
171         if (argc > 0 && !strncmp(argv[0], "ipv4", 4)) {
172                 ipv6 = 0;
173                 argc--;
174                 argv++;
175         }
176         if (argc > 0 && !strncmp(argv[0], "ipv6", 4)) {
177                 ipv4 = 0;
178                 argc--;
179                 argv++;
180         }
181
182         if (argc > 0 && !strncmp(argv[0], "verbose", strlen(argv[0])))
183                 verbose = 1;
184
185 #ifdef INET
186         if (ipv4) {
187                 if (verbose)
188                         do_show(4, &flow_cache_print_verbose);
189                 else
190                         do_show(4, &flow_cache_print);
191         }
192 #endif
193
194 #ifdef INET6
195         if (ipv6) {
196                 if (verbose)
197                         do_show(6, &flow_cache_print6_verbose);
198                 else
199                         do_show(6, &flow_cache_print6);
200         }
201 #endif
202 }
203
204 #if defined(INET) || defined(INET6)
205 static void
206 do_show(int version, void (*func)(struct ngnf_show_header *))
207 {
208         struct ng_mesg *ng_mesg;
209         struct ngnf_show_header req, *resp;
210         int token, nread;
211
212         ng_mesg = alloca(SORCVBUF_SIZE);
213
214         req.version = version;
215         req.hash_id = req.list_id = 0;
216
217         for (;;) {
218                 /* request set of accounting records */
219                 token = NgSendMsg(cs, ng_path, NGM_NETFLOW_COOKIE,
220                     NGM_NETFLOW_SHOW, (void *)&req, sizeof(req));
221                 if (token == -1)
222                         err(1, "NgSendMsg(NGM_NETFLOW_SHOW)");
223
224                 /* read reply */
225                 nread = NgRecvMsg(cs, ng_mesg, SORCVBUF_SIZE, NULL);
226                 if (nread == -1)
227                         err(1, "NgRecvMsg() failed");
228
229                 if (ng_mesg->header.token != token)
230                         err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): token mismatch");
231
232                 resp = (struct ngnf_show_header *)ng_mesg->data;
233                 if ((ng_mesg->header.arglen < (sizeof(*resp))) ||
234                     (ng_mesg->header.arglen < (sizeof(*resp) +
235                     (resp->nentries * sizeof(struct flow_entry_data)))))
236                         err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): arglen too small");
237
238                 (*func)(resp);
239
240                 if (resp->hash_id != 0)
241                         req.hash_id = resp->hash_id;
242                 else
243                         break;
244                 req.list_id = resp->list_id;
245         }
246 }
247 #endif
248
249 #ifdef INET
250 static void
251 flow_cache_print(struct ngnf_show_header *resp)
252 {
253         struct flow_entry_data *fle;
254         char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN];
255         char src_if[IFNAMSIZ], dst_if[IFNAMSIZ];
256         int i;
257
258         if (resp->version != 4)
259                 errx(EX_SOFTWARE, "%s: version mismatch: %u",
260                     __func__, resp->version);
261
262         printf(CISCO_SH_FLOW_HEADER);
263
264         fle = (struct flow_entry_data *)(resp + 1);
265         for (i = 0; i < resp->nentries; i++, fle++) {
266                 inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src));
267                 inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst));
268                 printf(CISCO_SH_FLOW,
269                         if_indextoname(fle->fle_i_ifx, src_if),
270                         src,
271                         if_indextoname(fle->fle_o_ifx, dst_if),
272                         dst,
273                         fle->r.r_ip_p,
274                         ntohs(fle->r.r_sport),
275                         ntohs(fle->r.r_dport),
276                         fle->packets);
277                         
278         }
279 }
280 #endif
281
282 #ifdef INET6
283 static void
284 flow_cache_print6(struct ngnf_show_header *resp)
285 {
286         struct flow6_entry_data *fle6;
287         char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN];
288         char src_if[IFNAMSIZ], dst_if[IFNAMSIZ];
289         int i;
290
291         if (resp->version != 6)
292                 errx(EX_SOFTWARE, "%s: version mismatch: %u",
293                     __func__, resp->version);
294
295         printf(CISCO_SH_FLOW6_HEADER);
296
297         fle6 = (struct flow6_entry_data *)(resp + 1);
298         for (i = 0; i < resp->nentries; i++, fle6++) {
299                 inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6));
300                 inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6));
301                 printf(CISCO_SH_FLOW6,
302                         if_indextoname(fle6->fle_i_ifx, src_if),
303                         src6,
304                         if_indextoname(fle6->fle_o_ifx, dst_if),
305                         dst6,
306                         fle6->r.r_ip_p,
307                         ntohs(fle6->r.r_sport),
308                         ntohs(fle6->r.r_dport),
309                         fle6->packets);
310                         
311         }
312 }
313 #endif
314
315 #ifdef INET
316 static void
317 flow_cache_print_verbose(struct ngnf_show_header *resp)
318 {
319         struct flow_entry_data *fle;
320         char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN], next[INET_ADDRSTRLEN];
321         char src_if[IFNAMSIZ], dst_if[IFNAMSIZ];
322         int i;
323
324         if (resp->version != 4)
325                 errx(EX_SOFTWARE, "%s: version mismatch: %u",
326                     __func__, resp->version);
327
328         printf(CISCO_SH_VERB_FLOW_HEADER);
329
330         fle = (struct flow_entry_data *)(resp + 1);
331         for (i = 0; i < resp->nentries; i++, fle++) {
332                 inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src));
333                 inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst));
334                 inet_ntop(AF_INET, &fle->next_hop, next, sizeof(next));
335                 printf(CISCO_SH_VERB_FLOW,
336                         if_indextoname(fle->fle_i_ifx, src_if),
337                         src,
338                         if_indextoname(fle->fle_o_ifx, dst_if),
339                         dst,
340                         fle->r.r_ip_p,
341                         fle->r.r_tos,
342                         fle->tcp_flags,
343                         fle->packets,
344                         ntohs(fle->r.r_sport),
345                         fle->src_mask,
346                         0,
347                         ntohs(fle->r.r_dport),
348                         fle->dst_mask,
349                         0,
350                         next,
351                         (u_int)(fle->bytes / fle->packets),
352                         0);
353                         
354         }
355 }
356 #endif
357
358 #ifdef INET6
359 static void
360 flow_cache_print6_verbose(struct ngnf_show_header *resp)
361 {
362         struct flow6_entry_data *fle6;
363         char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN], next6[INET6_ADDRSTRLEN];
364         char src_if[IFNAMSIZ], dst_if[IFNAMSIZ];
365         int i;
366
367         if (resp->version != 6)
368                 errx(EX_SOFTWARE, "%s: version mismatch: %u",
369                     __func__, resp->version);
370
371         printf(CISCO_SH_VERB_FLOW6_HEADER);
372
373         fle6 = (struct flow6_entry_data *)(resp + 1);
374         for (i = 0; i < resp->nentries; i++, fle6++) {
375                 inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6));
376                 inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6));
377                 inet_ntop(AF_INET6, &fle6->n.next_hop6, next6, sizeof(next6));
378                 printf(CISCO_SH_VERB_FLOW6,
379                         if_indextoname(fle6->fle_i_ifx, src_if),
380                         src6,
381                         if_indextoname(fle6->fle_o_ifx, dst_if),
382                         dst6,
383                         fle6->r.r_ip_p,
384                         fle6->r.r_tos,
385                         fle6->tcp_flags,
386                         fle6->packets,
387                         ntohs(fle6->r.r_sport),
388                         fle6->src_mask,
389                         0,
390                         ntohs(fle6->r.r_dport),
391                         fle6->dst_mask,
392                         0,
393                         next6,
394                         (u_int)(fle6->bytes / fle6->packets),
395                         0);
396         }
397 }
398 #endif
399
400 static void
401 help(void)
402 {
403         extern char *__progname;
404
405         fprintf(stderr, "usage: %s [-d level] nodename command\n", __progname);
406         exit (0);
407 }