From 953bcf62fbd110f63c946905f9642d17b63c50cf Mon Sep 17 00:00:00 2001
From: "/C=EU/ST=EU/CN=Pablo Neira Ayuso/emailAddress=pablo@netfilter.org"
 </C=EU/ST=EU/CN=Pablo Neira Ayuso/emailAddress=pablo@netfilter.org>
Date: Wed, 16 Apr 2008 14:54:24 +0000
Subject: o fix NAT filtering via --src-nat and --dst-nat (reported by
 K.Oledzki) o recover the ID support o show display counters to stderr o
 enable filtering by status and ID

---
 ChangeLog             |  3 ++
 configure.in          |  2 +-
 conntrack.8           | 27 +++++++++------
 include/conntrack.h   | 12 +++----
 qa/test-conntrack.c   | 18 +++++++---
 qa/testsuite/00create |  4 +++
 qa/testsuite/01delete |  8 +++--
 qa/testsuite/02filter | 20 +++++++++++
 src/conntrack.c       | 92 +++++++++++++++++++++++++++++----------------------
 9 files changed, 120 insertions(+), 66 deletions(-)
 create mode 100644 qa/testsuite/02filter

diff --git a/ChangeLog b/ChangeLog
index f15849b..d6fbe30 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -14,7 +14,10 @@ o simplify parameter-handling code
 o check for missing source/address IP/ports in creation and get operations
 o way more flexible conntrack updates and deletions
 o fix NAT filtering via --src-nat and --dst-nat (reported by K.Oledzki)
+o recover the ID support
 o show display counters to stderr
+o enable filtering by status and ID
+o update manpage
 o minor cleanups
 
 = conntrackd =
diff --git a/configure.in b/configure.in
index 46dd7b8..17101e9 100644
--- a/configure.in
+++ b/configure.in
@@ -18,7 +18,7 @@ esac
 
 dnl Dependencies
 LIBNFNETLINK_REQUIRED=0.0.32
-LIBNETFILTER_CONNTRACK_REQUIRED=0.0.91
+LIBNETFILTER_CONNTRACK_REQUIRED=0.0.92
 
 AC_CHECK_PROG(HAVE_PKG_CONFIG, pkg-config, yes)
 if test "x$HAVE_PKG_CONFIG" = "x"
diff --git a/conntrack.8 b/conntrack.8
index 670770a..9fb9508 100644
--- a/conntrack.8
+++ b/conntrack.8
@@ -73,9 +73,8 @@ Flush the whole given table
 Atomically zero counters after reading them.  This option is only valid in
 combination with the "-L, --dump" command options.
 .TP
-.BI "-o, --output [extended,xml,timestamp] "
-Display output in a certain format. This option is only valid in combination
-with the "-L, --dump", "-E, --event" and "-G, --get" command options.
+.BI "-o, --output [extended,xml,timestamp,id] "
+Display output in a certain format. 
 .TP
 .BI "-e, --event-mask " "[ALL|NEW|UPDATES|DESTROY][,...]"
 Set the bitmask of events that are to be generated by the in-kernel ctnetlink
@@ -136,10 +135,10 @@ Specify the destination address mask of an expectation.
 .TP
 TCP-specific fields:
 .TP
-.BI "--orig-port-src " "PORT"
+.BI "--sport, --orig-port-src " "PORT"
 Source port in original direction
 .TP
-.BI "--orig-port-dst " "PORT"
+.BI "--dport, --orig-port-dst " "PORT"
 Destination port in original direction
 .TP
 .BI "--reply-port-src " "PORT"
@@ -153,10 +152,10 @@ TCP state
 .TP
 UDP-specific fields:
 .TP
-.BI "--orig-port-src " "PORT"
+.BI "--sport, --orig-port-src " "PORT"
 Source port in original direction
 .TP
-.BI "--orig-port-dst " "PORT"
+.BI "--dport, --orig-port-dst " "PORT"
 Destination port in original direction
 .TP
 .BI "--reply-port-src " "PORT"
@@ -182,22 +181,28 @@ cause an exit code of 1.
 .SH EXAMPLES
 .TP
 .B conntrack \-L
-Dump the connection tracking table in /proc/net/ip_conntrack format
+Show the connection tracking table in /proc/net/ip_conntrack format
 .TP
 .B conntrack \-L -o extended
-Dump the connection tracking table in /proc/net/nf_conntrack format
+Show the connection tracking table in /proc/net/nf_conntrack format
 .TP
 .B conntrack \-L \-o xml
