]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: add ct timeout support
authorHarsha Sharma <harshasharmaiitr@gmail.com>
Mon, 13 Aug 2018 23:06:56 +0000 (01:06 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Fri, 31 Aug 2018 16:40:08 +0000 (18:40 +0200)
This patch adds support for adding, listing and deleting ct timeout
objects which can be assigned via rule to assign connection tracking
timeout policies via objref infrastructure.

 % nft add table filter
 % nft add chain filter output
 % nft add ct timeout filter test-tcp { protocol tcp \; policy = { established: 132, close: 13, close_wait: 17 } \; }
 % nft add rule filter output ct timeout set test-tcp
 % nft list ruleset

 table ip filter {
ct timeout test-tcp {
protocol tcp;
l3proto ip
policy = {established: 132, close_wait: 17, close: 13}
}

chain output {
ct timeout set "test-tcp"
}
 }

 % nft delete rule filter output handle <handle>
 % nft delete ct timeout filter test-tcp

Note: Original patch has been rework to use fixed size array for
timeouts and to validate timeout policy from the evaluation phase, once
we have access to the layer 4 protocol number. --pablo

Signed-off-by: Harsha Sharma <harshasharmaiitr@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/linux/netfilter/nf_tables.h
include/rule.h
src/evaluate.c
src/netlink.c
src/parser_bison.y
src/rule.c
src/statement.c

index fe65652583d05d225e086d387f13a71847be6293..143ebe287a1cc91795aff88010cacfdf1a3be063 100644 (file)
@@ -972,6 +972,7 @@ enum nft_osf_attributes {
  * @NFT_CT_DST_IP: conntrack layer 3 protocol destination (IPv4 address)
  * @NFT_CT_SRC_IP6: conntrack layer 3 protocol source (IPv6 address)
  * @NFT_CT_DST_IP6: conntrack layer 3 protocol destination (IPv6 address)
+ * @NFT_CT_TIMEOUT: connection tracking timeout policy assigned to conntrack
  */
 enum nft_ct_keys {
        NFT_CT_STATE,
@@ -997,6 +998,7 @@ enum nft_ct_keys {
        NFT_CT_DST_IP,
        NFT_CT_SRC_IP6,
        NFT_CT_DST_IP6,
+       NFT_CT_TIMEOUT,
        __NFT_CT_MAX
 };
 #define NFT_CT_MAX             (__NFT_CT_MAX - 1)
@@ -1403,13 +1405,23 @@ enum nft_ct_helper_attributes {
 };
 #define NFTA_CT_HELPER_MAX     (__NFTA_CT_HELPER_MAX - 1)
 
+enum nft_ct_timeout_attributes {
+       NFTA_CT_TIMEOUT_L3PROTO,
+       NFTA_CT_TIMEOUT_L4PROTO,
+       NFTA_CT_TIMEOUT_DATA,
+       __NFTA_CT_TIMEOUT_MAX,
+};
+#define NFTA_CT_TIMEOUT_MAX     (__NFTA_CT_TIMEOUT_MAX - 1)
+
 #define NFT_OBJECT_UNSPEC      0
 #define NFT_OBJECT_COUNTER     1
 #define NFT_OBJECT_QUOTA       2
 #define NFT_OBJECT_CT_HELPER   3
 #define NFT_OBJECT_LIMIT       4
 #define NFT_OBJECT_CONNLIMIT   5
-#define __NFT_OBJECT_MAX       6
+#define NFT_OBJECT_TUNNEL      6
+#define NFT_OBJECT_CT_TIMEOUT  7
+#define __NFT_OBJECT_MAX       8
 #define NFT_OBJECT_MAX         (__NFT_OBJECT_MAX - 1)
 
 /**
index cfbbcf1f13d71448b437b9c04a9e5044f08da05e..88478aa6b59b133d700809148f1854f6302d1cf6 100644 (file)
@@ -4,6 +4,8 @@
 #include <stdint.h>
 #include <nftables.h>
 #include <list.h>
+#include <netinet/in.h>
+#include <libnftnl/object.h>   /* For NFTNL_CTTIMEOUT_ARRAY_MAX. */
 
 /**
  * struct handle_spec - handle ID
@@ -324,6 +326,21 @@ struct ct_helper {
        uint8_t l4proto;
 };
 
+struct timeout_state {
+       struct list_head head;
+       struct location location;
+       uint8_t timeout_index;
+       const char *timeout_str;
+       unsigned int timeout_value;
+};
+
+struct ct_timeout {
+       uint16_t l3proto;
+       uint8_t l4proto;
+       uint32_t timeout[NFTNL_CTTIMEOUT_ARRAY_MAX];
+       struct list_head timeout_list;
+};
+
 struct limit {
        uint64_t        rate;
        uint64_t        unit;
@@ -352,6 +369,7 @@ struct obj {
                struct quota            quota;
                struct ct_helper        ct_helper;
                struct limit            limit;
+               struct ct_timeout       ct_timeout;
        };
 };
 
@@ -478,6 +496,7 @@ enum cmd_obj {
        CMD_OBJ_LIMITS,
        CMD_OBJ_FLOWTABLE,
        CMD_OBJ_FLOWTABLES,
+       CMD_OBJ_CT_TIMEOUT,
 };
 
 struct markup {
@@ -633,4 +652,13 @@ enum udata_set_elem_flags {
        SET_ELEM_F_INTERVAL_OPEN        = 0x1,
 };
 
+struct timeout_protocol {
+       uint32_t array_size;
+       const char *const *state_to_name;
+       uint32_t *dflt_timeout;
+};
+
+extern struct timeout_protocol timeout_protocol[IPPROTO_MAX];
+extern int timeout_str2num(uint16_t l4proto, struct timeout_state *ts);
+
 #endif /* NFTABLES_RULE_H */
index a3a7874440e85d3d36ec3edef84fa54606ec86c4..9a7118eccc0c40c0e9eb7a9b34667fa4a9f56fa8 100644 (file)
@@ -3196,11 +3196,36 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
        return 0;
 }
 
+static int obj_evaluate(struct eval_ctx *ctx, struct obj *obj)
+{
+       struct ct_timeout *ct = &obj->ct_timeout;
+       struct timeout_state *ts, *next;
+       unsigned int i;
+
+       if (obj->type != NFT_OBJECT_CT_TIMEOUT)
+               return 0;
+
+       for (i = 0; i < timeout_protocol[ct->l4proto].array_size; i++)
+               ct->timeout[i] = timeout_protocol[ct->l4proto].dflt_timeout[i];
+
+       list_for_each_entry_safe(ts, next, &ct->timeout_list, head) {
+               if (timeout_str2num(ct->l4proto, ts) < 0)
+                       return __stmt_binary_error(ctx, &ts->location, NULL,
+                                                  "invalid state for this protocol");
+
+               ct->timeout[ts->timeout_index] = ts->timeout_value;
+               list_del(&ts->head);
+               xfree(ts);
+       }
+       return 0;
+}
+
 static int table_evaluate(struct eval_ctx *ctx, struct table *table)
 {
        struct flowtable *ft;
        struct chain *chain;
        struct set *set;
+       struct obj *obj;
 
        if (table_lookup(&ctx->cmd->handle, ctx->cache) == NULL) {
                if (table == NULL) {
@@ -3232,6 +3257,11 @@ static int table_evaluate(struct eval_ctx *ctx, struct table *table)
                if (flowtable_evaluate(ctx, ft) < 0)
                        return -1;
        }
+       list_for_each_entry(obj, &table->objs, list) {
+               handle_merge(&obj->handle, &table->handle);
+               if (obj_evaluate(ctx, obj) < 0)
+                       return -1;
+       }
 
        ctx->table = NULL;
        return 0;
@@ -3281,7 +3311,8 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd)
        case CMD_OBJ_QUOTA:
        case CMD_OBJ_CT_HELPER:
        case CMD_OBJ_LIMIT:
-               return 0;
+       case CMD_OBJ_CT_TIMEOUT:
+               return obj_evaluate(ctx, cmd->object);
        default:
                BUG("invalid command object type %u\n", cmd->obj);
        }
@@ -3307,6 +3338,7 @@ static int cmd_evaluate_delete(struct eval_ctx *ctx, struct cmd *cmd)
        case CMD_OBJ_COUNTER:
        case CMD_OBJ_QUOTA:
        case CMD_OBJ_CT_HELPER:
+       case CMD_OBJ_CT_TIMEOUT:
        case CMD_OBJ_LIMIT:
                return 0;
        default:
@@ -3439,6 +3471,8 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
                return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_COUNTER);
        case CMD_OBJ_CT_HELPER:
                return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_CT_HELPER);
