Date: Thu, 19 Jul 2012 13:00:32 +0000 (UTC) From: "Alexander V. Chernikov" <melifaro@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org Subject: svn commit: r238620 - stable/8/usr.sbin/flowctl Message-ID: <201207191300.q6JD0WTs083280@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: melifaro Date: Thu Jul 19 13:00:31 2012 New Revision: 238620 URL: http://svn.freebsd.org/changeset/base/238620 Log: MFC r223788, r223790, r223824 236808 Merge netflow v9 support (user side) r223788 - Rewrite the flowctl utility to add it support for displaying both IPv4 and IPv4 flows. r223790 - Update manual page. Mention IPv6 support, .. r223824 - Conditionally compile in the IPv4/IPv6 address family support. r236808 - Add "human" option to print IPv4/IPv6 flows in human-readable format. Additionally, implement compatibility code for syntax changes introduced in r223788. Flowctl supports specifying node both as absolute path (new style) and as node name without colon (old style). No objection from: glebius Approved by: ae(mentor) Modified: stable/8/usr.sbin/flowctl/Makefile stable/8/usr.sbin/flowctl/flowctl.8 stable/8/usr.sbin/flowctl/flowctl.c Directory Properties: stable/8/usr.sbin/ (props changed) stable/8/usr.sbin/flowctl/ (props changed) Modified: stable/8/usr.sbin/flowctl/Makefile ============================================================================== --- stable/8/usr.sbin/flowctl/Makefile Thu Jul 19 11:43:31 2012 (r238619) +++ stable/8/usr.sbin/flowctl/Makefile Thu Jul 19 13:00:31 2012 (r238620) @@ -2,6 +2,8 @@ # $FreeBSD$ # +.include <bsd.own.mk> + PROG= flowctl MAN= flowctl.8 @@ -9,4 +11,11 @@ WARNS?= 2 DPADD= ${LIBNETGRAPH} LDADD= -lnetgraph +.if ${MK_INET6_SUPPORT} != "no" +CFLAGS+= -DINET6 +.endif +.if ${MK_INET_SUPPORT} != "no" +CFLAGS+= -DINET +.endif + .include <bsd.prog.mk> Modified: stable/8/usr.sbin/flowctl/flowctl.8 ============================================================================== --- stable/8/usr.sbin/flowctl/flowctl.8 Thu Jul 19 11:43:31 2012 (r238619) +++ stable/8/usr.sbin/flowctl/flowctl.8 Thu Jul 19 13:00:31 2012 (r238620) @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd March 23, 2005 +.Dd June 8, 2012 .Dt FLOWCTL 8 .Os .Sh NAME @@ -35,7 +35,7 @@ control utility .Sh SYNOPSIS .Nm .Op Fl d Ar level -.Ar node command +.Ar path command .Sh DESCRIPTION The .Nm @@ -56,16 +56,23 @@ Currently, supports only one command. .Pp .Bl -tag -width ".Cm show" -.It Cm show +.It Cm show Oo Cm ipv4|ipv6 Oc Op Cm human|verbose This command is the analog of the .Dq "show ip cache flow" command of a Cisco router. It dumps the contents of the flow cache in Cisco-like format. +Specifing either +.Cm ipv4 +or +.Cm ipv6 +would extract only IPv4 or IPv6 flows respectively. It has optional parameter .Cm verbose , which is analog of the .Dq "show ip cache verbose flow" -command. +command. Additionally, +.Cm human +parameter can be specify to show selected flows in human-readable format. .El .Sh EXIT STATUS .Ex -std Modified: stable/8/usr.sbin/flowctl/flowctl.c ============================================================================== --- stable/8/usr.sbin/flowctl/flowctl.c Thu Jul 19 11:43:31 2012 (r238619) +++ stable/8/usr.sbin/flowctl/flowctl.c Thu Jul 19 13:00:31 2012 (r238620) @@ -46,29 +46,61 @@ static const char rcs_id[] = #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sysexits.h> #include <unistd.h> #include <netgraph.h> #include <netgraph/netflow/ng_netflow.h> -#define CISCO_SH_FLOW_HEADER "SrcIf SrcIPaddress DstIf DstIPaddress Pr SrcP DstP Pkts\n" +#define CISCO_SH_FLOW_HEADER "SrcIf SrcIPaddress " \ +"DstIf DstIPaddress Pr SrcP DstP Pkts\n" #define CISCO_SH_FLOW "%-13s %-15s %-13s %-15s %2u %4.4x %4.4x %6lu\n" -#define CISCO_SH_VERB_FLOW_HEADER "SrcIf SrcIPaddress DstIf DstIPaddress Pr TOS Flgs Pkts\n" \ +/* human-readable IPv4 header */ +#define CISCO_SH_FLOW_HHEADER "SrcIf SrcIPaddress " \ +"DstIf DstIPaddress Proto SrcPort DstPort Pkts\n" +#define CISCO_SH_FLOW_H "%-13s %-15s %-13s %-15s %5u %8d %8d %8lu\n" + +#define CISCO_SH_FLOW6_HEADER "SrcIf SrcIPaddress " \ +"DstIf DstIPaddress Pr SrcP DstP Pkts\n" +#define CISCO_SH_FLOW6 "%-13s %-30s %-13s %-30s %2u %4.4x %4.4x %6lu\n" + +/* Human-readable IPv6 headers */ +#define CISCO_SH_FLOW6_HHEADER "SrcIf SrcIPaddress " \ +"DstIf DstIPaddress Proto SrcPort DstPort Pkts\n" +#define CISCO_SH_FLOW6_H "%-13s %-36s %-13s %-36s %5u %8d %8d %8lu\n" + +#define CISCO_SH_VERB_FLOW_HEADER "SrcIf SrcIPaddress " \ +"DstIf DstIPaddress Pr TOS Flgs Pkts\n" \ "Port Msk AS Port Msk AS NextHop B/Pk Active\n" #define CISCO_SH_VERB_FLOW "%-14s %-15s %-14s %-15s %2u %3x %4x %6lu\n" \ "%4.4x /%-2u %-5u %4.4x /%-2u %-5u %-15s %9u %8u\n\n" -static int flow_cache_print(struct ngnf_flows *recs); -static int flow_cache_print_verbose(struct ngnf_flows *recs); -static int ctl_show(int, char **); +#define CISCO_SH_VERB_FLOW6_HEADER "SrcIf SrcIPaddress " \ +"DstIf DstIPaddress Pr TOS Flgs Pkts\n" \ +"Port Msk AS Port Msk AS NextHop B/Pk Active\n" + +#define CISCO_SH_VERB_FLOW6 "%-14s %-30s %-14s %-30s %2u %3x %4x %6lu\n" \ + "%4.4x /%-2u %-5u %4.4x /%-2u %-5u %-30s %9u %8u\n\n" +#ifdef INET +static void flow_cache_print(struct ngnf_show_header *resp); +static void flow_cache_print_verbose(struct ngnf_show_header *resp); +#endif +#ifdef INET6 +static void flow_cache_print6(struct ngnf_show_header *resp); +static void flow_cache_print6_verbose(struct ngnf_show_header *resp); +#endif +static void ctl_show(int, char **); +#if defined(INET) || defined(INET6) +static void do_show(int, void (*func)(struct ngnf_show_header *)); +#endif static void help(void); static void execute_command(int, char **); struct ip_ctl_cmd { char *cmd_name; - int (*cmd_func)(int argc, char **argv); + void (*cmd_func)(int argc, char **argv); }; struct ip_ctl_cmd cmds[] = { @@ -76,8 +108,8 @@ struct ip_ctl_cmd cmds[] = { {NULL, NULL}, }; -int cs; -char ng_nodename[NG_PATHSIZ]; +int cs, human = 0; +char *ng_path, ng_nodename[NG_PATHSIZ]; int main(int argc, char **argv) @@ -85,7 +117,6 @@ main(int argc, char **argv) int c; char sname[NG_NODESIZ]; int rcvbuf = SORCVBUF_SIZE; - char *ng_name; /* parse options */ while ((c = getopt(argc, argv, "d:")) != -1) { @@ -98,14 +129,26 @@ main(int argc, char **argv) argc -= optind; argv += optind; - ng_name = argv[0]; - if (ng_name == NULL) + ng_path = argv[0]; + if (ng_path == NULL || (strlen(ng_path) > NG_PATHSIZ)) help(); + + /* + * Compatibility check: + * If ng_path contains ':' we assume absolute path (new style) + * e.g. ( '.:', '[3f]:', 'foo:' or 'foo:hook1[.hook2][.hook3]' ) + * otherwise assume old style (node name) + */ + + if (strchr(ng_path, ':') == NULL) { + /* Old style, convert to absolute path */ + snprintf(ng_nodename, sizeof(ng_nodename), "%s:", ng_path); + ng_path = ng_nodename; + } + argc--; argv++; - snprintf(ng_nodename, sizeof(ng_nodename), "%s:", ng_name); - /* create control socket. */ snprintf(sname, sizeof(sname), "flowctl%i", getpid()); @@ -145,77 +188,116 @@ execute_command(int argc, char **argv) (*cmds[cindex].cmd_func)(argc, argv); } -static int +static void ctl_show(int argc, char **argv) { - struct ng_mesg *ng_mesg; - struct ngnf_flows *data; - char path[NG_PATHSIZ]; - int token, nread, last = 0; - int verbose = 0; + int ipv4, ipv6, verbose = 0; + + ipv4 = feature_present("inet"); + ipv6 = feature_present("inet6"); + + if (argc > 0 && !strncmp(argv[0], "ipv4", 4)) { + ipv6 = 0; + argc--; + argv++; + } + if (argc > 0 && !strncmp(argv[0], "ipv6", 4)) { + ipv4 = 0; + argc--; + argv++; + } if (argc > 0 && !strncmp(argv[0], "verbose", strlen(argv[0]))) verbose = 1; + if (argc > 0 && !strncmp(argv[0], "human", strlen(argv[0]))) + human = 1; + +#ifdef INET + if (ipv4) { + if (verbose) + do_show(4, &flow_cache_print_verbose); + else + do_show(4, &flow_cache_print); + } +#endif + +#ifdef INET6 + if (ipv6) { + if (verbose) + do_show(6, &flow_cache_print6_verbose); + else + do_show(6, &flow_cache_print6); + } +#endif +} + +#if defined(INET) || defined(INET6) +static void +do_show(int version, void (*func)(struct ngnf_show_header *)) +{ + struct ng_mesg *ng_mesg; + struct ngnf_show_header req, *resp; + int token, nread; + ng_mesg = alloca(SORCVBUF_SIZE); - if (verbose) - printf(CISCO_SH_VERB_FLOW_HEADER); - else - printf(CISCO_SH_FLOW_HEADER); + req.version = version; + req.hash_id = req.list_id = 0; for (;;) { /* request set of accounting records */ - token = NgSendMsg(cs, ng_nodename, NGM_NETFLOW_COOKIE, - NGM_NETFLOW_SHOW, (void *)&last, sizeof(last)); + token = NgSendMsg(cs, ng_path, NGM_NETFLOW_COOKIE, + NGM_NETFLOW_SHOW, (void *)&req, sizeof(req)); if (token == -1) err(1, "NgSendMsg(NGM_NETFLOW_SHOW)"); /* read reply */ - nread = NgRecvMsg(cs, ng_mesg, SORCVBUF_SIZE, path); + nread = NgRecvMsg(cs, ng_mesg, SORCVBUF_SIZE, NULL); if (nread == -1) err(1, "NgRecvMsg() failed"); if (ng_mesg->header.token != token) err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): token mismatch"); - data = (struct ngnf_flows*)ng_mesg->data; - if ((ng_mesg->header.arglen < (sizeof(*data))) || - (ng_mesg->header.arglen < (sizeof(*data) + - (data->nentries * sizeof(struct flow_entry_data))))) + resp = (struct ngnf_show_header *)ng_mesg->data; + if ((ng_mesg->header.arglen < (sizeof(*resp))) || + (ng_mesg->header.arglen < (sizeof(*resp) + + (resp->nentries * sizeof(struct flow_entry_data))))) err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): arglen too small"); - if (verbose) - (void )flow_cache_print_verbose(data); - else - (void )flow_cache_print(data); + (*func)(resp); - if (data->last != 0) - last = data->last; + if (resp->hash_id != 0) + req.hash_id = resp->hash_id; else break; + req.list_id = resp->list_id; } - - return (0); } +#endif -static int -flow_cache_print(struct ngnf_flows *recs) +#ifdef INET +static void +flow_cache_print(struct ngnf_show_header *resp) { struct flow_entry_data *fle; char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; int i; - /* quick check */ - if (recs->nentries == 0) - return (0); + if (resp->version != 4) + errx(EX_SOFTWARE, "%s: version mismatch: %u", + __func__, resp->version); - fle = recs->entries; - for (i = 0; i < recs->nentries; i++, fle++) { + if (resp->nentries > 0) + printf(human ? CISCO_SH_FLOW_HHEADER : CISCO_SH_FLOW_HEADER); + + fle = (struct flow_entry_data *)(resp + 1); + for (i = 0; i < resp->nentries; i++, fle++) { inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src)); inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst)); - printf(CISCO_SH_FLOW, + printf(human ? CISCO_SH_FLOW_H : CISCO_SH_FLOW, if_indextoname(fle->fle_i_ifx, src_if), src, if_indextoname(fle->fle_o_ifx, dst_if), @@ -226,24 +308,60 @@ flow_cache_print(struct ngnf_flows *recs fle->packets); } - - return (i); } +#endif + +#ifdef INET6 +static void +flow_cache_print6(struct ngnf_show_header *resp) +{ + struct flow6_entry_data *fle6; + char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN]; + char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; + int i; + + if (resp->version != 6) + errx(EX_SOFTWARE, "%s: version mismatch: %u", + __func__, resp->version); + + if (resp->nentries > 0) + printf(human ? CISCO_SH_FLOW6_HHEADER : CISCO_SH_FLOW6_HEADER); + + fle6 = (struct flow6_entry_data *)(resp + 1); + for (i = 0; i < resp->nentries; i++, fle6++) { + inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6)); + inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6)); + printf(human ? CISCO_SH_FLOW6_H : CISCO_SH_FLOW6, + if_indextoname(fle6->fle_i_ifx, src_if), + src6, + if_indextoname(fle6->fle_o_ifx, dst_if), + dst6, + fle6->r.r_ip_p, + ntohs(fle6->r.r_sport), + ntohs(fle6->r.r_dport), + fle6->packets); + + } +} +#endif -static int -flow_cache_print_verbose(struct ngnf_flows *recs) +#ifdef INET +static void +flow_cache_print_verbose(struct ngnf_show_header *resp) { struct flow_entry_data *fle; char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN], next[INET_ADDRSTRLEN]; char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; int i; - /* quick check */ - if (recs->nentries == 0) - return (0); + if (resp->version != 4) + errx(EX_SOFTWARE, "%s: version mismatch: %u", + __func__, resp->version); - fle = recs->entries; - for (i = 0; i < recs->nentries; i++, fle++) { + printf(CISCO_SH_VERB_FLOW_HEADER); + + fle = (struct flow_entry_data *)(resp + 1); + for (i = 0; i < resp->nentries; i++, fle++) { inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src)); inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst)); inet_ntop(AF_INET, &fle->next_hop, next, sizeof(next)); @@ -267,9 +385,50 @@ flow_cache_print_verbose(struct ngnf_flo 0); } - - return (i); } +#endif + +#ifdef INET6 +static void +flow_cache_print6_verbose(struct ngnf_show_header *resp) +{ + struct flow6_entry_data *fle6; + char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN], next6[INET6_ADDRSTRLEN]; + char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; + int i; + + if (resp->version != 6) + errx(EX_SOFTWARE, "%s: version mismatch: %u", + __func__, resp->version); + + printf(CISCO_SH_VERB_FLOW6_HEADER); + + fle6 = (struct flow6_entry_data *)(resp + 1); + for (i = 0; i < resp->nentries; i++, fle6++) { + inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6)); + inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6)); + inet_ntop(AF_INET6, &fle6->n.next_hop6, next6, sizeof(next6)); + printf(CISCO_SH_VERB_FLOW6, + if_indextoname(fle6->fle_i_ifx, src_if), + src6, + if_indextoname(fle6->fle_o_ifx, dst_if), + dst6, + fle6->r.r_ip_p, + fle6->r.r_tos, + fle6->tcp_flags, + fle6->packets, + ntohs(fle6->r.r_sport), + fle6->src_mask, + 0, + ntohs(fle6->r.r_dport), + fle6->dst_mask, + 0, + next6, + (u_int)(fle6->bytes / fle6->packets), + 0); + } +} +#endif static void help(void)
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201207191300.q6JD0WTs083280>