-Dump the connection tracking table in XML
+Show the connection tracking table in XML
 .TP
 .B conntrack \-L -f ipv6 -o extended
 Only dump IPv6 connections in /proc/net/nf_conntrack format
 .TP
 .B conntrack \-L --src-nat
-Dump source NAT connections
+Show source NAT connections
 .TP
 .B conntrack \-E \-o timestamp
 Show connection events together with the timestamp
+.TP
+.B conntrack \-D \-s 1.2.3.4
+Delete all flow whose source address is 1.2.3.4
+.TP
+.B conntrack \-U \-s 1.2.3.4 \-m 1
+Set connmark to 1 of all the flows whose source address is 1.2.3.4
 .SH BUGS
 Bugs? What's this ;-)
 .SH SEE ALSO
diff --git a/include/conntrack.h b/include/conntrack.h
index 9e005d9..2e17475 100644
--- a/include/conntrack.h
+++ b/include/conntrack.h
@@ -138,14 +138,10 @@ enum options {
 #define NUMBER_OF_OPT	CT_OPT_MAX+1
 
 enum {
-	_O_XML_BIT		= 0,
-	_O_XML			= (1 << _O_XML_BIT),
-
-	_O_EXT_BIT		= 1,
-	_O_EXT			= (1 << _O_EXT_BIT),
-
-	_O_TMS_BIT		= 2,
-	_O_TMS			= (1 << _O_TMS_BIT),
+	_O_XML			= (1 << 0),
+	_O_EXT			= (1 << 1),
+	_O_TMS			= (1 << 2),
+	_O_ID			= (1 << 3),
 };
 
 struct ctproto_handler {
diff --git a/qa/test-conntrack.c b/qa/test-conntrack.c
index c58aa8d..c9097b6 100644
--- a/qa/test-conntrack.c
+++ b/qa/test-conntrack.c
@@ -21,7 +21,7 @@
 
 int main()
 {
-	int ret, ok = 0, bad = 0;
+	int ret, ok = 0, bad = 0, line;
 	FILE *fp;
 	DIR *d;
 	char buf[1024];
@@ -34,6 +34,8 @@ int main()
 
 		sprintf(file, "testsuite/%s", dent->d_name);
 
+		line = 0;
+
 		fp = fopen(file, "r");
 		if (fp == NULL) {
 			perror("cannot find testsuite file");
@@ -44,15 +46,22 @@ int main()
 			char tmp[1024] = CT_PROG, *res;
 			tmp[strlen(CT_PROG)] = ' ';
 
+			line++;
+
 			if (buf[0] == '#' || buf[0] == ' ')
 				continue;
 
 			res = strchr(buf, ';');
+			if (!res) {
+				printf("malformed file %s at line %d\n", 
+					dent->d_name, line);
+				exit(EXIT_FAILURE);
+			}
 			*res = '\0';
 			res+=2;
 
 			strcpy(tmp + strlen(CT_PROG) + 1, buf);
-			printf("Executing: %s\n", tmp);
+			printf("(%d) Executing: %s\n", line, tmp);
 
 			ret = system(tmp);
 
@@ -75,10 +84,11 @@ int main()
 					printf("^----- BAD\n");
 				}
 			}
+			printf("=====\n");
 		}
+		fclose(fp);
 	}
+	closedir(d);
 
 	fprintf(stdout, "OK: %d BAD: %d\n", ok, bad);
-
-	fclose(fp);
 }
diff --git a/qa/testsuite/00create b/qa/testsuite/00create
index 7af7d37..40e2c19 100644
--- a/qa/testsuite/00create
+++ b/qa/testsuite/00create
@@ -12,5 +12,9 @@
 -I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
 # create again
 -I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+# delete
+-D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ; OK
 # create from reply
 -I -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+# delete reverse