+       case CMD_OBJ_CT_TIMEOUT:
+               return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_CT_TIMEOUT);
        case CMD_OBJ_LIMIT:
                return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_LIMIT);
        case CMD_OBJ_COUNTERS:
index bd4727994bef0deb4a211bad0c073985756af636..f795d984084bed1da79d0fc1f67127bc6ed8332a 100644 (file)
@@ -334,6 +334,14 @@ alloc_nftnl_obj(const struct handle *h, struct obj *obj)
                        nftnl_obj_set_u16(nlo, NFTNL_OBJ_CT_HELPER_L3PROTO,
                                          obj->ct_helper.l3proto);
                break;
+       case NFT_OBJECT_CT_TIMEOUT:
+               nftnl_obj_set_u8(nlo, NFTNL_OBJ_CT_TIMEOUT_L4PROTO,
+                                 obj->ct_timeout.l4proto);
+               if (obj->ct_timeout.l3proto)
+                       nftnl_obj_set_u16(nlo, NFTNL_OBJ_CT_TIMEOUT_L3PROTO,
+                                         obj->ct_timeout.l3proto);
+               nftnl_obj_set(nlo, NFTNL_OBJ_CT_TIMEOUT_ARRAY, obj->ct_timeout.timeout);
+               break;
        case NFT_OBJECT_LIMIT:
                nftnl_obj_set_u64(nlo, NFTNL_OBJ_LIMIT_RATE, obj->limit.rate);
                nftnl_obj_set_u64(nlo, NFTNL_OBJ_LIMIT_UNIT, obj->limit.unit);
