summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog31
-rw-r--r--extensions/libct_proto_icmp.c31
-rw-r--r--extensions/libct_proto_sctp.c108
-rw-r--r--extensions/libct_proto_tcp.c118
-rw-r--r--extensions/libct_proto_udp.c94
-rw-r--r--include/libct_proto.h25
-rw-r--r--src/conntrack.c535
-rw-r--r--src/libct.c526
-rw-r--r--test.sh77
9 files changed, 1027 insertions, 518 deletions
diff --git a/ChangeLog b/ChangeLog
index 688b0cc..0ae9fc5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2005-07-12
+<pablo@eurodev.net>
+ o Use conntrack netlink attributes: Major change
+ o Kill action setting: Mask based dumping
+ o Fix ChangeLog
+
2005-05-23
<laforge@netfilter.org>
o Fixed syntax error (tab/space issue) in help message
@@ -9,11 +15,16 @@
o Add SCTP extension
o Add support for expect creation
o Bump version number to 0.63
-2005-04-25
+
+2005-05-17
<pablo@eurodev.net>
- o Added support for mask based event dumping
- o Added support for mask based event notification
- o On-demand autoload of ip_conntrack_netlink
+ o Added descriptive error messages.
+ o Fix wrong flags check in [tcp|udp] proto helpers.
+
+2005-05-16
+<pablo@eurodev.net>
+ o Implemented ICMP proto helper
+ o Added help() and final_check() functions for proto helpers.
2005-05-01
<pablo@eurodev.net>
@@ -33,12 +44,8 @@
o Autoconf stuff for conntrack + some pablo's modifications.
o Fixed packet counters formatting (use %llu instead of %lu)
-2005-05-16
-<pablo@eurodev.net>
- o Implemented ICMP proto helper
- o Added help() and final_check() functions for proto helpers.
-
-2005-05-17
+2005-04-25
<pablo@eurodev.net>
- o Added descriptive error messages.
- o Fix wrong flags check in [tcp|udp] proto helpers.
+ o Added support for mask based event dumping
+ o Added support for mask based event notification
+ o On-demand autoload of ip_conntrack_netlink
diff --git a/extensions/libct_proto_icmp.c b/extensions/libct_proto_icmp.c
index 6a2db92..24d3d3f 100644
--- a/extensions/libct_proto_icmp.c
+++ b/extensions/libct_proto_icmp.c
@@ -11,8 +11,6 @@
#include <getopt.h>
#include <stdlib.h>
#include <netinet/in.h> /* For htons */
-#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
-#include <linux/netfilter_ipv4/ip_conntrack.h>
#include "libct_proto.h"
static struct option opts[] = {
@@ -41,27 +39,28 @@ void help()
}
int parse(char c, char *argv[],
- struct ip_conntrack_tuple *orig,
- struct ip_conntrack_tuple *reply,
- union ip_conntrack_proto *proto,
+ struct ctnl_tuple *orig,
+ struct ctnl_tuple *reply,
+ struct ctnl_tuple *mask,
+ union ctnl_protoinfo *proto,
unsigned int *flags)
{
switch(c) {
case '1':
if (optarg) {
- orig->dst.u.icmp.type = atoi(optarg);
+ orig->l4dst.icmp.type = atoi(optarg);
*flags |= ICMP_TYPE;
}
break;
case '2':
if (optarg) {
- orig->dst.u.icmp.code = atoi(optarg);
+ orig->l4dst.icmp.code = atoi(optarg);
*flags |= ICMP_CODE;
}
break;
case '3':
if (optarg) {
- reply->src.u.icmp.id = atoi(optarg);
+ orig->l4src.icmp.id = atoi(optarg);
*flags |= ICMP_ID;
}
break;
@@ -69,7 +68,9 @@ int parse(char c, char *argv[],
return 1;
}
-int final_check(unsigned int flags)
+int final_check(unsigned int flags,
+ struct ctnl_tuple *orig,
+ struct ctnl_tuple *reply)
{
if (!(flags & ICMP_TYPE))
return 0;
@@ -79,18 +80,18 @@ int final_check(unsigned int flags)
return 1;
}
-void print_tuple(struct ip_conntrack_tuple *t)
+void print_proto(struct ctnl_tuple *t)
{
- fprintf(stdout, "type=%d code=%d id=%d ", t->dst.u.icmp.type,
- t->dst.u.icmp.code,
- t->src.u.icmp.id);
+ fprintf(stdout, "type=%d code=%d id=%d", t->l4dst.icmp.type,
+ t->l4dst.icmp.code,
+ t->l4src.icmp.id);
}
static struct ctproto_handler icmp = {
.name = "icmp",
.protonum = 1,
- .parse = parse,
- .print_tuple = print_tuple,
+ .parse_opts = parse,
+ .print_proto = print_proto,
.final_check = final_check,
.help = help,
.opts = opts
diff --git a/extensions/libct_proto_sctp.c b/extensions/libct_proto_sctp.c
index b84c2ba..5b50fbc 100644
--- a/extensions/libct_proto_sctp.c
+++ b/extensions/libct_proto_sctp.c
@@ -1,26 +1,26 @@
/*
- * (C) 2005 by Harald Welte <lafoorge@netfilter.org>
+ * (C) 2005 by Harald Welte <laforge@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License Version 2 as
- * published by the Free Software Foundation
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
*/
#include <stdio.h>
#include <getopt.h>
#include <stdlib.h>
-#include <string.h>
#include <netinet/in.h> /* For htons */
-#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
-#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_netlink.h>
#include "libct_proto.h"
+#include "libctnetlink.h"
static struct option opts[] = {
{"orig-port-src", 1, 0, '1'},
{"orig-port-dst", 1, 0, '2'},
{"reply-port-src", 1, 0, '3'},
{"reply-port-dst", 1, 0, '4'},
- {"state", 1, 0, '5'},
+ {"state", 1, 0, '7'},
{0, 0, 0, 0}
};
@@ -52,43 +52,44 @@ static const char *states[] = {
"SHUTDOWN_ACK_SENT",
};
-static void help()
+void help()
{
fprintf(stdout, "--orig-port-src original source port\n");
fprintf(stdout, "--orig-port-dst original destination port\n");
fprintf(stdout, "--reply-port-src reply source port\n");
fprintf(stdout, "--reply-port-dst reply destination port\n");
- fprintf(stdout, "--state SCTP state, eg. ESTABLISHED\n");
+ fprintf(stdout, "--state SCTP state, fe. ESTABLISHED\n");
}
-static int parse(char c, char *argv[],
- struct ip_conntrack_tuple *orig,
- struct ip_conntrack_tuple *reply,
- union ip_conntrack_proto *proto,
- unsigned int *flags)
+int parse_options(char c, char *argv[],
+ struct ctnl_tuple *orig,
+ struct ctnl_tuple *reply,
+ struct ctnl_tuple *mask,
+ union ctnl_protoinfo *proto,
+ unsigned int *flags)
{
switch(c) {
case '1':
if (optarg) {
- orig->src.u.sctp.port = htons(atoi(optarg));
+ orig->l4src.sctp.port = htons(atoi(optarg));
*flags |= ORIG_SPORT;
}
break;
case '2':
if (optarg) {
- orig->dst.u.sctp.port = htons(atoi(optarg));
+ orig->l4dst.sctp.port = htons(atoi(optarg));
*flags |= ORIG_DPORT;
}
break;
case '3':
if (optarg) {
- reply->src.u.sctp.port = htons(atoi(optarg));
+ reply->l4src.sctp.port = htons(atoi(optarg));
*flags |= REPL_SPORT;
}
break;
case '4':
if (optarg) {
- reply->dst.u.sctp.port = htons(atoi(optarg));
+ reply->l4dst.sctp.port = htons(atoi(optarg));
*flags |= REPL_DPORT;
}
break;
@@ -97,7 +98,9 @@ static int parse(char c, char *argv[],
int i;
for (i=0; i<10; i++) {
if (strcmp(optarg, states[i]) == 0) {
- proto->sctp.state = i;
+ /* FIXME: Add state to
+ * ctnl_protoinfo
+ proto->sctp.state = i; */
break;
}
}
@@ -111,39 +114,68 @@ static int parse(char c, char *argv[],
return 1;
}
-static int final_check(unsigned int flags)
+int final_check(unsigned int flags,
+ struct ctnl_tuple *orig,
+ struct ctnl_tuple *reply)
{
- if ((flags & ORIG_SPORT) && (flags & ORIG_DPORT))
+ if ((flags & (ORIG_SPORT|ORIG_DPORT))
+ && !(flags & (REPL_SPORT|REPL_DPORT))) {
+ reply->l4src.sctp.port = orig->l4dst.sctp.port;
+ reply->l4dst.sctp.port = orig->l4src.sctp.port;
return 1;
- else if ((flags & REPL_SPORT) && (flags & REPL_DPORT))
+ } else if (!(flags & (ORIG_SPORT|ORIG_DPORT))
+ && (flags & (REPL_SPORT|REPL_DPORT))) {
+ orig->l4src.sctp.port = reply->l4dst.sctp.port;
+ orig->l4dst.sctp.port = reply->l4src.sctp.port;
+ return 1;
+ }
+ if ((flags & (ORIG_SPORT|ORIG_DPORT))
+ && ((flags & (REPL_SPORT|REPL_DPORT))))
return 1;
return 0;
}
-static void print_tuple(struct ip_conntrack_tuple *t)
+void parse_proto(struct nfattr *cda[], struct ctnl_tuple *tuple)
+{
+ if (cda[CTA_PROTO_SCTP_SRC-1])
+ tuple->l4src.sctp.port =
+ *(u_int16_t *)NFA_DATA(cda[CTA_PROTO_SCTP_SRC-1]);
+ if (cda[CTA_PROTO_SCTP_DST-1])
+ tuple->l4dst.sctp.port =
+ *(u_int16_t *)NFA_DATA(cda[CTA_PROTO_SCTP_DST-1]);
+}
+
+void parse_protoinfo(struct nfattr *cda[], struct ctnl_conntrack *ct)
+{
+/* if (cda[CTA_PROTOINFO_SCTP_STATE-1])
+ ct->protoinfo.sctp.state =
+ *(u_int8_t *)NFA_DATA(cda[CTA_PROTOINFO_SCTP_STATE-1]);
+*/
+}
+
+void print_protoinfo(union ctnl_protoinfo *protoinfo)
{
- fprintf(stdout, "sport=%d dport=%d ", ntohs(t->src.u.sctp.port),
- ntohs(t->dst.u.sctp.port));
+/* fprintf(stdout, "%s ", states[protoinfo->sctp.state]); */
}
-static void print_proto(union ip_conntrack_proto *proto)
+void print_proto(struct ctnl_tuple *tuple)
{
- if (proto->sctp.state > sizeof(states)/sizeof(char *))
- fprintf(stdout, "[%u] ", proto->sctp.state);
- else
- fprintf(stdout, "[%s] ", states[proto->sctp.state]);
+ fprintf(stdout, "sport=%u dport=%u ", htons(tuple->l4src.sctp.port),
+ htons(tuple->l4dst.sctp.port));
}
static struct ctproto_handler sctp = {
- .name = "sctp",
- .protonum = 132,
- .parse = parse,
- .print_tuple = print_tuple,
- .print_proto = print_proto,
- .final_check = final_check,
- .help = help,
- .opts = opts,
+ .name = "sctp",
+ .protonum = 132,
+ .parse_opts = parse_options,
+ .parse_protoinfo = parse_protoinfo,
+ .parse_proto = parse_proto,
+ .print_proto = print_proto,
+ .print_protoinfo = print_protoinfo,
+ .final_check = final_check,
+ .help = help,
+ .opts = opts
};
void __attribute__ ((constructor)) init(void);
diff --git a/extensions/libct_proto_tcp.c b/extensions/libct_proto_tcp.c
index 45ee29b..5fa3652 100644
--- a/extensions/libct_proto_tcp.c
+++ b/extensions/libct_proto_tcp.c
@@ -10,18 +10,19 @@
#include <stdio.h>
#include <getopt.h>
#include <stdlib.h>
-#include <string.h>
#include <netinet/in.h> /* For htons */
-#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
-#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_netlink.h>
#include "libct_proto.h"
+#include "libctnetlink.h"
static struct option opts[] = {
{"orig-port-src", 1, 0, '1'},
{"orig-port-dst", 1, 0, '2'},
{"reply-port-src", 1, 0, '3'},
{"reply-port-dst", 1, 0, '4'},
- {"state", 1, 0, '5'},
+ {"mask-port-src", 1, 0, '5'},
+ {"mask-port-dst", 1, 0, '6'},
+ {"state", 1, 0, '7'},
{0, 0, 0, 0}
};
@@ -38,7 +39,13 @@ enum tcp_param_flags {
REPL_DPORT_BIT = 3,
REPL_DPORT = (1 << REPL_DPORT_BIT),
- STATE_BIT = 4,
+ MASK_SPORT_BIT = 4,
+ MASK_SPORT = (1 << MASK_SPORT_BIT),
+
+ MASK_DPORT_BIT = 5,
+ MASK_DPORT = (1 << MASK_DPORT_BIT),
+
+ STATE_BIT = 6,
STATE = (1 << STATE_BIT)
};
@@ -55,48 +62,63 @@ static const char *states[] = {
"LISTEN"
};
-static void help()
+void help()
{
fprintf(stdout, "--orig-port-src original source port\n");
fprintf(stdout, "--orig-port-dst original destination port\n");
fprintf(stdout, "--reply-port-src reply source port\n");
fprintf(stdout, "--reply-port-dst reply destination port\n");
+ fprintf(stdout, "--mask-port-src mask source port\n");
+ fprintf(stdout, "--mask-port-dst mask destination port\n");
fprintf(stdout, "--state TCP state, fe. ESTABLISHED\n");
}
-static int parse(char c, char *argv[],
- struct ip_conntrack_tuple *orig,
- struct ip_conntrack_tuple *reply,
- union ip_conntrack_proto *proto,
- unsigned int *flags)
+int parse_options(char c, char *argv[],
+ struct ctnl_tuple *orig,
+ struct ctnl_tuple *reply,
+ struct ctnl_tuple *mask,
+ union ctnl_protoinfo *proto,
+ unsigned int *flags)
{
switch(c) {
case '1':
if (optarg) {
- orig->src.u.tcp.port = htons(atoi(optarg));
+ orig->l4src.tcp.port = htons(atoi(optarg));
*flags |= ORIG_SPORT;
}
break;
case '2':
if (optarg) {
- orig->dst.u.tcp.port = htons(atoi(optarg));
+ orig->l4dst.tcp.port = htons(atoi(optarg));
*flags |= ORIG_DPORT;
}
break;
case '3':
if (optarg) {
- reply->src.u.tcp.port = htons(atoi(optarg));
+ reply->l4src.tcp.port = htons(atoi(optarg));
*flags |= REPL_SPORT;
}
break;
case '4':
if (optarg) {
- reply->dst.u.tcp.port = htons(atoi(optarg));
+ reply->l4dst.tcp.port = htons(atoi(optarg));
*flags |= REPL_DPORT;
}
break;
case '5':
if (optarg) {
+ mask->l4src.tcp.port = htons(atoi(optarg));
+ *flags |= MASK_SPORT;
+ }
+ break;
+ case '6':
+ if (optarg) {
+ mask->l4src.tcp.port = htons(atoi(optarg));
+ *flags |= MASK_DPORT;
+ }
+ break;
+ case '7':
+ if (optarg) {
int i;
for (i=0; i<10; i++) {
if (strcmp(optarg, states[i]) == 0) {
@@ -114,39 +136,67 @@ static int parse(char c, char *argv[],
return 1;
}
-static int final_check(unsigned int flags)
+int final_check(unsigned int flags,
+ struct ctnl_tuple *orig,
+ struct ctnl_tuple *reply)
{
- if ((flags & ORIG_SPORT) && (flags & ORIG_DPORT))
+ if ((flags & (ORIG_SPORT|ORIG_DPORT))
+ && !(flags & (REPL_SPORT|REPL_DPORT))) {
+ reply->l4src.tcp.port = orig->l4dst.tcp.port;
+ reply->l4dst.tcp.port = orig->l4src.tcp.port;
return 1;
- else if ((flags & REPL_SPORT) && (flags & REPL_DPORT))
+ } else if (!(flags & (ORIG_SPORT|ORIG_DPORT))
+ && (flags & (REPL_SPORT|REPL_DPORT))) {
+ orig->l4src.tcp.port = reply->l4dst.tcp.port;
+ orig->l4dst.tcp.port = reply->l4src.tcp.port;
+ return 1;
+ }
+ if ((flags & (ORIG_SPORT|ORIG_DPORT))
+ && ((flags & (REPL_SPORT|REPL_DPORT))))
return 1;
return 0;
}
-static void print_tuple(struct ip_conntrack_tuple *t)
+void parse_proto(struct nfattr *cda[], struct ctnl_tuple *tuple)
+{
+ if (cda[CTA_PROTO_TCP_SRC-1])
+ tuple->l4src.tcp.port =
+ *(u_int16_t *)NFA_DATA(cda[CTA_PROTO_TCP_SRC-1]);
+ if (cda[CTA_PROTO_TCP_DST-1])
+ tuple->l4dst.tcp.port =
+ *(u_int16_t *)NFA_DATA(cda[CTA_PROTO_TCP_DST-1]);
+}
+
+void parse_protoinfo(struct nfattr *cda[], struct ctnl_conntrack *ct)
+{
+ if (cda[CTA_PROTOINFO_TCP_STATE-1])
+ ct->protoinfo.tcp.state =
+ *(u_int8_t *)NFA_DATA(cda[CTA_PROTOINFO_TCP_STATE-1]);
+}
+
+void print_protoinfo(union ctnl_protoinfo *protoinfo)
{
- fprintf(stdout, "sport=%d dport=%d ", ntohs(t->src.u.tcp.port),
- ntohs(t->dst.u.tcp.port));
+ fprintf(stdout, "%s ", states[protoinfo->tcp.state]);
}
-static void print_proto(union ip_conntrack_proto *proto)
+void print_proto(struct ctnl_tuple *tuple)
{
- if (proto->tcp.state > sizeof(states)/sizeof(char *))
- fprintf(stdout, "[%u] ", states[proto->tcp.state]);
- else
- fprintf(stdout, "[%s] ", states[proto->tcp.state]);
+ fprintf(stdout, "sport=%u dport=%u ", htons(tuple->l4src.tcp.port),
+ htons(tuple->l4dst.tcp.port));
}
static struct ctproto_handler tcp = {
- .name = "tcp",
- .protonum = 6,
- .parse = parse,
- .print_tuple = print_tuple,
- .print_proto = print_proto,
- .final_check = final_check,
- .help = help,
- .opts = opts,
+ .name = "tcp",
+ .protonum = 6,
+ .parse_opts = parse_options,
+ .parse_protoinfo = parse_protoinfo,
+ .parse_proto = parse_proto,
+ .print_proto = print_proto,
+ .print_protoinfo = print_protoinfo,
+ .final_check = final_check,
+ .help = help,
+ .opts = opts
};
void __attribute__ ((constructor)) init(void);
diff --git a/extensions/libct_proto_udp.c b/extensions/libct_proto_udp.c
index 0088cc5..aa733f0 100644
--- a/extensions/libct_proto_udp.c
+++ b/extensions/libct_proto_udp.c
@@ -11,15 +11,17 @@
#include <getopt.h>
#include <stdlib.h>
#include <netinet/in.h> /* For htons */
-#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
-#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_netlink.h>
#include "libct_proto.h"
+#include "libctnetlink.h"
static struct option opts[] = {
{"orig-port-src", 1, 0, '1'},
{"orig-port-dst", 1, 0, '2'},
{"reply-port-src", 1, 0, '3'},
{"reply-port-dst", 1, 0, '4'},
+ {"mask-port-src", 1, 0, '5'},
+ {"mask-port-dst", 1, 0, '6'},
{0, 0, 0, 0}
};
@@ -35,6 +37,12 @@ enum udp_param_flags {
REPL_DPORT_BIT = 3,
REPL_DPORT = (1 << REPL_DPORT_BIT),
+
+ MASK_SPORT_BIT = 4,
+ MASK_SPORT = (1 << MASK_SPORT_BIT),
+
+ MASK_DPORT_BIT = 5,
+ MASK_DPORT = (1 << MASK_DPORT_BIT),
};
void help()
@@ -43,67 +51,105 @@ void help()
fprintf(stdout, "--orig-port-dst original destination port\n");
fprintf(stdout, "--reply-port-src reply source port\n");
fprintf(stdout, "--reply-port-dst reply destination port\n");
+ fprintf(stdout, "--mask-port-src mask source port\n");
+ fprintf(stdout, "--mask-port-dst mask destination port\n");
}
-int parse(char c, char *argv[],
- struct ip_conntrack_tuple *orig,
- struct ip_conntrack_tuple *reply,
- union ip_conntrack_proto *proto,
- unsigned int *flags)
+int parse_options(char c, char *argv[],
+ struct ctnl_tuple *orig,
+ struct ctnl_tuple *reply,
+ struct ctnl_tuple *mask,
+ union ctnl_protoinfo *proto,
+ unsigned int *flags)
{
switch(c) {
case '1':
if (optarg) {
- orig->src.u.udp.port = htons(atoi(optarg));
+ orig->l4src.udp.port = htons(atoi(optarg));
*flags |= ORIG_SPORT;
}
break;
case '2':
if (optarg) {
- orig->dst.u.udp.port = htons(atoi(optarg));
+ orig->l4dst.udp.port = htons(atoi(optarg));
*flags |= ORIG_DPORT;
}
break;
case '3':
if (optarg) {
- reply->src.u.udp.port = htons(atoi(optarg));
+ reply->l4src.udp.port = htons(atoi(optarg));
*flags |= REPL_SPORT;
}
break;
case '4':
if (optarg) {
- reply->dst.u.udp.port = htons(atoi(optarg));
+ reply->l4dst.udp.port = htons(atoi(optarg));
*flags |= REPL_DPORT;
}
break;
+ case '5':
+ if (optarg) {
+ mask->l4src.udp.port = htons(atoi(optarg));
+ *flags |= MASK_SPORT;
+ }
+ break;
+ case '6':
+ if (optarg) {
+ mask->l4src.udp.port = htons(atoi(optarg));
+ *flags |= MASK_DPORT;
+ }
+ break;
}
return 1;
}
-int final_check(unsigned int flags)
+int final_check(unsigned int flags,
+ struct ctnl_tuple *orig,
+ struct ctnl_tuple *reply)
{
- if ((flags & ORIG_SPORT) && (flags & ORIG_DPORT))
+ if ((flags & (ORIG_SPORT|ORIG_DPORT))
+ && !(flags & (REPL_SPORT|REPL_DPORT))) {
+ reply->l4src.udp.port = orig->l4dst.udp.port;
+ reply->l4dst.udp.port = orig->l4src.udp.port;
+ return 1;
+ } else if (!(flags & (ORIG_SPORT|ORIG_DPORT))
+ && (flags & (REPL_SPORT|REPL_DPORT))) {
+ orig->l4src.udp.port = reply->l4dst.udp.port;
+ orig->l4dst.udp.port = reply->l4src.udp.port;
return 1;
- else if ((flags & REPL_SPORT) && (flags & REPL_DPORT))
+ }
+ if ((flags & (ORIG_SPORT|ORIG_DPORT))
+ && ((flags & (REPL_SPORT|REPL_DPORT))))
return 1;
return 0;
}
-void print_tuple(struct ip_conntrack_tuple *t)
+void parse_proto(struct nfattr *cda[], struct ctnl_tuple *tuple)
+{
+ if (cda[CTA_PROTO_UDP_SRC-1])
+ tuple->l4src.udp.port =
+ *(u_int16_t *)NFA_DATA(cda[CTA_PROTO_UDP_SRC-1]);
+ if (cda[CTA_PROTO_UDP_DST-1])
+ tuple->l4dst.udp.port =
+ *(u_int16_t *)NFA_DATA(cda[CTA_PROTO_UDP_DST-1]);
+}
+
+void print_proto(struct ctnl_tuple *tuple)
{
- fprintf(stdout, "sport=%d dport=%d ", ntohs(t->src.u.udp.port),
- ntohs(t->dst.u.udp.port));
+ fprintf(stdout, "sport=%u dport=%u ", htons(tuple->l4src.udp.port),
+ htons(tuple->l4dst.udp.port));
}
static struct ctproto_handler udp = {
- .name = "udp",
- .protonum = 17,
- .parse = parse,
- .print_tuple = print_tuple,
- .final_check = final_check,
- .help = help,
- .opts = opts
+ .name = "udp",
+ .protonum = 17,
+ .parse_opts = parse_options,
+ .parse_proto = parse_proto,
+ .print_proto = print_proto,
+ .final_check = final_check,
+ .help = help,
+ .opts = opts
};
void __attribute__ ((constructor)) init(void);
diff --git a/include/libct_proto.h b/include/libct_proto.h
index 6df03e7..51e23fc 100644
--- a/include/libct_proto.h
+++ b/include/libct_proto.h
@@ -5,6 +5,7 @@
#include "linux_list.h"
#include <getopt.h>
+#include "libctnetlink.h"
struct cta_proto;
@@ -13,16 +14,24 @@ struct ctproto_handler {
char *name;
u_int16_t protonum;
+
+ enum ctattr_protoinfo protoinfo_attr;
- int (*parse)(char c, char *argv[],
- struct ip_conntrack_tuple *orig,
- struct ip_conntrack_tuple *reply,
- union ip_conntrack_proto *proto,
+ int (*parse_opts)(char c, char *argv[],
+ struct ctnl_tuple *orig,
+ struct ctnl_tuple *reply,
+ struct ctnl_tuple *mask,
+ union ctnl_protoinfo *proto,
unsigned int *flags);
- void (*print_tuple)(struct ip_conntrack_tuple *t);
- void (*print_proto)(union ip_conntrack_proto *proto);
-
- int (*final_check)(unsigned int flags);
+ void (*parse_proto)(struct nfattr *cda[], struct ctnl_tuple *tuple);
+ void (*parse_protoinfo)(struct nfattr *cda[],
+ struct ctnl_conntrack *ct);
+ void (*print_proto)(struct ctnl_tuple *t);
+ void (*print_protoinfo)(union ctnl_protoinfo *protoinfo);
+
+ int (*final_check)(unsigned int flags,
+ struct ctnl_tuple *orig,
+ struct ctnl_tuple *reply);
void (*help)();
diff --git a/src/conntrack.c b/src/conntrack.c
index dfedf68..a6efd8b 100644
--- a/src/conntrack.c
+++ b/src/conntrack.c
@@ -41,7 +41,6 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
-#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
#include <linux/netfilter_ipv4/ip_conntrack.h>
#include "libctnetlink.h"
#include "libnfnetlink.h"
@@ -62,34 +61,60 @@
#endif
enum action {
+ CT_NONE = 0,
+
CT_LIST_BIT = 0,
CT_LIST = (1 << CT_LIST_BIT),
CT_CREATE_BIT = 1,
CT_CREATE = (1 << CT_CREATE_BIT),
+
+ CT_UPDATE_BIT = 2,
+ CT_UPDATE = (1 << CT_UPDATE_BIT),
- CT_DELETE_BIT = 2,
+ CT_DELETE_BIT = 3,
CT_DELETE = (1 << CT_DELETE_BIT),
- CT_GET_BIT = 3,
+ CT_GET_BIT = 4,
CT_GET = (1 << CT_GET_BIT),
- CT_FLUSH_BIT = 4,
+ CT_FLUSH_BIT = 5,
CT_FLUSH = (1 << CT_FLUSH_BIT),
- CT_EVENT_BIT = 5,
+ CT_EVENT_BIT = 6,
CT_EVENT = (1 << CT_EVENT_BIT),
- CT_ACTION_BIT = 6,
- CT_ACTION = (1 << CT_ACTION_BIT),
-
CT_VERSION_BIT = 7,
CT_VERSION = (1 << CT_VERSION_BIT),
CT_HELP_BIT = 8,
CT_HELP = (1 << CT_HELP_BIT),
+
+ EXP_LIST_BIT = 9,
+ EXP_LIST = (1 << EXP_LIST_BIT),
+
+ EXP_CREATE_BIT = 10,
+ EXP_CREATE = (1 << EXP_CREATE_BIT),
+
+ EXP_DELETE_BIT = 11,
+ EXP_DELETE = (1 << EXP_DELETE_BIT),
+
+ EXP_GET_BIT = 12,
+ EXP_GET = (1 << EXP_GET_BIT),
+
+ EXP_FLUSH_BIT = 13,
+ EXP_FLUSH = (1 << EXP_FLUSH_BIT),
+
+ EXP_EVENT_BIT = 14,
+ EXP_EVENT = (1 << EXP_EVENT_BIT),
};
-#define NUMBER_OF_CMD 9
+#define NUMBER_OF_CMD 15
+
+static const char cmdflags[NUMBER_OF_CMD]
+= {'L','I','U','D','G','F','E','V','h','L','I','D','G','F','E'};
+
+static const char cmd_need_param[NUMBER_OF_CMD]
+= {' ','x','x','x','x',' ',' ',' ',' ',' ','x','x','x',' ',' '};
enum options {
CT_OPT_ORIG_SRC_BIT = 0,
@@ -120,36 +145,34 @@ enum options {
CT_OPT_ZERO_BIT = 7,
CT_OPT_ZERO = (1 << CT_OPT_ZERO_BIT),
- CT_OPT_DUMP_MASK_BIT = 8,
- CT_OPT_DUMP_MASK = (1 << CT_OPT_DUMP_MASK_BIT),
-
- CT_OPT_GROUP_MASK_BIT = 9,
- CT_OPT_GROUP_MASK = (1 << CT_OPT_GROUP_MASK_BIT),
-
- CT_OPT_EVENT_MASK_BIT = 10,
+ CT_OPT_EVENT_MASK_BIT = 8,
CT_OPT_EVENT_MASK = (1 << CT_OPT_EVENT_MASK_BIT),
- CT_OPT_TUPLE_SRC_BIT = 11,
- CT_OPT_TUPLE_SRC = (1 << CT_OPT_TUPLE_SRC_BIT),
+ CT_OPT_EXP_SRC_BIT = 9,
+ CT_OPT_EXP_SRC = (1 << CT_OPT_EXP_SRC_BIT),
- CT_OPT_TUPLE_DST_BIT = 12,
- CT_OPT_TUPLE_DST = (1 << CT_OPT_TUPLE_DST_BIT),
+ CT_OPT_EXP_DST_BIT = 10,
+ CT_OPT_EXP_DST = (1 << CT_OPT_EXP_DST_BIT),
- CT_OPT_MASK_SRC_BIT = 13,
+ CT_OPT_MASK_SRC_BIT = 11,
CT_OPT_MASK_SRC = (1 << CT_OPT_MASK_SRC_BIT),
- CT_OPT_MASK_DST_BIT = 14,
+ CT_OPT_MASK_DST_BIT = 12,
CT_OPT_MASK_DST = (1 << CT_OPT_MASK_DST_BIT),
+
+ CT_OPT_NATRANGE_BIT = 13,
+ CT_OPT_NATRANGE = (1 << CT_OPT_NATRANGE_BIT),
};
-#define NUMBER_OF_OPT 15
+#define NUMBER_OF_OPT 14
static const char optflags[NUMBER_OF_OPT]
-= { 's', 'd', 'r', 'q', 'p', 't', 'u', 'z','m','g','e', '[',']','{','}'};
+= {'s','d','r','q','p','t','u','z','e','[',']','{','}','a'};
static struct option original_opts[] = {
{"dump", 2, 0, 'L'},
{"create", 1, 0, 'I'},
{"delete", 1, 0, 'D'},
+ {"update", 1, 0, 'U'},
{"get", 1, 0, 'G'},
{"flush", 1, 0, 'F'},
{"event", 1, 0, 'E'},
@@ -164,13 +187,12 @@ static struct option original_opts[] = {
{"timeout", 1, 0, 't'},
{"status", 1, 0, 'u'},
{"zero", 0, 0, 'z'},
- {"dump-mask", 1, 0, 'm'},
- {"groups", 1, 0, 'g'},
{"event-mask", 1, 0, 'e'},
{"tuple-src", 1, 0, '['},
{"tuple-dst", 1, 0, ']'},
{"mask-src", 1, 0, '{'},
{"mask-dst", 1, 0, '}'},
+ {"nat-range", 1, 0, 'a'},
{0, 0, 0, 0}
};
@@ -188,22 +210,29 @@ static unsigned int global_option_offset = 0;
* optional
*/
+/* FIXME: I'd need something different than this table to catch up some
+ * particular cases. Better later Pablo */
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 -m -g -e ts td ms md */
-/*LIST*/ {'x','x','x','x','x','x','x',' ','x','x','x','x','x','x','x'},
-/*CREATE*/ {'+','+','+','+','+','+','+','x','x','x','x','+','+','+','+'},
-/*DELETE*/ {' ',' ',' ',' ',' ','x','x','x','x','x','x',' ',' ',' ',' '},
-/*GET*/ {' ',' ',' ',' ','+','x','x','x','x','x','x',' ',' ',' ',' '},
-/*FLUSH*/ {'x','x','x','x','x','x','x','x','x','x','x','x','x','x','x'},
-/*EVENT*/ {'x','x','x','x','x','x','x','x','x',' ','x','x','x','x','x'},
-/*ACTION*/ {'x','x','x','x','x','x','x','x',' ','x',' ','x','x','x','x'},
-/*VERSION*/ {'x','x','x','x','x','x','x','x','x','x','x','x','x','x','x'},
-/*HELP*/ {'x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x'},
+ /* -s -d -r -q -p -t -u -z -e -x -y -k -l -a */
+/*CT_LIST*/ {'x','x','x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*CT_CREATE*/ {' ',' ',' ',' ','+','+','+','x','x','x','x','x','x',' '},
+/*CT_UPDATE*/ {' ',' ',' ',' ','+','+','+','x','x','x','x','x','x','x'},
+/*CT_DELETE*/ {' ',' ',' ',' ',' ','x','x','x','x','x','x','x','x','x'},
+/*CT_GET*/ {' ',' ',' ',' ','+','x','x','x','x','x','x','x','x','x'},
+/*CT_FLUSH*/ {'x','x','x','x','x','x','x','x','x','x','x','x','x','x'},
+/*CT_EVENT*/ {'x','x','x','x','x','x','x','x',' ','x','x','x','x','x'},
+/*VERSION*/ {'x','x','x','x','x','x','x','x','x','x','x','x','x','x'},
+/*HELP*/ {'x','x','x','x',' ','x','x','x','x','x','x','x','x','x'},
+/*EXP_LIST*/ {'x','x','x','x','x','x','x','x','x','x','x','x','x','x'},
+/*EXP_CREATE*/{'+','+',' ',' ','+','+',' ','x','x','+','+','+','+','x'},
+/*EXP_DELETE*/{'+','+',' ',' ','+','x','x','x','x','x','x','x','x','x'},
+/*EXP_GET*/ {'+','+',' ',' ','+','x','x','x','x','x','x','x','x','x'},
+/*EXP_FLUSH*/ {'x','x','x','x','x','x','x','x','x','x','x','x','x','x'},
+/*EXP_EVENT*/ {'x','x','x','x','x','x','x','x','x','x','x','x','x','x'},
};
-/* FIXME: hardcoded!, this must be defined during compilation time */
char *lib_dir = CONNTRACK_LIB_DIR;
LIST_HEAD(proto_list);
@@ -259,6 +288,22 @@ exit_error(enum exittype status, char *msg, ...)
}
static void
+generic_cmd_check(int command, int options)
+{
+ int i;
+
+ for (i = 0; i < NUMBER_OF_CMD; i++) {
+ if (!(command & (1<<i)))
+ continue;
+
+ if (cmd_need_param[i] == 'x' && !options)
+ exit_error(PARAMETER_PROBLEM,
+ "You need to supply parameters to `-%c'\n",
+ cmdflags[i]);
+ }
+}
+
+static void
generic_opt_check(int command, int options)
{
int i, j, legal = 0;
@@ -332,11 +377,14 @@ err2str(int err, enum action command)
} table [] =
{ { CT_LIST, -ENOTSUPP, "function not implemented" },
{ 0xFFFF, -EINVAL, "invalid parameters" },
+ { CT_CREATE, -EEXIST, "Such conntrack exists, try -U to update" },
{ CT_CREATE|CT_GET|CT_DELETE, -ENOENT,
"such conntrack doesn't exist" },
{ CT_CREATE|CT_GET, -ENOMEM, "not enough memory" },
{ CT_GET, -EAFNOSUPPORT, "protocol not supported" },
{ CT_CREATE, -ETIME, "conntrack has expired" },
+ { EXP_CREATE, -ENOENT, "master conntrack not found" },
+ { EXP_CREATE, -EINVAL, "invalid parameters" }
};
for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
@@ -347,48 +395,29 @@ err2str(int err, enum action command)
return strerror(err);
}
-static void dump_tuple(struct ip_conntrack_tuple *tp)
+static void dump_tuple(struct ctnl_tuple *tp)
{
fprintf(stdout, "tuple %p: %u %u.%u.%u.%u:%hu -> %u.%u.%u.%u:%hu\n",
- tp, tp->dst.protonum,
- NIPQUAD(tp->src.ip), ntohs(tp->src.u.all),
- NIPQUAD(tp->dst.ip), ntohs(tp->dst.u.all));
+ tp, tp->protonum,
+ NIPQUAD(tp->src.v4), ntohs(tp->l4src.all),
+ NIPQUAD(tp->dst.v4), ntohs(tp->l4dst.all));
}
-void not_implemented_yet()
-{
- exit_error(OTHER_PROBLEM, "Sorry, not implemented yet :(\n");
-}
-
-
#define PARSE_STATUS 0
-#define PARSE_GROUP 1
-#define PARSE_EVENT 2
-#define PARSE_DUMP 3
-#define PARSE_MAX PARSE_DUMP+1
+#define PARSE_EVENT 1
+#define PARSE_MAX 2
static struct parse_parameter {
- char *parameter[10];
+ char *parameter[5];
size_t size;
- unsigned int value[10];
+ unsigned int value[5];
} parse_array[PARSE_MAX] = {
- { {"EXPECTED", "ASSURED", "SEEN_REPLY", "CONFIRMED", "SNAT", "DNAT",
- "SEQ_ADJUST", "UNSET"},
- 8,
- { IPS_EXPECTED, IPS_ASSURED, IPS_SEEN_REPLY, IPS_CONFIRMED,
- IPS_SRC_NAT, IPS_DST_NAT, IPS_SEQ_ADJUST, 0} },
- { {"ALL", "TCP", "UDP", "ICMP"},
- 4,
- {~0U, NFGRP_IPV4_CT_TCP, NFGRP_IPV4_CT_UDP, NFGRP_IPV4_CT_ICMP} },
- { {"ALL", "NEW", "RELATED", "DESTROY", "REFRESH", "STATUS",
- "PROTOINFO", "HELPER", "HELPINFO", "NATINFO"},
- 10,
- {~0U, IPCT_NEW, IPCT_RELATED, IPCT_DESTROY, IPCT_REFRESH, IPCT_STATUS,
- IPCT_PROTOINFO, IPCT_HELPER, IPCT_HELPINFO, IPCT_NATINFO} },
- { {"ALL", "TUPLE", "STATUS", "TIMEOUT", "PROTOINFO", "HELPINFO",
- "COUNTERS", "MARK"}, 8,
- {~0U, DUMP_TUPLE, DUMP_STATUS, DUMP_TIMEOUT, DUMP_PROTOINFO,
- DUMP_HELPINFO, DUMP_COUNTERS, DUMP_MARK} }
+ { {"ASSURED", "SEEN_REPLY", "UNSET", "SRC_NAT", "DST_NAT"}, 5,
+ { IPS_ASSURED, IPS_SEEN_REPLY, 0,
+ IPS_SRC_NAT_DONE, IPS_DST_NAT_DONE} },
+ { {"ALL", "NEW", "UPDATES", "DESTROY"}, 4,
+ {~0U, NF_NETLINK_CONNTRACK_NEW, NF_NETLINK_CONNTRACK_UPDATE,
+ NF_NETLINK_CONNTRACK_DESTROY} },
};
static int
@@ -425,6 +454,14 @@ parse_parameter(const char *arg, unsigned int *status, int parse_type)
exit_error(PARAMETER_PROBLEM, "Bad parameter `%s'", arg);
}
+static void
+add_command(int *cmd, const int newcmd, const int othercmds)
+{
+ if (*cmd & (~othercmds))
+ exit_error(PARAMETER_PROBLEM, "Invalid commands combination\n");
+ *cmd |= newcmd;
+}
+
unsigned int check_type(int argc, char *argv[])
{
char *table = NULL;
@@ -515,15 +552,90 @@ int iptables_insmod(const char *modname, const char *modprobe)
return -1;
}
+/* Shamelessly stolen from libipt_DNAT ;). Ranges expected in network order. */
+static void
+nat_parse(char *arg, int portok, struct ctnl_nat *range)
+{
+ char *colon, *dash, *error;
+ unsigned long ip;
+
+ memset(range, 0, sizeof(range));
+ colon = strchr(arg, ':');
+
+ if (colon) {
+ int port;
+
+ if (!portok)
+ exit_error(PARAMETER_PROBLEM,
+ "Need TCP or UDP with port specification");
+
+ port = atoi(colon+1);
+ if (port == 0 || port > 65535)
+ exit_error(PARAMETER_PROBLEM,
+ "Port `%s' not valid\n", colon+1);
+
+ error = strchr(colon+1, ':');
+ if (error)
+ exit_error(PARAMETER_PROBLEM,
+ "Invalid port:port syntax - use dash\n");
+
+ dash = strchr(colon, '-');
+ if (!dash) {
+ range->l4min.tcp.port
+ = range->l4max.tcp.port
+ = htons(port);
+ } else {
+ int maxport;
+
+ maxport = atoi(dash + 1);
+ if (maxport == 0 || maxport > 65535)
+ exit_error(PARAMETER_PROBLEM,
+ "Port `%s' not valid\n", dash+1);
+ if (maxport < port)
+ // People are stupid.
+ exit_error(PARAMETER_PROBLEM,
+ "Port range `%s' funky\n", colon+1);
+ range->l4min.tcp.port = htons(port);
+ range->l4max.tcp.port = htons(maxport);
+ }
+ // Starts with a colon? No IP info...
+ if (colon == arg)
+ return;
+ *colon = '\0';
+ }
+
+ dash = strchr(arg, '-');
+ if (colon && dash && dash > colon)
+ dash = NULL;
+
+ if (dash)
+ *dash = '\0';
+
+ ip = inet_addr(arg);
+ if (!ip)
+ exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
+ arg);
+ range->min_ip = ip;
+ if (dash) {
+ ip = inet_addr(dash+1);
+ if (!ip)
+ exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
+ dash+1);
+ range->max_ip = ip;
+ } else
+ range->max_ip = range->min_ip;
+}
+
void usage(char *prog) {
fprintf(stdout, "Tool to manipulate conntrack and expectations. Version %s\n", VERSION);
fprintf(stdout, "Usage: %s [commands] [options]\n", prog);
fprintf(stdout, "\n");
fprintf(stdout, "Commands:\n");
fprintf(stdout, "-L [table] [-z] List conntrack or expectation table\n");
-fprintf(stdout, "-G [table] parameters Get conntrack or expectation\n");
+fprintf(stdout, "-G [table] parameters Get conntrack or expectation\n");
fprintf(stdout, "-D [table] parameters Delete conntrack or expectation\n");
fprintf(stdout, "-I [table] parameters Create a conntrack or expectation\n");
+fprintf(stdout, "-U [table] parameters Update a conntrack\n");
fprintf(stdout, "-E [table] [options] Show events\n");
fprintf(stdout, "-F [table] Flush table\n");
fprintf(stdout, "-A [table] [options] Set action\n");
@@ -535,108 +647,127 @@ fprintf(stdout, "--reply-src ip Source addres from reply direction\n");
fprintf(stdout, "--reply-dst ip Destination address from reply direction\n");
fprintf(stdout, "--tuple-src ip Source address in expect tuple\n");
fprintf(stdout, "--tuple-dst ip Destination address in expect tuple\n");
-fprintf(stdout, "--mask-src ip Source mask in expect\n");
-fprintf(stdout, "--mask-dst ip Destination mask in expect\n");
+fprintf(stdout, "--mask-src ip Source mask address for expectation\n");
+fprintf(stdout, "--mask-dst ip Destination mask address for expectations\n");
fprintf(stdout, "-p proto Layer 4 Protocol\n");
fprintf(stdout, "-t timeout Set timeout\n");
fprintf(stdout, "-u status Set status\n");
fprintf(stdout, "-m dumpmask Set dump mask\n");
fprintf(stdout, "-g groupmask Set group mask\n");
fprintf(stdout, "-e eventmask Set event mask\n");
+fprintf(stdout, "-a min_ip[-max_ip] NAT ip range\n");
fprintf(stdout, "-z Zero Counters\n");
}
int main(int argc, char *argv[])
{
- int c;
+ char c;
unsigned int command = 0, options = 0;
- struct ip_conntrack_tuple orig, reply, tuple, mask, *o = NULL, *r = NULL;
+ struct ctnl_tuple orig, reply, mask, *o = NULL, *r = NULL;
+ struct ctnl_tuple exptuple;
struct ctproto_handler *h = NULL;
- union ip_conntrack_proto proto;
+ union ctnl_protoinfo proto;
+ struct ctnl_nat range;
unsigned long timeout = 0;
- unsigned int status = 0, group_mask = 0;
+ unsigned int status = IPS_CONFIRMED;
unsigned long id = 0;
unsigned int type = 0, dump_mask = 0, extra_flags = 0, event_mask = 0;
+ int manip = -1;
int res = 0, retry = 2;
- memset(&proto, 0, sizeof(union ip_conntrack_proto));
- memset(&orig, 0, sizeof(struct ip_conntrack_tuple));
- memset(&reply, 0, sizeof(struct ip_conntrack_tuple));
- orig.dst.dir = IP_CT_DIR_ORIGINAL;
- reply.dst.dir = IP_CT_DIR_REPLY;
+ memset(&proto, 0, sizeof(union ctnl_protoinfo));
+ memset(&orig, 0, sizeof(struct ctnl_tuple));
+ memset(&reply, 0, sizeof(struct ctnl_tuple));
+ memset(&mask, 0, sizeof(struct ctnl_tuple));
+ memset(&range, 0, sizeof(struct ctnl_nat));
while ((c = getopt_long(argc, argv,
- "L::I::D::G::E::A::F::hVs:d:r:q:p:t:u:m:g:e:z[:]:{:}:",
+ "L::I::U::D::G::E::A::F::hVs:d:r:q:p:t:u:e:a:z[:]:{:}:",
opts, NULL)) != -1) {
switch(c) {
case 'L':
- command |= CT_LIST;
type = check_type(argc, argv);
+ if (type == 0)
+ add_command(&command, CT_LIST, CT_NONE);
+ else if (type == 1)
+ add_command(&command, EXP_LIST, CT_NONE);
break;
case 'I':
- command |= CT_CREATE;
type = check_type(argc, argv);
+ if (type == 0)
+ add_command(&command, CT_CREATE, CT_NONE);
+ else if (type == 1)
+ add_command(&command, EXP_CREATE, CT_NONE);
+ break;
+ case 'U':
+ type = check_type(argc, argv);
+ if (type == 0)
+ add_command(&command, CT_UPDATE, CT_NONE);
+ else
+ exit_error(PARAMETER_PROBLEM, "Can't update "
+ "expectations");
break;
case 'D':
- command |= CT_DELETE;
type = check_type(argc, argv);
+ if (type == 0)
+ add_command(&command, CT_DELETE, CT_NONE);
+ else if (type == 1)
+ add_command(&command, EXP_DELETE, CT_NONE);
break;
case 'G':
- command |= CT_GET;
type = check_type(argc, argv);
+ if (type == 0)
+ add_command(&command, CT_GET, CT_NONE);
+ else if (type == 1)
+ add_command(&command, EXP_GET, CT_NONE);
break;
case 'F':
- command |= CT_FLUSH;
type = check_type(argc, argv);
+ if (type == 0)
+ add_command(&command, CT_FLUSH, CT_NONE);
+ else if (type == 1)
+ add_command(&command, EXP_FLUSH, CT_NONE);
break;
case 'E':
- command |= CT_EVENT;
- type = check_type(argc, argv);
- break;
- case 'A':
- command |= CT_ACTION;
type = check_type(argc, argv);
+ if (type == 0)
+ add_command(&command, CT_EVENT, CT_NONE);
+ else if (type == 1)
+ add_command(&command, EXP_EVENT, CT_NONE);
break;
case 'V':
- command |= CT_VERSION;
+ add_command(&command, CT_VERSION, CT_NONE);
break;
case 'h':
- command |= CT_HELP;
- break;
- case 'm':
- if (!optarg)
- continue;
-
- options |= CT_OPT_DUMP_MASK;
- parse_parameter(optarg, &dump_mask, PARSE_DUMP);
+ add_command(&command, CT_HELP, CT_NONE);
break;
case 's':
options |= CT_OPT_ORIG_SRC;
if (optarg)
- orig.src.ip = inet_addr(optarg);
+ orig.src.v4 = inet_addr(optarg);
break;
case 'd':
options |= CT_OPT_ORIG_DST;
if (optarg)
- orig.dst.ip = inet_addr(optarg);
+ orig.dst.v4 = inet_addr(optarg);
break;
case 'r':
options |= CT_OPT_REPL_SRC;
if (optarg)
- reply.src.ip = inet_addr(optarg);
+ reply.src.v4 = inet_addr(optarg);
break;
case 'q':
options |= CT_OPT_REPL_DST;
if (optarg)
- reply.dst.ip = inet_addr(optarg);
+ reply.dst.v4 = inet_addr(optarg);
break;
case 'p':
options |= CT_OPT_PROTO;
h = findproto(optarg);
if (!h)
exit_error(PARAMETER_PROBLEM, "proto needed\n");
- orig.dst.protonum = h->protonum;
- reply.dst.protonum = h->protonum;
+ orig.protonum = h->protonum;
+ reply.protonum = h->protonum;
opts = merge_options(opts, h->opts,
&h->option_offset);
break;
@@ -646,20 +777,13 @@ int main(int argc, char *argv[])
timeout = atol(optarg);
break;
case 'u': {
- /* FIXME: NAT stuff, later... */
if (!optarg)
continue;
options |= CT_OPT_STATUS;
parse_parameter(optarg, &status, PARSE_STATUS);
- /* Just insert confirmed conntracks */
- status |= IPS_CONFIRMED;
break;
}
- case 'g':
- options |= CT_OPT_GROUP_MASK;
- parse_parameter(optarg, &group_mask, PARSE_GROUP);
- break;
case 'e':
options |= CT_OPT_EVENT_MASK;
parse_parameter(optarg, &event_mask, PARSE_EVENT);
@@ -667,30 +791,35 @@ int main(int argc, char *argv[])
case 'z':
options |= CT_OPT_ZERO;
break;
- case '[':
- options |= CT_OPT_TUPLE_SRC;
+ case 'k':
+ options |= CT_OPT_MASK_SRC;
if (optarg)
- tuple.src.ip = inet_addr(optarg);
+ mask.src.v4 = inet_addr(optarg);
break;
- case ']':
- options |= CT_OPT_TUPLE_DST;
+ case 'l':
+ options |= CT_OPT_MASK_DST;
if (optarg)
- tuple.dst.ip = inet_addr(optarg);
+ mask.dst.v4 = inet_addr(optarg);
break;
- case '{':
- options |= CT_OPT_MASK_SRC;
+ case 'x':
+ options |= CT_OPT_EXP_SRC;
if (optarg)
- mask.src.ip = inet_addr(optarg);
+ exptuple.src.v4 = inet_addr(optarg);
break;
- case '}':
- options |= CT_OPT_MASK_DST;
+ case 'y':
+ options |= CT_OPT_EXP_DST;
if (optarg)
- mask.dst.ip = inet_addr(optarg);
+ exptuple.dst.v4 = inet_addr(optarg);
+ break;
+ case 'a':
+ options |= CT_OPT_NATRANGE;
+ nat_parse(optarg, 1, &range);
break;
default:
- if (h && h->parse && !h->parse(c - h->option_offset,
- argv, &orig, &reply,
- &proto, &extra_flags))
+ if (h && h->parse_opts
+ &&!h->parse_opts(c - h->option_offset, argv, &orig,
+ &reply, &mask, &proto,
+ &extra_flags))
exit_error(PARAMETER_PROBLEM, "parse error\n");
/* Unknown argument... */
@@ -703,10 +832,12 @@ int main(int argc, char *argv[])
}
}
+ generic_cmd_check(command, options);
generic_opt_check(command, options);
if (!(command & CT_HELP)
- && h && h->final_check && !h->final_check(extra_flags)) {
+ && h && h->final_check
+ && !h->final_check(extra_flags, &orig, &reply)) {
usage(argv[0]);
extension_help(h);
exit_error(PARAMETER_PROBLEM, "Missing protocol arguments!\n");
@@ -716,71 +847,115 @@ int main(int argc, char *argv[])
retry--;
switch(command) {
case CT_LIST:
- if (type == 0) {
- if (options & CT_OPT_ZERO)
- res = dump_conntrack_table(1);
- else
- res = dump_conntrack_table(0);
- } else
- res = dump_expect_list();
+ if (options & CT_OPT_ZERO)
+ res = dump_conntrack_table(1);
+ else
+ res = dump_conntrack_table(0);
+ break;
+
+ case EXP_LIST:
+ res = dump_expect_list();
break;
case CT_CREATE:
- if (type == 0)
+ if ((options & CT_OPT_ORIG)
+ && !(options & CT_OPT_REPL)) {
+ reply.src.v4 = orig.dst.v4;
+ reply.dst.v4 = orig.src.v4;
+ } else if (!(options & CT_OPT_ORIG)
+ && (options & CT_OPT_REPL)) {
+ orig.src.v4 = reply.dst.v4;
+ orig.dst.v4 = reply.src.v4;
+ }
+ if (options & CT_OPT_NATRANGE)
res = create_conntrack(&orig, &reply, timeout,
- &proto, status);
+ &proto, status, &range);
else
- res = create_expect(&tuple, &mask, &orig,
- &reply, timeout);
+ res = create_conntrack(&orig, &reply, timeout,
+ &proto, status, NULL);
+ break;
+
+ case EXP_CREATE:
+ if (options & CT_OPT_ORIG)
+ res = create_expectation(&orig,
+ CTA_TUPLE_ORIG,
+ &exptuple,
+ &mask,
+ timeout);
+ else if (options & CT_OPT_REPL)
+ res = create_expectation(&reply,
+ CTA_TUPLE_RPLY,
+ &exptuple,
+ &mask,
+ timeout);
+ break;
+
+ case CT_UPDATE:
+ if ((options & CT_OPT_ORIG)
+ && !(options & CT_OPT_REPL)) {
+ reply.src.v4 = orig.dst.v4;
+ reply.dst.v4 = orig.src.v4;
+ } else if (!(options & CT_OPT_ORIG)
+ && (options & CT_OPT_REPL)) {
+ orig.src.v4 = reply.dst.v4;
+ orig.dst.v4 = reply.src.v4;
+ }
+ res = update_conntrack(&orig, &reply, timeout,
+ &proto, status);
break;
case CT_DELETE:
- if (type == 0) {
- if (options & CT_OPT_ORIG)
- res =delete_conntrack(&orig, CTA_ORIG,
- id);
- else if (options & CT_OPT_REPL)
- res = delete_conntrack(&reply, CTA_RPLY,
- id);
- } else
- not_implemented_yet();
+ if (options & CT_OPT_ORIG)
+ res = delete_conntrack(&orig, CTA_TUPLE_ORIG,
+ CTNL_DIR_ORIGINAL);
+ else if (options & CT_OPT_REPL)
+ res = delete_conntrack(&reply, CTA_TUPLE_RPLY,
+ CTNL_DIR_REPLY);
break;
-
+
+ case EXP_DELETE:
+ if (options & CT_OPT_ORIG)
+ res = delete_expectation(&orig, CTA_TUPLE_ORIG);
+ else if (options & CT_OPT_REPL)
+ res = delete_expectation(&reply, CTA_TUPLE_RPLY);
+ break;
+
case CT_GET:
- if (type == 0) {
- if (options & CT_OPT_ORIG)
- res = get_conntrack(&orig, CTA_ORIG,
- id);
- else if (options & CT_OPT_REPL)
- res = get_conntrack(&reply, CTA_RPLY,
- id);
- } else
- not_implemented_yet();
+ if (options & CT_OPT_ORIG)
+ res = get_conntrack(&orig, CTA_TUPLE_ORIG, id);
+ else if (options & CT_OPT_REPL)
+ res = get_conntrack(&reply, CTA_TUPLE_RPLY, id);
break;
-
+
+ case EXP_GET:
+ if (options & CT_OPT_ORIG)
+ res = get_expect(&orig, CTA_TUPLE_ORIG);
+ else if (options & CT_OPT_REPL)
+ res = get_expect(&reply, CTA_TUPLE_RPLY);
+ break;
+
case CT_FLUSH:
- if (type == 0)
- res = flush_conntrack();
- else
- not_implemented_yet();
+ res = flush_conntrack();
+ break;
+
+ case EXP_FLUSH:
+ res = flush_expectation();
break;
case CT_EVENT:
- if (type == 0) {
- if (options & CT_OPT_GROUP_MASK)
- res = event_conntrack(group_mask);
- else
- res = event_conntrack(~0U);
- } else
- not_implemented_yet();
-
- case CT_ACTION:
- if (type == 0)
- if (options & CT_OPT_DUMP_MASK)
- res = set_mask(dump_mask, 0);
- else if (options & CT_OPT_EVENT_MASK)
- res = set_mask(event_mask, 1);
+ if (options & CT_OPT_EVENT_MASK)
+ res = event_conntrack(event_mask);
+ else
+ res = event_conntrack(~0U);
break;
+
+ case EXP_EVENT:
+ if (options & CT_OPT_EVENT_MASK)
+ res = event_expectation(event_mask);
+ else
+ res = event_expectation(~0U);
+ break;
+
case CT_VERSION:
fprintf(stdout, "%s v%s\n", PROGNAME, VERSION);
break;
diff --git a/src/libct.c b/src/libct.c
index 0555ec8..94e5b24 100644
--- a/src/libct.c
+++ b/src/libct.c
@@ -1,5 +1,5 @@
/*
- * (C) 2005 by Pablo Neira Ayuso <pablo@eurodev.net>,
+ * (C) 2005 by Pablo Neira Ayuso <pablo@eurodev.net>
* Harald Welte <laforge@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
@@ -11,9 +11,13 @@
#include <getopt.h>
#include <dlfcn.h>
#include <stdlib.h>
+#include <signal.h>
#include <errno.h>
-#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
+/* From kernel.h */
+#define INT_MAX ((int)(~0U>>1))
+#define INT_MIN (-INT_MAX - 1)
#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_netlink.h>
#include "libctnetlink.h"
#include "libnfnetlink.h"
#include "linux_list.h"
@@ -25,6 +29,7 @@
#define DEBUGP
#endif
+static struct ctnl_handle cth;
extern char *lib_dir;
extern struct list_head proto_list;
extern char *proto2str[];
@@ -37,89 +42,192 @@ static void print_status(unsigned int status)
fprintf(stdout, "[UNREPLIED] ");
}
+static void parse_ip(struct nfattr *attr, struct ctnl_tuple *tuple)
+{
+ struct nfattr *tb[CTA_IP_MAX];
+
+ memset(tb, 0, CTA_IP_MAX * sizeof(struct nfattr *));
+
+ nfnl_parse_attr(tb, CTA_IP_MAX, NFA_DATA(attr), NFA_PAYLOAD(attr));
+ if (tb[CTA_IP_V4_SRC-1])
+ tuple->src.v4 = *(u_int32_t *)NFA_DATA(tb[CTA_IP_V4_SRC-1]);
+
+ if (tb[CTA_IP_V4_DST-1])
+ tuple->dst.v4 = *(u_int32_t *)NFA_DATA(tb[CTA_IP_V4_DST-1]);
+}
+
+static void parse_proto(struct nfattr *attr, struct ctnl_tuple *tuple)
+{
+ struct nfattr *tb[CTA_PROTO_MAX];
+ struct ctproto_handler *h;
+ int dir = CTNL_DIR_REPLY;
+
+ memset(tb, 0, CTA_PROTO_MAX * sizeof(struct nfattr *));
+
+ nfnl_parse_attr(tb, CTA_IP_MAX, NFA_DATA(attr), NFA_PAYLOAD(attr));
+ if (tb[CTA_PROTO_NUM-1])
+ tuple->protonum = *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_NUM-1]);
+
+ h = findproto(proto2str[tuple->protonum]);
+ if (h && h->parse_proto)
+ h->parse_proto(tb, tuple);
+}
+
+static void parse_tuple(struct nfattr *attr, struct ctnl_tuple *tuple)
+{
+ struct nfattr *tb[CTA_TUPLE_MAX];
+
+ memset(tb, 0, CTA_TUPLE_MAX*sizeof(struct nfattr *));
+
+ nfnl_parse_attr(tb, CTA_TUPLE_MAX, NFA_DATA(attr), NFA_PAYLOAD(attr));
+ if (tb[CTA_TUPLE_IP-1])
+ parse_ip(tb[CTA_TUPLE_IP-1], tuple);
+ if (tb[CTA_TUPLE_PROTO-1])
+ parse_proto(tb[CTA_TUPLE_PROTO-1], tuple);
+}
+
+static void parse_protoinfo(struct nfattr *attr, struct ctnl_conntrack *ct)
+{
+ struct nfattr *tb[CTA_PROTOINFO_MAX];
+ struct ctproto_handler *h;
+
+ memset(tb, 0, CTA_PROTOINFO_MAX*sizeof(struct nfattr *));
+
+ nfnl_parse_attr(tb,CTA_PROTOINFO_MAX,NFA_DATA(attr), NFA_PAYLOAD(attr));
+
+ h = findproto(proto2str[ct->tuple[CTNL_DIR_ORIGINAL].protonum]);
+ if (h && h->parse_protoinfo)
+ h->parse_protoinfo(tb, ct);
+}
+
+static void parse_counters(struct nfattr *attr, struct ctnl_conntrack *ct,
+ enum ctattr_type parent)
+{
+ struct nfattr *tb[CTA_COUNTERS_MAX];
+
+ memset(tb, 0, CTA_COUNTERS_MAX*sizeof(struct nfattr *));
+
+ nfnl_parse_attr(tb, CTA_COUNTERS_MAX, NFA_DATA(attr),NFA_PAYLOAD(attr));
+ if (tb[CTA_COUNTERS_PACKETS_ORIG-1])
+ ct->counters[CTNL_DIR_ORIGINAL].packets
+ = *(u_int64_t *)NFA_DATA(tb[CTA_COUNTERS_PACKETS_ORIG-1]);
+ if (tb[CTA_COUNTERS_BYTES_ORIG-1])
+ ct->counters[CTNL_DIR_ORIGINAL].bytes
+ = *(u_int64_t *)NFA_DATA(tb[CTA_COUNTERS_BYTES_ORIG-1]);
+ if (tb[CTA_COUNTERS_PACKETS_RPLY-1])
+ ct->counters[CTNL_DIR_REPLY].packets
+ = *(u_int64_t *)NFA_DATA(tb[CTA_COUNTERS_PACKETS_RPLY-1]);
+ if (tb[CTA_COUNTERS_BYTES_RPLY-1])
+ ct->counters[CTNL_DIR_REPLY].bytes
+ = *(u_int64_t *)NFA_DATA(tb[CTA_COUNTERS_BYTES_RPLY-1]);
+}
+
+#define STATUS 1
+#define PROTOINFO 2
+#define TIMEOUT 4
+#define MARK 8
+#define COUNTERS 16
+#define USE 32
+
static int handler(struct sockaddr_nl *sock, struct nlmsghdr *nlh, void *arg)
{
struct nfgenmsg *nfmsg;
struct nfattr *nfa;
- int min_len = 0;
+ int min_len = sizeof(struct nfgenmsg);;
struct ctproto_handler *h = NULL;
struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh));
int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
+ struct ctnl_conntrack ct;
+ unsigned int flags = 0;
- struct ip_conntrack_tuple *orig, *reply;
- struct cta_counters *ctr;
- unsigned long *status, *timeout;
- struct cta_proto *proto;
- unsigned long *id, *mark;
-
- DEBUGP("netlink header\n");
- DEBUGP("len: %d type: %d flags: %d seq: %d pid: %d\n",
- nlh->nlmsg_len, nlh->nlmsg_type, nlh->nlmsg_flags,
- nlh->nlmsg_seq, nlh->nlmsg_pid);
+ memset(&ct, 0, sizeof(struct ctnl_conntrack));
nfmsg = NLMSG_DATA(nlh);
- DEBUGP("nfmsg->nfgen_family: %d\n", nfmsg->nfgen_family);
+// min_len = sizeof(struct nfgenmsg);
- min_len = sizeof(struct nfgenmsg);
if (nlh->nlmsg_len < min_len)
return -EINVAL;
- DEBUGP("size:%d\n", nlh->nlmsg_len);
-
while (NFA_OK(attr, attrlen)) {
switch(attr->nfa_type) {
- case CTA_ORIG:
- orig = NFA_DATA(attr);
- fprintf(stdout, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
- NIPQUAD(orig->src.ip),
- NIPQUAD(orig->dst.ip));
- h = findproto(proto2str[orig->dst.protonum]);
- if (h && h->print_tuple)
- h->print_tuple(orig);
+ case CTA_TUPLE_ORIG:
+ parse_tuple(attr, &ct.tuple[CTNL_DIR_ORIGINAL]);
break;
- case CTA_RPLY:
- reply = NFA_DATA(attr);
- fprintf(stdout, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
- NIPQUAD(reply->src.ip),
- NIPQUAD(reply->dst.ip));
- h = findproto(proto2str[reply->dst.protonum]);
- if (h && h->print_tuple)
- h->print_tuple(reply);
+ case CTA_TUPLE_RPLY:
+ parse_tuple(attr, &ct.tuple[CTNL_DIR_REPLY]);
break;
case CTA_STATUS:
- status = NFA_DATA(attr);
- print_status(*status);
+ ct.status = *(unsigned int *)NFA_DATA(attr);
+ flags |= STATUS;
break;
case CTA_PROTOINFO:
- proto = NFA_DATA(attr);
- if (proto2str[proto->num_proto]) {
- fprintf(stdout, "%s %d ", proto2str[proto->num_proto], proto->num_proto);
- h = findproto(proto2str[proto->num_proto]);
- if (h && h->print_proto)
- h->print_proto(&proto->proto);
- } else
- fprintf(stdout, "unknown %d ", proto->num_proto);
+ parse_protoinfo(attr, &ct);
+ flags |= PROTOINFO;
break;
case CTA_TIMEOUT:
- timeout = NFA_DATA(attr);
- fprintf(stdout, "timeout=%lu ", *timeout);
+ ct.timeout = *(unsigned long *)NFA_DATA(attr);
+ flags |= TIMEOUT;
break;
case CTA_MARK:
- mark = NFA_DATA(attr);
- fprintf(stdout, "mark=%lu ", *mark);
+ ct.mark = *(unsigned long *)NFA_DATA(attr);
+ flags |= MARK;
break;
case CTA_COUNTERS:
- ctr = NFA_DATA(attr);
- fprintf(stdout, "orig_packets=%llu orig_bytes=%llu, "
- "reply_packets=%llu reply_bytes=%llu ",
- ctr->orig.packets, ctr->orig.bytes,
- ctr->reply.packets, ctr->reply.bytes);
+ parse_counters(attr, &ct, CTA_COUNTERS-1);
+ flags |= COUNTERS;
+ break;
+ case CTA_USE:
+ ct.use = *(unsigned int *)NFA_DATA(attr);
+ flags |= USE;
break;
}
- DEBUGP("nfa->nfa_type: %d\n", attr->nfa_type);
- DEBUGP("nfa->nfa_len: %d\n", attr->nfa_len);
attr = NFA_NEXT(attr, attrlen);
}
+
+ fprintf(stdout, "%-8s %u ",
+ proto2str[ct.tuple[CTNL_DIR_ORIGINAL].protonum] == NULL ?
+ "unknown" : proto2str[ct.tuple[CTNL_DIR_ORIGINAL].protonum],
+ ct.tuple[CTNL_DIR_ORIGINAL].protonum);
+
+ if (flags & TIMEOUT)
+ fprintf(stdout, "%lu ", ct.timeout);
+
+ h = findproto(proto2str[ct.tuple[CTNL_DIR_ORIGINAL].protonum]);
+ if ((flags & PROTOINFO) && h && h->print_protoinfo)
+ h->print_protoinfo(&ct.protoinfo);
+
+ fprintf(stdout, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
+ NIPQUAD(ct.tuple[CTNL_DIR_ORIGINAL].src.v4),
+ NIPQUAD(ct.tuple[CTNL_DIR_ORIGINAL].dst.v4));
+
+ if (h && h->print_proto)
+ h->print_proto(&ct.tuple[CTNL_DIR_ORIGINAL]);
+
+ if (flags & COUNTERS)
+ fprintf(stdout, "packets=%llu bytes=%llu ",
+ ct.counters[CTNL_DIR_ORIGINAL].packets,
+ ct.counters[CTNL_DIR_ORIGINAL].bytes);
+
+ fprintf(stdout, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
+ NIPQUAD(ct.tuple[CTNL_DIR_REPLY].src.v4),
+ NIPQUAD(ct.tuple[CTNL_DIR_REPLY].dst.v4));
+
+ h = findproto(proto2str[ct.tuple[CTNL_DIR_ORIGINAL].protonum]);
+ if (h && h->print_proto)
+ h->print_proto(&ct.tuple[CTNL_DIR_REPLY]);
+
+ if (flags & COUNTERS)
+ fprintf(stdout, "packets=%llu bytes=%llu ",
+ ct.counters[CTNL_DIR_REPLY].packets,
+ ct.counters[CTNL_DIR_REPLY].bytes);
+
+ print_status(ct.status);
+
+ if (flags & MARK)
+ fprintf(stdout, "mark=%lu ", ct.mark);
+ if (flags & USE)
+ fprintf(stdout, "use=%u ", ct.use);
+
fprintf(stdout, "\n");
return 0;
@@ -148,140 +256,133 @@ static int event_handler(struct sockaddr_nl *sock, struct nlmsghdr *nlh,
return handler(sock, nlh, arg);
}
+void parse_expect(struct nfattr *attr, struct ctnl_tuple *tuple,
+ struct ctnl_tuple *mask, unsigned long *timeout)
+{
+ struct nfattr *tb[CTA_EXPECT_MAX];
+
+ memset(tb, 0, CTA_EXPECT_MAX*sizeof(struct nfattr *));
+
+ nfnl_parse_attr(tb, CTA_EXPECT_MAX, NFA_DATA(attr), NFA_PAYLOAD(attr));
+ if (tb[CTA_EXPECT_TUPLE-1])
+ parse_tuple(tb[CTA_EXPECT_TUPLE-1], tuple);
+ if (tb[CTA_EXPECT_MASK-1])
+ parse_tuple(tb[CTA_EXPECT_MASK-1], mask);
+ if (tb[CTA_EXPECT_TIMEOUT-1])
+ *timeout = *(unsigned long *)NFA_DATA(tb[CTA_EXPECT_TIMEOUT-1]);
+}
+
static int expect_handler(struct sockaddr_nl *sock, struct nlmsghdr *nlh, void *arg)
{
struct nfgenmsg *nfmsg;
struct nfattr *nfa;
- int min_len = 0;
+ int min_len = sizeof(struct nfgenmsg);;
struct ctproto_handler *h = NULL;
struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh));
int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
+ struct ctnl_tuple tuple, mask;
+ unsigned long timeout = 0;
- struct ip_conntrack_tuple *exp, *mask;
- unsigned long *timeout;
-
- DEBUGP("netlink header\n");
- DEBUGP("len: %d type: %d flags: %d seq: %d pid: %d\n",
- nlh->nlmsg_len, nlh->nlmsg_type, nlh->nlmsg_flags,
- nlh->nlmsg_seq, nlh->nlmsg_pid);
+ memset(&tuple, 0, sizeof(struct ctnl_tuple));
+ memset(&mask, 0, sizeof(struct ctnl_tuple));
nfmsg = NLMSG_DATA(nlh);
- DEBUGP("nfmsg->nfgen_family: %d\n", nfmsg->nfgen_family);
- min_len = sizeof(struct nfgenmsg);
if (nlh->nlmsg_len < min_len)
return -EINVAL;
- DEBUGP("size:%d\n", nlh->nlmsg_len);
-
while (NFA_OK(attr, attrlen)) {
switch(attr->nfa_type) {
- case CTA_EXP_TUPLE:
- exp = NFA_DATA(attr);
- fprintf(stdout, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
- NIPQUAD(exp->src.ip),
- NIPQUAD(exp->dst.ip));
- h = findproto(proto2str[exp->dst.protonum]);
- if (h && h->print_tuple)
- h->print_tuple(exp);
- break;
- case CTA_EXP_MASK:
- mask = NFA_DATA(attr);
- fprintf(stdout, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
- NIPQUAD(mask->src.ip),
- NIPQUAD(mask->dst.ip));
- h = findproto(proto2str[mask->dst.protonum]);
- if (h && h->print_tuple)
- h->print_tuple(mask);
- break;
- case CTA_EXP_TIMEOUT:
- timeout = NFA_DATA(attr);
- fprintf(stdout, "timeout:%lu ", *timeout);
- break;
+ case CTA_EXPECT:
+ parse_expect(attr, &tuple, &mask, &timeout);
+ break;
}
- DEBUGP("nfa->nfa_type: %d\n", attr->nfa_type);
- DEBUGP("nfa->nfa_len: %d\n", attr->nfa_len);
attr = NFA_NEXT(attr, attrlen);
}
- fprintf(stdout, "\n");
+ fprintf(stdout, "%ld proto=%d ", timeout, tuple.protonum);
+
+ fprintf(stdout, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
+ NIPQUAD(tuple.src.v4),
+ NIPQUAD(tuple.dst.v4));
+
+ fprintf(stdout, "src=%u.%u.%u.%u dst=%u.%u.%u.%u\n",
+ NIPQUAD(mask.src.v4),
+ NIPQUAD(mask.dst.v4));
return 0;
}
-int create_conntrack(struct ip_conntrack_tuple *orig,
- struct ip_conntrack_tuple *reply,
+int create_conntrack(struct ctnl_tuple *orig,
+ struct ctnl_tuple *reply,
unsigned long timeout,
- union ip_conntrack_proto *proto,
- unsigned int status)
+ union ctnl_protoinfo *proto,
+ unsigned int status,
+ struct ctnl_nat *range)
{
- struct cta_proto cta;
- struct nfattr *cda[CTA_MAX];
- struct ctnl_handle cth;
+ struct ctnl_conntrack ct;
int ret;
+
+ memset(&ct, 0, sizeof(struct ctnl_conntrack));
+ ct.tuple[CTNL_DIR_ORIGINAL] = *orig;
+ ct.tuple[CTNL_DIR_REPLY] = *reply;
+ ct.timeout = timeout;
+ ct.status = status;
+ ct.protoinfo = *proto;
+ if (range)
+ ct.nat = *range;
- cta.num_proto = orig->dst.protonum;
- memcpy(&cta.proto, proto, sizeof(*proto));
if ((ret = ctnl_open(&cth, 0)) < 0)
return ret;
- if ((ret = ctnl_new_conntrack(&cth, orig, reply, timeout, &cta,
- status)) < 0)
- return ret;
+ ret = ctnl_new_conntrack(&cth, &ct);
- if ((ret = ctnl_close(&cth)) < 0)
- return ret;
+ ctnl_close(&cth);
- return 0;
+ return ret;
}
-int create_expect(struct ip_conntrack_tuple *tuple,
- struct ip_conntrack_tuple *mask,
- struct ip_conntrack_tuple *master_tuple_orig,
- struct ip_conntrack_tuple *master_tuple_reply,
- unsigned long timeout)
+int update_conntrack(struct ctnl_tuple *orig,
+ struct ctnl_tuple *reply,
+ unsigned long timeout,
+ union ctnl_protoinfo *proto,
+ unsigned int status)
{
- struct ctnl_handle cth;
+ struct ctnl_conntrack ct;
int ret;
+ memset(&ct, 0, sizeof(struct ctnl_conntrack));
+ ct.tuple[CTNL_DIR_ORIGINAL] = *orig;
+ ct.tuple[CTNL_DIR_REPLY] = *reply;
+ ct.timeout = timeout;
+ ct.status = status;
+ ct.protoinfo = *proto;
+
if ((ret = ctnl_open(&cth, 0)) < 0)
return ret;
- if ((ret = ctnl_new_expect(&cth, tuple, mask, master_tuple_orig,
- master_tuple_reply, timeout)) < 0)
- return ret;
-
- if ((ret = ctnl_close(&cth)) < 0)
- return ret;
+ ret = ctnl_upd_conntrack(&cth, &ct);
- return -1;
+ ctnl_close(&cth);
+
+ return ret;
}
-int delete_conntrack(struct ip_conntrack_tuple *tuple,
- enum ctattr_type_t t)
+int delete_conntrack(struct ctnl_tuple *tuple, int dir)
{
- struct nfattr *cda[CTA_MAX];
- struct ctnl_handle cth;
int ret;
if ((ret = ctnl_open(&cth, 0)) < 0)
return ret;
- if ((ret = ctnl_del_conntrack(&cth, tuple, t)) < 0)
- return ret;
-
- if ((ret = ctnl_close(&cth)) < 0)
- return ret;
+ ret = ctnl_del_conntrack(&cth, tuple, dir);
+ ctnl_close(&cth);
- return 0;
+ return ret;
}
/* get_conntrack_handler */
-int get_conntrack(struct ip_conntrack_tuple *tuple,
- enum ctattr_type_t t,
- unsigned long id)
+int get_conntrack(struct ctnl_tuple *tuple, int dir)
{
- struct nfattr *cda[CTA_MAX];
- struct ctnl_handle cth;
struct ctnl_msg_handler h = {
.type = 0,
.handler = handler
@@ -293,22 +394,17 @@ int get_conntrack(struct ip_conntrack_tuple *tuple,
ctnl_register_handler(&cth, &h);
- /* FIXME!!!! get_conntrack_handler returns -100 */
- if ((ret = ctnl_get_conntrack(&cth, tuple, t)) != -100)
- return ret;
-
- if ((ret = ctnl_close(&cth)) < 0)
- return ret;
+ ret = ctnl_get_conntrack(&cth, tuple, dir);
+ ctnl_close(&cth);
- return 0;
+ return ret;
}
int dump_conntrack_table(int zero)
{
int ret;
- struct ctnl_handle cth;
struct ctnl_msg_handler h = {
- .type = 0, /* Hm... really? */
+ .type = IPCTNL_MSG_CT_NEW, /* Hm... really? */
.handler = handler
};
@@ -322,38 +418,37 @@ int dump_conntrack_table(int zero)
} else
ret = ctnl_list_conntrack(&cth, AF_INET);
- if (ret != -100)
- return ret;
+ ctnl_close(&cth);
- if ((ret = ctnl_close(&cth)) < 0)
- return ret;
+ return ret;
+}
- return 0;
+static void event_sighandler(int s)
+{
+ fprintf(stdout, "Now closing conntrack event dumping...\n");
+ ctnl_close(&cth);
}
int event_conntrack(unsigned int event_mask)
{
- struct ctnl_handle cth;
struct ctnl_msg_handler hnew = {
- .type = 0, /* new */
+ .type = IPCTNL_MSG_CT_NEW,
.handler = event_handler
};
struct ctnl_msg_handler hdestroy = {
- .type = 2, /* destroy */
+ .type = IPCTNL_MSG_CT_DELETE,
.handler = event_handler
};
int ret;
-
+
if ((ret = ctnl_open(&cth, event_mask)) < 0)
return ret;
+ signal(SIGINT, event_sighandler);
ctnl_register_handler(&cth, &hnew);
ctnl_register_handler(&cth, &hdestroy);
- if ((ret = ctnl_event_conntrack(&cth, AF_INET)) < 0)
- return ret;
-
- if ((ret = ctnl_close(&cth)) < 0)
- return ret;
+ ret = ctnl_event_conntrack(&cth, AF_INET);
+ ctnl_close(&cth);
return 0;
}
@@ -404,9 +499,8 @@ void unregister_proto(struct ctproto_handler *h)
int dump_expect_list()
{
- struct ctnl_handle cth;
struct ctnl_msg_handler h = {
- .type = 5, /* Hm... really? */
+ .type = IPCTNL_MSG_EXP_NEW,
.handler = expect_handler
};
int ret;
@@ -416,58 +510,114 @@ int dump_expect_list()
ctnl_register_handler(&cth, &h);
- if ((ret = ctnl_list_expect(&cth, AF_INET)) != -100)
- return ret;
+ ret = ctnl_list_expect(&cth, AF_INET);
+ ctnl_close(&cth);
+
+ return ret;
+}
- if ((ret = ctnl_close(&cth)) < 0)
+int flush_conntrack()
+{
+ int ret;
+
+ if ((ret = ctnl_open(&cth, 0)) < 0)
return ret;
- return 0;
+ ret = ctnl_flush_conntrack(&cth);
+ ctnl_close(&cth);
+
+ return ret;
}
-int set_mask(unsigned int mask, int type)
+int get_expect(struct ctnl_tuple *tuple,
+ enum ctattr_type t)
{
- struct ctnl_handle cth;
- enum ctattr_type_t cta_type;
+ /*
+ struct ctnl_msg_handler h = {
+ .type = IPCTNL_MSG_EXP_NEW,
+ .handler = expect_handler
+ };
int ret;
- switch(type) {
- case 0:
- cta_type = CTA_DUMPMASK;
- break;
- case 1:
- cta_type = CTA_EVENTMASK;
- break;
- default:
- /* Shouldn't happen */
- return -1;
- }
+ if ((ret = ctnl_open(&cth, 0)) < 0)
+ return 0;
+
+ ctnl_register_handler(&cth, &h);
+
+ ret = ctnl_get_expect(&cth, tuple, t);
+ ctnl_close(&cth);
+
+ return ret;
+ */
+}
+int create_expectation(struct ctnl_tuple *tuple,
+ enum ctattr_type t,
+ struct ctnl_tuple *exptuple,
+ struct ctnl_tuple *mask,
+ unsigned long timeout)
+{
+ /*
+ int ret;
+
if ((ret = ctnl_open(&cth, 0)) < 0)
return ret;
+
+ ret = ctnl_new_expect(&cth, tuple, t, exptuple, mask, timeout);
+ ctnl_close(&cth);
+
+ return ret;
+ */
+}
+
+int delete_expectation(struct ctnl_tuple *tuple, enum ctattr_type t)
+{
+ /*
+ int ret;
- if ((ret = ctnl_set_mask(&cth, mask, cta_type)) < 0)
+ if ((ret = ctnl_open(&cth, 0)) < 0)
return ret;
+
+ ret = ctnl_del_expect(&cth, tuple, t);
+ ctnl_close(&cth);
+
+ return ret;
+ */
+}
+
+int event_expectation(unsigned int event_mask)
+{
+ struct ctnl_msg_handler hnew = {
+ .type = IPCTNL_MSG_EXP_NEW,
+ .handler = expect_handler
+ };
+ struct ctnl_msg_handler hdestroy = {
+ .type = IPCTNL_MSG_EXP_DELETE,
+ .handler = expect_handler
+ };
+ int ret;
- if ((ret = ctnl_close(&cth)) < 0)
+ if ((ret = ctnl_open(&cth, event_mask)) < 0)
return ret;
- return 0;
+ ctnl_register_handler(&cth, &hnew);
+ ctnl_register_handler(&cth, &hdestroy);
+ ret = ctnl_event_expect(&cth, AF_INET);
+ ctnl_close(&cth);
+
+ return ret;
}
-int flush_conntrack()
+int flush_expectation()
{
- struct ctnl_handle cth;
int ret;
if ((ret = ctnl_open(&cth, 0)) < 0)
return ret;
- if ((ret = ctnl_flush_conntrack(&cth)) < 0)
- return ret;
+ ret = ctnl_flush_expect(&cth);
+ ctnl_close(&cth);
- if ((ret = ctnl_close(&cth)) < 0)
- return ret;
-
- return 0;
+ return ret;
}
+
diff --git a/test.sh b/test.sh
index 5999a8f..08c840f 100644
--- a/test.sh
+++ b/test.sh
@@ -2,27 +2,17 @@ CONNTRACK=conntrack
SRC=1.1.1.1
DST=2.2.2.2
-SPORT=1980
-DPORT=2005
+SPORT=2005
+DPORT=21
case $1 in
dump)
- # Setting dump mask
- echo "dump mask set to TUPLE"
- $CONNTRACK -A -m TUPLE
+ echo "Dumping conntrack table"
$CONNTRACK -L
- echo "Press any key to continue..."
- read
- echo "dump mask set to TUPLE,COUNTERS"
- $CONNTRACK -A -m TUPLE,COUNTERS
- $CONNTRACK -L
- echo "Press any key to continue..."
- read
- echo "dump mask set to ALL"
- $CONNTRACK -A -m ALL
- $CONNTRACK -L
- echo "Press any key to continue..."
- read
+ ;;
+ flush)
+ echo "Flushing conntrack table"
+ $CONNTRACK -F
;;
new)
echo "creating a new conntrack"
@@ -32,6 +22,18 @@ case $1 in
--reply-port-src $DPORT --reply-port-dst $SPORT \
--state LISTEN -u SEEN_REPLY -t 50
;;
+ new-simple)
+ echo "creating a new conntrack (simplified)"
+ $CONNTRACK -I --orig-src $SRC --orig-dst $DST \
+ -p tcp --orig-port-src $SPORT --orig-port-dst $DPORT \
+ --state LISTEN -u SEEN_REPLY -t 50
+ ;;
+ new-nat)
+ echo "creating a new conntrack (NAT)"
+ $CONNTRACK -I --orig-src $SRC --orig-dst $DST \
+ -p tcp --orig-port-src $SPORT --orig-port-dst $DPORT \
+ --state LISTEN -u SEEN_REPLY,SRC_NAT -t 50 -a 8.8.8.8
+ ;;
get)
echo "getting a conntrack"
$CONNTRACK -G --orig-src $SRC --orig-dst $DST \
@@ -40,7 +42,7 @@ case $1 in
;;
change)
echo "change a conntrack"
- $CONNTRACK -I --orig-src $SRC --orig-dst $DST \
+ $CONNTRACK -U --orig-src $SRC --orig-dst $DST \
--reply-src $DST --reply-dst $SRC -p tcp \
--orig-port-src $SPORT --orig-port-dst $DPORT \
--reply-port-src $DPORT --reply-port-dst $SPORT \
@@ -64,7 +66,44 @@ case $1 in
fi
fi
;;
+ dump-expect)
+ $CONNTRACK -L expect
+ ;;
+ flush-expect)
+ $CONNTRACK -F expect
+ ;;
+ create-expect)
+ # requires modprobe ip_conntrack_ftp
+ $CONNTRACK -I expect --orig-src $SRC --orig-dst $DST \
+ --exp-src 4.4.4.4 --exp-dst 5.5.5.5 \
+ --mask-src 255.255.255.0 --mask-dst 255.255.255.255 \
+ -p tcp --orig-port-src $SPORT --orig-port-dst $DPORT \
+ -t 200 --mask-port-src 10 --mask-port-dst 300
+ ;;
+ get-expect)
+ $CONNTRACK -G expect --orig-src 4.4.4.4 --orig-dst 5.5.5.5 \
+ --p tcp --orig-port-src 0 --orig-port-dst 0 \
+ --mask-port-src 10 --mask-port-dst 11
+ ;;
+ delete-expect)
+ $CONNTRACK -D expect --orig-src 4.4.4.4 \
+ --orig-dst 5.5.5.5 -p tcp --orig-port-src 0 \
+ --orig-port-dst 0 --mask-port-src 10 --mask-port-dst 11
+ ;;
*)
- echo "Usage: $0 [dump|new|change|delete|output]"
+ echo "Usage: $0 [dump"
+ echo " |new"
+ echo " |new-simple"
+ echo " |new-nat"
+ echo " |get"
+ echo " |change"
+ echo " |delete"
+ echo " |output"
+ echo " |flush"
+ echo " |dump-expect"
+ echo " |flush-expect"
+ echo " |create-expect"
+ echo " |get-expect"
+ echo " |delete-expect]"
;;
esac