+-D -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 ; OK
diff --git a/qa/testsuite/01delete b/qa/testsuite/01delete
index dd3ca8b..3c38ac5 100644
--- a/qa/testsuite/01delete
+++ b/qa/testsuite/01delete
@@ -1,2 +1,6 @@
-# delete
--D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ; OK
+# create dummy
+-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+# delete bad source
+-D -s 2.2.2.2 -p tcp --sport 10 --dport 20 ; BAD
+# delete by source
+-D -s 1.1.1.1 ; OK
diff --git a/qa/testsuite/02filter b/qa/testsuite/02filter
new file mode 100644
index 0000000..1ae9abd
--- /dev/null
+++ b/qa/testsuite/02filter
@@ -0,0 +1,20 @@
+# create dummy
+conntrack -I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+# filter by source
+conntrack -L -s 1.1.1.1 ; OK
+# filter by destination
+conntrack -L -d 2.2.2.2 ; OK
+# filter by protocol
+conntrack -L -p tcp ; OK
+# filter by status
+conntrack -L -u SEEN_REPLY ; OK
+# filter by TCP protocol state
+conntrack -L -p tcp --state LISTEN ; OK
+# update mark of dummy conntrack
+conntrack -U -s 1.1.1.1 -m 1 ; OK
+# filter by mark
+conntrack -L -m 1 ; OK
+# filter by layer 3 protocol
+conntrack -L -f ipv4 ; OK
+# delete dummy
+conntrack -D -d 2.2.2.2 ; OK
diff --git a/src/conntrack.c b/src/conntrack.c
index 2dfb601..9ab4558 100644
--- a/src/conntrack.c
+++ b/src/conntrack.c
@@ -121,7 +121,7 @@ static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
 /* Well, it's better than "Re: Linux vs FreeBSD" */
 {
           /*   s d r q p t u z e [ ] { } a m i f n g o c */
-/*CT_LIST*/   {2,2,2,2,2,0,0,2,0,0,0,0,0,0,2,2,2,2,2,2,2},
+/*CT_LIST*/   {2,2,2,2,2,0,2,2,0,0,0,0,0,0,2,0,2,2,2,2,2},
 /*CT_CREATE*/ {2,2,2,2,1,1,1,0,0,0,0,0,0,2,2,0,0,2,2,0,2},
 /*CT_UPDATE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,2},
 /*CT_DELETE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,2},
@@ -130,7 +130,7 @@ static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
 /*CT_EVENT*/  {2,2,2,2,2,0,0,0,2,0,0,0,0,0,2,0,0,2,2,2,2},
 /*VERSION*/   {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
 /*HELP*/      {0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_LIST*/  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0},
+/*EXP_LIST*/  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0},
 /*EXP_CREATE*/{1,1,2,2,1,1,2,0,0,1,1,1,1,0,0,0,0,0,0,0,0},
 /*EXP_DELETE*/{1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
 /*EXP_GET*/   {1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
@@ -143,7 +143,7 @@ static LIST_HEAD(proto_list);
 static unsigned int options;
 
 #define CT_COMPARISON (CT_OPT_PROTO | CT_OPT_ORIG | CT_OPT_REPL | CT_OPT_MARK |\
-		       CT_OPT_SECMARK)
+		       CT_OPT_SECMARK |  CT_OPT_STATUS | CT_OPT_ID)
 
 void register_proto(struct ctproto_handler *h)
 {
@@ -328,8 +328,8 @@ static struct parse_parameter {
 	{ {"ALL", "NEW", "UPDATES", "DESTROY"}, 4,
 	  {~0U, NF_NETLINK_CONNTRACK_NEW, NF_NETLINK_CONNTRACK_UPDATE, 
 	   NF_NETLINK_CONNTRACK_DESTROY} },
-	{ {"xml", "extended", "timestamp" }, 3, 
-	  { _O_XML, _O_EXT, _O_TMS },
+	{ {"xml", "extended", "timestamp", "id" }, 4, 
+	  { _O_XML, _O_EXT, _O_TMS, _O_ID },
 	},
 };
 
@@ -603,13 +603,13 @@ static int ignore_nat(const struct nf_conntrack *obj,
 	return 0;
 }
 
-static int events_counter;
+static int counter;
 
 static void __attribute__((noreturn))
 event_sighandler(int s)
 {
 	fprintf(stderr, "%s v%s: ", PROGNAME, VERSION);
-	fprintf(stderr, "%d flow events has been shown.\n", events_counter);
+	fprintf(stderr, "%d flow events has been shown.\n", counter);
 	nfct_close(cth);
 	exit(0);
 }
@@ -640,19 +640,19 @@ static int event_cb(enum nf_conntrack_msg_type type,
 			printf("[%-8ld.%-6ld]\t", tv.tv_sec, tv.tv_usec);
 		} else
 			op_flags |= NFCT_OF_TIME;
-	}	
+	}
+	if (output_mask & _O_ID)
+		op_flags |= NFCT_OF_ID;
 
 	nfct_snprintf(buf, 1024, ct, type, op_type, op_flags);
 	printf("%s\n", buf);
 	fflush(stdout);
 
-	events_counter++;
+	counter++;
 
 	return NFCT_CB_CONTINUE;
 }
 