@@ -1437,6 +1445,13 @@ struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
                obj->ct_helper.l3proto = nftnl_obj_get_u16(nlo, NFTNL_OBJ_CT_HELPER_L3PROTO);
                obj->ct_helper.l4proto = nftnl_obj_get_u8(nlo, NFTNL_OBJ_CT_HELPER_L4PROTO);
                break;
+       case NFT_OBJECT_CT_TIMEOUT:
+               obj->ct_timeout.l3proto = nftnl_obj_get_u16(nlo, NFTNL_OBJ_CT_TIMEOUT_L3PROTO);
+               obj->ct_timeout.l4proto = nftnl_obj_get_u8(nlo, NFTNL_OBJ_CT_TIMEOUT_L4PROTO);
+               memcpy(obj->ct_timeout.timeout,
+                      nftnl_obj_get(nlo, NFTNL_OBJ_CT_TIMEOUT_ARRAY),
+                      NFTNL_CTTIMEOUT_ARRAY_MAX * sizeof(uint32_t));
+               break;
        case NFT_OBJECT_LIMIT:
                obj->limit.rate =
                        nftnl_obj_get_u64(nlo, NFTNL_OBJ_LIMIT_RATE);
index 86036124a6a6ab5f2a7c715643c0c7f87dcdcd20..85830d880b05554631625b874033632a5910a470 100644 (file)
@@ -554,7 +554,7 @@ int nft_lex(void *, void *, void *);
 %type <flowtable>              flowtable_block_alloc flowtable_block
 %destructor { flowtable_free($$); }    flowtable_block_alloc
 
-%type <obj>                    obj_block_alloc counter_block quota_block ct_helper_block limit_block
+%type <obj>                    obj_block_alloc counter_block quota_block ct_helper_block ct_timeout_block limit_block
 %destructor { obj_free($$); }  obj_block_alloc
 
 %type <list>                   stmt_list
@@ -755,6 +755,9 @@ int nft_lex(void *, void *, void *);
 
 %type <val>                    ct_l4protoname ct_obj_type
 
+%type <list>                   timeout_states timeout_state
+%destructor { xfree($$); }     timeout_states timeout_state
+
 %%
 
 input                  :       /* empty */
@@ -962,6 +965,10 @@ add_cmd                    :       TABLE           table_spec
 
                                $$ = cmd_alloc_obj_ct(CMD_ADD, NFT_OBJECT_CT_HELPER, &$3, &@$, $4);
                        }
