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;
                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

Signed-off-by: Harsha Sharma <[email protected]>
---
Changes in v4:
 - updated syntax and log message
 - fix parser_bison to parse input from files for ct timeout obj
 - output similar to output
Changes in v3:
 - parse multiple timeout policies
 - return error for invalid timeout state name
 - change in log message
Changes in v2:
 - Change in syntax for addition of ct timeout objects
 - remove tokens from scanner and parser_bison for timeout states
 - list only updated timeout values
 - change in log message accordingly
 - other minor changes

 include/linux/netfilter/nf_tables.h |  14 ++++-
 include/rule.h                      |  25 ++++++++
 src/evaluate.c                      |   4 ++
 src/netlink.c                       |  21 +++++++
 src/parser_bison.y                  | 109 +++++++++++++++++++++++++++++++++-
 src/rule.c                          | 113 +++++++++++++++++++++++++++++++++++-
 src/statement.c                     |   4 ++
 7 files changed, 286 insertions(+), 4 deletions(-)

diff --git a/include/linux/netfilter/nf_tables.h 
b/include/linux/netfilter/nf_tables.h
index 63b9054..0948987 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -971,6 +971,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,
@@ -996,6 +997,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)
@@ -1402,13 +1404,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)
 
 /**
diff --git a/include/rule.h b/include/rule.h
index 909ff36..2068887 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -4,6 +4,8 @@
 #include <stdint.h>
 #include <nftables.h>
 #include <list.h>
+#include <libnftnl/cttimeout.h>
+#include <netinet/in.h>
 
 /**
  * struct handle_spec - handle ID
@@ -308,6 +310,18 @@ struct ct_helper {
        uint8_t l4proto;
 };
 
+struct ct_timeout {
+       uint16_t l3proto;
+       uint8_t l4proto;
+       uint32_t *timeout;
+       struct timeout_state {
+               uint8_t timeout_index;
+               uint32_t timeout_value;
+               uint8_t l4;
+               struct list_head timeout_list;
+       } timeout_state;
+};
+
 struct limit {
        uint64_t        rate;
        uint64_t        unit;
@@ -336,6 +350,7 @@ struct obj {
                struct quota            quota;
                struct ct_helper        ct_helper;
                struct limit            limit;
+               struct ct_timeout       ct_timeout;
        };
 };
 
@@ -462,6 +477,7 @@ enum cmd_obj {
        CMD_OBJ_LIMITS,
        CMD_OBJ_FLOWTABLE,
        CMD_OBJ_FLOWTABLES,
+       CMD_OBJ_CT_TIMEOUT,
 };
 
 struct markup {
@@ -617,4 +633,13 @@ enum udata_set_elem_flags {
        SET_ELEM_F_INTERVAL_OPEN        = 0x1,
 };
 
+struct timeout_protocol {
+       uint32_t attr_max;
+       const char *const *state_to_name;
+       uint32_t *dflt_timeout;
+};
+
+extern struct timeout_protocol timeout_protocol[IPPROTO_MAX];
+extern void timeout_str2num(const char *timeout_state, struct timeout_state 
*ts);
+
 #endif /* NFTABLES_RULE_H */
diff --git a/src/evaluate.c b/src/evaluate.c
index 1fc861f..304990e 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -3224,6 +3224,7 @@ static int cmd_evaluate_add(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:
@@ -3251,6 +3252,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:
@@ -3383,6 +3385,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:
diff --git a/src/netlink.c b/src/netlink.c
index 394af2f..967d9cc 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -28,6 +28,7 @@
 #include <libnftnl/udata.h>
 #include <libnftnl/ruleset.h>
 #include <libnftnl/common.h>
+#include <libnftnl/cttimeout.h>
 #include <linux/netfilter/nfnetlink.h>
 #include <linux/netfilter/nf_tables.h>
 #include <linux/netfilter.h>
@@ -41,6 +42,7 @@
 #include <utils.h>
 #include <erec.h>
 #include <iface.h>
+#include <rule.h>
 
 #define nft_mon_print(monh, ...) nft_print(monh->ctx->octx, __VA_ARGS__)
 
@@ -334,6 +336,20 @@ 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);
+               else
+                       nftnl_obj_set_u16(nlo, NFTNL_OBJ_CT_TIMEOUT_L3PROTO,
+                                         NFPROTO_IPV4);
+               for (unsigned int i = 0; i < 
timeout_protocol[obj->ct_timeout.l4proto].attr_max; ++i) {
+                       if (obj->ct_timeout.timeout[i])
+                               nftnl_timeout_policy_attr_set_u32(nlo, i, 
obj->ct_timeout.timeout[i]);
+               }
+               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 +1453,11 @@ 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);
+               obj->ct_timeout.timeout = nftnl_obj_get_void(nlo, 
NFTNL_OBJ_CT_TIMEOUT_DATA);
+               break;
        case NFT_OBJECT_LIMIT:
                obj->limit.rate =
                        nftnl_obj_get_u64(nlo, NFTNL_OBJ_LIMIT_RATE);
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 9a75120..a2592ab 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -550,7 +550,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
@@ -753,6 +753,10 @@ 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 */
@@ -960,6 +964,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);
@@ -1041,6 +1049,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);
@@ -1233,6 +1245,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
@@ -1464,6 +1480,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
@@ -1759,6 +1784,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
@@ -3228,12 +3262,43 @@ quota_obj               :       quota_config
                        ;
 
 ct_obj_type            :       HELPER          { $$ = NFT_OBJECT_CT_HELPER; }
