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