-static int list_counter;
-
 static int dump_cb(enum nf_conntrack_msg_type type,
 		   struct nf_conntrack *ct,
 		   void *data)
@@ -672,17 +672,17 @@ static int dump_cb(enum nf_conntrack_msg_type type,
 		op_type = NFCT_O_XML;
 	if (output_mask & _O_EXT)
 		op_flags = NFCT_OF_SHOW_LAYER3;
+	if (output_mask & _O_ID)
+		op_flags |= NFCT_OF_ID;
 
 	nfct_snprintf(buf, 1024, ct, NFCT_T_UNKNOWN, op_type, op_flags);
 	printf("%s\n", buf);
 
-	list_counter++;
+	counter++;
 
 	return NFCT_CB_CONTINUE;
 }
 
-static int delete_counter;
-
 static int delete_cb(enum nf_conntrack_msg_type type,
 		     struct nf_conntrack *ct,
 		     void *data)
@@ -709,17 +709,17 @@ static int delete_cb(enum nf_conntrack_msg_type type,
 		op_type = NFCT_O_XML;
 	if (output_mask & _O_EXT)
 		op_flags = NFCT_OF_SHOW_LAYER3;
+	if (output_mask & _O_ID)
+		op_flags |= NFCT_OF_ID;
 
 	nfct_snprintf(buf, 1024, ct, NFCT_T_UNKNOWN, op_type, op_flags);
 	printf("%s\n", buf);
 
-	delete_counter++;
+	counter++;
 
 	return NFCT_CB_CONTINUE;
 }
 
-static int update_counter;
-
 static int update_cb(enum nf_conntrack_msg_type type,
 		     struct nf_conntrack *ct,
 		     void *data)