+                       |       CT      TIMEOUT obj_spec        ct_obj_alloc    '{' ct_timeout_block '}' stmt_separator
+                       {
+                               $$ = cmd_alloc_obj_ct(CMD_ADD, NFT_OBJECT_CT_TIMEOUT, &$3, &@$, $4);
+                       }
                        |       LIMIT           obj_spec        limit_obj
                        {
                                $$ = cmd_alloc(CMD_ADD, CMD_OBJ_LIMIT, &$2, &@$, $3);
@@ -1043,6 +1050,10 @@ create_cmd               :       TABLE           table_spec
                        {
                                $$ = cmd_alloc_obj_ct(CMD_CREATE, NFT_OBJECT_CT_HELPER, &$3, &@$, $4);
                        }
+                       |       CT      TIMEOUT obj_spec        ct_obj_alloc    '{' ct_timeout_block '}' stmt_separator
+                       {
+                               $$ = cmd_alloc_obj_ct(CMD_CREATE, NFT_OBJECT_CT_TIMEOUT, &$3, &@$, $4);
+                       }
                        |       LIMIT           obj_spec        limit_obj
                        {
                                $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_LIMIT, &$2, &@$, $3);
@@ -1235,6 +1246,10 @@ list_cmd         :       TABLE           table_spec
                        {
                                $$ = cmd_alloc(CMD_LIST, CMD_OBJ_CT_HELPERS, &$4, &@$, NULL);
                        }
+                       |       CT              TIMEOUT         TABLE           table_spec
+                       {
+                               $$ = cmd_alloc(CMD_LIST, CMD_OBJ_CT_TIMEOUT, &$4, &@$, NULL);
+                       }
                        ;
 
 reset_cmd              :       COUNTERS        ruleset_spec
@@ -1466,6 +1481,15 @@ table_block              :       /* empty */     { $$ = $<table>-1; }
                                list_add_tail(&$5->list, &$1->objs);
                                $$ = $1;
                        }
+                       |       table_block     CT      TIMEOUT obj_identifier obj_block_alloc '{'      ct_timeout_block        '}' stmt_separator
+                       {
+                               $5->location = @4;
+                               $5->type = NFT_OBJECT_CT_TIMEOUT;
+                               handle_merge(&$5->handle, &$4);
+                               handle_free(&$4);
+                               list_add_tail(&$5->list, &$1->objs);
+                               $$ = $1;
+                       }
                        |       table_block     LIMIT           obj_identifier
                                        obj_block_alloc '{'     limit_block     '}'
                                        stmt_separator
@@ -1761,6 +1785,15 @@ ct_helper_block          :       /* empty */     { $$ = $<obj>-1; }
                        }
                        ;
 
+ct_timeout_block       :       /*empty */      { $$ = $<obj>-1; }
+                       |       ct_timeout_block     common_block
+                       |       ct_timeout_block     stmt_separator
+                       |       ct_timeout_block     ct_timeout_config
+                       {
+                               $$ = $1;
+                       }
+                       ;
+
 limit_block            :       /* empty */     { $$ = $<obj>-1; }
                        |       limit_block     common_block
                        |       limit_block     stmt_separator
@@ -3279,6 +3312,7 @@ quota_obj         :       quota_config
                        ;
 
 ct_obj_type            :       HELPER          { $$ = NFT_OBJECT_CT_HELPER; }
+                       |       TIMEOUT         { $$ = NFT_OBJECT_CT_TIMEOUT; }
                        ;
 
 ct_l4protoname         :       TCP     { $$ = IPPROTO_TCP; }
@@ -3306,6 +3340,55 @@ ct_helper_config         :       TYPE    QUOTED_STRING   PROTOCOL        ct_l4protoname  stmt_separator
                        }
                        ;
 
