From: Ari Pollak <aripollak@gmail.com>
Date: Mon, 4 Oct 2010 22:08:49 -0400
Subject: [PATCH] Add host+port aggregation
 Closes: #250032

---
 README           |   29 +++++++++++++++--------------
 jbase.c          |    2 +-
 jbase.h          |    3 +--
 jconfig.c        |    6 +++++-
 jcursesdisplay.c |   10 +++++-----
 jnettop.8        |   12 ++++++------
 jnettop.c        |    8 ++++----
 jprocessor.c     |   12 ++++++++++++
 jutil.c          |   17 +++++++++--------
 9 files changed, 58 insertions(+), 41 deletions(-)

diff --git a/README b/README
index 940ba4b..54fa773 100644
--- a/README
+++ b/README
@@ -38,10 +38,11 @@ Is jnettop crashing under your FreeBSD? Try passing
   (What is it?)
 
 Aggregation is a way, how to modify packet classicication rules. There are
-two types of aggregation in jnettop: host and port. If there is port
-aggregation enabled, it means, that all packets seem to come(go to) same port
-numbers. If there is host aggregation enabled, it means, that all packets
-seem to come(go to) same host. Let's have an example:
+two types of aggregation in jnettop: host, port, and host+port. If there 
+is port aggregation enabled, it means, that all packets seem to come(go 
+to) same port numbers. If there is host aggregation enabled, it means, 
+that all packets seem to come(go to) same host. If host+port 
+aggregation is enabled, it aggregates both. Let's have an example:
 Suppose we have following configuration:
 
                                                              +--- host0
@@ -51,14 +52,14 @@ Suppose we have following configuration:
 i.e. typical border router with eth0 interface looking into internet and
 eth1 interface looking to intranet. Suppose we're running jnettop on router
 sniffing on interface eth0. To see how many bytes every host consumes out
-of the internet connection, we enable remote host aggregation and local port
-aggregation. i.e. All internet will behave as one endpoint and all programs
-on one host will, too, behave as one endpoint. This way, every stream we see
-in jnettop will be from one of the hosts in intranet to a public internet.
-Aggregation is very powerfull laser-knife in network traffic analysis.
-I understand, that it is not very user friendly implementation in jnettop and
-I will gratefully welcome every suggestion on how to make this topic clear and
-more clear-to-use.
+of the internet connection, we enable remote host+port aggregation and 
+local port aggregation. i.e. All internet will behave as one endpoint 
+and all programs on one host will, too, behave as one endpoint. This 
+way, every stream we see in jnettop will be from one of the hosts in 
+intranet to a public internet. Aggregation is very powerfull laser-knife 
+in network traffic analysis. I understand, that it is not very user 
+friendly implementation in jnettop and I will gratefully welcome every 
+suggestion on how to make this topic clear and more clear-to-use.
 
 -- .jnettop configuration file --
 
@@ -105,14 +106,14 @@ which interface to listen after you start it up. Example:
 
 Since version 0.9, jnettop supports following new keywors:
 
-  local_aggregation	[none|host|port]
+  local_aggregation	[none|host|port|host+port]
 
     this keyword sets startup local aggregation value
     ex.
 
       local_aggregation  port
 
-  remote_aggregation	[none|host|port]
+  remote_aggregation	[none|host|port|host+port]
 
     this keyword sets startup remote aggregation value
     ex.
diff --git a/jbase.c b/jbase.c
index 5af47b2..aebdbf1 100644
--- a/jbase.c
+++ b/jbase.c
@@ -26,5 +26,5 @@ char	pcap_errbuf[PCAP_ERRBUF_SIZE];
 
 gchar 	*JBASE_PROTOCOLS[] = { "UNK.", "IP", "TCP", "UDP", "ARP", "ETHER", 
                               "SLL", "AGGR.", "ICMP", "IP6", "TCP6", "UDP6", "ICMP6" };
-gchar 	*JBASE_AGGREGATION[] = { "none", "port", "host" };
+gchar 	*JBASE_AGGREGATION[] = { "none", "port", "host", "host+port" };
 
