@@ -83,6 +83,7 @@ static char dccp_commands_v_options[NUMBER_OF_CMD][DCCP_OPT_MAX] =
[EXP_GET_BIT] = {1,1,1,1,0,0,0,0,0,0},
[EXP_FLUSH_BIT] = {0,0,0,0,0,0,0,0,0,0},
[EXP_EVENT_BIT] = {0,0,0,0,0,0,0,0,0,0},
+ [CT_ADD_BIT] = {3,3,3,3,0,0,1,0,0,1},
};
static const char *dccp_states[DCCP_CONNTRACK_MAX] = {
@@ -82,6 +82,7 @@ static char gre_commands_v_options[NUMBER_OF_CMD][GRE_OPT_MAX] =
[EXP_GET_BIT] = {1,1,1,1,0,0,0,0},
[EXP_FLUSH_BIT] = {0,0,0,0,0,0,0,0},
[EXP_EVENT_BIT] = {0,0,0,0,0,0,0,0},
+ [CT_ADD_BIT] = {3,3,3,3,0,0,0,0},
};
static int parse_options(char c,
@@ -56,6 +56,7 @@ static char icmp_commands_v_options[NUMBER_OF_CMD][ICMP_NUMBER_OF_OPT] =
[EXP_GET_BIT] = {0,0,0},
[EXP_FLUSH_BIT] = {0,0,0},
[EXP_EVENT_BIT] = {0,0,0},
+ [CT_ADD_BIT] = {1,1,2},
};
static void help(void)
@@ -59,6 +59,7 @@ static char icmpv6_commands_v_options[NUMBER_OF_CMD][ICMPV6_NUMBER_OF_OPT] =
[EXP_GET_BIT] = {0,0,0},
[EXP_FLUSH_BIT] = {0,0,0},
[EXP_EVENT_BIT] = {0,0,0},
+ [CT_ADD_BIT] = {1,1,2},
};
static void help(void)
@@ -86,6 +86,7 @@ static char sctp_commands_v_options[NUMBER_OF_CMD][SCTP_OPT_MAX] =
[EXP_GET_BIT] = {1,1,1,1,0,0,0,0,0,0,0},
[EXP_FLUSH_BIT] = {0,0,0,0,0,0,0,0,0,0,0},
[EXP_EVENT_BIT] = {0,0,0,0,0,0,0,0,0,0,0},
+ [CT_ADD_BIT] = {3,3,3,3,0,0,1,0,0,1,1},
};
static const char *sctp_states[SCTP_CONNTRACK_MAX] = {
@@ -70,6 +70,7 @@ static char tcp_commands_v_options[NUMBER_OF_CMD][TCP_NUMBER_OF_OPT] =
[EXP_GET_BIT] = {1,1,1,1,0,0,0,0,0},
[EXP_FLUSH_BIT] = {0,0,0,0,0,0,0,0,0},
[EXP_EVENT_BIT] = {0,0,0,0,0,0,0,0,0},
+ [CT_ADD_BIT] = {3,3,3,3,0,0,1,0,0},
};
static const char *tcp_states[TCP_CONNTRACK_MAX] = {
@@ -78,6 +78,7 @@ static char udp_commands_v_options[NUMBER_OF_CMD][UDP_NUMBER_OF_OPT] =
[EXP_GET_BIT] = {1,1,1,1,0,0,0,0},
[EXP_FLUSH_BIT] = {0,0,0,0,0,0,0,0},
[EXP_EVENT_BIT] = {0,0,0,0,0,0,0,0},
+ [CT_ADD_BIT] = {3,3,3,3,0,0,0,0},
};
static int parse_options(char c,
@@ -86,6 +86,7 @@ static char udplite_commands_v_options[NUMBER_OF_CMD][UDP_OPT_MAX] =
[EXP_GET_BIT] = {1,1,1,1,0,0,0,0},
[EXP_FLUSH_BIT] = {0,0,0,0,0,0,0,0},
[EXP_EVENT_BIT] = {0,0,0,0,0,0,0,0},
+ [CT_ADD_BIT] = {3,3,3,3,0,0,0,0},
};
static int parse_options(char c,
@@ -71,7 +71,10 @@ enum ct_command {
EXP_STATS_BIT = 18,
EXP_STATS = (1 << EXP_STATS_BIT),
- _CT_BIT_MAX = 19,
+ CT_ADD_BIT = 19,
+ CT_ADD = (1 << CT_ADD_BIT),
+
+ _CT_BIT_MAX = 20,
};
#define NUMBER_OF_CMD _CT_BIT_MAX
@@ -291,6 +291,7 @@ static const char *optflags[NUMBER_OF_OPT] = {
static struct option original_opts[] = {
{"dump", 2, 0, 'L'},
{"create", 2, 0, 'I'},
+ {"add", 2, 0, 'A'},
{"delete", 2, 0, 'D'},
{"update", 2, 0, 'U'},
{"get", 2, 0, 'G'},
@@ -334,7 +335,7 @@ static struct option original_opts[] = {
{0, 0, 0, 0}
};
-static const char *getopt_str = ":L::I::U::D::G::E::F::hVs:d:r:q:"
+static const char *getopt_str = ":L::I::U::D::G::E::F::A::hVs:d:r:q:"
"p:t:u:e:a:z[:]:{:}:m:i:f:o:n::"
"g::c:b:C::Sj::w:l:<:>::(:):";
@@ -371,6 +372,7 @@ static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
[EXP_COUNT_BIT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
[CT_STATS_BIT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
[EXP_STATS_BIT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ [CT_ADD_BIT] = {3,3,3,3,1,1,2,0,0,0,0,0,0,2,2,0,0,2,2,0,0,0,0,2,0,2,0,2,2},
};
static const int cmd2type[][2] = {
@@ -385,6 +387,7 @@ static const int cmd2type[][2] = {
['C'] = { CT_COUNT, EXP_COUNT },
['S'] = { CT_STATS, EXP_STATS },
['U'] = { CT_UPDATE, 0 },
+ ['A'] = { CT_ADD, 0 },
};
static const int opt2type[] = {
@@ -488,6 +491,7 @@ static char exit_msg[NUMBER_OF_CMD][64] = {
[CT_EVENT_BIT] = "%d flow events have been shown.\n",
[EXP_LIST_BIT] = "%d expectations have been shown.\n",
[EXP_DELETE_BIT] = "%d expectations have been shown.\n",
+ [CT_ADD_BIT] = "%d flow entries have been added.\n",
};
static const char usage_commands[] =
@@ -745,7 +749,7 @@ static int ct_save_snprintf(char *buf, size_t len,
switch (type) {
case NFCT_T_NEW:
- ret = snprintf(buf + offset, len, "-I ");
+ ret = snprintf(buf + offset, len, "-A ");
BUFFER_SIZE(ret, size, len, offset);
break;
case NFCT_T_UPDATE:
@@ -1054,11 +1058,11 @@ err2str(int err, enum ct_command command)
{ { 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,
+ { CT_CREATE|CT_GET|CT_DELETE|CT_ADD, ENOENT,
"such conntrack doesn't exist" },
- { CT_CREATE|CT_GET, ENOMEM, "not enough memory" },
+ { CT_CREATE|CT_GET|CT_ADD, ENOMEM, "not enough memory" },
{ CT_GET, EAFNOSUPPORT, "protocol not supported" },
- { CT_CREATE, ETIME, "conntrack has expired" },
+ { CT_CREATE|CT_ADD, ETIME, "conntrack has expired" },
{ EXP_CREATE, ENOENT, "master conntrack not found" },
{ EXP_CREATE, EINVAL, "invalid parameters" },
{ ~0U, EPERM, "sorry, you must be root or get "
@@ -2881,7 +2885,8 @@ static int print_stats(const struct ct_cmd *cmd)
if (cmd->command && exit_msg[cmd->cmd][0]) {
fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION);
fprintf(stderr, exit_msg[cmd->cmd], counter);
- if (counter == 0 && !(cmd->command & (CT_LIST | EXP_LIST)))
+ if (counter == 0 &&
+ !(cmd->command & (CT_LIST | EXP_LIST | CT_ADD)))
return -1;
}
@@ -2935,6 +2940,7 @@ static void do_parse(struct ct_cmd *ct_cmd, int argc, char *argv[])
case 'C':
case 'S':
case 'U':
+ case 'A':
type = check_type(argc, argv);
if (type == CT_TABLE_DYING ||
type == CT_TABLE_UNCONFIRMED) {
@@ -3286,6 +3292,7 @@ static int do_command_ct(const char *progname, struct ct_cmd *cmd,
break;
case CT_CREATE:
+ case CT_ADD:
if ((cmd->options & CT_OPT_ORIG) && !(cmd->options & CT_OPT_REPL))
nfct_setobjopt(cmd->tmpl.ct, NFCT_SOPT_SETUP_REPLY);
else if (!(cmd->options & CT_OPT_ORIG) && (cmd->options & CT_OPT_REPL))
@@ -3304,7 +3311,8 @@ static int do_command_ct(const char *progname, struct ct_cmd *cmd,
NULL, cmd->tmpl.ct, NULL);
if (res >= 0)
counter++;
-
+ else if (errno == EEXIST && cmd->command == CT_ADD)
+ res = 0;
break;
case EXP_CREATE:
@@ -3810,7 +3818,8 @@ int main(int argc, char *argv[])
ct_parse_file(&cmd_list, argv[0], argv[2]);
list_for_each_entry(cmd, &cmd_list, list) {
- if (!(cmd->command & (CT_CREATE | CT_UPDATE | CT_DELETE | CT_FLUSH)))
+ if (!(cmd->command &
+ (CT_CREATE | CT_ADD | CT_UPDATE | CT_DELETE | CT_FLUSH)))
exit_error(PARAMETER_PROBLEM,
"Cannot use command `%s' with --load-file",
ct_unsupp_cmd_file(cmd));
@@ -77,4 +77,49 @@
-D -w 123 ;
-R - ; OK
# validate it via standard command line way
--D -w 123 ; BAD
\ No newline at end of file
+-D -w 123 ; BAD
+# create with -A
+# create a conntrack
+-A -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ;
+# create from reply
+-A -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 ;
+# create a v6 conntrack
+-A -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ;
+# creae icmp ping request entry
+-A -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ;
+-R - ; 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
+-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 ; BAD
+-I -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+-I -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; BAD
+# repeat, it should succeed
+-A -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ;
+# create from reply
+-A -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 ;
+# create a v6 conntrack
+-A -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ;
+# creae icmp ping request entry
+-A -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ;
+-R - ; OK
+# delete
+-D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ;
+# empty lines should be just ignored
+;
+;
+# delete reverse
+-D -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 ;
+# empty lines with spaces or tabs should be ignored as well
+ ;
+ ;
+ ;
+ ;
+ ;
+ ;
+# delete v6 conntrack
+-D -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 ;
+# delete icmp ping request entry
+-D -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ;
+;
+;
+-R - ; OK
new file mode 100644
@@ -0,0 +1,42 @@
+#missing destination
+-A -s 1.1.1.1 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+#missing source
+-A -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+#missing protocol
+-A -s 1.1.1.1 -d 2.2.2.2 --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+#missing source port
+-A -s 1.1.1.1 -d 2.2.2.2 -p tcp --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+#missing destination port
+-A -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+#missing timeout
+-A -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY ; BAD
+# create a conntrack
+-A -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
+-A -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
+-D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ; OK
+# delete again
+-D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ; BAD
+# create from reply
+-A -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
+# create again from reply
+-A -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
+# delete reverse again
+-D -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 ; BAD
+# create a v6 conntrack
+-A -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+# create again a v6 conntrack
+-A -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+# delete v6 conntrack
+-D -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 ; OK
+# mismatched address family
+-A -s 2001:DB8::1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+# creae icmp ping request entry
+-A -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; OK
+# creae again icmp ping request entry
+-A -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; OK
+# delete icmp ping request entry
+-D -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; OK
The -A command works exactly the same way as -I except that it does not fail if the ct entry already exists. This command is useful for the batched ct loads to not abort if some entries being applied exist. The ct entry dump in the "save" format is now switched to use the -A command as well for the generated output. Also tests added to cover the -A command. Signed-off-by: Mikhail Sennikovsky <mikhail.sennikovskii@ionos.com> --- extensions/libct_proto_dccp.c | 1 + extensions/libct_proto_gre.c | 1 + extensions/libct_proto_icmp.c | 1 + extensions/libct_proto_icmpv6.c | 1 + extensions/libct_proto_sctp.c | 1 + extensions/libct_proto_tcp.c | 1 + extensions/libct_proto_udp.c | 1 + extensions/libct_proto_udplite.c | 1 + include/conntrack.h | 5 +++- src/conntrack.c | 25 ++++++++++------ tests/conntrack/testsuite/08stdin | 47 ++++++++++++++++++++++++++++++- tests/conntrack/testsuite/10add | 42 +++++++++++++++++++++++++++ 12 files changed, 117 insertions(+), 10 deletions(-) create mode 100644 tests/conntrack/testsuite/10add