+                       |       TIMEOUT         { $$ = NFT_OBJECT_CT_TIMEOUT; }
                        ;
 
 ct_l4protoname         :       TCP     { $$ = IPPROTO_TCP; }
                        |       UDP     { $$ = IPPROTO_UDP; }
                        ;
 
+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));
+                               timeout_str2num($1, ts);
+                               if (!ts) {
+                                       erec_queue(error(&@2, "invalid timeout 
state name '%s'\n", $1), state->msgs);
+                                       YYERROR;
+                               }
+                               ts->timeout_value = $3;
+                               init_list_head(&ts->timeout_list);
+                               $$ = &ts->timeout_list;
+                       }
+                       ;
+
 ct_helper_config               :       TYPE    QUOTED_STRING   PROTOCOL        
ct_l4protoname  stmt_separator
                        {
                                struct ct_helper *ct;
@@ -3255,6 +3320,42 @@ ct_helper_config         :       TYPE    QUOTED_STRING   
PROTOCOL        ct_l4protoname  stmt_separator
                        }
                        ;
 
+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
+                       {
+                               int l4proto = IPPROTO_TCP;
+                               size_t timeout_array_size;
+                               struct timeout_state *ts;
+                               struct ct_timeout *ct;
+                               uint32_t *timeout;
+
+                               ct = &$<obj>0->ct_timeout;
+                               init_list_head(&ct->timeout_state.timeout_list);
+                               timeout_array_size = sizeof(uint32_t) * 
(timeout_protocol[l4proto].attr_max);
+                               timeout = xzalloc(timeout_array_size);
+                               list_for_each_entry(ts, $4, timeout_list) {
+                                       if (ct->l4proto == ts->l4) {
+                                               timeout[ts->timeout_index] = 
ts->timeout_value;
+                                       } else {
+                                               erec_queue(error(&@2, "invalid 
timeout state name for given l4proto\n"), state->msgs);
+                                               YYERROR;
+                                       }
+                               }
+                               ct->timeout = timeout;
+                       }
+                       |       L3PROTOCOL      family_spec_explicit    
stmt_separator
+                       {
+                               $<obj>0->ct_timeout.l3proto = $2;
+                       }
+                       ;
+
 ct_obj_alloc           :
                        {
                                $$ = obj_alloc(&@$);
@@ -3730,6 +3831,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
                        ;
 
@@ -3778,6 +3880,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;
diff --git a/src/rule.c b/src/rule.c
index 7a7ac73..99b0dc4 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -29,6 +29,72 @@
 #include <linux/netfilter.h>
 #include <linux/netfilter_arp.h>
 
+const char *const tcp_state_to_name[] = {
+       [NFTA_CT_TIMEOUT_ATTR_TCP_SYN_SENT]     = "syn_sent",
+       [NFTA_CT_TIMEOUT_ATTR_TCP_SYN_RECV]     = "syn_recv",
+       [NFTA_CT_TIMEOUT_ATTR_TCP_ESTABLISHED]  = "established",
+       [NFTA_CT_TIMEOUT_ATTR_TCP_FIN_WAIT]     = "fin_wait",
+       [NFTA_CT_TIMEOUT_ATTR_TCP_CLOSE_WAIT]   = "close_wait",
+       [NFTA_CT_TIMEOUT_ATTR_TCP_LAST_ACK]     = "last_ack",
+       [NFTA_CT_TIMEOUT_ATTR_TCP_TIME_WAIT]    = "time_wait",
+       [NFTA_CT_TIMEOUT_ATTR_TCP_CLOSE]        = "close",
+       [NFTA_CT_TIMEOUT_ATTR_TCP_SYN_SENT2]    = "syn_sent2",
+       [NFTA_CT_TIMEOUT_ATTR_TCP_RETRANS]      = "retrans",
+       [NFTA_CT_TIMEOUT_ATTR_TCP_UNACK]        = "unack",
+};
+
+const char *const udp_state_to_name[] = {
+       [NFTA_CT_TIMEOUT_ATTR_UDP_UNREPLIED]    = "unreplied",
+       [NFTA_CT_TIMEOUT_ATTR_UDP_REPLIED]      = "replied",
+};
+
+uint32_t tcp_dflt_timeout[] = {
+       [NFTA_CT_TIMEOUT_ATTR_TCP_SYN_SENT]     = 120,
+       [NFTA_CT_TIMEOUT_ATTR_TCP_SYN_RECV]     = 60,
+       [NFTA_CT_TIMEOUT_ATTR_TCP_ESTABLISHED]  = 432000,
+       [NFTA_CT_TIMEOUT_ATTR_TCP_FIN_WAIT]     = 120,
+       [NFTA_CT_TIMEOUT_ATTR_TCP_CLOSE_WAIT]   = 60,
+       [NFTA_CT_TIMEOUT_ATTR_TCP_LAST_ACK]     = 30,
+       [NFTA_CT_TIMEOUT_ATTR_TCP_TIME_WAIT]    = 120,
+       [NFTA_CT_TIMEOUT_ATTR_TCP_CLOSE]        = 10,
+       [NFTA_CT_TIMEOUT_ATTR_TCP_SYN_SENT2]    = 120,
+       [NFTA_CT_TIMEOUT_ATTR_TCP_RETRANS]      = 300,
+       [NFTA_CT_TIMEOUT_ATTR_TCP_UNACK]        = 300,
+
+};
+
+uint32_t udp_dflt_timeout[] = {
+       [NFTA_CT_TIMEOUT_ATTR_UDP_UNREPLIED]    = 30,
+       [NFTA_CT_TIMEOUT_ATTR_UDP_REPLIED]      = 180,
+};
+
+struct timeout_protocol timeout_protocol[IPPROTO_MAX] = {
+       [IPPROTO_TCP]   = {
+               .attr_max       = NFTA_CT_TIMEOUT_ATTR_TCP_MAX,
+               .state_to_name  = tcp_state_to_name,
+               .dflt_timeout   = tcp_dflt_timeout,
+       },
+       [IPPROTO_UDP]   = {
+               .attr_max       = NFTA_CT_TIMEOUT_ATTR_UDP_MAX,
+               .state_to_name  = udp_state_to_name,
+               .dflt_timeout   = udp_dflt_timeout,
+       },
+};
+
+void timeout_str2num(const char *timeout_state, struct timeout_state *ts)
+{
+       unsigned int i, l4;
+
+       for (l4 = 0; l4 < IPPROTO_MAX; l4++) {
+               for (i = 0; i < timeout_protocol[l4].attr_max; i++) {
+                       if (!strcmp(timeout_protocol[l4].state_to_name[i], 
timeout_state)) {
+                               ts->timeout_index = i;
+                               ts->l4 = l4;
+                       }
+               }
+       }
+}
+
 void handle_free(struct handle *h)
 {
        xfree(h->table.name);
@@ -1093,6 +1159,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;
@@ -1187,6 +1254,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:
@@ -1272,6 +1340,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:
@@ -1417,9 +1488,28 @@ 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, uint32_t *timeout,
+                                      struct output_ctx *octx)
+{
+       unsigned int i, b = 0;
+
+       nft_print(octx, "\t\tpolicy = {");
+       for (i = 0; i < timeout_protocol[l4].attr_max; i++) {
+               if (timeout[i] != timeout_protocol[l4].dflt_timeout[i]) {
+                       if (b)
+                               nft_print(octx, ", ");
+                       nft_print(octx, "%s: %u",
+                                 timeout_protocol[l4].state_to_name[i],
+                                 timeout[i]);
+                       b = 1;
+               }
+       }
+       nft_print(octx, "}");
 }
 
 static void obj_print_data(const struct obj *obj,
@@ -1466,9 +1556,21 @@ 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);
+               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;
@@ -1515,6 +1617,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)
@@ -1529,6 +1632,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)
@@ -1877,6 +1981,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);
@@ -2094,6 +2200,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");
        }
diff --git a/src/statement.c b/src/statement.c
index 3040476..25d75e9 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -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));
-- 
2.14.1

--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to