diff --git a/jbase.h b/jbase.h
index 25073ab..6a5b84b 100644
--- a/jbase.h
+++ b/jbase.h
@@ -250,12 +250,11 @@ typedef struct _jbase_network_mask_list {
 
 #define JBASE_AF_SIZE(a)	(a == AF_INET6 ? sizeof(struct in6_addr) : sizeof(struct in_addr))
 
-extern gchar  *JBASE_PROTOCOLS[];
-
 #define AGG_UNKNOWN		(-1)
 #define AGG_NONE		0
 #define AGG_PORT		1
 #define AGG_HOST		2
+#define AGG_BOTH		3
 
 extern gchar *JBASE_PROTOCOLS[];
 extern gchar *JBASE_AGGREGATION[];
diff --git a/jconfig.c b/jconfig.c
index 0a1fe80..bc8edf1 100644
--- a/jconfig.c
+++ b/jconfig.c
@@ -115,6 +115,10 @@ gboolean jconfig_ParseFile(char *configFileName) {
 	}
 
 	s = g_scanner_new(NULL);
+	/* Add + to accepted identifier characters: */
+	s->config->cset_identifier_nth = g_strjoin(NULL,
+		s->config->cset_identifier_nth, "+", NULL);
+
 	g_scanner_input_file(s, fileno(f));
 	while (!g_scanner_eof(s)) {
 		GTokenType tt;
@@ -211,7 +215,7 @@ gboolean jconfig_ParseFile(char *configFileName) {
 		if (!g_ascii_strcasecmp(s->value.v_identifier, "local_aggregation")) {
 			int val = parse_aggregation(s);
 			if (val == AGG_UNKNOWN) {
-				fprintf(stderr, "Parse error on line %d: expecting none or host or port.\n", line);
+				fprintf(stderr, "Parse error on line %d: expecting none, host, port, or host+port.\n", line);
 				return FALSE;
 			}
 			if (jconfig_Settings.localAggregation == AGG_UNKNOWN)
diff --git a/jcursesdisplay.c b/jcursesdisplay.c
index 897078c..197a291 100644
--- a/jcursesdisplay.c
+++ b/jcursesdisplay.c
@@ -84,7 +84,7 @@ static void drawScreen() {
 		attrset(A_NORMAL);
 
 		mvprintw(0, 0, "run XXX:XX:XX device XXXXXXXXXX pkt[f]ilter: XXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
-		mvprintw(1, 0, "[c]ntfilter: XXX [b]ps=XXXXXXX [l]ocal aggr.: XXXX [r]emote aggr.: XXXX   ");
+		mvprintw(1, 0, "[c]ntfilter: XXX [b]ps=XXXXXXX [l]ocal aggr: XXXX      [r]emote aggr: XXXX     ");
 		mvprintw(0, activeColumns-1, ".");
 
 		{
@@ -138,8 +138,8 @@ static void drawHeader() {
 	mvprintw(0, 45, "%-29.29s", jconfig_GetSelectedBpfFilterName());
 	mvprintw(1, 13, "%s", jprocessor_ContentFiltering?"on ":"off");
 	mvprintw(1, 23, "%s", onoffPackets ? "pckts/s" : (onoffBitValues?"bits/s ":"bytes/s"));
-	mvprintw(1, 46, "%s", JBASE_AGGREGATION[jprocessor_LocalAggregation]);
-	mvprintw(1, 67, "%s", JBASE_AGGREGATION[jprocessor_RemoteAggregation]);
+	mvprintw(1, 45, "%-9s", JBASE_AGGREGATION[jprocessor_LocalAggregation]);
+	mvprintw(1, 70, "%-9s", JBASE_AGGREGATION[jprocessor_RemoteAggregation]);
 
 	attroff(A_BOLD);
 
@@ -340,10 +340,10 @@ static void displayLoop() {
 						displayMode = DISPLAYMODE_HELP;
 						break;
 					case 'l':
-						jprocessor_SetLocalAggregation((jprocessor_LocalAggregation + 1) % 3);
+						jprocessor_SetLocalAggregation((jprocessor_LocalAggregation + 1) % 4);
 						break;
 					case 'r':
-						jprocessor_SetRemoteAggregation((jprocessor_RemoteAggregation + 1) % 3);
+						jprocessor_SetRemoteAggregation((jprocessor_RemoteAggregation + 1) % 4);
 						break;
 					case '0':
 					case '1':
diff --git a/jnettop.8 b/jnettop.8
index 14af8ea..2564a5e 100644
--- a/jnettop.8
+++ b/jnettop.8
@@ -52,7 +52,7 @@ reads configuration from filename. defaults to ~/.jnettop. an example can be fou
 .B \-i, \-\-interface name
 capture packets on specified interface
 .TP
-.B \-\-local-aggr [none|host|port]
+.B \-\-local-aggr [none|host|port|host+port]
 set local aggregation to specified value
 .TP
 .B \-n, \-\-no-resolver
@@ -61,7 +61,7 @@ disable resolving of ip addresses
 .B \-p, \-\-promiscuous
 enables promiscuous mode on the sniffed interface
 .TP
-.B \-\-remote-aggr [none|host|port]
+.B \-\-remote-aggr [none|host|port|host+port]
 set remote aggregation to specified value
 .TP
 .B \-s, \-\-select-rule name
@@ -83,8 +83,8 @@ The \fBinterface\fP keyword specifies network interface on which to start listen
 interface "eth0"
 .RE
 .TP
-\fBlocal_aggregation [none|host|port]\fR
-The \fBlocal_aggregation\fP keyword specifies initial active local aggregation. Valid values are \fBnone\fP, \fBhost\fP and \fBport\fP. Example:
+\fBlocal_aggregation [none|host|port|host+port]\fR
+The \fBlocal_aggregation\fP keyword specifies initial active local aggregation. Valid values are \fBnone\fP, \fBhost\fP, \fBport\fP, and \fBhost+port\fP. Example:
 .RS
 .PP
 local_aggregation host
@@ -97,8 +97,8 @@ The \fBpromisc\fP keyword specifies, whether jnettop captures packets in promisc
 promisc on
 .RE
 .TP
-\fBremote_aggregation [none|host|port]\fR
-The \fBremote_aggregation\fP keyword specifies initial active remote aggregation. Valid values are \fBnone\fP, \fBhost\fP and \fBport\fP. Example:
+\fBremote_aggregation [none|host|port|host+port]\fR
+The \fBremote_aggregation\fP keyword specifies initial active remote aggregation. Valid values are \fBnone\fP, \fBhost\fP, \fBport\fP, and \fBhost+port\fP. Example:
 .RS
 .PP
 remote_aggregation port
diff --git a/jnettop.c b/jnettop.c
index 1ed45a8..a4fe553 100644
--- a/jnettop.c
+++ b/jnettop.c
@@ -90,10 +90,10 @@ void parseCommandLineAndConfig(int argc, char ** argv) {
 				"    -f, --config-file name reads configuration from file. defaults to ~/.jnettop\n"
 				"    --format format        list of fields to list in text output\n"
 				"    -i, --interface name   capture packets on specified interface\n"
-				"    --local-aggr arg       set local aggregation to none/host/port\n"
+				"    --local-aggr arg       set local aggregation to none/host/port/host+port\n"
 				"    -n, --no-resolver      disable resolving of addresses\n"
 				"    -p, --promiscuous      enable promisc mode on the devices\n"
-				"    --remote-aggr arg      set remote aggregation to none/host/port\n"
+				"    --remote-aggr arg      set remote aggregation to none/host/port/host+port\n"
 				"    -s, --select-rule rule selects one of the rules defined in config file\n"
 				"                           by it's name\n"
 				"    -t, --timeout sec      timeout in seconds after which jnettop ends (text display)\n"
@@ -215,14 +215,14 @@ void parseCommandLineAndConfig(int argc, char ** argv) {
 		}
 		if (!strcmp(argv[a], "--local-aggr")) {
 			if (a+1>=argc || (jconfig_Settings.localAggregation = jutil_ParseAggregation(argv[++a]))==-1) {
-				fprintf(stderr, "%s switch requires none, host or port as an argument\n", argv[a]);
+				fprintf(stderr, "%s switch requires none, host, port, or host+port as an argument\n", argv[a]);
 				exit(255);
 			}
 			continue;
 		}
 		if (!strcmp(argv[a], "--remote-aggr")) {
 			if (a+1>=argc || (jconfig_Settings.remoteAggregation = jutil_ParseAggregation(argv[++a]))==-1) {
-				fprintf(stderr, "%s switch requires none, host or port as an argument\n", argv[a]);
+				fprintf(stderr, "%s switch requires none, host, port, or host+port as an argument\n", argv[a]);
 				exit(255);
 			}
 			continue;
diff --git a/jprocessor.c b/jprocessor.c
index 5b23e07..cf10121 100644
--- a/jprocessor.c
+++ b/jprocessor.c
@@ -211,14 +211,26 @@ static void	aggregateStream(jbase_stream *stream) {
 	switch (jprocessor_LocalAggregation) {
 		case AGG_HOST:
 			setToHostAggregation(JBASE_AF(stream->proto), &stream->src);
+			break;
 		case AGG_PORT:
 			stream->srcport = -1;
+			break;
+		case AGG_BOTH:
+			setToHostAggregation(JBASE_AF(stream->proto), &stream->src);
+			stream->srcport = -1;
+			break;
 	}
 	switch (jprocessor_RemoteAggregation) {
 		case AGG_HOST:
 			setToHostAggregation(JBASE_AF(stream->proto), &stream->dst);
+			break;
 		case AGG_PORT:
 			stream->dstport = -1;
+			break;
+		case AGG_BOTH:
+			setToHostAggregation(JBASE_AF(stream->proto), &stream->dst);
+			stream->dstport = -1;
+			break;
 	}
 }
 
diff --git a/jutil.c b/jutil.c
index d5c0907..16c1810 100644
--- a/jutil.c
+++ b/jutil.c
@@ -98,15 +98,16 @@ const char * jutil_Address2String(int af, const jbase_mutableaddress *src, char
 }
 
 guint     jutil_ParseAggregation(const char *agg) {
-	if (strcmp(agg, "none") && strcmp(agg,"host") && strcmp(agg,"port")) {
+	if (strcmp(agg, "none") == 0)
+		return AGG_NONE;
+	else if (strcmp(agg,"host") == 0)
+		return AGG_HOST;
+	else if (strcmp(agg,"port") == 0)
+		return AGG_PORT;
+	else if (strcmp(agg,"host+port") == 0)
+		return AGG_BOTH;
+	else
 		return AGG_UNKNOWN;
-	}
-	switch (*agg) {
-		case 'n': return AGG_NONE;
-		case 'h': return AGG_HOST;
-		case 'p': return AGG_PORT;
-	}
-	return AGG_UNKNOWN;
 }
 
 void memand(char *buf1, const char *buf2, int length) {
-- 
