Skip site navigation (1)Skip section navigation (2)
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>