+timeout_states         :       timeout_state
+                       {
+                               $$ = xmalloc(sizeof(*$$));
+                               init_list_head($$);
+                               list_add_tail($1, $$);
+                       }
+                       |       timeout_states  COMMA   timeout_state
+                       {
+                               list_add_tail($3, $1);
+                               $$ = $1;
+                       }
+                       ;
+
+timeout_state          :       STRING  COLON   NUM
+
+                       {
+                               struct timeout_state *ts;
+
+                               ts = xzalloc(sizeof(*ts));
+                               ts->timeout_str = $1;
+                               ts->timeout_value = $3;
+                               ts->location = @1;
+                               init_list_head(&ts->head);
+                               $$ = &ts->head;
+                       }
+                       ;
+
+ct_timeout_config      :       PROTOCOL        ct_l4protoname  SEMICOLON
+                       {
+                               struct ct_timeout *ct;
+                               int l4proto = $2;
+
+                               ct = &$<obj>0->ct_timeout;
+                               ct->l4proto = l4proto;
+                       }
+                       |       POLICY  '='     '{'     timeout_states  '}'      stmt_separator
+                       {
+                               struct ct_timeout *ct;
+
+                               ct = &$<obj>0->ct_timeout;
+                               init_list_head(&ct->timeout_list);
+                               list_splice_tail($4, &ct->timeout_list);
+                       }
+                       |       L3PROTOCOL      family_spec_explicit    stmt_separator
+                       {
+                               $<obj>0->ct_timeout.l3proto = $2;
+                       }
+                       ;
+
 ct_obj_alloc           :
                        {
                                $$ = obj_alloc(&@$);
@@ -3781,6 +3864,7 @@ ct_key                    :       L3PROTOCOL      { $$ = NFT_CT_L3PROTOCOL; }
                        |       PROTO_DST       { $$ = NFT_CT_PROTO_DST; }
                        |       LABEL           { $$ = NFT_CT_LABELS; }
                        |       EVENT           { $$ = NFT_CT_EVENTMASK; }
+                       |       TIMEOUT         { $$ = NFT_CT_TIMEOUT; }
                        |       ct_key_dir_optional
                        ;
 
@@ -3829,6 +3913,11 @@ ct_stmt                  :       CT      ct_key          SET     stmt_expr
                                        $$->objref.type = NFT_OBJECT_CT_HELPER;
                                        $$->objref.expr = $4;
                                        break;
+                               case NFT_CT_TIMEOUT:
+                                       $$ = objref_stmt_alloc(&@$);
+                                       $$->objref.type = NFT_OBJECT_CT_TIMEOUT;
+                                       $$->objref.expr = $4;
+                                       break;
                                default:
                                        $$ = ct_stmt_alloc(&@$, $2, -1, $4);
                                        break;
index 470b112ecacf97e47c7dc234748009b1ce7afa4d..68abdc34d4c6197dfae4aaf16751863d899c5d27 100644 (file)
 #include <net/if.h>
 #include <linux/netfilter_bridge.h>
 
+static const char *const tcp_state_to_name[] = {
+       [NFTNL_CTTIMEOUT_TCP_SYN_SENT]          = "syn_sent",
+       [NFTNL_CTTIMEOUT_TCP_SYN_RECV]          = "syn_recv",
+       [NFTNL_CTTIMEOUT_TCP_ESTABLISHED]       = "established",
+       [NFTNL_CTTIMEOUT_TCP_FIN_WAIT]          = "fin_wait",
+       [NFTNL_CTTIMEOUT_TCP_CLOSE_WAIT]        = "close_wait",
+       [NFTNL_CTTIMEOUT_TCP_LAST_ACK]          = "last_ack",
+       [NFTNL_CTTIMEOUT_TCP_TIME_WAIT]         = "time_wait",
+       [NFTNL_CTTIMEOUT_TCP_CLOSE]             = "close",
+       [NFTNL_CTTIMEOUT_TCP_SYN_SENT2]         = "syn_sent2",
+       [NFTNL_CTTIMEOUT_TCP_RETRANS]           = "retrans",
+       [NFTNL_CTTIMEOUT_TCP_UNACK]             = "unack",
+};
+
+static const char *const udp_state_to_name[] = {
+       [NFTNL_CTTIMEOUT_UDP_UNREPLIED]         = "unreplied",
+       [NFTNL_CTTIMEOUT_UDP_REPLIED]           = "replied",
+};
+
+static uint32_t tcp_dflt_timeout[] = {
+       [NFTNL_CTTIMEOUT_TCP_SYN_SENT]          = 120,
+       [NFTNL_CTTIMEOUT_TCP_SYN_RECV]          = 60,
+       [NFTNL_CTTIMEOUT_TCP_ESTABLISHED]       = 432000,
+       [NFTNL_CTTIMEOUT_TCP_FIN_WAIT]          = 120,
+       [NFTNL_CTTIMEOUT_TCP_CLOSE_WAIT]        = 60,
+       [NFTNL_CTTIMEOUT_TCP_LAST_ACK]          = 30,
+       [NFTNL_CTTIMEOUT_TCP_TIME_WAIT]         = 120,
+       [NFTNL_CTTIMEOUT_TCP_CLOSE]             = 10,
+       [NFTNL_CTTIMEOUT_TCP_SYN_SENT2]         = 120,
+       [NFTNL_CTTIMEOUT_TCP_RETRANS]           = 300,
+       [NFTNL_CTTIMEOUT_TCP_UNACK]             = 300,
+};
+
+static uint32_t udp_dflt_timeout[] = {
+       [NFTNL_CTTIMEOUT_UDP_UNREPLIED]         = 30,
+       [NFTNL_CTTIMEOUT_UDP_REPLIED]           = 180,
+};
+
+struct timeout_protocol timeout_protocol[IPPROTO_MAX] = {
+       [IPPROTO_TCP]   = {
+               .array_size     = NFTNL_CTTIMEOUT_TCP_MAX,
+               .state_to_name  = tcp_state_to_name,
+               .dflt_timeout   = tcp_dflt_timeout,
+       },
+       [IPPROTO_UDP]   = {
+               .array_size     = NFTNL_CTTIMEOUT_UDP_MAX,
+               .state_to_name  = udp_state_to_name,
+               .dflt_timeout   = udp_dflt_timeout,
+       },
+};
+
+int timeout_str2num(uint16_t l4proto, struct timeout_state *ts)
+{
+       unsigned int i;
+
+       for (i = 0; i < timeout_protocol[l4proto].array_size; i++) {
+               if (!strcmp(timeout_protocol[l4proto].state_to_name[i], ts->timeout_str)) {
+                       ts->timeout_index = i;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
 void handle_free(struct handle *h)
 {
        xfree(h->table.name);
@@ -1261,6 +1325,7 @@ void cmd_free(struct cmd *cmd)
                case CMD_OBJ_COUNTER:
                case CMD_OBJ_QUOTA:
                case CMD_OBJ_CT_HELPER:
+               case CMD_OBJ_CT_TIMEOUT:
                case CMD_OBJ_LIMIT:
                        obj_free(cmd->object);
                        break;
@@ -1359,6 +1424,7 @@ static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl)
        case CMD_OBJ_COUNTER:
        case CMD_OBJ_QUOTA:
        case CMD_OBJ_CT_HELPER:
+       case CMD_OBJ_CT_TIMEOUT:
        case CMD_OBJ_LIMIT:
                return netlink_add_obj(ctx, cmd, flags);
        case CMD_OBJ_FLOWTABLE:
@@ -1444,6 +1510,9 @@ static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd)
                return netlink_delete_obj(ctx, cmd, NFT_OBJECT_QUOTA);
        case CMD_OBJ_CT_HELPER:
                return netlink_delete_obj(ctx, cmd, NFT_OBJECT_CT_HELPER);
+       case CMD_OBJ_CT_TIMEOUT:
+               return netlink_delete_obj(ctx, cmd,
+                                         NFT_OBJECT_CT_TIMEOUT);
        case CMD_OBJ_LIMIT:
                return netlink_delete_obj(ctx, cmd, NFT_OBJECT_LIMIT);
        case CMD_OBJ_FLOWTABLE:
@@ -1589,9 +1658,29 @@ static void print_proto_name_proto(uint8_t l4, struct output_ctx *octx)
        const struct protoent *p = getprotobynumber(l4);
 
        if (p)
-               nft_print(octx, "%s\n", p->p_name);
+               nft_print(octx, "%s", p->p_name);
        else
-               nft_print(octx, "%d\n", l4);
+               nft_print(octx, "%d", l4);
+}
+
+static void print_proto_timeout_policy(uint8_t l4, const uint32_t *timeout,
+                                      struct output_ctx *octx)
+{
+       bool comma = false;
+       unsigned int i;
+
+       nft_print(octx, "\t\tpolicy = {");
+       for (i = 0; i < timeout_protocol[l4].array_size; i++) {
+               if (timeout[i] != timeout_protocol[l4].dflt_timeout[i]) {
+                       if (comma)
+                               nft_print(octx, ", ");
+                       nft_print(octx, "%s: %u",
+                                 timeout_protocol[l4].state_to_name[i],
+                                 timeout[i]);
+                       comma = true;
+               }
+       }
+       nft_print(octx, "}");
 }
 
 static void obj_print_data(const struct obj *obj,
@@ -1638,9 +1727,24 @@ static void obj_print_data(const struct obj *obj,
                nft_print(octx, "\t\ttype \"%s\" protocol ",
                          obj->ct_helper.name);
                print_proto_name_proto(obj->ct_helper.l4proto, octx);
+               nft_print(octx, "\n");
                nft_print(octx, "\t\tl3proto %s",
                          family2str(obj->ct_helper.l3proto));
                break;
+       case NFT_OBJECT_CT_TIMEOUT:
+               nft_print(octx, "ct timeout %s {", obj->handle.obj.name);
+               if (octx->handle > 0)
+                       nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
+               nft_print(octx, "%s", opts->nl);
+               nft_print(octx, "\t\tprotocol ");
+               print_proto_name_proto(obj->ct_timeout.l4proto, octx);
+               nft_print(octx, ";%s", opts->nl);
+               nft_print(octx, "\t\tl3proto %s",
+                         family2str(obj->ct_timeout.l3proto));
+               nft_print(octx, "%s", opts->nl);
+               print_proto_timeout_policy(obj->ct_timeout.l4proto,
+                                          obj->ct_timeout.timeout, octx);
+               break;
        case NFT_OBJECT_LIMIT: {
                bool inv = obj->limit.flags & NFT_LIMIT_F_INV;
                const char *data_unit;
@@ -1687,6 +1791,7 @@ static const char * const obj_type_name_array[] = {
        [NFT_OBJECT_QUOTA]      = "quota",
        [NFT_OBJECT_CT_HELPER]  = "",
        [NFT_OBJECT_LIMIT]      = "limit",
+       [NFT_OBJECT_CT_TIMEOUT] = "",
 };
 
 const char *obj_type_name(enum stmt_types type)
@@ -1701,6 +1806,7 @@ static uint32_t obj_type_cmd_array[NFT_OBJECT_MAX + 1] = {
        [NFT_OBJECT_QUOTA]      = CMD_OBJ_QUOTA,
        [NFT_OBJECT_CT_HELPER]  = CMD_OBJ_CT_HELPER,
        [NFT_OBJECT_LIMIT]      = CMD_OBJ_LIMIT,
+       [NFT_OBJECT_CT_TIMEOUT] = CMD_OBJ_CT_TIMEOUT,
 };
 
 uint32_t obj_type_to_cmd(uint32_t type)
@@ -2054,6 +2160,8 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
        case CMD_OBJ_CT_HELPER:
        case CMD_OBJ_CT_HELPERS:
                return do_list_obj(ctx, cmd, NFT_OBJECT_CT_HELPER);
+       case CMD_OBJ_CT_TIMEOUT:
+               return do_list_obj(ctx, cmd, NFT_OBJECT_CT_TIMEOUT);
        case CMD_OBJ_LIMIT:
        case CMD_OBJ_LIMITS:
                return do_list_obj(ctx, cmd, NFT_OBJECT_LIMIT);
@@ -2271,6 +2379,9 @@ struct cmd *cmd_alloc_obj_ct(enum cmd_ops op, int type, const struct handle *h,
        case NFT_OBJECT_CT_HELPER:
                cmd_obj = CMD_OBJ_CT_HELPER;
                break;
+       case NFT_OBJECT_CT_TIMEOUT:
+               cmd_obj = CMD_OBJ_CT_TIMEOUT;
+               break;
        default:
                BUG("missing type mapping");
        }
index 45d2067c5c702d266055d9186dcd39d3e38f251d..a02ebc84e55842780b5e37630855279fcaa9643a 100644 (file)
@@ -203,6 +203,7 @@ static const char *objref_type[NFT_OBJECT_MAX + 1] = {
        [NFT_OBJECT_QUOTA]      = "quota",
        [NFT_OBJECT_CT_HELPER]  = "ct helper",
        [NFT_OBJECT_LIMIT]      = "limit",
+       [NFT_OBJECT_CT_TIMEOUT] = "ct timeout",
 };
 
 const char *objref_type_name(uint32_t type)
@@ -219,6 +220,9 @@ static void objref_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
        case NFT_OBJECT_CT_HELPER:
                nft_print(octx, "ct helper set ");
                break;
+       case NFT_OBJECT_CT_TIMEOUT:
+               nft_print(octx, "ct timeout set ");
+               break;
        default:
                nft_print(octx, "%s name ",
                          objref_type_name(stmt->objref.type));