summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMikhail Sennikovsky <mikhail.sennikovskii@cloud.ionos.com>2020-10-29 12:51:55 +0100
committerPablo Neira Ayuso <pablo@netfilter.org>2020-11-02 14:45:19 +0100
commit1c596b9ec8f26ee5e044e033509e656e8376a395 (patch)
tree4d55a85ca61484b2306e8810451978962b54667f
parentb07644aac48467adab45a514eea10d6ce2fd44db (diff)
conntrack: implement save output format
This commit allows dumping conntrack entries in the format used by the conntrack parameters, aka "save" output format. This is useful for saving ct entry data to allow applying it later on. To enable the "save" output the "-o save" parameter needs to be passed to the conntrack tool invocation. [ pablo@netfilter.org: several updates to the original patch ] Signed-off-by: Mikhail Sennikovsky <mikhail.sennikovskii@cloud.ionos.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r--extensions/libct_proto_dccp.c17
-rw-r--r--extensions/libct_proto_gre.c9
-rw-r--r--extensions/libct_proto_icmp.c8
-rw-r--r--extensions/libct_proto_icmpv6.c8
-rw-r--r--extensions/libct_proto_sctp.c12
-rw-r--r--extensions/libct_proto_tcp.c10
-rw-r--r--extensions/libct_proto_udp.c9
-rw-r--r--extensions/libct_proto_udplite.c9
-rw-r--r--include/conntrack.h30
-rw-r--r--src/conntrack.c283
10 files changed, 391 insertions, 4 deletions
diff --git a/extensions/libct_proto_dccp.c b/extensions/libct_proto_dccp.c
index f6258ad..e9da474 100644
--- a/extensions/libct_proto_dccp.c
+++ b/extensions/libct_proto_dccp.c
@@ -198,6 +198,22 @@ static int parse_options(char c,
return 1;
}
+
+static const char *dccp_roles[__DCCP_CONNTRACK_ROLE_MAX] = {
+ [DCCP_CONNTRACK_ROLE_CLIENT] = "client",
+ [DCCP_CONNTRACK_ROLE_SERVER] = "server",
+};
+
+static const struct ct_print_opts dccp_print_opts[] = {
+ { "--sport", ATTR_ORIG_PORT_SRC, CT_ATTR_TYPE_BE16, 0, NULL },
+ { "--dport", ATTR_ORIG_PORT_DST, CT_ATTR_TYPE_BE16, 0, NULL },
+ { "--reply-port-src", ATTR_REPL_PORT_SRC, CT_ATTR_TYPE_BE16, 0, NULL },
+ { "--reply-port-dst", ATTR_REPL_PORT_DST, CT_ATTR_TYPE_BE16, 0, NULL },
+ { "--state", ATTR_DCCP_STATE, CT_ATTR_TYPE_U8, DCCP_CONNTRACK_MAX, dccp_states },
+ { "--role", ATTR_DCCP_ROLE, CT_ATTR_TYPE_U8, __DCCP_CONNTRACK_ROLE_MAX, dccp_roles },
+ {},
+};
+
#define DCCP_VALID_FLAGS_MAX 2
static unsigned int dccp_valid_flags[DCCP_VALID_FLAGS_MAX] = {
CT_DCCP_ORIG_SPORT | CT_DCCP_ORIG_DPORT,
@@ -235,6 +251,7 @@ static struct ctproto_handler dccp = {
.protonum = IPPROTO_DCCP,
.parse_opts = parse_options,
.final_check = final_check,
+ .print_opts = dccp_print_opts,
.help = help,
.opts = opts,
.version = VERSION,
diff --git a/extensions/libct_proto_gre.c b/extensions/libct_proto_gre.c
index 2dc63d1..a36d111 100644
--- a/extensions/libct_proto_gre.c
+++ b/extensions/libct_proto_gre.c
@@ -144,6 +144,14 @@ static int parse_options(char c,
return 1;
}
+static const struct ct_print_opts gre_print_opts[] = {
+ { "--srckey", ATTR_ORIG_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--dstkey", ATTR_ORIG_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--reply-key-src", ATTR_REPL_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--reply-key-dst", ATTR_REPL_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0 },
+ {},
+};
+
#define GRE_VALID_FLAGS_MAX 2
static unsigned int gre_valid_flags[GRE_VALID_FLAGS_MAX] = {
CT_GRE_ORIG_SKEY | CT_GRE_ORIG_DKEY,
@@ -181,6 +189,7 @@ static struct ctproto_handler gre = {
.protonum = IPPROTO_GRE,
.parse_opts = parse_options,
.final_check = final_check,
+ .print_opts = gre_print_opts,
.help = help,
.opts = opts,
.version = VERSION,
diff --git a/extensions/libct_proto_icmp.c b/extensions/libct_proto_icmp.c
index 7fc82bd..ec52c39 100644
--- a/extensions/libct_proto_icmp.c
+++ b/extensions/libct_proto_icmp.c
@@ -102,6 +102,13 @@ static int parse(char c,
return 1;
}
+static const struct ct_print_opts icmp_print_opts[] = {
+ { "--icmp-type", ATTR_ICMP_TYPE, CT_ATTR_TYPE_U8, 0, 0 },
+ { "--icmp-code", ATTR_ICMP_CODE, CT_ATTR_TYPE_U8, 0, 0 },
+ { "--icmp-id", ATTR_ICMP_ID, CT_ATTR_TYPE_BE16, 0, 0 },
+ {}
+};
+
static void final_check(unsigned int flags,
unsigned int cmd,
struct nf_conntrack *ct)
@@ -117,6 +124,7 @@ static struct ctproto_handler icmp = {
.protonum = IPPROTO_ICMP,
.parse_opts = parse,
.final_check = final_check,
+ .print_opts = icmp_print_opts,
.help = help,
.opts = opts,
.version = VERSION,
diff --git a/extensions/libct_proto_icmpv6.c b/extensions/libct_proto_icmpv6.c
index f872c23..fe16a1d 100644
--- a/extensions/libct_proto_icmpv6.c
+++ b/extensions/libct_proto_icmpv6.c
@@ -105,6 +105,13 @@ static int parse(char c,
return 1;
}
+static const struct ct_print_opts icmpv6_print_opts[] = {
+ {"--icmpv6-type", ATTR_ICMP_TYPE, CT_ATTR_TYPE_U8, 0, 0},
+ {"--icmpv6-code", ATTR_ICMP_CODE, CT_ATTR_TYPE_U8, 0, 0},
+ {"--icmpv6-id", ATTR_ICMP_ID, CT_ATTR_TYPE_BE16, 0, 0},
+ {},
+};
+
static void final_check(unsigned int flags,
unsigned int cmd,
struct nf_conntrack *ct)
@@ -119,6 +126,7 @@ static struct ctproto_handler icmpv6 = {
.protonum = IPPROTO_ICMPV6,
.parse_opts = parse,
.final_check = final_check,
+ .print_opts = icmpv6_print_opts,
.help = help,
.opts = opts,
.version = VERSION,
diff --git a/extensions/libct_proto_sctp.c b/extensions/libct_proto_sctp.c
index 04828bf..a58ccde 100644
--- a/extensions/libct_proto_sctp.c
+++ b/extensions/libct_proto_sctp.c
@@ -198,6 +198,17 @@ parse_options(char c, struct nf_conntrack *ct,
return 1;
}
+static const struct ct_print_opts sctp_print_opts[] = {
+ { "--sport", ATTR_ORIG_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--dport", ATTR_ORIG_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--reply-port-src", ATTR_REPL_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--reply-port-dst", ATTR_REPL_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--state", ATTR_SCTP_STATE, CT_ATTR_TYPE_U8, SCTP_CONNTRACK_MAX, sctp_states },
+ { "--orig-vtag", ATTR_SCTP_VTAG_ORIG, CT_ATTR_TYPE_BE32, 0, 0 },
+ { "--reply-vtag", ATTR_SCTP_VTAG_REPL, CT_ATTR_TYPE_BE32, 0, 0 },
+ {},
+};
+
#define SCTP_VALID_FLAGS_MAX 2
static unsigned int dccp_valid_flags[SCTP_VALID_FLAGS_MAX] = {
CT_SCTP_ORIG_SPORT | CT_SCTP_ORIG_DPORT,
@@ -235,6 +246,7 @@ static struct ctproto_handler sctp = {
.protonum = IPPROTO_SCTP,
.parse_opts = parse_options,
.final_check = final_check,
+ .print_opts = sctp_print_opts,
.help = help,
.opts = opts,
.version = VERSION,
diff --git a/extensions/libct_proto_tcp.c b/extensions/libct_proto_tcp.c
index 8a37a55..3da0dc6 100644
--- a/extensions/libct_proto_tcp.c
+++ b/extensions/libct_proto_tcp.c
@@ -177,6 +177,15 @@ static int parse_options(char c,
return 1;
}
+static const struct ct_print_opts tcp_print_opts[] = {
+ { "--sport", ATTR_ORIG_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--dport", ATTR_ORIG_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--reply-port-src", ATTR_REPL_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--reply-port-dst", ATTR_REPL_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--state", ATTR_TCP_STATE, CT_ATTR_TYPE_U8, TCP_CONNTRACK_MAX, tcp_states },
+ {},
+};
+
#define TCP_VALID_FLAGS_MAX 2
static unsigned int tcp_valid_flags[TCP_VALID_FLAGS_MAX] = {
CT_TCP_ORIG_SPORT | CT_TCP_ORIG_DPORT,
@@ -228,6 +237,7 @@ static struct ctproto_handler tcp = {
.protonum = IPPROTO_TCP,
.parse_opts = parse_options,
.final_check = final_check,
+ .print_opts = tcp_print_opts,
.help = help,
.opts = opts,
.version = VERSION,
diff --git a/extensions/libct_proto_udp.c b/extensions/libct_proto_udp.c
index e30637c..fe43548 100644
--- a/extensions/libct_proto_udp.c
+++ b/extensions/libct_proto_udp.c
@@ -144,6 +144,14 @@ static int parse_options(char c,
return 1;
}
+static const struct ct_print_opts udp_print_opts[] = {
+ {"--sport", ATTR_ORIG_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0},
+ {"--dport", ATTR_ORIG_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0},
+ {"--reply-port-src", ATTR_REPL_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0},
+ {"--reply-port-dst", ATTR_REPL_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0},
+ {},
+};
+
#define UDP_VALID_FLAGS_MAX 2
static unsigned int udp_valid_flags[UDP_VALID_FLAGS_MAX] = {
CT_UDP_ORIG_SPORT | CT_UDP_ORIG_DPORT,
@@ -181,6 +189,7 @@ static struct ctproto_handler udp = {
.protonum = IPPROTO_UDP,
.parse_opts = parse_options,
.final_check = final_check,
+ .print_opts = udp_print_opts,
.help = help,
.opts = opts,
.version = VERSION,
diff --git a/extensions/libct_proto_udplite.c b/extensions/libct_proto_udplite.c
index f46cef0..2bece38 100644
--- a/extensions/libct_proto_udplite.c
+++ b/extensions/libct_proto_udplite.c
@@ -148,6 +148,14 @@ static int parse_options(char c,
return 1;
}
+static const struct ct_print_opts udplite_print_opts[] = {
+ { "--sport", ATTR_ORIG_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--dport", ATTR_ORIG_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--reply-port-src", ATTR_REPL_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--reply-port-dst", ATTR_REPL_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0 },
+ {},
+};
+
#define UDPLITE_VALID_FLAGS_MAX 2
static unsigned int udplite_valid_flags[UDPLITE_VALID_FLAGS_MAX] = {
CT_UDPLITE_ORIG_SPORT | CT_UDPLITE_ORIG_DPORT,
@@ -186,6 +194,7 @@ static struct ctproto_handler udplite = {
.protonum = IPPROTO_UDPLITE,
.parse_opts = parse_options,
.final_check = final_check,
+ .print_opts = udplite_print_opts,
.help = help,
.opts = opts,
.version = VERSION,
diff --git a/include/conntrack.h b/include/conntrack.h
index 37ccf6e..1c1720e 100644
--- a/include/conntrack.h
+++ b/include/conntrack.h
@@ -8,6 +8,9 @@
#include <netinet/in.h>
+#include <linux/netfilter/nf_conntrack_common.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+
#define NUMBER_OF_CMD 19
#define NUMBER_OF_OPT 29
@@ -32,6 +35,8 @@ struct ctproto_handler {
unsigned int command,
struct nf_conntrack *ct);
+ const struct ct_print_opts *print_opts;
+
void (*help)(void);
struct option *opts;
@@ -53,6 +58,31 @@ void exit_error(enum exittype status, const char *msg, ...);
extern void register_proto(struct ctproto_handler *h);
+enum ct_attr_type {
+ CT_ATTR_TYPE_NONE = 0,
+ CT_ATTR_TYPE_U8,
+ CT_ATTR_TYPE_BE16,
+ CT_ATTR_TYPE_U16,
+ CT_ATTR_TYPE_BE32,
+ CT_ATTR_TYPE_U32,
+ CT_ATTR_TYPE_U64,
+ CT_ATTR_TYPE_U32_BITMAP,
+ CT_ATTR_TYPE_IPV4,
+ CT_ATTR_TYPE_IPV6,
+};
+
+struct ct_print_opts {
+ const char *name;
+ enum nf_conntrack_attr type;
+ enum ct_attr_type datatype;
+ short val_mapping_count;
+ const char **val_mapping;
+};
+
+extern int ct_snprintf_opts(char *buf, unsigned int len,
+ const struct nf_conntrack *ct,
+ const struct ct_print_opts *attrs);
+
extern void register_tcp(void);
extern void register_udp(void);
extern void register_udplite(void);
diff --git a/src/conntrack.c b/src/conntrack.c
index 6e8363e..d05a599 100644
--- a/src/conntrack.c
+++ b/src/conntrack.c
@@ -54,6 +54,7 @@
#include <sys/socket.h>
#include <sys/time.h>
#include <time.h>
+#include <inttypes.h>
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
@@ -608,6 +609,252 @@ void register_proto(struct ctproto_handler *h)
list_add(&h->head, &proto_list);
}
+#define BUFFER_SIZE(ret, size, len, offset) do { \
+ if ((int)ret > (int)len) \
+ ret = len; \
+ size += ret; \
+ offset += ret; \
+ len -= ret; \
+} while(0)
+
+static int ct_snprintf_u32_bitmap(char *buf, size_t size,
+ const struct nf_conntrack *ct,
+ const struct ct_print_opts *attr)
+{
+ unsigned int offset = 0, ret, len = size;
+ bool found = false;
+ uint32_t val;
+ int i;
+
+ val = nfct_get_attr_u32(ct, attr->type);
+
+ for (i = 0; i < attr->val_mapping_count; i++) {
+ if (!(val & (1 << i)))
+ continue;
+ if (!attr->val_mapping[i])
+ continue;
+
+ ret = snprintf(buf + offset, len, "%s,", attr->val_mapping[i]);
+ BUFFER_SIZE(ret, size, len, offset);
+ found = true;
+ }
+
+ if (found) {
+ offset--;
+ ret = snprintf(buf + offset, len, " ");
+ BUFFER_SIZE(ret, size, len, offset);
+ }
+
+ return offset;
+}
+
+static int ct_snprintf_attr(char *buf, size_t size,
+ const struct nf_conntrack *ct,
+ const struct ct_print_opts *attr)
+{
+ char ipstr[INET6_ADDRSTRLEN] = {};
+ unsigned int offset = 0;
+ int type = attr->type;
+ int len = size, ret;
+
+ ret = snprintf(buf, len, "%s ", attr->name);
+ BUFFER_SIZE(ret, size, len, offset);
+
+ switch (attr->datatype) {
+ case CT_ATTR_TYPE_U8:
+ if (attr->val_mapping)
+ ret = snprintf(buf + offset, len, "%s ",
+ attr->val_mapping[nfct_get_attr_u8(ct, type)]);
+ else
+ ret = snprintf(buf + offset, len, "%u ",
+ nfct_get_attr_u8(ct, type));
+ break;
+ case CT_ATTR_TYPE_BE16:
+ ret = snprintf(buf + offset, len, "%u ",
+ ntohs(nfct_get_attr_u16(ct, type)));
+ break;
+ case CT_ATTR_TYPE_U16:
+ ret = snprintf(buf + offset, len, "%u ",
+ nfct_get_attr_u16(ct, type));
+ break;
+ case CT_ATTR_TYPE_BE32:
+ ret = snprintf(buf + offset, len, "%u ",
+ ntohl(nfct_get_attr_u32(ct, type)));
+ break;
+ case CT_ATTR_TYPE_U32:
+ ret = snprintf(buf + offset, len, "%u ",
+ nfct_get_attr_u32(ct, type));
+ break;
+ case CT_ATTR_TYPE_U64:
+ ret = snprintf(buf + offset, len, "%lu ",
+ nfct_get_attr_u64(ct, type));
+ break;
+ case CT_ATTR_TYPE_IPV4:
+ inet_ntop(AF_INET, nfct_get_attr(ct, type),
+ ipstr, sizeof(ipstr));
+ ret = snprintf(buf + offset, len, "%s ", ipstr);
+ break;
+ case CT_ATTR_TYPE_IPV6:
+ inet_ntop(AF_INET6, nfct_get_attr(ct, type),
+ ipstr, sizeof(ipstr));
+ ret = snprintf(buf + offset, len, "%s ", ipstr);
+ break;
+ case CT_ATTR_TYPE_U32_BITMAP:
+ ret = ct_snprintf_u32_bitmap(buf + offset, len, ct, attr);
+ if (ret == 0 && type == ATTR_STATUS)
+ ret = snprintf(buf + offset, len, "UNSET ");
+ break;
+ default:
+ /* Unsupported datatype, should not ever happen */
+ ret = 0;
+ break;
+ }
+ BUFFER_SIZE(ret, size, len, offset);
+
+ return offset;
+}
+
+int ct_snprintf_opts(char *buf, unsigned int len, const struct nf_conntrack *ct,
+ const struct ct_print_opts *attrs)
+{
+ unsigned int size = 0, offset = 0, ret;
+ int i;
+
+ for (i = 0; attrs[i].name; ++i) {
+ if (!nfct_attr_is_set(ct, attrs[i].type))
+ continue;
+
+ ret = ct_snprintf_attr(buf + offset, len, ct, &attrs[i]);
+ BUFFER_SIZE(ret, size, len, offset);
+ }
+
+ return offset;
+}
+
+static const struct ct_print_opts attrs_ip_map[] = {
+ [ATTR_ORIG_IPV4_SRC] = { "-s", ATTR_ORIG_IPV4_SRC, CT_ATTR_TYPE_IPV4, 0, 0 },
+ [ATTR_ORIG_IPV4_DST] = { "-d", ATTR_ORIG_IPV4_DST, CT_ATTR_TYPE_IPV4, 0, 0 },
+ [ATTR_DNAT_IPV4] = { "-g", ATTR_REPL_IPV4_SRC, CT_ATTR_TYPE_IPV4, 0, 0 },
+ [ATTR_SNAT_IPV4] = { "-n", ATTR_REPL_IPV4_DST, CT_ATTR_TYPE_IPV4, 0, 0 },
+ [ATTR_REPL_IPV4_SRC] = { "-r", ATTR_REPL_IPV4_SRC, CT_ATTR_TYPE_IPV4, 0, 0 },
+ [ATTR_REPL_IPV4_DST] = { "-q", ATTR_REPL_IPV4_DST, CT_ATTR_TYPE_IPV4, 0, 0 },
+ [ATTR_ORIG_IPV6_SRC] = { "-s", ATTR_ORIG_IPV6_SRC, CT_ATTR_TYPE_IPV6, 0, 0 },
+ [ATTR_ORIG_IPV6_DST] = { "-d", ATTR_ORIG_IPV6_DST, CT_ATTR_TYPE_IPV6, 0, 0 },
+ [ATTR_DNAT_IPV6] = { "-g", ATTR_REPL_IPV6_SRC, CT_ATTR_TYPE_IPV6, 0, 0 },
+ [ATTR_SNAT_IPV6] = { "-n", ATTR_REPL_IPV6_DST, CT_ATTR_TYPE_IPV6, 0, 0 },
+ [ATTR_REPL_IPV6_SRC] = { "-r", ATTR_REPL_IPV6_SRC, CT_ATTR_TYPE_IPV6, 0, 0 },
+ [ATTR_REPL_IPV6_DST] = { "-q", ATTR_REPL_IPV6_DST, CT_ATTR_TYPE_IPV6, 0, 0 },
+};
+
+static const char *conntrack_status_map[] = {
+ [IPS_SEEN_REPLY_BIT] = "SEEN_REPLY",
+ [IPS_ASSURED_BIT] = "ASSURED",
+ [IPS_FIXED_TIMEOUT_BIT] = "FIXED_TIMEOUT",
+ [IPS_EXPECTED_BIT] = "EXPECTED"
+};
+
+static struct ct_print_opts attrs_generic[] = {
+ { "-t", ATTR_TIMEOUT, CT_ATTR_TYPE_U32, 0, 0 },
+ { "-u", ATTR_STATUS, CT_ATTR_TYPE_U32_BITMAP,
+ sizeof(conntrack_status_map)/sizeof(conntrack_status_map[0]),
+ conntrack_status_map },
+ { "-c", ATTR_SECMARK, CT_ATTR_TYPE_U32, 0, 0 },
+ { "-w", ATTR_ZONE, CT_ATTR_TYPE_U16, 0, 0 },
+ { "--orig-zone", ATTR_ORIG_ZONE, CT_ATTR_TYPE_U16, 0, 0 },
+ { "--reply-zone", ATTR_REPL_ZONE, CT_ATTR_TYPE_U16, 0, 0 },
+ {},
+};
+
+static int ct_save_snprintf(char *buf, size_t len,
+ const struct nf_conntrack *ct,
+ struct nfct_labelmap *map,
+ enum nf_conntrack_msg_type type)
+{
+ struct ct_print_opts tuple_attr_print[5] = {};
+ unsigned int size = 0, offset = 0;
+ struct ctproto_handler *cur;
+ uint8_t l3proto, l4proto;
+ int tuple_attrs[4] = {};
+ unsigned i;
+ int ret;
+
+ switch (type) {
+ case NFCT_T_NEW:
+ ret = snprintf(buf + offset, len, "-I ");
+ BUFFER_SIZE(ret, size, len, offset);
+ break;
+ case NFCT_T_UPDATE:
+ ret = snprintf(buf + offset, len, "-U ");
+ BUFFER_SIZE(ret, size, len, offset);
+ break;
+ case NFCT_T_DESTROY:
+ ret = snprintf(buf + offset, len, "-D ");
+ BUFFER_SIZE(ret, size, len, offset);
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+
+ ret = ct_snprintf_opts(buf + offset, len, ct, attrs_generic);
+ BUFFER_SIZE(ret, size, len, offset);
+
+ l3proto = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO);
+ if (!l3proto)
+ l3proto = nfct_get_attr_u8(ct, ATTR_REPL_L3PROTO);
+ switch (l3proto) {
+ case AF_INET:
+ tuple_attrs[0] = ATTR_ORIG_IPV4_SRC;
+ tuple_attrs[1] = ATTR_ORIG_IPV4_DST;
+ tuple_attrs[2] = nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT) ?
+ ATTR_DNAT_IPV4 : ATTR_REPL_IPV4_SRC;
+ tuple_attrs[3] = nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT) ?
+ ATTR_SNAT_IPV4 : ATTR_REPL_IPV4_DST;
+ break;
+ case AF_INET6:
+ tuple_attrs[0] = ATTR_ORIG_IPV6_SRC;
+ tuple_attrs[1] = ATTR_ORIG_IPV6_DST;
+ tuple_attrs[2] = nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT) ?
+ ATTR_DNAT_IPV6 : ATTR_REPL_IPV6_SRC;
+ tuple_attrs[3] = nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT) ?
+ ATTR_SNAT_IPV6 : ATTR_REPL_IPV6_DST;
+ break;
+ default:
+ fprintf(stderr, "WARNING: unsupported l3proto %d, skipping.\n",
+ l3proto);
+ return 0;
+ }
+
+ for (i = 0; i < sizeof(tuple_attrs) / sizeof(tuple_attrs[0]); i++) {
+ memcpy(&tuple_attr_print[i], &attrs_ip_map[tuple_attrs[i]],
+ sizeof(tuple_attr_print[0]));
+ }
+
+ ret = ct_snprintf_opts(buf + offset, len, ct, tuple_attr_print);
+ BUFFER_SIZE(ret, size, len, offset);
+
+ l4proto = nfct_get_attr_u8(ct, ATTR_L4PROTO);
+
+ /* is it in the list of supported protocol? */
+ list_for_each_entry(cur, &proto_list, head) {
+ if (cur->protonum != l4proto)
+ continue;
+
+ ret = snprintf(buf + offset, len, "-p %s ", cur->name);
+ BUFFER_SIZE(ret, size, len, offset);
+
+ ret = ct_snprintf_opts(buf + offset, len, ct, cur->print_opts);
+ BUFFER_SIZE(ret, size, len, offset);
+ break;
+ }
+
+ /* skip trailing space, if any */
+ for (; size && buf[size-1] == ' '; --size)
+ buf[size-1] = '\0';
+
+ return size;
+}
+
extern struct ctproto_handler ct_proto_unknown;
static struct ctproto_handler *findproto(char *name, int *pnum)
@@ -857,6 +1104,7 @@ enum {
_O_KTMS = (1 << 4),
_O_CL = (1 << 5),
_O_US = (1 << 6),
+ _O_SAVE = (1 << 7),
};
enum {
@@ -867,7 +1115,7 @@ enum {
};
static struct parse_parameter {
- const char *parameter[7];
+ const char *parameter[8];
size_t size;
unsigned int value[8];
} parse_array[PARSE_MAX] = {
@@ -875,8 +1123,8 @@ static struct parse_parameter {
{ IPS_ASSURED, IPS_SEEN_REPLY, 0, IPS_FIXED_TIMEOUT, IPS_EXPECTED, IPS_OFFLOAD, IPS_HW_OFFLOAD} },
{ {"ALL", "NEW", "UPDATES", "DESTROY"}, 4,
{ CT_EVENT_F_ALL, CT_EVENT_F_NEW, CT_EVENT_F_UPD, CT_EVENT_F_DEL } },
- { {"xml", "extended", "timestamp", "id", "ktimestamp", "labels", "userspace" }, 7,
- { _O_XML, _O_EXT, _O_TMS, _O_ID, _O_KTMS, _O_CL, _O_US },
+ { {"xml", "extended", "timestamp", "id", "ktimestamp", "labels", "userspace", "save"}, 8,
+ { _O_XML, _O_EXT, _O_TMS, _O_ID, _O_KTMS, _O_CL, _O_US, _O_SAVE },
},
};
@@ -1462,6 +1710,11 @@ static int event_cb(const struct nlmsghdr *nlh, void *data)
nfct_filter(obj, ct))
goto out;
+ if (output_mask & _O_SAVE) {
+ ct_save_snprintf(buf, sizeof(buf), ct, labelmap, type);
+ goto done;
+ }
+
if (output_mask & _O_XML) {
op_type = NFCT_O_XML;
if (dump_xml_header_done) {
@@ -1486,7 +1739,7 @@ static int event_cb(const struct nlmsghdr *nlh, void *data)
op_flags |= NFCT_OF_ID;
nfct_snprintf_labels(buf, sizeof(buf), ct, type, op_type, op_flags, labelmap);
-
+done:
if (output_mask & _O_US) {
if (nlh->nlmsg_pid)
userspace = true;
@@ -1515,6 +1768,11 @@ static int dump_cb(enum nf_conntrack_msg_type type,
if (nfct_filter(obj, ct))
return NFCT_CB_CONTINUE;
+ if (output_mask & _O_SAVE) {
+ ct_save_snprintf(buf, sizeof(buf), ct, labelmap, NFCT_T_NEW);
+ goto done;
+ }
+
if (output_mask & _O_XML) {
op_type = NFCT_O_XML;
if (dump_xml_header_done) {
@@ -1531,6 +1789,7 @@ static int dump_cb(enum nf_conntrack_msg_type type,
op_flags |= NFCT_OF_ID;
nfct_snprintf_labels(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags, labelmap);
+done:
printf("%s\n", buf);
counter++;
@@ -1557,6 +1816,11 @@ static int delete_cb(enum nf_conntrack_msg_type type,
"Operation failed: %s",
err2str(errno, CT_DELETE));
+ if (output_mask & _O_SAVE) {
+ ct_save_snprintf(buf, sizeof(buf), ct, labelmap, NFCT_T_DESTROY);
+ goto done;
+ }
+
if (output_mask & _O_XML)
op_type = NFCT_O_XML;
if (output_mask & _O_EXT)
@@ -1565,6 +1829,7 @@ static int delete_cb(enum nf_conntrack_msg_type type,
op_flags |= NFCT_OF_ID;
nfct_snprintf(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags);
+done:
printf("%s\n", buf);
counter++;
@@ -1580,6 +1845,11 @@ static int print_cb(enum nf_conntrack_msg_type type,
unsigned int op_type = NFCT_O_DEFAULT;
unsigned int op_flags = 0;
+ if (output_mask & _O_SAVE) {
+ ct_save_snprintf(buf, sizeof(buf), ct, labelmap, NFCT_T_NEW);
+ goto done;
+ }
+
if (output_mask & _O_XML)
op_type = NFCT_O_XML;
if (output_mask & _O_EXT)
@@ -1588,6 +1858,7 @@ static int print_cb(enum nf_conntrack_msg_type type,
op_flags |= NFCT_OF_ID;
nfct_snprintf_labels(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags, labelmap);
+done:
printf("%s\n", buf);
return NFCT_CB_CONTINUE;
@@ -2451,6 +2722,10 @@ int main(int argc, char *argv[])
parse_parameter(optarg, &output_mask, PARSE_OUTPUT);
if (output_mask & _O_CL)
labelmap_init();
+ if ((output_mask & _O_SAVE) &&
+ (output_mask & (_O_EXT |_O_TMS |_O_ID | _O_KTMS | _O_CL | _O_XML)))
+ exit_error(OTHER_PROBLEM,
+ "cannot combine save output with any other output type, use -o save only");
break;
case 'z':
options |= CT_OPT_ZERO;