@@ -737,6 +737,10 @@ static int update_cb(enum nf_conntrack_msg_type type,
 	if (ignore_nat(tmp, ct))
 		return NFCT_CB_CONTINUE;
 
+	if (nfct_attr_is_set(obj, ATTR_ID) && nfct_attr_is_set(ct, ATTR_ID) &&
+	    nfct_get_attr_u32(obj, ATTR_ID) != nfct_get_attr_u32(ct, ATTR_ID))
+	    	return NFCT_CB_CONTINUE;
+
 	if (options & CT_OPT_TUPLE_ORIG && !nfct_cmp(tmp, ct, NFCT_CMP_ORIG))
 		return NFCT_CB_CONTINUE;
 	if (options & CT_OPT_TUPLE_REPL && !nfct_cmp(tmp, ct, NFCT_CMP_REPL))
@@ -754,11 +758,13 @@ static int update_cb(enum nf_conntrack_msg_type type,
 		op_type = NFCT_O_XML;
 	if (output_mask & _O_EXT)
 		op_flags = NFCT_OF_SHOW_LAYER3;
+	if (output_mask & _O_ID)
+		op_flags |= NFCT_OF_ID;
 
 	nfct_snprintf(buf, 1024, ct, NFCT_T_UNKNOWN, op_type, op_flags);
 	printf("%s\n", buf);
 
-	update_counter++;
+	counter++;
 
 	return NFCT_CB_CONTINUE;
 }
@@ -801,6 +807,7 @@ static const int opt2type[] = {
 	['g']	= CT_OPT_DST_NAT,
 	['m']	= CT_OPT_MARK,
 	['c']	= CT_OPT_SECMARK,
+	['i']	= CT_OPT_ID,
 };
 
 static const int opt2family_attr[][2] = {
@@ -821,6 +828,18 @@ static const int opt2attr[] = {
 	['q']	= ATTR_REPL_L3PROTO,
 	['m']	= ATTR_MARK,
 	['c']	= ATTR_SECMARK,
+	['i']	= ATTR_ID,
+};
+
+static char exit_msg[][64] = {
+	[CT_LIST_BIT] 		= "%d flow entries has been shown.\n",
+	[CT_CREATE_BIT]		= "%d flow entries has been created.\n",
+	[CT_UPDATE_BIT]		= "%d flow entries has been updated.\n",
+	[CT_DELETE_BIT]		= "%d flow entries has been deleted.\n",
+	[CT_GET_BIT] 		= "%d flow entries has been shown.\n",
+	[CT_EVENT_BIT]		= "%d flow events has been shown.\n",
+	[EXP_LIST_BIT]		= "%d expectations has been shown.\n",
+	[EXP_DELETE_BIT]	= "%d expectations has been shown.\n",
 };
 
 int main(int argc, char *argv[])
@@ -853,7 +872,7 @@ int main(int argc, char *argv[])
 	register_icmpv6();
 
 	while ((c = getopt_long(argc, argv, "L::I::U::D::G::E::F::hVs:d:r:q:"
-					    "p:t:u:e:a:z[:]:{:}:m:i::f:o:n::"
+					    "p:t:u:e:a:z[:]:{:}:m:i:f:o:n::"
 					    "g::c:", 
 					    opts, NULL)) != -1) {
 	switch(c) {
@@ -999,6 +1018,7 @@ int main(int argc, char *argv[])
 			nat_parse(tmp, 1, obj, opt2type[c]);
 			break;
 		}
+		case 'i':
 		case 'm':
 		case 'c':
 			options |= opt2type[c];
@@ -1006,9 +1026,10 @@ int main(int argc, char *argv[])
 				exit_error(PARAMETER_PROBLEM, 
 					   "-%c requires value", c);
 
-			nfct_set_attr_u32(obj, opt2attr[c], atol(optarg));
+			nfct_set_attr_u32(obj,
+					  opt2attr[c],
+					  strtoul(optarg, NULL, 0));
 			break;
-		case 'i':
 		case 'a':
 			fprintf(stderr, "WARNING: ignoring -%c, "
 					"deprecated option.\n", c);
@@ -1084,10 +1105,6 @@ int main(int argc, char *argv[])
 			res = nfct_query(cth, NFCT_Q_DUMP, &family);
 
 		nfct_close(cth);
-
-		fprintf(stderr, "%s v%s: ", PROGNAME, VERSION);
-		fprintf(stderr, "%d flow entries has been shown.\n",
-			list_counter);
 		break;
 
 	case EXP_LIST:
@@ -1111,10 +1128,9 @@ int main(int argc, char *argv[])
 			exit_error(OTHER_PROBLEM, "Can't open handler");
 
 		res = nfct_query(cth, NFCT_Q_CREATE, obj);
+		if (res != -1)
+			counter++;
 		nfct_close(cth);
-		fprintf(stderr, "%s v%s: ", PROGNAME, VERSION);
-		fprintf(stderr, "%d flow entry has been created.\n",
-			res == -1 ? 0 : 1);
 		break;
 
 	case EXP_CREATE:
@@ -1142,10 +1158,6 @@ int main(int argc, char *argv[])
 		res = nfct_query(cth, NFCT_Q_DUMP, &family);
 		nfct_close(ith);
 		nfct_close(cth);
-
-		fprintf(stderr, "%s v%s: ", PROGNAME, VERSION);
-		fprintf(stderr, "%d flow entries has been updated.\n",
-			update_counter);
 		break;
 		
 	case CT_DELETE:
@@ -1159,10 +1171,6 @@ int main(int argc, char *argv[])
 		res = nfct_query(cth, NFCT_Q_DUMP, &family);
 		nfct_close(ith);
 		nfct_close(cth);
-
-		fprintf(stderr, "%s v%s: ", PROGNAME, VERSION);
-		fprintf(stderr, "%d flow entries has been deleted.\n", 
-			delete_counter);
 		break;
 
 	case EXP_DELETE:
@@ -1184,9 +1192,6 @@ int main(int argc, char *argv[])
 		nfct_callback_register(cth, NFCT_T_ALL, dump_cb, obj);
 		res = nfct_query(cth, NFCT_Q_GET, obj);
 		nfct_close(cth);
-		fprintf(stderr, "%s v%s: ", PROGNAME, VERSION);
-		fprintf(stderr, "%d flow entry has been shown.\n",
-			res == -1 ? 0 : 1);
 		break;
 
 	case EXP_GET:
@@ -1268,5 +1273,12 @@ int main(int argc, char *argv[])
 		exit_error(OTHER_PROBLEM, "Operation failed: %s",
 			   err2str(errno, command));
 
-	return 0;
+	if (exit_msg[cmd][0]) {
+		fprintf(stderr, "%s v%s: ", PROGNAME, VERSION);
+		fprintf(stderr, exit_msg[cmd], counter);
+		if (counter == 0 && !(command & (CT_LIST | EXP_LIST)))
+			return EXIT_FAILURE;
+	}
+
+	return EXIT_SUCCESS;
 }
-- 
cgit v1.2.3